Tuesday, July 21, 2009

RESTful clients with the Jersey Client API

[Note: This is one in a series of posts about JAX-RS. You may wish to start at the beginning]


Jersey, the reference implementation of the JAX-RS specification, is a framework that makes it very easy to implement RESTful web services. But there is more to Jersey than just the server side. In this post I will spend a little time exploring the Jersey client library.


Depending upon how you design your RESTful service, you may or may not want to have separate URLs for separate representations of the same resource. This presents a problem when trying to test with a browser. There is no way to tell popular browsers that you want a text/html representation or an image/jpeg representation. The browser has a list of preferred media types, but none that I'm aware of allow you to customize this (either in general or for a particular request). Even more importantly, we need to be able to build solid unit tests for our services. The Jersey client framework provides a good solution to this problem. It is designed to allow developers of RESTful web services to write good unit tests, but is more general purpose than that. It can also be used to write RESTful client applications.


There is an excellent tutorial on the Jersey client API, which you should download and read if you plan to use it. But I will give you a taste of the API here.


The Jersey client API is very clean, and requires a minimum of fuss to use. As an example, let's write a unit test which tests our web service serving up information about fighter planes. First, we write the code to set up and tear down our service implementation. This code is the same as that in our Main class before, just split up between the setup and tear-down methods.

