Skip to main content

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. Select information in your profile (name, country/region, and company) is displayed to the public and will accompany any content you post. You may update your IBM account at any time.

All information submitted is secure.

  • Close [x]

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

  • Close [x]

Introducing Project Zero, Part 2: RESTful applications in an SOA

Roland Barcia, Senior Technical Staff Member, EMC
Photo: Roland Barcia
Roland Barcia is a senior technical staff member and lead Web 2.0 architect for IBM Software Services for WebSphere. He is a co-author of IBM WebSphere: Deployment and Advanced Configuration and the forthcoming Persistence within the Enterprise . You can find out more about Roland by visiting his blog.
(An IBM developerWorks Master Author, Level 2)
Steve Ims, Senior Technical Staff Member, IBM
Steve Ims
Steve Ims is a senior technical staff member and lead for the Project Zero run-time "core." You'll find Steve on the Project Zero forums.

Summary:  In this series about IBM’s Project Zero, get a hands-on guided tour of Zero’s innovations to create, assemble, and deploy Web applications. Part 1 introduced you to Project Zero by building a simple RESTful service. In this article, continue learning how Project Zero can help you build RESTful solutions. The focus is on application-centric design, modeling RESTful data, security with REST, and simplified RIA and integration.

View more content in this series

Date:  15 Jan 2008
Level:  Introductory
Also available in:   Chinese  Japanese

Activity:  14225 views
Comments:  

Modeling resources RESTfully

The Project Zero community
Project Zero explains the powerful, but radically simple, development and execution platform for modern Web applications. The active community discusses project development, provides help to developers, and wants to hear your ideas.

Part 1 "Building RESTful services for your Web application," showed you how to expose a simple table as a RESTful resource using resource handlers and the data API. You also built a sample Dojo application to invoke the services.

This article enhances the example to show how you can expose more complex data in a RESTful fashion. You'll also build a second Consumer application, which will illustrate how to model resources with Zero and build a quick rich client with a Zero event-based client programming model.

Expanding the example

The exercise in Part 1 exposed an incentive table as a RESTful service. You will expand that common example in this article. Figure 1 shows the use case diagram.


Figure 1. Use case
use case

There are two roles: a consumer and incentive. You will build two applications:

Provider Incentive application
For providers to manage the incentives they offer and to find target consumers. This article only focuses on building the RESTful services for the incentive.
Consumer Incentive application
Allows consumers to find incentives. They will be able to search for incentives either by provider or location.

Application-centric versus server-centric design

In the enterprise, platforms (such as Java™ EE-based servers) follow a server-centric mind set. Web applications are built and deployed onto an application server platform. Server platforms, such as WebSphere®, would provide all the qualities of service that the Java EE specification demanded. Examples of these services include queue-based messaging, distributed transactions, or protocol management. Often, the application server ran several applications on the same Java Virtual machine. Architects designed applications around the notion of sharing software and data resources with other applications, and services provided by the application server (even if they were not being used, in some cases).

Even if applications were deployed in isolated application servers, the servers themselves often still carried all the available services. Application servers allow for an enterprise-level integration. Characteristics of enterprise integration include distributed transactions across various systems, queue-based messaging for critical data delivery, or various other types of services. Figure 2 below highlights an example of enterprise integration. Platforms in the enterprise are designed around managing various protocols and middleware. Sometimes they talk to enterprise databases that service many applications.


Figure 2. Enterprise integration
enterprise integration

The Web 2.0 world involves a class of less-critical integration at the HTTP level. Applications usually are designed around a set of data, meant to be exposed and mixed with other data sets to create new applications that perhaps the data provider does not anticipate. Figure 3 shows an example of how integration may look.


Figure 3. Integration in Web 2.0
integration

Applications are designed around the data, and exposed RESTfully through HTTP. Rich Internet applications can then use the data by mixing and matching them to create new situations. For example, a map application can expose its points on the map RESTfully. A totally different application, such as an employment management tool, can use the map application and mix it with a set of employees to create a visual map of where an employee may be located. The two sets of data were mashed to create this opportunity. In the enterprise, such an application, although not critical, may have required more of an enterprise process to roll out.

Server-centric versus application-centric design is not a statement on scale. Application-centric scale involves having an external entity to scale the application, which can involve software such as WebSphere XD to manage processes and replicate state. Server-centric design involves a more integrated scaling solution, where the application server may have built-in mechanisms of scaling.

With an application-centric design, it is easier to start small and scale up as users increase, thus realizing the value proposition of Web 2.0-style design. Another important characteristic is that Web 2.0-style applications can invoke enterprise applications, and vice versa. Figure 4 shows a mashup that accesses an enterprise application with HTTP. You can use technologies such as the WebSphere Web 2.0 Feature Pack to expose enterprise artifacts RESTfully so they can participate in a mashup or Rich Internet Application (RIA).


Figure 4. Mashup accessing the enterprise
mashup enterprise

There are advantages and disadvantages to both approaches. Server-centric approaches are often easier to manage, while application-centric design is easier for developers to code. It often takes external software to manage application-centric solutions.

RESTful Data
As shown, exposing data RESTfully is fundamental to becoming part of a Web 2.0 mashup. Part 1 discussed how REST is central to Project Zero. REST is used to expose resources. Resources become the foundation for exposing information over the Internet so it can be easily consumed to form the next class of Web applications. Mashups can quickly be composed out of multiple resources. Project Zero is optimized around the concept of exposing resources RESTfully. In the example in this article, we want to expose incentives that are provided by energy providers. Figure 5 has the data model, which shows a provider and its relationship to incentives.


Figure 5. Provider and incentive data model
fig 5

The goal is to give interested parties the ability to find incentives. And, providers need to maintain incentives. Table 1 shows how to expose this data RESTfully. (Part 1 showed how a table like this can be beneficial to modeling your resources RESTfully.)


