Using RESTful WebObjects
Using requests with the WebObjects REST Handler for HTTP requests.
The previous post described building a new request handler allowing REST behaviour in WebObjects applications. This post will explain how the handler responds to specific requests. The current test application was built by simply, creating a new WebObjects application in Eclipse, adding a business objects framework and adding the REST handler. The test framework is the same framework that provides the foundation for www.watershed.co.uk so the examples will deal with films, screenings, seasons & festivals. The examples below use two Entities from the model, Exhibits and Programmes. Essentially, a Programme represents a season or festival of films. An Exhibit, generally, represents a film. There is a to-many relationship from Programme to Exhibits; i.e. A Programme contains a collection of Exhibits. This can be seen in use at the Watershed site. The reverse relationship is also modelled, so Exhibits have a to-one relationship to a Programme.

Rewriting
REST URLs describe the location of the resource in a hierarchy, so arguments are sent mimicking a directory structure: www.watershed.co.uk/cgi-bin/WebObjects/WatershedAPI.woa/REST/exhibits/1527 The request URLs are often written into executable code or synthesised from String components so keeping the URLs simple and short aids their use. URLs generated by the data modules are 'pretty' and incoming requests are rewritten in Apache so the previous URL becomes: api.watershed.co.uk/exhibits/1527.
Tools
Although not particular to our WebObjects REST Handler, using special tools can make testing much more efficient and pleasant. Whilst a web browser can be used to generate GET and POST requests they struggle with the other HTTP methods. Using CURL, from Terminal, can be useful, optionally adding headers to, or, setting the method or body of, the HTTP request. Typing man curl into Terminal will display a page explaining the options. RESTClient is a Java Swing application to test RESTful web services. It can be used to test variety of HTTP communications and has GUI options for most of the HTTP 1.1 specification, including setting the method, authentication values and the message body as well as viewing the response headers. RESTClient is a useful tool for any type of web application development.
REST URLs
URL Structure
Just as a WebObjects' component action URL has a structure, then URLs accepted by the REST Handler have a structure. Each action has a target and a method. The method is part of the HTTP specification and is explained later. The URL structure is essentially:
host/entity_name/primary_key/relationship/...
The entity name and the primary key together define the target. The entity names in the URLs follow a casual, but established, convention and are lower-case, underscore-separated and plural. So the URL /exhibits/1527 will map to an Exhibit Enterprise Object with a primary key value of 1527. Using the model described earlier, we can find the Programme with the primary key of 57 by using the URL: api.watershed.co.uk/programmes/57/ . As the entity name is underscore separated then to set the target for the MediaSet entity with a primary key of 1234, we can use the URL: api.watershed.co.uk/media_sets/1234.
The REST Handler uses the EOModels in the application, or linked frameworks, to map the URL to the correct Entity names and thence to a table in the database. The Handler then retreives the correct Enterprise Objects and delivers these targets to the appropriate data module for output in the response.
Relationships & Key paths
Objects in isolation are often of little practical use. Modelling real world environments requires a hierarchy of objects and containers. Creating a new season isn't useful unless we can insert films into it.
Once the target object has been established key paths defined in the model are then available. The relationship between Exhibit and Programme, illustrated in the diagram above, can now be used. To find the Programme of an Exhibit we use the URL: api.watershed.co.uk/exhibits/1527/programme/. We can follow the key path, defined in the EOModel, even further by using the to-many relationship back to an array of Exhibits: api.watershed.co.uk/exhibits/1527/programme/exhibits/. In a similar fashion to entity names, key paths in the model are renamed to maintain the pretty URLs. Although not illustrated, Exhibit has a to-many relationship to an array of MediaSets, named mediaSets. Using this relationship results in a URL similar to: api.watershed.co.uk/exhibits/1527/media_sets/.
Now that we have URLs that point to relationships, we can use these to add objects into and remove objects from collections.
Model Delegates
Comparing the illustration of the Entities, above, and the actual XML generated by a GET request displays an inconsistency. Although, attributes with null values are not currently listed, the attributes notes and history are never included. The REST Handler and the data modules use the model delegate to provide some information about how to render the data. To hide the notes and history attributes:
public NSArray excludeAttributesForEntity(String entityName) {// Always remove notes and history regardless of// the entityreturn new NSArray(new String[] {"notes", "history"});}
The use of the model delegate is optional and if the delegate is not set, the entity and attribute names will be taken from the model and adjusted.
Varying Data Representations
Outlined in the last post was a technique whereby changing the Accept HTTP header changed the response data format or allowed a client to send updated data to the web application in a variety of formats. Sending a request using cURL in Terminal shows the effect of the accept header.
curl -H 'Accept: text/xml' http://api.watershed.co.uk/exhibits/1530/
produces an XML response, whilst:
curl -H 'Accept: text/plist' http://api.watershed.co.uk/exhibits/1530/
produces a response in Apple's Property List format.
Actions
As the URL specifies the resource then the HTTP method specifies the action to perform on it. The REST Handler responds to the HTTP actions defined in the 1.1 specification. Four of the HTTP methods are mapped to the CRUD actions. The currently deployed application will not receive requests other than GET, HEAD & POST. The FreeBSD web server adapter, for watershed.co.uk applications, was compiled from the Apple source code and does not support the PUT & DELETE methods. The sites will be moving to a new virtual server in the next couple of weeks and we will compile a new adapter from the WebObjects 5.4 adapter source.
To read an Exhibit from the data store:
curl -X GET http://api.watershed.co.uk/exhibits/1530/
To delete an Exhibit from the data store:
curl -X DELETE http://api.watershed.co.uk/exhibits/1530/
To update the title attribute of an Exhibit in the data store:
curl -X PUT -d "
To create a new Exhibit in the data store:
curl -X POST -d "
By following the key paths in the URLs we can insert and remove objects from a relationship. This part is still very much a work in progress and is still not 'quite right'.
To insert a new Exhibit in the data store and add it to the array of exhibits in a programme, specify the array as the target resource:
curl -X POST -d "
http://api.watershed.co.uk/programmes/57/exhibits/
To remove an Exhibit from the to many relationship of a Programme but leave it in the data store:
curl -X DELETE http://api.watershed.co.uk/programmes/57/exhibits/1530/
Notes and Incomplete
As mentioned previously, the adapter installed on the web server does not handle PUT & DELETE requests but we will compile a new version that supports these methods shortly. Manipulating relationship collections feels 'clunky' at the moment and this needs some work. The data modules, both the XML and the Property List, need significant work. A JSON data module would be a welcome addition and would compliment the XML and Property List moduels.
One of the original reasons for REST Handler was to provide an efficient and quick data flow to a new persistence framework for Cocoa applications. We already have a framework that consumes large chunks of data from a database, we need to deliver data in a constant trickle just as it's needed. Large collections, and objects in a collection, should be available in a more terse output to reduce response times and bandwidth.