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.
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

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

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

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

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

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
| Resource | URI | Method | Representation | Description |
| 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
| Resource | URI | Method | Role | Instance 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.
To create a new application:
- Open a fresh workspace.
Figure 6.
- Create a new Project.
Figure 7.
- Select Project Zero -> Project Zero Application as the
project type.
Figure 8.
- Name the application
ProviderIncentiveApp.
Figure 9.
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.)
- Open the ivy.xml file under the config directory.
Figure 10.
- Under the Dependencies section, click Add.
Figure 11.
- Add the following dependencies:
- zero.data
- zero.data.setup.webtools
- derby
The dependency wizard is shown below.
Figure 12.
- 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> - 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.
- Click OK to check for updates.
Figure 14.
- When you are done, close the editor.
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.
- Right-click on the ProviderIncentiveApp project.
Figure 15.
- Select General->File System.
Figure 16.
- Assuming you extracted the downloadable material into the C: directory,
select C:\ProjectZeroArticleSeries/Part2Artifacts/ProviderAppArtifacts.
Select sql, then click Finish.
Figure 17.
- Your project directory layout should look like the following figure.
Figure 18.
- Open the create.sql file. You will see the DDL for creating the
PROVIDER and INCENTIVE table, as shown in Listing 2.
Listing 2CREATE 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); - 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.
- Examine the console to ensure it has started and is running.
Figure 20.
- Open a browser and go to
http://localhost:8080/setup. Your
browser should look like the figure below.
Figure 21.
- Enter the information below, then click Create Tables.
- Database Name:
PRO_DB - Database Type: Apache Derby (Embedded)
- User Name:
APP
Figure 22.
The tool should report that it successfully executed the create.sql script.
- Database Name:
- Click Add Sample Data. The tool should report
that it executed the sample.sql script successfully.
Figure 23.
- 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.
Create the RESTful services for the Incentive application
To create the RESTful services for the Incentive application:
- Right-click on the app/resources folder and select
New -> File.
Figure 25.
- Name the file
provider.groovy.
Figure 26.
- Click Yes to add groovy support to the project.
Figure 27.
- 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
onRetrievemethod 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 3import 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() } } - 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.
- Examine the first part of the zero.config file.
Notice the new simplified configuration syntax for configuration.
Figure 29.
- 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.
- Launch your application from the launch button on the menu bar.
Figure 31.
- From your browser, go to
http://localhost:8080/resources/docs.
The figure below shows the available RESTful resources. Select Provider.
Figure 32.
- 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.
- By clicking on the format, as shown below, you can see the
sample.
Figure 34.
Figure 35.
- By clicking the URI shown below, you can launch a test driver for your
RESTful service.
Figure 36.
- Click Send.
Figure 37.
- The result will show the return of a JSON Object.
Figure 38.
In this section you create the incentive service and associate it with the provider service.
- Create another file in the /app/resources folder and call it
incentive.groovy.
Figure 39.
- Create another file in the /app/resources folder and call it
incentive.bnd.
Figure 40.
- 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.
- Add the following
onListmethod (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 theproviderIdis 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 4import 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() } } - Save the file.
Back in the browser, go back to http://localhost:8080/resources/doc and select the Incentive link.
Figure 42.
- 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.
- Click on the URI. For Provider ID, enter
austinEnergy.
Figure 44.
- You should get back the list of incentives for Austin Energy.
Figure 45.
- 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.
- Using the browser, or a tool like Firefox Poster, execute a
GETrequest http://localhost:8080/resources/incentive?location=Texas. The figure below shows the result using the Poster tool.
Figure 47.
- 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; } }
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.
- 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.
- UserName:
- Enter another user, as shown in the figure below.
- UserName:
centerPoint - Password:
passw0rd - Group:
Provider
Figure 49.
- UserName:
- You should now have two users in the File Registry.
Figure 50.
- Examine your config directory. You should see the file zero.users, as
shown below. (You might have to refresh the Eclipse Project.)
Figure 51.
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.
- Create a new file in the config directory.
Figure 52.
- Name the file
security.config.
Figure 53.
- 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
uridefines 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" } - Open the zero.config file in the config directory.
Figure 54.
- Add an
includeentry for the security.config file.
Figure 55.
Now that you have secured your RESTful resources, you can test the security using the REST documentation tool.
- Terminate (by clicking the Stop button on the console)
and Relaunch your Provider application. You can use the launch
shortcut.
Figure 56.
- In the browser, go to
http://localhost:8080/resources/docs.
Click Incentive.
Figure 57.
- You will see all the HTTP methods available, as shown below.
Figure 58.
- Click on GET for the individual record.
Figure 59.
- Enter
austinEnergyfor Provider ID and1for Incentive ID.
Figure 60.
- You should get the JSON object back.
Figure 61.
- Close the result window.
Click on the format for the POST.
Figure 62.
- Copy the JSON example.
Figure 63.
- Click on the POST URI. Paste that value into the Body, and enter
austinEnergyas the Provider ID.
Figure 64.
- You should get authenticated.
Enter
austinEnergyfor the User Name, andpassw0rdfor the Password.
Figure 65.
- You should get a result as shown below. Make note of the
Location of the posted result.
Figure 66.
- You can test the PUT and DELETE with 5, if desired. The Delete is shown in
the figure below.
Figure 67.
- Now that you are logged in as austinEnergy, reopen the POST using URI.
Try to POST data under the
centerPointURI.
Figure 68.
- You should get a 403 Forbidden error.
Figure 69.
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
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.
The first step is to create a new application.
- Create a new Project Zero Application called
ConsumerIncentiveApp.
Figure 70.
- Right-click on the ConsumerIncentive application and select
Import.
Figure 71.
- The From directory should be
C:\ProjectZeroArticleSeries\Part2Artifacts\ConsumerAppArtifacts. Select the sql folder only, as shown.
Figure 72.
- Examine the create.sql file shown in the figure below.
Figure 73.
- Notice the very simple consumer table with five fields.
Figure 74.
- Open the zero.config file.
Figure 75.
- Change the default port number to
8081. This lets you run both applications at the same time.
Figure 76.
- Open ivy.xml.
Figure 77.
- Add the following Dependencies:
- derby
- dojo
- zero.data
- zero.data.setup.webtools
- zero.web.template
- zero.resource
Figure 78.
- Update your dependencies.
Figure 79.
- Run the Consumer.
Figure 80.
- Examine the console to ensure it is running on Port 8081.
Figure 81.
- Run the database setup tool for the Consumer application. The Port should
be http://localhost:8081/setup/.
Enter
CON_DBfor the database name, Apache Derby (Embedded) for the Database Type, andAPPfor the User Name.
Figure 82.
- Click Add Sample Data.
Figure 83.
- 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.
- 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.)
- Create a new source folder by right-clicking the app directory and
selecting New -> Source folder.
Figure 85.
- Create a folder called
app/models.
Figure 86.
- Create a new file in the app/models directory called
consumer.groovy.
Figure 87.
- Click Finish to add Groovy support.
- 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 8import zero.resource.fields.* fields = [ name: [type:'CharField'], state:[type:'CharField'] , provider:[type:'CharField'] ] - 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.
- Also name it consumer.groovy. There will be an error about
duplicate consumer.groovy files; you can ignore that error.
Figure 89.
- Enter the following code,
ZRM.delegate(), as shown below. This will expose the model using HTTP.
Figure 90.
- Terminate the Consumer application (not Provider) if running and launch the consumer application.
Figure 91.
- Open a Browser (or use the Poster Firefox plug-in) to
http://localhost:8081/resources/consumer.
Figure 92.
- Open the resulting JSON text to see the list of consumers.
Figure 93.
- Open the following URL:
http://localhost:8081/resources/consumer/1.
You should get a single record.
Figure 94.
- A single record should be returned.
Figure 95.
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.
- Create a new File under the public directory.
Figure 96.
- Name it
incentiveSearch.zhtml.
Figure 97.
- 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_event Specify event that fired before any Ajax requests, for show loading, data validation, and so on. after_event Specify event that fired after any Ajax request, for hide loading, UI update, and so forth. application_event Event 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> - 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.
- Enter the code in Listing 10 (or paste it from
C:\ProjectZeroArticleSeries\Part2Artifacts\ConsumerAppArtifacts\conSnippet4.txt).
The
onInitializemethod 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 10import 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 = []; } } - 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.
- 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 11formatDate = 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); } - The incentiveSearch.css file is provided to enhance the UI. Import
C:\ProjectZeroArticleSeries\Part2Artifacts\ConsumerAppArtifacts\incentiveSearch.css
into the public directory.
Figure 100.
- Launch the UI by opening a browser and going to
http://localhost:8081/locationSearch.zhtml.
Your client should render as shown below.
Figure 101.
- Click on the Texas link to trigger an incentive search.
Figure 102.
- Similarly, click on each provider to trigger a provider search.
Figure 103.
Figure 104.
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.
| Description | Name | Size | Download method |
|---|---|---|---|
| Sample code for this article | Part2Artifacts.zip | 10KB | HTTP |
Information about download methods
Learn
-
Learn all about the Project Zero
simple environment for creating, assembling and executing applications based on
popular Web technologies.
- The Project Zero
Developer's Guide
explains the core concepts that define the architecture of a Zero application.
- Use the Project Zero
Forum for help,
feedback, alerts, discussion of ongoing development, and more.
-
"Why do non-functional requirements matter?" (developerWorks, Jan 2006) is a good
practical treatment on nonfunctional requirements.
- Learn about Groovy, an agile
dynamic language for the Java Platform.
- Read how to
install and configure PHP
to develop Project Zero applications and new PHP extensions for Project Zero.
- See additional developerWorks
articles and tutorials about Project Zero.
- See additional developerWorks
articles and tutorials about REST.
- Learn all about Dojo, the
JavaScript toolkit, and Dojo widgets.
- Read about
Representational State Transfer (REST)
in Roy Thomas Fielding's dissertation, "Architectural Styles and the Design of
Network-based Software Architectures."
- Get details on JSON and
JSON support
in Project Zero.
- Learn all about Eclipse, an open
development platform.
- Get more details about the Web browser
Firefox by Mozilla and the
Poster plug-in.
- Read about Apache Derby, an open
source relational database implemented entirely in Java.
- Find out more about caching with a
caching tutorial for Web authors
and webmasters.
- Learn about
Community-driven commercial development
(CD/CD).
- Read
"Resource-oriented vs. activity-oriented Web services"
(developerWorks, Oct 2004) for a quick look at the relationship of REST-style and SOAP-style Web services.
- RSS feed for this series.
(Find out more about RSS.)
- Browse the
technology bookstore
for books on these and other technical topics.
- In the
Architecture area on developerWorks,
get the resources you need to advance your skills in the architecture arena.
- Browse the
technology bookstore
for books on these and other technical topics.
Get products and technologies
- Project Zero Downloads
- Download
IBM product evaluation versions
and get your hands on application development tools and middleware products from
DB2®, Lotus®, Rational®, Tivoli®, and
WebSphere.
Discuss
- Participate in the discussion forum.
- Check out
developerWorks
blogs and
get involved in the
developerWorks community.
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 is a senior technical staff member and lead for the Project Zero run-time "core." You'll find Steve on the Project Zero forums.