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.

Listing 4. The DictionaryRestlet

package net.bosatsu.restlet.spell;

import org.restlet.Restlet;
import org.restlet.data.*;

import com.swabunga.spell.event.SpellChecker;

public class DictionaryRestlet extends Restlet {
    private SpellChecker spellChecker;

    public DictionaryRestlet(SpellChecker spellChecker) {
        this.spellChecker = spellChecker;
    }

    @Override
    public void handle(Request request, Response response) {
        String word = (String) request.getAttributes().get("word");

        if(request.getMethod().equals(Method.PUT)) {
            spellChecker.addToDictionary(word);
            response.setStatus(Status.SUCCESS_NO_CONTENT);
        } if(request.getMethod().equals(Method.GET)) {
            // Left as an exercise. Consider fetching the word definition
            // from http://dictionary.reference.com/browse/<word> or redirecting
            // to it.

            if(spellChecker.isCorrect(word)) {
                response.setEntity("Not yet implemented", MediaType.TEXT_PLAIN);
            } else {
                response.setEntity("Word not found: " + word, MediaType.TEXT_PLAIN);
                response.setStatus(Status.CLIENT_ERROR_NOT_FOUND);
            }
        } else {
            response.setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
        }
   }
}

At present, the DictionaryRestlet handles only PUT and GET requests, but you can imagine issuing DELETE requests to remove words from the dictionary if you wanted to. If a word is successfully added to the dictionary, the client is told that things are happy but there is nothing else to say by using a Status.SUCCESS_NO_CONTENT response. Doing something meaningful with GET is left as an exercise for the reader, although Listing 4 contains a hint. It is important to take advantage of the Status response code to indicate when something succeeds, fails, the request is malformed, the client is not authorized, and so on.

The /spellchecker information space works a little differently from its /dictionary friend. On success, what should be returned? If the client has just passed in a properly spelled word, do you really need to send it back? The SpellCheckerRestlet, shown in Listing 5, reuses Status.SUCCESS_NO_CONTENT, meaning the request was valid and successful, but there is really nothing to say about it.

Listing 5. The SpellCheckerRestlet

package net.bosatsu.restlet.spell;

import org.restlet.Restlet;
import org.restlet.resource.Representation;
import org.restlet.resource.StringRepresentation;
import org.restlet.data.*;
import com.swabunga.spell.event.SpellChecker;
import com.swabunga.spell.engine.Word;

import java.util.StringTokenizer;
import java.util.List;
import java.util.Iterator;

public class SpellCheckerRestlet extends Restlet {
  private SpellChecker spellChecker;

  public SpellCheckerRestlet(SpellChecker spellChecker) {
    this.spellChecker = spellChecker;
  }

  @Override
  public void handle(Request request, Response response) {
    String word = (String) request.getAttributes().get("word");
    if(request.getMethod().equals(Method.GET)) {
      if(spellChecker.isCorrect(word)) {
        response.setStatus(Status.SUCCESS_NO_CONTENT);
      } else {
        String resp = null;
        StringBuffer sb = new StringBuffer();
        List l = spellChecker.getSuggestions(word, 5);
        Iterator itor = l.iterator();

        while(itor.hasNext()) {
          Word w = (Word) itor.next();
          // Obviously hard-coding the URL like this is bad. Use the API to discover
          // the actual HTTP context and build off of that.
          sb.append("http://localhost:8182/dictionary/");
          sb.append(w.getWord());
          sb.append("\n");
        }
        resp = sb.toString();
        response.setEntity(resp, MediaType.TEXT_PLAIN);
      }
    } else {
      response.setStatus(Status.CLIENT_ERROR_BAD_REQUEST);
    }
  }
}

If an incorrectly spelled word is passed in, you do not want to return "Not Found" as was done with the /dictionary information space. You want instead to return a list of suggestions. Because, as Roy Fielding says, "hypermedia is the engine of application state," you want to return links back to the actual information resources. This way, clients can iterate over the results and discover new word information resources to resolve if they choose to do so. In this way, REST is not just about data, but also about discovering links to other resources.

The suggestive responses are simple and easy to parse when they are one per line as they are in Listing 5. It seems unnecessary to involve angle brackets although you certainly can wrap the responses in XML if you choose. The goal here is to make a simple, lightweight request that can be used from a variety of applications (thick clients, Ajax applications, command-line tools, and so on) to do spell checking. Focusing on logical names for information resources (correctly spelled words or lists of suggestions) makes substantive language bindings or contracts unnecessary. And it is easy to get both reuse and the freedom to change implementations without breaking the clients.

JSR 311 integration

Sun is adding support for REST to the JEE stack. REST support in JEE hasn't been completely lacking -- servlets can respond to HTTP requests, after all -- but now a legitimate set of guidelines for building enterprise RESTful APIs with the Java technology stack is available.

JSR 311 (JAX-RS) tries to limit the degrees of freedom in REST and focuses on using annotations to make plain old Java objects (POJOs) and resources available through HTTP. (A reference implementation called Jersey is available from the GlassFish project.) Jérome Louvel has made a nice extension to Restlet to support the JAX-RS effort. This article is not intended to be a comprehensive introduction to this specification, but it should be enough to get you going quickly.

The JAX-RS API lets you annotate a class to indicate which HTTP contexts should trigger method invocations, as illustrated by the @Path annotation in Listing 6.

Listing 6. A JAX-RS-enabled Restlet

package net.bosatsu.restlet.jaxrs;

import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.GET;

@Path("sample")
public class SampleResource {
  private String message;