Table 1. REST design
ResourceURIMethodRepresentationDescription
Provider /provider/<providerId> GET JSON Object Retrieve provider record
Incentive /provider/<providerId>/incentive POST JSON Object Create a new incentive
Incentive /provider/<providerId>/incentive GET JSON Array Retrieve a list of incentives for a particular providerId
Incentive /provider/<providerId>/incentive/<incentiveId> GET JSON Object Retrieve an individual incentive
Incentive /provider/<providerId>/incentive/<incentiveId> PUT JSON Object Update a single incentive
Incentive /provider/<providerId>/incentive/<incentiveId> DELETE Delete a single incentive
Incentive /incentive?location=<state_name> GET JSON Array Return a list of incentives in a particular state for any provider

Unlike in Part 1, incentives are accessible under the provider namespace. An incentive lifecycle is tied to a provider, and therefore is managed by the provider that offers it. RESTfully, we can ensure that the incentive record is nested within the provider to meet the requirement. However, a consumer may be interested in searching for incentives across providers. The /incentive namespace can be used to provide a list of incentives that span multiple providers.

You must take into account that an incentive namespace can become quite large if there are many providers, so you need to limit it somehow. The example only allows /incentive namespace to be accessed with a location specified. We also chose to use the query parameter to filter the choice down.

Applying nonfunctional requirements to your RESTful services

Once you define the services, you can begin to attach qualities of service to them. Nonfunctional requirements come in different shapes and sizes. (For a good practical treatment on nonfunctional requirements, see the article "Why do non-functional requirements matter?") With REST we are providing HTTP-based services, so applying nonfunctional requirements may be simplified. The example will deal with security, which means you'll apply security rules to the REST resources by securing URLs. Table 2 shows an example of a REST table with a security focus.


Table 2. Secure REST resources
ResourceURIMethodRoleInstance level security required?
Provider /provider/<providerId> GET All No
Incentive /provider/<providerId>/incentive POST Provider Yes: A provider can only manage his own incentives.
Incentive /provider/<providerId>/incentive GET All No
Incentive /provider/<providerId>/incentive/<incentiveId> GET All No
Incentive /provider/<providerId>/incentive/<incentiveId> PUT Provider Yes: A provider can only manage his own incentives.
Incentive /provider/<providerId>/incentive/<incentiveId> DELETE Provider Yes: A provider can only manage his own incentives.
Incentive /incentive?location=<state_name> GET All No
Any other resource /<Anything Else> ALL No one else NA

Table 2 shows the URL resource and the role that can access it. It is important to consider that REST defines a pattern for exposing entities. Often, some data may need instance-based security; the data can only be viewed by the data owner. In this case, incentives can only be created, updated, and deleted by the owning provider. Being part of the provider role is not enough. The design table above has a column to mark if the data needs to be protected by instance.


Prerequisites for running the example

To run the example in this article you will need:

  • Eclipse 3.2 or higher. Eclipse 3.3 was used in the example.
  • Project Zero Java and Groovy Plug-in for Eclipse.

    The example was built using the M3 Release of Zero. Make sure you set your Eclipse Update Site to http://www.projectzero.org/update/zero.eclipse.M3.

    You will not need the PHP plug-in for this exercise. For information on installing the plug-in, see Project Zero Downloads.

    Note: If you did Part 1, you will have to uninstall the M1 plug-in before installing the M3 feature and plug-in. Refer to the Eclipse Help for instructions on removing plug-ins.

  • The download file with this article. Extract it to your C: drive.
  • The Firefox browser
  • A good understanding of the concepts taught in Part 1 of this series.

Other useful tools:


Building your provider RESTful application

You'll begin by building the provider application. The goal is to create the RESTful services defined earlier. The Incentive application will further build on what you learned in Part 1.

Create a new application

To create a new application:

  1. Open a fresh workspace.

    Figure 6.
    workspace

  2. Create a new Project.

    Figure 7.
    project

  3. Select Project Zero -> Project Zero Application as the project type.

    Figure 8.
    type

  4. Name the application ProviderIncentiveApp.

    Figure 9.
    app

Add dependencies

You must add the dependencies you need. As shown in Part 1, the ivy technology was used to manage dependencies. (See Part 1, or the Project Zero Developer’s Guide, for more details.)

  1. Open the ivy.xml file under the config directory.

    Figure 10.
    ivy

  2. Under the Dependencies section, click Add.

    Figure 11.
    add dep

  3. Add the following dependencies:
    • zero.data
    • zero.data.setup.webtools
    • derby

    The dependency wizard is shown below.



    Figure 12.
    fig12

  4. Save the ivy.xml file. If you examine the source, it should look like Listing 1 below.

    Listing 1
            <!-- Note: dependencies from maven require the maven2 notation. -->
        <dependencies>
            <dependency name="zero.core" org="zero" rev="1.0+"/>
            <dependency name="zero.webtools" org="zero" rev="1.0+"/>
            <dependency name="zero.data" org="zero" rev="1+"/>
            <dependency name="zero.data.setup.webtools" org="zero" rev="1+"/>
            <dependency name="derby" org="org.apache.derby" rev="10+"/>
       </dependencies>
                  

  5. Click the Update Dependencies icon. If you recall, the icon makes checks both in the remote repository and local repository for the latest version of any configured packages.

    Figure 13.
    update

  6. Click OK to check for updates.

    Figure 14.
    fig14

  7. When you are done, close the editor.

Create tables

