Monday, December 07, 2009

JAX-RS and Jersey talk materials

I recently gave a talk on JAX-RS, JSR 311, and Jersey at the St. Louis Java User's Group website. You can find the presentation and the sample code on their website.

Friday, November 13, 2009

Curl has a bug with redirects?

I gave a presentation at the JUG last night about JAX-RS and Jersey. The talk went well, and it was a great crowd with a lot of participation (I'll post more once I have the materials cleaned up and they are posted to the JUG website).

But one thing that didn't go right last night was when I was using the 'curl' command line tool to create new artists in the RESTful music database service I was using as an example.

The curl man page on my MacBook Pro says:

When curl follows a redirect and the request is not a plain GET (for example POST or PUT), it will do the following request with a GET if the HTTP response was 301, 302, or 303. If the response code was any other 3xx code, curl will re-send the following request using the same unmodified method.

Yet, when I was using it, it wasn't doing that. For example when trying to create artist 'Jane':


brian@Widget: curl -v -H "Accept: text/html" -X POST -L "http://localhost:3131/artists/Jane"
* About to connect() to localhost port 3131 (#0)
* Trying ::1... Connection refused
* Trying fe80::1... Connection refused
* Trying 127.0.0.1... connected
* Connected to localhost (127.0.0.1) port 3131 (#0)
> POST /artists/Jane HTTP/1.1
> User-Agent: curl/7.16.3 (powerpc-apple-darwin9.0) libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3
> Host: localhost:3131
> Accept: text/html
>
< HTTP/1.1 303 See Other
< server: grizzly/1.9.10
< Location: http://localhost:3131/artists/id/313
< Content-Type: text/plain; charset=iso-8859-1
< Content-Length: 0
< Date: Fri, 13 Nov 2009 05:24:12 GMT
<
* Connection #0 to host localhost left intact
* Issue another request to this URL: 'http://localhost:3131/artists/id/313'
* Re-using existing connection! (#0) with host localhost
* Connected to localhost (127.0.0.1) port 3131 (#0)
> POST /artists/id/313 HTTP/1.1
> User-Agent: curl/7.16.3 (powerpc-apple-darwin9.0) libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3
> Host: localhost:3131
> Accept: text/html
>
< HTTP/1.1 405 Method Not Allowed
< server: grizzly/1.9.10
< Allow: DELETE,OPTIONS,HEAD,GET
< Content-Type: text/plain; charset=iso-8859-1
< Content-Length: 0
< Date: Fri, 13 Nov 2009 05:24:12 GMT
<

When it followed the redirect and issued the follow-up request, it went to the right place:


...
< HTTP/1.1 303 See Other
< server: grizzly/1.9.10
< Location: http://localhost:3131/artists/id/313
...
* Connection #0 to host localhost left intact
* Issue another request to this URL: 'http://localhost:3131/artists/id/313'

But it didn't switch to GET:

> POST /artists/id/313 HTTP/1.1

As a result, I got a 405 error code because curl tried to do a POST to the redirected URL rather than doing a GET as is documented.

I did some further experiments last evening after the talk and confirmed this behavior. I even tried switching the return code to a 302 just to see if it would make a difference and it doesn't.

How does your curl behave? Does it switch from POST to GET when following a redirect using the -L option?

I'd appreciate it if anyone who knows anything about this could comment. Is curl doing the wrong thing, or is the man page wrong and curl is no longer supposed to do this?

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.

Friday, June 12, 2009

JAX-RS and Jersey posts

I'm writing a series of posts on JAX-RS and Jersey (the reference implementation for JAX-RS). I'll be updating this post to provide links to all the others. This post gives you "one stop shopping" for all the related posts.

Monday, June 08, 2009

JAX-RS Hello World

[NOTE: All of my examples will use standalone services (not housed inside a container). The integration between Jersey and the Grizzly HTTP server makes this a snap]

So, what is the simplest, smallest hello world we can implement with JAX-RS? The Jersey site provides an example and it's surprisingly small. We will start with an example almost identical to the hello world example from the Jersey documentation and grow from there.

If we ignore the package statements and imports, there are two files of about ten lines each. The first one is our main class that starts up the service and generally gets things going. Later examples will show why this is it's own class separate from the rest of a service's implementation. Here's the class in its entirety:

package net.gilstraps.server;

import com.sun.grizzly.http.SelectorThread;
import com.sun.jersey.api.container.grizzly.GrizzlyWebContainerFactory;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class Main {

public static void main(String[] args) throws IOException {
Map<String,String> initParams = new HashMap<String, String>();
initParams.put( "com.sun.jersey.config.property.packages", "net.gilstraps.server" );
SelectorThread selector = GrizzlyWebContainerFactory.create( "http://localhost:9998/", initParams );
//noinspection ResultOfMethodCallIgnored
System.in.read();
threadSelector.stopEndpoint();
System.exit(0);
}

}

The code here is pretty easy to understand, so let's look at it. These two lines are a crucial part of configuring Jersey:

Map<String,String> initParams = new HashMap<String, String>();
initParams.put( "com.sun.jersey.config.property.packages", "net.gilstraps.server" );

With these lines, we tell Jersey which java packages should be examined at runtime to find classes which implement RESTful resources. We've specified that Jersey should look at the "net.gilstraps.server" package and examine the classes in that package to find ones which have JAX-RS annotations on them. Those annotations tell a JAX-RS implementation what resources each class serves up to clients and what URI's those resources have. There are also annotations for specifying which HTTP methods match up to which Java methods. We'll look at these shortly.

The next line actually instantiates the Grizzly HTTP server, telling it the base URI for all the URL's it should support and providing it with initialization parameters:

SelectorThread selector = GrizzlyWebContainerFactory.create( "http://localhost:9998/", initParams );

The call returns us a SelectorThread which represents the control object for the HTTP server. With the string "http://localhost:9998/", we are telling the service to use port 9998 of the host named 'localhost'. Grizzly has built-in knowledge of Jersey and our inclusion of the "com.sun.jersey.config.property.packages" parameter causes Jersey to used to provide the web service.

The remaining lines provide a simple means to shut down the service gracefully. The program reads input from System.in and then shuts things down cleanly. So if you run the program on a command line you can get it to shut down without killing it:

System.in.read();
threadSelector.stopEndpoint();
System.exit(0);

So, where is our cliched "hello world" resource? There is one more class used to implement our service, also quite small. Here it is:

package net.gilstraps.server;

import javax.ws.rs.Produces;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.swing.text.html.HTML;

@Path("/helloworld")
public class HelloWorldResource {
@GET @Produces("text/plain")
public String getClichedMessage() {
return "Hello Stage 1";
}
}

This class is almost a POJO (plain old Java object). It doesn't implement a particular interface, or extend a class. It has a very simple method ('getClichedMessage') that returns a regular Java string. How can this implement a hello world web service? The key to making it a JAX-RS resource class is the annotations. Let's examine each one:

@Path

The '@Path' annotation attached to the class itself tells a JAX-RS what URI or set of URIs this class serves. In this case, we are specifying just the URI of "/helloworld". Thus, with the base URI of "http://localhost:9998" we would access this resource via the URI "http://localhost:9998/helloworld".

@GET

The @GET annotation on a method tells JAX-RS that the Java method implements HTTP's GET verb (the verb your browser uses every time you visit a web page). So JAX-RS now knows that a client doing a GET of "http://localhost:9998/helloworld" should result in the 'getClichedMessage' method being invoked.

@Produces

The @Produces annotation tells JAX-RS that the MIME type of the resource returned by 'getClichedMessage' is "text/plan", which is the MIME type for a plain text document. We've aimed for a minimal service here, so we're going for the simplest type of resource we can. It's also possible to return other kinds of entities as the result of a request, and it's even possible to use separate conversion classes to convert different kinds of Java objects into particular kinds of response entities (like a JPEG image, for example). But this service just returns a text document.

At this point, JAX-RS knows everything it needs to know to support a client doing a GET on the resource identified by the URI "http://localhost:9998/helloworld". When we compile and run our service, it doesn't output anything at first. But if we open a browser and go to the URL, we see some output from Grizzly/Jersey:

Jun 12, 2009 9:02:51 PM com.sun.jersey.api.core.PackagesResourceConfig init
INFO: Scanning for root resource and provider classes in the packages:
net.gilstraps.server
Jun 12, 2009 9:02:51 PM com.sun.jersey.api.core.PackagesResourceConfig init
INFO: Root resource classes found:
class net.gilstraps.server.HelloWorldResource
Jun 12, 2009 9:02:51 PM com.sun.jersey.api.core.PackagesResourceConfig init
INFO: Provider classes found:

From this we can see that Grizzly brings Jersey in to handle the request we made with the browser. Jersey then scans for classes which serve up REST resources. It tells us that it found our class:

Jun 12, 2009 9:02:51 PM com.sun.jersey.api.core.PackagesResourceConfig init
INFO: Root resource classes found:
class net.gilstraps.server.HelloWorldResource

Even more interesting, the browser where we made the GET request gets the string that was returned from 'getClichedMessage' as the result of it's GET request:



And that's a simple 'hello world' implemented using JAX-RS with Jersey and Grizzly.

Tuesday, June 02, 2009

Setting up Jersey for use

If you use Maven you can use it to download Jersey and keep it up to date. There are links to the POM files on the getting started page. In particular, you need the jersey-server module, and the grizzly-servlet-webserver module. If you want to use WADL with JDK 1.5 you need the jaxb-impl module. If you are not using Maven, you need only a few JARs:

grizzly-servlet-webserver.jar
jersey-server.jar
jersey-core.jar
jsr311-api.jar
asm.jar

[NOTE: these projects are under active development, so you may find newer versions of these jars available]

As with using Jersey via Maven, if you want to use WADL with JDK 1.5, you need some additional jars:

jaxb-impl.jar
jaxb-api.jar
activation.jar
stax-api.jar

We will ignore WADL for now, which limits the required jars to just the first five. But in addition to the binaries, I prefer to have the sources and javadoc available, which makes the total number of jars 15 (a binary, source, and javadoc jar for each).

Once you have the JARs, configure your classpath or your IDE to include them in your project. You're then ready to write the obligatory 'hello world' program.

Wednesday, May 27, 2009

Exploring RESTful web services with JAX-RS and Jersey.

I've been reading about RESTful web services for a while now. The best book on the subject I've found is RESTful Web Services by Leonard Richardson and Sam Ruby. I've been reading this book for what seems like forever (a function of my life and available time, not the book). I'm getting close to done and have begun using Jersey, the reference implementation of the JAX-RS specification to experiment with building RESTful web services.

I really like Jersey. A big reason for that is because Jersey makes it straightforward to stand up a standalone RESTful service. And once you've done it once, standing up additional services is easy. Deploying services without a heavyweight container makes me happy. Containers were created in the early days of Java when Java's limitations made them necessary. Since then, they have taken on a life of their own. Yet, in today's world I think the container has outlived almost all of its purpose. It's disadvantages now outweigh the few remaining benefits.

In the coming months, I plan to explore how to build, deploy, replicate, secure, and evolve industrial-class services without a container. I think I will succeed, but who knows? Maybe I'll learn a bunch due to abject failures. Either way, it should be fun.

My next post will describe how you download all the various pieces needed to stand up a Jersey-based REST service. There aren't very many, and it doesn't take long. Stay tuned.