REST for Java developers, Part 2: Restlet for the weary

Easy interfaces for building and consuming RESTful Web services in Java

The open source Restlet API reduces the workload involved in building and consuming RESTful APIs in Java. In this second article in the REST for Java developers series, Brian Sletten introduces you to Restlet and walks through an example application in deploying its interfaces into the servlet containers you use today, while also preparing for the systems of the future. Brian also briefly introduces JSR 311: JAX-RS, Sun's effort to integrate RESTful APIs with the Java EE stack.

Java developers have long been interested in the REST architectural style, but few have yet traveled the distance between the familiar world of objects and the RESTful world of resources. While we may like the fact that RESTful services can be produced or consumed by other languages, we hate having to convert data to and from byte streams. We hate having to think about HTTP when using tools like Apache HTTP Client. We look longingly at objects created by the wsdl2java command, which lets us pass arguments into a SOAP service as easily as any other method call, sweeping the details of invoking a remote service under the rug. And we find the servlet model to be just slightly too disconnected from the resources being produced. Suffice it to say that while we've been able to build RESTful services from scratch, it has not been a pleasant experience.

Political issues have sometimes compounded the technical hurdles. Many managers feel that SOAP-based Web services are the prescribed way of building service-oriented architectures (SOAs) in Java EE. This is changing with the emergence of important activities such as JSR 311, JAX-RS: The Java API for RESTful Web Services, which you'll learn about in this article. If nothing else, this effort is legitimizing RESTful development in the JEE space.

Meanwhile, help has arrived. In elegant fashion, the open source Restlet framework makes it easy to avoid the thorny issues that can arise from using traditional JEE technology to build and consume RESTful services.

Restlet's roots

In an effort to address some of the technical issues involved in doing REST with Java, Jérome Louvel, a French software consultant, sought to create a framework that would provide a more natural fit. He looked first at the NetKernel environment as a starting point. As much as he liked it, it was not a perfect fit for the API-focused framework he sought to make available. The experience did help influence his thinking about the kinds of things that a REST-oriented environment can offer, however. (The next article in this series will explore NetKernel more fully.)

As Louvel worked on his framework, he developed three goals:

  • Simple actions should be simple for basic usage. The defaults should work with minimal effort but also allow for more complex configurations.
  • Code written to this API should be portable across containers. Although servlet-based systems can be moved among containers such as Tomcat, Jetty, and IBM WebSphere, Louvel had a bigger picture in mind. The Servlet specification is tied to HTTP and a blocking I/O model. He wanted his API to be separable from both of these and deployable into the containers in use today. He also wanted them to be usable with little effort in alternate and emerging containers such as Grizzly, AsyncWeb, and the Simple Framework.
  • It should enrich not just the server side of producing RESTful interfaces in Java, but the client side as well. The HttpURLConnection class and Apache HTTP Client are too low-level to integrate cleanly an directly into most applications.

With these goals in mind, he set out to produce the Restlet API. After a few years in flux, the API became stable and a community grew around it. Today, the core API has a vibrant user base, and significant activity is underway for supporting integration with other toolkits and initiatives such as JAX-RS. (Louvel is now on the JAX-RS expert group.)

Restlet basics

A basic server with the Restlet API could not possibly be easier, as shown in Listing 1.

Listing 1. A basic server with Restlet

package net.bosatsu.restlet.basic;

import org.restlet.Restlet;
import org.restlet.Server;
import org.restlet.data.MediaType;
import org.restlet.data.Protocol;
import org.restlet.data.Request;
import org.restlet.data.Response;

public class SimpleServer {
  public static void main(String[]args) throws Exception {
    Restlet restlet = new Restlet() {
        @Override
        public void handle(Request request, Response response) {
          response.setEntity("Hello, Java RESTafarians!", MediaType.TEXT_PLAIN);
        }
    };

    // Avoid conflicts with other Java containers listening on 8080!
    new Server(Protocol.HTTP, 8182, restlet).start();
  }
}

This application does not do much (except spread good cheer), but it shows off two of Restlet's fundamental principles. First, simple things are simple. More complex activities are certainly possible, but you only worry about them when you need to. REST does not lack the ability to enforce security, constraints, content negotiation, or other important tasks. Those remain largely orthogonal activities, quite distinct from the process of satisfying a RESTful API. You layer the complexity on as needed.

Second, the code in Listing 1 is designed to be portable among container types. Notice that it doesn't specify a container. Restlets are the actual resources that ultimately respond to the requests. There is no distinction between the container handling the request and the information resource responder, as there can be in the servlet model. If you type the code into an IDE and add dependencies on the org.restlet.jar and com.noelios.restlet.jar archives, you can run the application and should see a log message like this:

Dec 7, 2008 11:37:32 PM com.noelios.restlet.http.StreamServerHelper start
INFO: Starting the internal HTTP server

Point a browser to http://localhost:8182, and you should see the friendly greeting.

Behind the scenes, the org.restlet.jar contains all of the major interfaces for this API. The com.noelios.restlet.jar contains a basic implementation of these interfaces and provides a default HTTP handling capability. You will not want to go into production with this HTTP engine, but it is exceptionally convenient for development and testing purposes. You needn't start up a major container to test your RESTful code. Unit and integration testing can be much easier as a result.