You will use the embedded version of derby to code the application. This is a good option for development purposes. In creating the required tables for the sample, you will use the new database setup tool introduced in the M3 driver of Project Zero.

  1. Right-click on the ProviderIncentiveApp project.

    Figure 15.
    fig15

  2. Select General->File System.

    Figure 16.
    fig16

  3. Assuming you extracted the downloadable material into the C: directory, select C:\ProjectZeroArticleSeries/Part2Artifacts/ProviderAppArtifacts. Select sql, then click Finish.

    Figure 17.
    fig17

  4. Your project directory layout should look like the following figure.

    Figure 18.
    fig18

  5. Open the create.sql file. You will see the DDL for creating the PROVIDER and INCENTIVE table, as shown in Listing 2.

    Listing 2
    CREATE TABLE PROVIDER (
    		PROVIDER_ID VARCHAR (50) NOT NULL,
    		NAME VARCHAR(256) NOT NULL,
    		DESCRIPTION VARCHAR(256) NOT NULL,
    		LOCATION VARCHAR(50) NOT NULL,
    		PROVIDER_TYPE VARCHAR(128),
    		CONTACT VARCHAR(256));
    
    ALTER TABLE PROVIDER ADD CONSTRAINT PROVIDER_PK PRIMARY KEY (PROVIDER_ID);
    
    CREATE TABLE INCENTIVE (
    INCENTIVEID INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY(START WITH 1, INCREMENT BY 1),
    		NAME VARCHAR(256) NOT NULL,
    		DESCRIPTION VARCHAR(256) NOT NULL,
    		PROVIDER_ID VARCHAR (50) NOT NULL,
    		INCENTIVETYPE VARCHAR(128),
    		VALIDFROM TIMESTAMP,
    		VALIDTO TIMESTAMP,
    		WEBSITE VARCHAR(256)
    );
    	
    ALTER TABLE INCENTIVE ADD CONSTRAINT INCENTIVE_PK PRIMARY KEY (INCENTIVEID);
    ALTER TABLE INCENTIVE ADD CONSTRAINT INC_PPR_FK FOREIGN KEY (PROVIDER_ID)
    REFERENCES PROVIDER (PROVIDER_ID);
    
                  

  6. There is a file for deleting the tables and adding sample data. Next, you need to run your application. Right-click on your project and select Run As -> Project Zero Application.

    Figure 19.
    fig19

  7. Examine the console to ensure it has started and is running.

    Figure 20.
    fig20

  8. Open a browser and go to http://localhost:8080/setup. Your browser should look like the figure below.

    Figure 21.
    fig21

  9. Enter the information below, then click Create Tables.
    • Database Name: PRO_DB
    • Database Type: Apache Derby (Embedded)
    • User Name: APP


    Figure 22.
    fig22

    The tool should report that it successfully executed the create.sql script.

  10. Click Add Sample Data. The tool should report that it executed the sample.sql script successfully.

    Figure 23.
    fig23

  11. Stop the application by clicking the Stop button in the console.

    Stopping and starting applications like this is OK for development. For starting and stopping applications in test and production environments, using the management command line is preferred. See the Zero Management CLI extensions reference for more information.



    Figure 24.
    fig24

Create the RESTful services for the Incentive application