public class Test3b {

private SelectorThread selector;

@org.junit.Before
public void createService() throws IOException {
Map initParams = new HashMap();
initParams.put("com.sun.jersey.config.property.packages", "net.gilstraps.server");
selector = GrizzlyWebContainerFactory.create("http://localhost:9998/", initParams);
}

@org.junit.After
public void DestroyService() {
selector.stopEndpoint();
selector = null;
}

The only difference in this case is we don't read from standard in to shut down the service, since we always want to shut it down at the end of the unit test.


Now, we can test that the text/html representation returned by invoking the service is what we expect. First, we do a bit of hoop jumping to read in a copy of the HTML we expect to receive:


@org.junit.Test
public void testF22Html() {
try {
File expected = new File("f22.html");
long fileSize = expected.length();
if (fileSize > Integer.MAX_VALUE) {
throw new IllegalArgumentException("File it larger than a StringWriter can hold");
}
int size = (int) fileSize;
BufferedReader r = new BufferedReader(new FileReader(expected), size);
char[] chars = new char[size];
int readChars = r.read(chars);
if (readChars != size) {
throw new RuntimeException("Failed to read all chars of the expected result html file");
}
final String expectedText = new String(chars);

At this point, the variable expectedText contains what we should receive back from our request. Making the request is quite straightforward. First, we create a JAX-RS client:

Client client = new Client();

These clients are 'heavyweight' objects (taking time to create and using significant resources). As such, in a production client we would want to create this client once and use it many times. For the sake of independent unit tests, we will go ahead and create a Client object for each test.


Next, we specify the resource we want to retrieve using a WebResource object:

WebResource f22 = client.resource("http://localhost:9998/planes/f22/f22.html");

And then we ask the client to retrieve the resource for us, specifying that we want a text/html representation (MediaType.TEXT_HTML_TYPE) and specifying that we want to get back a ClientResponse object:


ClientResponse response =
f22.accept(MediaType.TEXT_HTML_TYPE).get(ClientResponse.class);

This code is using the builder pattern, where we build up our request through a chain of method calls. In this case, the chain is only two calls long. First, we call the accept method to specify the media types we will accept in the response (text/html), then we call the get method to actually retrieve the resource. It is possible to chain together more calls to specify other characteristics of either the request or the expected response (for more information, see the whitepaper on the Jersey client API).


Now that we have gotten a representation of the resource in the form of an HTTP response, we can then retrieve the HTML entity contained within that response as a string:


String returnedHTML = response.getEntity(String.class);

And finally, since this is a unit test, we make sure that what we got back matches what we expected:

assertEquals("Expected and actual HTML don't match"
expectedText, returnedHTML);

The ClientResponse object is useful if you want to look at other characteristics of the response, such as the returned headers. In this case, we could just as well have asked for the string from the WebResource directly. To do so, we would replace these two lines:

ClientResponse response =
f22.accept(MediaType.TEXT_HTML_TYPE).get(ClientResponse.class);
String returnedHTML = response.getEntity(String.class);

With this one:

String returnedHTML = asHTML.accept(MediaType.TEXT_HTML_TYPE).get(String.class);

Testing for retrieval of the JPEG representation of an image is almost identical. The only differences are that we read in the image file as an array of bytes, ask for the response entity as an array of bytes, and compare the two as arrays of bytes. Here is the entire test method:


@org.junit.Test
public void testF22JPEG() {
try {
// Read in our expected result
File imageFile = new File("f22.jpg");
long fileSize = imageFile.length();
if (fileSize > Integer.MAX_VALUE) {
throw new IllegalArgumentException("File it larger than a StringWriter can hold");
}
int size = (int) fileSize;
byte[] expectedBytes = new byte[size];
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(imageFile), size);
int bytesRead = bis.read(expectedBytes);
assertEquals(size, bytesRead);

// Request the representation
Client client = new Client();
WebResource f22 = client.resource("http://localhost:9998/planes/f22/f22.jpg");
ClientResponse response = f22.accept(MediaType.WILDCARD).get(ClientResponse.class);
MultivaluedMap headers = response.getHeaders();
for ( String key : headers.keySet() ) {
System.out.println( key + "=" + headers.get(key) );
}
byte[] returnedBytes = new byte[0];
returnedBytes = response.getEntity(returnedBytes.getClass());

// Compare the two sets of bytes to make sure they match
assertEquals(size, returnedBytes.length);
assertTrue(Arrays.equals(expectedBytes,returnedBytes));
}
catch (FileNotFoundException e) {
AssertionError ae = new AssertionError("File containing expected HTML not found!");
ae.initCause(e);
throw ae;
}
catch (IOException e) {
AssertionError ae = new AssertionError("Problems reading expected text!");
ae.initCause(e);
throw ae;
}
}

Just like the core of Jersey, the client library enables clear, short code that clearly expresses what the developer is trying to do. This is a marked departure from many other web frameworks and APIs. It is refreshing to be able to write web services clients in such a terse, yet clear style.

[Note: I plan to continue working with Jersey and continue posting about it. But I'm going to take a few weeks to look into some other topics first. I'll make sure to update the list of JAX-RS and Jersey posts as I add more]

Thursday, July 09, 2009

JAX-RS Response Entity Providers


[Note: This is one in a series of posts about JAX-RS. You may wish to start at the beginning]

If you read my previous entry about returning more than one representation of a resource, you saw this sample code for a RESTful F-22 fighter plane resource:
@Path("/planes/f22/")
public class F22 {
@GET @Produces("text/html") @Path( "f22.html" )
public File getHTMLRepresentation() throws IOException {
return new File( "f22.html");
}

@GET @Produces("image/jpeg") @Path( "f22.jpg" )
public File getImageRepresentation() {
return new File( "f22.jpg");
}
}

These methods might seem a bit odd at first sight. The getHTMLRepresentation method has a @Produces annotation which indicates the method produces a MIME type of "text/html", but the return type of the Java method is java.io.File. Similarly, the getImageRepresentation method produces "image/jpeg" but the Java method also returns a File.

This apparent disconnect is resolved by a JAX-RS feature called entity providers. The specification states that entity providers "supply mapping services between representations and their associated Java types". The job of entity providers is to take HTTP request entities and turn them into Java objects on the request side, and take Java objects and turn them into response entities on the result side. Entity providers which convert request entities to Java types implement the MessageBodyReader interface and must be marked with the @Provider annotation. Similarly, entity providers which convert Java return types to response entities implement the MessageBodyWriter interface and are marked with the @Provider annotation (see Figure 1).


Figure 1: Conversion between HTTP entities and Java objects

JAX-RS mandates a set of built-in entity providers covering common entity types such as strings, input streams, java.io.Reader objects, java.io.File objects, and a number of other sophisticated types of conversions.

In this case, Jersey takes the returned File object and uses it’s contents as the response entity which it marks as “text/html”. This mapping from Java types to HTTP entities can be quite powerful, as you don’t have to write code to read the contents of files and return them as byte arrays, Strings, etc. It also offers a JAX-RS implementation the opportunity to implement handling of some types much more efficiently. For example, a JAX-RS implementation might choose to use Java’s NIO facilities to read the contents of a file for performance reasons.

This notion of entity providers is a crucial aid to the POJO (Plain Old Java Objects) style of JAX-RS applications, and avoids what would otherwise be a great deal of boilerplate code in the application performing these conversions.

Wednesday, July 08, 2009

Multiple representations of a REST Resource using JAX-RS

[Note: I have a post that provides an in-order list of all the JAX-RS and Jersey-related posts I've written. You may want to start at the beginning if you haven't already read the previous posts]

One of the principles of RESTful web services is that a client makes a request for a representation of a resource. The resource itself is an abstract notion, just as an object-oriented model of some real world object is an abstraction. But in the case of a RESTful web service, it becomes possible to return different kinds of information based upon what the client requests.

As a simple example, imagine we are aficionados of military planes and decide to create a web service which provides non-secret information (and perhaps speculation) about various military aircraft (thanks to my friend Bruce for this idea). In the REST world, our resource might be "F-22". In this case the resource is the United States F-22 fighter plane. But there are many possible representations of an F-22. We might provide a summary description of the plane, or an image of the plane, or a CAD schematic of the plane, or any of a number of other representations. This basic notion that a single resource may have more than representation can be demonstrated with a simple object that can provide either an HTML text description of the F-22 or a simple graphic drawing of the plane.

Perhaps the simplest way to provide more than one representation for a given resource is to provide two different methods in a Java class with different MIME types in their @Produces annotations. In our example, assume we have a class named F22 which provides representations of an F-22. It can have a method that returns HTML and a different method that returns a JPEG image. This is very easy to do with JAX-RS:

@Path("/planes/f22")
public class F22 {
@GET @Produces("text/html")
public String getHTMLRepresentation() throws IOException {
File f = new File( "F22.html");
BufferedReader br = new BufferedReader(new FileReader(f));
StringWriter sw = new StringWriter();
for( String s = br.readLine(); s != null ; s = br.readLine() ) {
sw.write( s );
sw.write( '\n' );
}
return sw.toString();
}

@GET @Produces("image/jpeg")
public File getImageRepresentation() {
return new File( "F22.jpg");
}

}

We now run into a problem using a web browser for a testing client. The browser is going to specify in it’s HTTP request the formats it prefers for a response. The browser doesn’t allow us to specify the type of representation we want. So, do we get back the HTML representation or the JPEG image? The answer depends upon the particular web browser you are using. Firefox 3.5 on a Mac and Windows and Safari on a Mac return the HTML form:



IE 6 on Windows and IE 8 on Windows return the JPEG image:



There are other ways to return more than on representation. For example, can have a single method which returns a JAX-RS Response object. That Response can have different contents depending upon the requested representation. I may explore these in future posts.