  public SampleResource() {
    this("Hello");
  }

  public SampleResource(String message) {
    this.message = message;
  }

  @GET
  @Produces("text/html")
  public String getHTML() {
    StringBuffer sb = new StringBuffer("<html><body>");
    sb.append(message);
    sb.append("</body></html>");
    return sb.toString();
  }

  @GET
  @Produces("text/xml")
  public String getXML() {
    StringBuffer sb = new StringBuffer("<message>");
    sb.append(message);
    sb.append("</message>");
    return sb.toString();
  }

  @GET
  @Produces("text/plain")
  public String getPlain() {
    return message;
  }
}

Whichever JSR 311 container is told about this resource will set up the necessary infrastructure to route requests to /sample to instances of the SampleResource class. Specific methods to call are matched by the JSR 311 engine in order based upon the context of the request. This matching process potentially includes subpath information, HTTP method, requested MIME type, and so on. The first match that it finds will be used. An otherwise unconstrained GET request to /sample will result in the getHTML() method being invoked, as you can see in Listing 6. Should the client indicate a preference for a MIME type other than HTML, however, this method will no longer match, and some other method will be invoked.

Using Restlet's JAX-RS extensions, the Restlet engine binds JAX-RS resources based on the dictates of an Application configuration class. This is a different class from the Restlet Application class discussed above. (This is an unfortunately overloaded name that used to make more sense when JAX-RS called it ApplicationConfig.) The SampleApplication class in Listing 7 indicates that the server should bind the SampleResource class to the @Path annotations that are marked up in its definition.

Listing 7. The SampleApplication class

package net.bosatsu.restlet.jaxrs;

import javax.ws.rs.core.Application;
import java.util.Set;
import java.util.HashSet;

public class SampleApplication extends Application {
  final static Set<Class<?>> classes;

  static {
    classes = new HashSet<Class<?>>();
    classes.add(SampleResource.class);
  }

  public Set<Class<?>> getClasses() {
    return classes;
  }
}

Finally, as shown in Listing 8, a JaxRSApplication is configured with the Application instance to make all of the magic happen.

Listing 8. Using the JaxRsApplication class

package net.bosatsu.restlet.jaxrs;

import org.restlet.Component;
import org.restlet.Server;
import org.restlet.ext.jaxrs.JaxRsApplication;
import org.restlet.data.Protocol;

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

    JaxRsApplication app = new JaxRsApplication(server.getContext().createChildContext());
    app.add(new SampleApplication());
    server.getDefaultHost().attach(app);
    server.start();
  }
}

As a valid JSR 311 engine, the JaxRSApplication class iterates over all of the classes advertised from the getClasses() method of the SampleApplication class. It then sets up Routers and whatever else is necessary internally to handle the requests. The rest of the sample looks surprisingly like the previous examples for stock Restlet applications.

In order to make this example run, you need to have dependencies on:

  • com.noelios.restlet.jar
  • org.restlet.jar
  • javax.mail.jar
  • javax.ws.rs.jar
  • org.apache.commons.fileupload.jar
  • org.json.jar
  • org.restlet.ext.jaxrs_1.0.jar

These can all be found in the standard Restlet distribution under the install/lib directory. Note: I had some issues running the examples using JDK 1.6. They worked, but some stream handler made a lot of unpleasant noise when closing up the sockets, so you might want to stick with JDK 1.5.

One of the goals of this initiative is also to make it easy to support content-negotiated forms of the resources being requested. For example, client requests with appropriate "Accept" headers will be honored. The code in Listing 9 uses the Restlet client API to indicate that it will accept only a non-HTML MIME type.

Listing 9. Content negotiation with the Restlet client API

package net.bosatsu.restlet.jaxrs;

import org.restlet.data.*;
import org.restlet.Client;

import java.io.IOException;

public class SampleClient {
  public static void main(String [] args) throws IOException {
    Client client = new Client(Protocol.HTTP);
    Request r = new Request();
    r.setResourceRef("http://127.0.0.1:8182/sample");
    r.setMethod(Method.GET);
    r.getClientInfo().getAcceptedMediaTypes().add(new Preference<MediaType>(MediaType.TEXT_XML));
    client.handle(r).getEntity().write(System.out);
  }
}

When you execute this code, you should see <message>Hello</message> printed out instead of the HTML you saw earlier. Try changing the request to ask for a text/plain response. There are more elaborate ways of managing the object-to-MIME-type conversion, but this is intended to get you thinking about content-negotiated information resources. There's much more to JAX-RS, but this should give you a taste for what is possible and how it can help reduce some of the drudgery of Java-based REST interfaces.

In conclusion

Much of what has caused Java developers to grumble as we've built RESTful Web services using conventional JEE technology stacks has been resolved by the Restlet API. The goal of Restlet is to feel comfortable to servlet developers, but to allow them the freedom to pick appropriately sized containers to deploy their services. It is now much easier to expose meaningful, logical names to the information served by your applications.

In the next article in this series, we'll explore NetKernel, a resource-oriented computing platform that takes what is cool about REST, Unix, and SOAs and builds it into a flexible, scalable software environment.

Brian Sletten is President of Bosatsu Consulting, Inc. a services company focused on using Web and semantic-oriented technologies to solve architectural and data-integration problems not handled by conventional tools and techniques. He has a background as a system architect, developer, mentor, and trainer, with experience in the online game, defense, finance, and commercial domains. Brian has a B.S. in computer science from the College of William and Mary and lives in Fairfax, VA.

Learn more about this topic

More from JavaWorld

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