To create the RESTful services for the Incentive application:

  1. Right-click on the app/resources folder and select New -> File.

    Figure 25.
    fig25

  2. Name the file provider.groovy.

    Figure 26.
    fig26

  3. Click Yes to add groovy support to the project.

    Figure 27.
    fig27

  4. Ensure the provider.groovy file is open. Enter the code shown in Listing 3 (or you can paste it from C:\ProjectZeroArticleSeries\Part2Artifacts\ProviderAppArtifacts\codeSnippet\proSnippet.1.txt).

    The code uses the data API to execute queries, and stores the result in a JSON format. This pattern was shown in Part 1 of this series. See the data access section of the Project Zero Developer’s Guide for information on the API.

    The onRetrieve method is used to represent a handler for getting a single instance of a Provider. This pattern was also shown in Part 1 (or, see Resource handling in the Project Zero Developer’s Guide).

    Above the service there's a comment with annotated information. Project Zero lets you document your RESTful resources this way. Project Zero will use this to render RESTful documentation and a test tool for the REST service as well. You will later see this tool in action. (See RESTful documentation for more information.) In the example, we document the available HTTP return codes, describe the format, and give a sample for what the return value may look like.



    Listing 3
    import zero.data.groovy.Manager;
    /**
     *
     * @success 200 Returns the profile for the provider with the given ID.
     * @error 404 Not authorized Provider for User
     * @format application/json
     * @example
     * {
     *    "name": "Energy Provider Name", 
     *    "description": "Sample Output", 
     *    "location": "New Jersey"
     *    "provider_type": "energy"
     *    "contact": "roland@projectzero.org"
     * }
     *
     */
    def onRetrieve()
    {
    	def data = Manager.create('PRO_DB');
    	def result = null;
    	def id = request.params.providerId[0];
    	
    	//Note: Next piece of code should be in one line.
    	result = 
    	data.queryFirst
    	("select name,description,location,provider_type,contact from provider where
     	provider_id = $id");
    
    	if(result != null) {
            request.view='JSON'
            request.json.output = result
         	render()
        } else {
            request.status = HttpURLConnection.HTTP_NOT_FOUND
            request.error.message = "Provider $id not found."
            request.view = "error"
            render()
        }
    }
                  

  5. When using the database tool, a configuration for your database was generated inside a data.config file. For your application to make use of it, it must be included inside your master zero.config. Open the zero.config file. (Part 1 discussed the zero configuration model. See Configuration for more details on zero configurations.)

    Figure 28.
    fig28

  6. Examine the first part of the zero.config file. Notice the new simplified configuration syntax for configuration.

    Figure 29.
    fig29

  7. The second entry shown in the figure below shows the generated data configuration. The new syntax for complex properties uses the JSON format.

    Figure 30.
    fig30

  8. Launch your application from the launch button on the menu bar.

    Figure 31.
    fig31

  9. From your browser, go to http://localhost:8080/resources/docs. The figure below shows the available RESTful resources. Select Provider.

    Figure 32.
    fig32

  10. The resources documentation tool renders the information you commented in your groovy file. The REST tool can also be used to test your service.

    Figure 33.
    fig33

  11. By clicking on the format, as shown below, you can see the sample.

    Figure 34.
    fig34



    Figure 35.
    fig35

  12. By clicking the URI shown below, you can launch a test driver for your RESTful service.

    Figure 36.
    fig36

  13. Click Send.

    Figure 37.
    fig37

  14. The result will show the return of a JSON Object.

    Figure 38.
    fig38

Create the incentive service

In this section you create the incentive service and associate it with the provider service.

  1. Create another file in the /app/resources folder and call it incentive.groovy.

    Figure 39.
    fig39

  2. Create another file in the /app/resources folder and call it incentive.bnd.

    Figure 40.
    fig40

  3. Enter the following two lines:
    provider/incentive
    incentive

    This defines a relationship between provider and incentive. In this case, it is specifying that both /provider/<provider_id>/incentive and /incentive namespace are supported. If you only wanted to allow the nested pattern, then you would only specify provider/incentive.

    Figure 41.
    fig41

  4. Add the following onList method (or paste it from C:\ProjectZeroArticleSeries\Part2Artifacts\ProviderAppArtifacts\codeSnippet/proSnippet3.txt). Notice that we examine the headers using groovy shortcuts, first looking to see if the provider is present. If so, we execute a query for all incentives for a provider. If the providerId is not present, then the request is for /incentives namespace. There is also a check for the existence of a location parameter. If it exists, an SQL search by query is executed. Otherwise, no other call is allowed.

    Listing 4
    import zero.data.groovy.Manager;
    
    /**
     *
     * @success 200 Returns incentives by provider or location.
     * @error 404 Cannot Query all Incentives in the system.  
    /provider/providerId/incentive or /incentive?location=locationValue 
    must be used.
     * @format application/json
     * @example
     * {
     *    "incentiveId" : "Incentive Id"
     *    "name": "Incentive Name", 
     *    "description": "Sample Output", 
     *    "providerName": "Provider Name"
     *    "providerId": "Provider Id"
     *    "incentive_type": "Incentive Type"
     *    "validfrom": 1189396800000,
     *	  "validto": 1189396800000,
     *    "website": "http://www.projectzero.org"
     * }
     *
     */
    def onList()
    {
    	def data = Manager.create('PRO_DB');
    	
    	def provider = null
    	def location = null;
    	
    	
    	// List by provider flag
    	if(request.params.providerId)
    		provider = request.params.providerId[0];
    	
    	// List by location flag
    	if(request.params.location)
    			location = request.params.location[0];
    	
    
    	
    	def result = null;
    	if(provider != null)
    	{
    		result = data.queryList("select i.incentiveId,i.name,p.name 
    as providerName,i.provider_id,i.incentivetype as 
    incentive_type,i.validFrom,i.validTo,i.website,p.location from provider 
    as p,incentive as i where p.provider_id = $provider and i.provider_id = 
    p.provider_id");		
    	}
    	else if (location != null)
    	{
    		result = data.queryList("select i.incentiveId,i.name,p.name 
    as providerName,i.provider_id,i.incentivetype as 
    incentive_type,i.validFrom,i.validTo,i.website,p.location from provider 
    as p,incentive as i where p.location = $location and i.provider_id = 
    p.provider_id");
    	}
    	
    	else
    	{
    		request.status = HttpURLConnection.HTTP_NOT_FOUND
            request.error.message = "Cannot Query all Incentives in the 
    system.  /provider/providerId/incentive or 
    /incentive?location=locationValue must be used."
            request.view = "error"
            render()
            return;
    	}
    	if(result != null) {
            request.view='JSON'
            request.json.output = result
         	render()
        } 
    }
    
    

  5. Save the file.

    Back in the browser, go back to http://localhost:8080/resources/doc and select the Incentive link.



    Figure 42.
    fig42

  6. The incentive URI is under the provider namespace.

    There is a bug in M3 where Resource documentation only shows one pattern for the resource. This will be corrected in the next Milestone.



    Figure 43.
    fig43

  7. Click on the URI. For Provider ID, enter austinEnergy.

    Figure 44.
    fig44

  8. You should get back the list of incentives for Austin Energy.

    Figure 45.
    fig45

  9. In the browser, try to look for an incentive under http://localhost:8080/resources/incentive. You should receive an error like the one shown below.

    Figure 46.
    fig46

  10. Using the browser, or a tool like Firefox Poster, execute a GET request http://localhost:8080/resources/incentive?location=Texas. The figure below shows the result using the Poster tool.

    Figure 47.
    fig47

  11. Back inside the incentive.groovy file, enter the code in Listing 5 (or paste it from C:\ProjectZeroArticleSeries\Part2Artifacts\ProviderAppArtifacts\codeSnippet/proSnippet3.txt). The code contains the call for retrieving an individual incentive, and for creating, updating, and deleting an incentive. The code is very similar to the type of code in Part 1.

    Listing 5
     /**
     *
     * @success 200 Returns incentive by id.
     * @error 404 Incentive Id not found. 
     * @format application/json
     * @example
     * {
     *    "incentiveId" : "Incentive Id"
     *    "name": "Incentive Name", 
     *    "description": "Sample Output", 
     *    "providerName": "Provider Name"
     *    "incentive_type": "Incentive Type"
     *    "validfrom": 1189396800000,
     *	  "validto": 1189396800000,
     *    "website": "http://www.projectzero.org"
     * }
     *
     */
     
    def onRetrieve()
    {
    	def data = Manager.create('PRO_DB');
    	def id = request.params.incentiveId[0];
    	result = data.queryFirst("select 
    i.incentiveId,i.name,i.description,p.name as 
    providerName,i.incentivetype as 
    incentive_type,i.validFrom,i.validTo,i.website from provider as 
    p,incentive as i where i.incentiveId = $id");
    	if(result != null) {
            request.view='JSON'
            request.json.output = result
         	render()
        } else {
            request.status = HttpURLConnection.HTTP_NOT_FOUND
            request.error.message = "Incentive $id not found."
            request.view = "error"
            render()
        }
    }
     
     /**
     *
     * @success 204 Post new Incentive for the provider.
     * @error 403 Not authorized to insert this record.
     * @error 404 Cannot Post directly to /incentives, only 
    /provider/<providerId>/incentive
     * @format application/json
     * @example
     * {
     *    "name": "Incentive Name", 
     *    "description": "Sample Output", 
     *    "incentive_type": "Incentive Type",
     *    "validfrom": 1189396800000,
     *	  "validto": 1189396800000,
     *    "website": "http://www.projectzero.org"
     * }
     *
     */
     
     
     def onCreate()
     {
    	 def provider = request.params.providerId[0];
    	 if(provider != null)
    	 {
    	 	def data = Manager.create('PRO_DB');
    	 	def incentive = zero.json.JsonType.fromData(request.input[]).getJson()
    		def user = request.subject['remoteUser'];
    	 	def validFrom = new java.sql.Timestamp(incentive.validfrom);
    	 	def validTo = new java.sql.Timestamp(incentive.validto);
    	 	def key = data.insert("insert into incentive 
    (name,description,provider_id,incentivetype,validfrom,validto,website) 
    values ($incentive.name,$incentive.description,$provider,$incentive.incentivetype,
    $validFrom,$validTo,$incentive.website)",['incentiveId']);
    	 	def locationUri = getRequestedUri(false) + '/' + key
    	 	request.headers.out.Location = locationUri
    	 	request.status = HttpURLConnection.HTTP_NO_CONTENT
    	 
    	 }
    	 else
    	 {
    		 request.status = HttpURLConnection.HTTP_NOT_FOUND
    	     request.error.message = "Cannot POST directly to /incentive, 
    only /provider/<providerId>/incentive."
    	     request.view = "error"
    	     render()
    	     return;
    	 }
     }
     
     /**
     *
     * @success 204 Delete Incentive for the provider.
     * @error 403 Not authorized to insert this record.
     * @error 404 Cannot delete directly to /incentives, 
    only /provider/<providerId>/incentive
     * 
     */
     
     def onDelete()
     {
    	 def provider = request.params.providerId[0];
    	 if(provider != null)
    	 {
    	 	def data = Manager.create('PRO_DB');
    	 	def id = request.params.incentiveId[0];
    		def user = request.subject['remoteUser'];
    	 	data.update("delete from incentive where incentiveId = $id");
    	 	request.status = HttpURLConnection.HTTP_NO_CONTENT
    	 }
    	 else
    	 {
    		 request.status = HttpURLConnection.HTTP_NOT_FOUND
    	     request.error.message = "Cannot DELETE directly to 
    /incentive, only /provider/<providerId>/incentive."
    	     request.view = "error"
    	     render()
    	     return;
    	 }
     }
     
     /**
     *
     * @success 204 Incentive updated for provider.
     * @error 403 Not authorized to update this record.
     * @error 404 Cannot Put directly to /incentive/<incentiveId>, 
    only /provider/<providerId>/incentive/<incentiveId>
     * @format application/json
     * @example
     * @example
     * {
     *    "name": "Incentive Name", 
     *    "description": "Sample Output", 
     *    "incentive_type": "Incentive Type",
     *    "validfrom": 1189396800000,
     *	  "validto": 1189396800000,
     *    "website": "http://www.projectzero.org"
     * 
     *  }
     *
     */
     
     
     def onUpdate()
     {
    	 def provider = request.params.providerId[0];
    	 if(provider != null)
    	 {
    	 	def data = Manager.create('PRO_DB');
    	 	def incentive = zero.json.JsonType.fromData(request.input[]).getJson()
    		def user = request.subject['remoteUser'];
    	 	def id = request.params.incentiveId[0];
    	 	def validFrom = new java.sql.Timestamp(incentive.validfrom);
    	 	def validTo = new java.sql.Timestamp(incentive.validto);
    	 	data.update("update incentive set 
    name=$incentive.name,description=$incentive.description,incentivetype = 
    $incentive.incentiveType,validfrom=$validFrom,validto=$validTo,website=
    $incentive.website where incentiveId = $id ");
    	 	request.status = HttpURLConnection.HTTP_NO_CONTENT
    	 }
    	 else
    	 {
    		 request.status = HttpURLConnection.HTTP_NOT_FOUND
    	     request.error.message = "Cannot Put directly to 
    /incentive/<incentiveId>, only 
    /provider/<providerId>/incentive/<incentiveId>."
    	     request.view = "error"
    	     render()
    	     return;
    	 }
    }
                  

Secure the operations

As part of the requirements, you need to secure the create, update, and delete operations on an incentive to the provider that owns them. You will begin by using the built-in Zero File Registry. The User File Registry is a good option for development. Project Zero also supports LDAP-based registries. It is often a best practice to use LDAP in production. With Project Zero, it's easy to override the default registry at deployment time.

  1. In your browser, enter http://localhost:8080/zero/webtools/user as shown below.

    Enter the following:

    • UserName: austinEnergy
    • Password: passw0rd
    • Groups: Provider

    Click add.



    Figure 48.
    fig48

  2. Enter another user, as shown in the figure below.
    • UserName: centerPoint
    • Password: passw0rd
    • Group: Provider
    Click add.

    Figure 49.
    fig49

  3. You should now have two users in the File Registry.

    Figure 50.
    fig50

  4. Examine your config directory. You should see the file zero.users, as shown below. (You might have to refresh the Eclipse Project.)

    Figure 51.
    fig51

Add authorization rules

At this point you need to add your authorizations rules. You need to secure the RESTful URI. You will secure unsupported URIs with a non-existent group. You will also secure the RESTful data to the owner, which shows Project Zero’s support for instance based security.

  1. Create a new file in the config directory.

    Figure 52.
    fig52

  2. Name the file security.config.

    Figure 53.
    fig53

  3. Enter the text in Listing 6 (or paste it from C:\ProjectZeroArticleSeries\Part2Artifacts\ProviderAppArtifacts\codeSnippet\proSnippet4.txt).

    The rule.config is included, and a set of parameters is entered. The uri defines the REST url. The vertical bar will also take into account anything after the URI pattern. The first rule blocks anyone from executing the update HTTP methods on the /incentive namespace. The second rule only allows a provider who is logged in to execute the update methods. For example, if I logged in as austinEnergy, then I can only issue POST, DELETE, or PUT on the austinEnergy namespace. More details are in the Security considerations section of the Project Zero Developer’s Guide.



    Listing 6
    @include "${/config/dependencies/zero.core}/config/security/rule.config"
    {
    	"uri":"/resources/incentive|",
    	"condition":"urimatches",
    	"authType":"Basic",
    	"groups":"[NO_ONE]",
    	"methods":"DELETE|POST|PUT"
    }
    
    @include "${/config/dependencies/zero.core}/config/security/rule.config"
    {
    	"uri":"/resources/provider/{remoteUser}|",
    	"condition":"urimatches",
    	"authType":"Basic",
    	"methods":"DELETE|POST|PUT"
    }
                  

  4. Open the zero.config file in the config directory.

    Figure 54.
    fig54

  5. Add an include entry for the security.config file.

    Figure 55.
    fig55

Test the security

Now that you have secured your RESTful resources, you can test the security using the REST documentation tool.

  1. Terminate (by clicking the Stop button on the console) and Relaunch your Provider application. You can use the launch shortcut.

    Figure 56.
    fig56

  2. In the browser, go to http://localhost:8080/resources/docs. Click Incentive.

    Figure 57.
    fig57

  3. You will see all the HTTP methods available, as shown below.

    Figure 58.
    fig58

  4. Click on GET for the individual record.

    Figure 59.
    fig59

  5. Enter austinEnergy for Provider ID and 1 for Incentive ID.

    Figure 60.
    fig60

  6. You should get the JSON object back.

    Figure 61.
    fig61

  7. Close the result window.

    Click on the format for the POST.



    Figure 62.
    fig62

  8. Copy the JSON example.

    Figure 63.
    fig63

  9. Click on the POST URI. Paste that value into the Body, and enter austinEnergy as the Provider ID.

    Figure 64.
    fig64

  10. You should get authenticated.

    Enter austinEnergy for the User Name, and passw0rd for the Password.



    Figure 65.
    fig65

  11. You should get a result as shown below. Make note of the Location of the posted result.

    Figure 66.
    fig66

  12. You can test the PUT and DELETE with 5, if desired. The Delete is shown in the figure below.

    Figure 67.
    fig67

  13. Now that you are logged in as austinEnergy, reopen the POST using URI.

    Try to POST data under the centerPoint URI.



    Figure 68.
    fig68

  14. You should get a 403 Forbidden error.

    Figure 69.
    fig69

For this exercise you've completed the Incentive portion of the application. The application should remain running as you build the Consumer example.


Building your consumer application

A subsequent article will go into detail on the Consumer application and RESTful data.

Thus far you've seen how you can build resource handlers using the Data API. In M2, Project Zero introduces another way of modeling resources. Some developers prefer coding SQL, while others prefer using a persistence technology. The Zero Resource Model is focused on mapping data to REST (not necessarily objects).

In addition to the Zero Resource, you'll use the Zero Web Template technology to build a quick client that will use the Zero Connection API to call a remote REST resource.

Create a new application

The first step is to create a new application.

  1. Create a new Project Zero Application called ConsumerIncentiveApp.

    Figure 70.
    fig70

  2. Right-click on the ConsumerIncentive application and select Import.

    Figure 71.
    fig71

  3. The From directory should be C:\ProjectZeroArticleSeries\Part2Artifacts\ConsumerAppArtifacts. Select the sql folder only, as shown.

    Figure 72.
    fig72

  4. Examine the create.sql file shown in the figure below.

    Figure 73.
    fig73

  5. Notice the very simple consumer table with five fields.

    Figure 74.
    fig74

  6. Open the zero.config file.

    Figure 75.
    fig75

  7. Change the default port number to 8081. This lets you run both applications at the same time.

    Figure 76.
    fig76

  8. Open ivy.xml.

    Figure 77.
    fig77

  9. Add the following Dependencies:
    • derby
    • dojo
    • zero.data
    • zero.data.setup.webtools
    • zero.web.template
    • zero.resource


    Figure 78.
    fig78

  10. Update your dependencies.

    Figure 79.
    fig79

  11. Run the Consumer.

    Figure 80.
    fig80

  12. Examine the console to ensure it is running on Port 8081.

    Figure 81.
    fig81

  13. Run the database setup tool for the Consumer application. The Port should be http://localhost:8081/setup/.

    Enter CON_DB for the database name, Apache Derby (Embedded) for the Database Type, and APP for the User Name.



    Figure 82.
    fig82

  14. Click Add Sample Data.

    Figure 83.
    fig83

  15. Open the zero.config file for the application again and enter the line /config/reosurce/dbKey="COB_DB", as shown in Figure 84 below.

    Figure 84.
    fig84

  16. Terminate the Consumer application (not the Incentive).

Create a simple resource model

In this section you create a simple resource model based on the Consumer table. M2 is a preview release of this technology, so you'll use a very simple pattern to illustrate its usage. (In subsequent parts of this series we'll use a more elaborate example.)

  1. Create a new source folder by right-clicking the app directory and selecting New -> Source folder.

    Figure 85.
    fig85

  2. Create a folder called app/models.

    Figure 86.
    fig86

  3. Create a new file in the app/models directory called consumer.groovy.

    Figure 87.
    fig87

  4. Click Finish to add Groovy support.
  5. Enter the code from Listing 8 into consumer.groovy (or paste it from C:\ProjectZeroArticleSeries\Part2Artifacts\ConsumerAppArtifacts\conSnippet2.txt). This creates a simple model.

    Listing 8
    import zero.resource.fields.*
    fields = [ 
            	name: [type:'CharField'],
            	state:[type:'CharField'] ,
            	provider:[type:'CharField'] 
    ]
                  

  6. You can now access the data locally using the Zero API, or using HTTP.

    To expose the model with HTTP, create another file in the /app/resources.



    Figure 88.
    fig88

  7. Also name it consumer.groovy. There will be an error about duplicate consumer.groovy files; you can ignore that error.

    Figure 89.
    fig89

  8. Enter the following code, ZRM.delegate(), as shown below. This will expose the model using HTTP.

    Figure 90.
    fig90

  9. Terminate the Consumer application (not Provider) if running and launch the consumer application.

    Figure 91.
    fig91

  10. Open a Browser (or use the Poster Firefox plug-in) to http://localhost:8081/resources/consumer.

    Figure 92.
    fig92

  11. Open the resulting JSON text to see the list of consumers.

    Figure 93.
    fig93

  12. Open the following URL: http://localhost:8081/resources/consumer/1. You should get a single record.

    Figure 94.
    fig94

  13. A single record should be returned.

    Figure 95.
    fig95

Create a simple client

In this section you create a very simple Rich Internet client using the Zero Web templates. Part 1 showed how a Dojo client can invoke a RESTful service. Zero also supports other patterns, such as fetching HTML fragments, which we'll illustrate. (Later in the series we'll use Dojo together with Zero Templating.) For more details on the Zero client, see Writing rich Web applications.

The Zero client programming model provides the ability for Zero server events to respond to JavaScript events. They can also share data using the client zone of the global context.

  1. Create a new File under the public directory.

    Figure 96.
    fig96

  2. Name it incentiveSearch.zhtml.

    Figure 97.
    fig97

  3. Enter the HTML code from Listing 9 (or paste it from C:\ProjectZeroArticleSeries\Part2Artifacts\ConsumerAppArtifacts\conSnippet3.txt).

    The HTML template is using two Dojo Grid components: one to hold the consumer data, and the other to hold incentive results. The use of the groovy language lets you create application events either on the server or the client in response to UI events. Project Zero uses the following definition syntax to declare an application event with the Ajax lifecycle:

    on("<UI_event>").fire("<application_event>").before
    ("<before_event>").after("<after_event>")



    before_eventSpecify event that fired before any Ajax requests, for show loading, data validation, and so on.
    after_eventSpecify event that fired after any Ajax request, for hide loading, UI update, and so forth.
    application_eventEvent that fired, for example update/fetch data, update UI or any custom biz/UI logics.


    At run time, the event processing sequence is: before_event --> application_event --> after_event.

    application_event should be handled at the server side, which makes sense with the Ajax lifecycle. before_event and after_event are normally handled in the client side. For more information, see Writing Rich Web Applications.



    Listing 9
    <html>
    <head>
    <title>Incentive Search</title>
    	
    <style type="text/css">
    	@import "incentiveSearch.css";
    </style>
    
    <script type="text/javascript" src="/dojo/dojo.js" djConfig="isDebug: 
    true, parseOnLoad: true"></script>
    <script type="text/javascript">
    dojo.require("dojox.grid.Grid");
    dojo.require("dojox.grid._data.model");
    dojo.require("dojo.data.ItemFileWriteStore");
    dojo.require("dijit.form.ValidationTextBox");
    dojo.require("dijit.form.DateTextBox");
    dojo.require("dojox.grid._data.editors");
    dojo.require("dojox.grid.editors");
    dojo.require("dojox.grid._data.dijitEditors");
    dojo.require("dojo.parser");
    </script>
    
    <%
    	on(".search:submit").fire("incentiveSearch").after("incentiveRender")
    %>
    
    </head>
    	<body class="tundra">
    		<h1> Consumer Incentive Search </h1>
    		<br>
    		<br>
    			<form class=.search>
    			<div id="consumerGrid" dojoType="dojox.Grid" structure=
                          "consumerLayout">
    			</div>
    			</form>
    		<br>
    		<br>
    		<hr>
    		<div id="incentiveGrid" dojoType="dojox.Grid" structure="incentiveLayout">
    		</div>					
    	</body>
    	
    </html>
                  

  4. Create another file in the public directory called incentiveSearch.groovy. This file will contain event handlers that respond to client events for the incentiveSearch.groovy file.

    Figure 98.
    fig98

  5. Enter the code in Listing 10 (or paste it from C:\ProjectZeroArticleSeries\Part2Artifacts\ConsumerAppArtifacts\conSnippet4.txt).

    The onInitialize method gets called when the template is loaded. We use the local API for the resource model to get the customer and we put it in the client zone. See Programmatic Model API for more about the Resource API.

    There's one event handler for each of the events. It looks for the existence of a request parameter to determine to do a provider search or location search. (For more information on the connection, see Using the Connection API.) We also use the JSON API to convert the payload into the client zone. The client zone is accessible to both the JavaScript client and the server groovy code.



    Listing 10
    import zero.core.connection.*;
    import zero.resource.*;
    
    def onInitialize()
    {
    	def collection = TypeCollection.retrieve('consumer');
    	def result = collection.list();
    	def items = [];
    	for(resultItem in result)
    	{
    		def item =
     [name:resultItem.name ,id:resultItem.id ,state:resultItem.state,
    provider:resultItem.provider ]
    		items.add(item);
    	}
    	client.data.consumer = items;
    	client.data.incentives = [];
    }
    
    
    def onIncentiveSearch()
    {
    	if(event.input.location[])
    	{
    		Connection.Response response = 
    Connection.doGET("http://localhost:8080/resources/incentive?location=
    ${event.input.location[]}");
    		client.data.incentives = 
    zero.json.java.JSONArray.parse(response.getResponseBodyAsString());
    	}
    	else if(event.input.provider[])
    	{
    		Connection.Response response = 
    Connection.doGET("http://localhost:8080/resources/provider/
    ${event.input.provider[]}/incentive");
    		client.data.incentives = 
    zero.json.java.JSONArray.parse(response.getResponseBodyAsString());
    	}
    	else
    	{
    		client.data.incentives = [];
    	}
    	
    }
                  

  6. Create another file in the public directory and name it incentiveSearch.js. This will have the client events. These callbacks are used to render the results from the server call.

    Figure 99.
    fig98a

  7. Enter the code in Listing 11 below. The Dojo FileStore and Grid API are used to load the data locally. Refer to the Grid documentation for details on using the Dojo Grid.

    Listing 11
    formatDate = function(date) {
    	
    	var d = new Date();
    	d.setTime(date);
    	return d;
    }
    
    formatLocationLink = function(location) {
    	var locationLink = '<input type="submit" name="location" 
    class="button linktd" value="';
    	locationLink += location;
    	locationLink += '"/>';
    	return locationLink;
    }
    
    formatProviderLink = function(provider) {
    	
    	var providerLink = "<input type='submit' name='provider' 
    class='button linktd' value='";
    	providerLink += provider;
    	providerLink += "'/>";
    	return providerLink;
    }
    
    var consumerLayout = [{
            cells: [[
                    {name: 'Id', field: "id", width:"10%"},
                    {name: 'Name', field: "name", width:"30%"},
                    {name: 'State', field: "state", 
    width:"35%",formatter:formatLocationLink},
                    {name: 'Provider', field: "provider", 
    width:"25%",formatter:formatProviderLink}
              ]
            ]
    }];
    
    var incentiveLayout = [{
            cells: [[
                    {name: 'Provider', field: "providername",width:"20%"},
                    {name: 'Type', field: "incentive_type",width:"20%"},
                    {name: 'Provider Location', field: "location",width:"20%"},
                    {name: 'Valid From', field: "validfrom",width:"20%",formatter:formatDate},
                    {name: 'Valid To', field: "validto",width:"20%",formatter:formatDate}
              ]
            ]
    }];
    
    var consumerMeta =
    {
    		id:'id',
    		items:[],
    		label:'consumer'
    }
    
    var incentiveMeta =
    {
    		id:'incentiveid',
    		items:[],
    		label:'incentive'
    }
    
    function onLoad(){
    	zfire("renderConsumers");
    }
    
    function onRenderConsumers()
    {
    	consumerMeta.items = dojo.clone(zget("/client/data/consumer"));
    	console.debug(consumerMeta.items);
    	var consumerStore = new dojo.data.ItemFileWriteStore({data: consumerMeta});
    	var consumerModel = new dojox.grid.data.DojoData();
    	consumerModel.store = consumerStore;
    	consumerModel.query = {id:'*'};
    	var consumerGrid = dijit.byId('consumerGrid');
    	consumerGrid.setModel(consumerModel);
    }
    
    function onIncentiveRender()
    {
    	incentiveMeta.items = dojo.clone(zget("/client/data/incentives"));
    	console.debug(incentiveMeta.items);
    	var incentiveStore = new dojo.data.ItemFileWriteStore({data: incentiveMeta});
    	var incentiveModel = new dojox.grid.data.DojoData();
    	incentiveModel.store = incentiveStore;
    	incentiveModel.query = {incentiveid:'*'};
    	var incentiveGrid = dijit.byId('incentiveGrid');
    	incentiveGrid.setModel(incentiveModel);
    }
    

  8. The incentiveSearch.css file is provided to enhance the UI. Import C:\ProjectZeroArticleSeries\Part2Artifacts\ConsumerAppArtifacts\incentiveSearch.css into the public directory.

    Figure 100.
    fig100

  9. Launch the UI by opening a browser and going to http://localhost:8081/locationSearch.zhtml. Your client should render as shown below.

    Figure 101.
    fig101

  10. Click on the Texas link to trigger an incentive search.

    Figure 102.
    fig102

  11. Similarly, click on each provider to trigger a provider search.

    Figure 103.
    fig103



    Figure 104.
    fig104


Conclusion

This article introduces several new concepts in Zero. We furthered the discussion of application-centric design, focusing on exposing more complex data and securing URL-based resources.

You also learned about the new resource modeling and Zero client APIs.

The next installment will further discuss resource modeling, and will focus on using Dojo 1.0 and the Zero client programming model to assemble more solutions.



Download

DescriptionNameSizeDownload method
Sample code for this articlePart2Artifacts.zip10KBHTTP

Information about download methods


Resources

Learn

Get products and technologies

Discuss

About the authors

Photo: Roland Barcia developerWorks Master author level 2

Roland Barcia is a senior technical staff member and lead Web 2.0 architect for IBM Software Services for WebSphere. He is a co-author of IBM WebSphere: Deployment and Advanced Configuration and the forthcoming Persistence within the Enterprise . You can find out more about Roland by visiting his blog.

Steve Ims

Steve Ims is a senior technical staff member and lead for the Project Zero run-time "core." You'll find Steve on the Project Zero forums.

Report abuse help

Report abuse

Thank you. This entry has been flagged for moderator attention.


Report abuse help

Report abuse

Report abuse submission failed. Please try again later.


developerWorks: Sign in


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Select information in your profile (name, country/region, and company) is displayed to the public and will accompany any content you post. You may update your IBM account at any time.

Choose your display name

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


Rate this article

Comments

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Sample IT projects, Web development, WebSphere
ArticleID=281658
ArticleTitle=Introducing Project Zero, Part 2: RESTful applications in an SOA
publish-date=01152008