The sample in Listing 1 uses a lot of default behavior to create a default Application instance (I'll discuss Application in the next example) and listen for HTTP protocol requests on port 8182. The StreamServerHelper class starts listening on this port and dispatches requests to the Restlet instance as they come in.

Louvel's goal of supporting client-side RESTful Java is also met with ease, as you can see in Listing 2.

Listing 2. A Restlet client

package net.bosatsu.restlet.basic;

import java.io.IOException;

import org.restlet.Client;
import org.restlet.data.Protocol;

public class SimpleClient {
  public static void main(String [] args) throws IOException {
    String uri = (args.length > 0) ? args[0] : "http://localhost:8182" ;
    Client client = new Client(Protocol.HTTP);
    client.get(uri).getEntity().write(System.out);
  }
}

With the SimpleServer still running, launching this new client code with the same JAR dependencies should print out the friendly greeting to the console. Printing the output in this style would obviously not work for binary-oriented MIME types but, again, it is a convenient starting point.

Non-CRUD example

Most pedagogical REST examples show CRUDish services (Create, Retrieve, Update, Delete) around simple objects. Although that style certainly works well with REST, it is by no means the only approach that makes sense -- and most of us are tired of CRUD examples, anyway. The following example demonstrates the basics of a Restlet application by wrapping the Jazzy open source spell checker.

REST is about managing information, not invoking arbitrary behavior, so you need to exercise care when considering a behavior-oriented API like Jazzy. The trick is to treat the RESTful API as an information space for words that do and do not exist within the dictionaries in use. The problem could be solved in a variety of ways, but this article will define two information spaces. /dictionary is used to manage words in the dictionary. /spellchecker is used to find suggestions for words similar to misspelled words. Both focus on information by considering the absence or presence of words in the information spaces.

In a RESTful architecture, this HTTP command could return a definition of a word in the dictionary:

GET http://localhost:8182/dictionary/word

It would probably return the HTTP response code "Not Found" for words that are not in the dictionary. In this information space, it is fine to indicate that words do not exist. Jazzy does not provide definitions for words, so I'll leave returning some content as an exercise for the reader.

This next HTTP command should add a word to the dictionary:

PUT http://localhost:8182/dictionary/word

This example uses PUT because you can figure out what the URI in the /dictionary information space should be beforehand, and issuing multiple PUTs should not make a difference. (PUT is an idempotent request, like GET. Issuing the same command multiple times should not make a difference.) If you want to add definitions, you could pass them in as bodies to the PUT handler. If you want to accept multiple definitions over time, you may wish to POST those definitions in, because PUT is an overwrite operation.

Don't overlook synchronization

In the interest of keeping the examples focused, this article pays no special attention to synchronization issues. Do not treat your production code so nonchalantly! Consult a resource such as Java Concurrency in Practice for more information.

The Restlet instances that I'll create need to be bound to the appropriate information spaces, as shown in Listing 3.

Listing 3. A simple RESTful spell checker

package net.bosatsu.restlet.spell;

import com.swabunga.spell.event.SpellChecker;
import com.swabunga.spell.engine.GenericSpellDictionary;
import com.swabunga.spell.engine.SpellDictionary;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;

import org.restlet.data.Protocol;
import org.restlet.*;

public class SpellCheckingServer extends Application {
  public static String dictionary = "Restlet/dict/english.0";
  public static SpellDictionary spellingDict;
  public static SpellChecker spellChecker;
  public static Restlet spellCheckerRestlet;
  public static Restlet dictionaryRestlet;

  static {
    try {
      spellingDict = new GenericSpellDictionary(new File(dictionary));
      spellChecker = new SpellChecker(spellingDict);
      spellCheckerRestlet = new SpellCheckerRestlet(spellChecker);
      dictionaryRestlet = new DictionaryRestlet(spellChecker);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  public static void main(String [] args) throws Exception {
    Component component = new Component();
    component.getServers().add(Protocol.HTTP, 8182);

    SpellCheckingServer spellingService = new SpellCheckingServer();
    component.getDefaultHost().attach("", spellingService);
    component.start();
  }

  public Restlet createRoot() {
    Router router = new Router(getContext());
    router.attach("/spellchecker/{word}", spellCheckerRestlet);
    router.attach("/dictionary/{word}", dictionaryRestlet);
    return router;
  }
}

After it builds up the dictionary instance and the spell checker, the Restlet setup in Listing 3 is slightly more complicated than in the earlier basic example (but not much!). The SpellCheckingServer is an instance of a Restlet Application. An Application is an organizational class that coordinates deployment of functionally connected Restlet instances. The surrounding Component asks an Application for its root Restlet by calling the createRoot() method. The root Restlet returned indicates who should respond to the external requests. In this example, a class called Router is used to dispatch to the subordinate information spaces. In addition to performing this context binding, it sets up a URL pattern that allows the "word" portion of the URL to be available as an attribute on the request. This will be leveraged in the Restlets created in Listings 4 and 5.

The DictionaryRestlet, shown in Listing 4, is responsible for handling requests for manipulating the /dictionary information space.

1 2 Page
Join the discussion
Be the first to comment on this article. Our Commenting Policies
See more