Restlets
You've defined a RESTful API that maps quite nicely to CRUDing races and runners. And you've defined the format of the communication: XML documents. In this section, you'll start to put this all together using an innovative framework that is modeled after servlets.
Restlet applications are akin to servlet applications in that they reside in a container, but in practice they are quite different in two major ways. First, Restlets use no direct notion of HTTP or its stateful manifestations such as cookies or sessions, per se. Second, the Restlet framework is extremely lightweight. As you'll see, a fully functional RESTful application can be built with a handful of classes that extend from a few core Restlet base classes. Configuration and deployment leverage existing container models, so you simply update the customary web.xml file and deploy a standard Web archive (WAR) file.
For the most part, the bulk of a RESTful application built with the Restlet framework
requires the use of two base classes: Application and Resource. Logically speaking, an Application instance maps URIs to Resource instances. Resource instances do the work of handling the basic CRUD commands, which are, of course, mapped to GET, POST, PUT, and DELETE.
You create a starting point with the Restlet framework by extending from the framework's Application class. In this class, you define Resources that respond to URIs. This definition process is done with the framework's Router class. For example, if you have a URI such as order/order_id, you need to specify which object can handle these requests. This object is an instance of the framework's Resource type. You link objects with URIs by attaching them to a Router instance, as in Listing 5:
Listing 5. Creating
Router instances and mapping URIs
Router router = new Router(this.getContext());
router.attach("order/{order_id}", Order.class);
|
So in this example, the URI order/order_id is logically mapped to an Order class (which, in turn, extends Resource).
Acme Racing has the four logical RESTful URIs that you've already defined — four patterns that work with various aspects of races and runners:
- /race
- /race/race_id
- /race/race_id/runner
- /race/race_id/runner/runner_id
The behavior of each URI (such as if it works with POST, DELETE, GET, and so on) isn't important at this point. The behavior of each Resource is the job of a Resource instance; however, the Application instance is used to map these URIs to (yet-to-be-defined) Resources via a Router instance, as shown in Listing 6:
Listing 6. Mapping Acme Racing's URIs to
Resources
public class RaceApplication extends Application{
public RaceApplication(Context context) {
super(context);
}
public Restlet createRoot() {
Router router = new Router(this.getContext());
router.attach("/race", RacesResource.class);
router.attach("/race/{race_id}", RaceResource.class);
router.attach("/race/{race_id}/runner", RaceRunnersResource.class);
router.attach("/race/{race_id}/runner/{runner_id}", RaceRunnerResource.class);
return router;
}
}
|
The base class, Application, is an abstract class. Extending classes must implement the createRoot() method. In this method, you can create a Router instance and attach Resources to URIs as I've done in Listing 6.
As you can see, there are four different Resource classes. I've
named them to match the desired high-level behavior of the URI. For instance, the /race URI is intended to work with multiple race instances; consequently the Resource type is named RacesResource. Once an id is included in the URI (/race/race_id), the implication is that a single race is being manipulated; accordingly, the Resource type is apply named RaceResource.
Now that you've defined the Application instance to handle four different URI patterns, you must implement the four Resources.
Resource types in the Restlet framework are known as Restlets. They are the heart of any RESTful application developed with the Restlet framework. Unlike the Application type, the base Resource class is not abstract. It's more like a template with default behavior that you can override as needed.
At a high level, Resource has four methods that require
overriding. Not coincidentally, they map to the basic HTTP commands that are the touchstone of REST —
GET, POST, PUT, and DELETE. Because the Resource class is nonabstract, the framework requires a paired method to be implemented for desired behavior to be invoked. For instance, if you want a particular resource to respond to DELETE requests, you would first implement the delete() method. Second, you also must implement the allowDelete() method and have this method return true (it defaults to false). By default, the corresponding PUT, POST, and DELETE allow methods return false, and the allowGet() method returns true. This means for read-only Resources, you need to override only one method (instead of two in the other three cases). You can alternatively call the setModifcation(true) in a Resource class and thus not have to override individual HTTP verb allow methods.
For instance, the RacesResource is intended to respond to GET requests with an XML document that describes the races in the system. Users can also create new races via this Resource type. Therefore, the RacesResource class overrides at least three methods from the Resource base class:
-
getRepresentation() -
allowPost() -
post()
Remember, Resources instances, by default, are read-only. Hence
the allowGet() method doesn't need to be overridden.




