Jersey – a review

We’ve been getting more interested in using RESTful web services and AJAX based applications recently. One of the tools we’ve been using with that is Jersey – the JAX-RS (JSR-331) reference implementation. I’ve been using it in anger for about a month now, and thought I’d write up some thoughts I had about it.

Jersey 101

Let’s start with what Jersey is. As I said, Jersey is the reference implementation for JAX-RS: the Java API for RESTful Web Services. In other words, it’s a library designed to help with writing web-based services that follow the REST principles. If you don’t know about those, then stop reading now. Or better yet, read about them first, then come back here.

At it’s heart, Jersey is a web-framework. That means you need to start with a WAR application, and a web.xml. In its simplest form, you just need to provide a servlet and servlet-mapping. We’re using it with Spring, so our web.xml looks kind of like this:



  <!-- Configure Spring in this application. -->
  
    contextConfigLocation
    classpath:applicationContext.xml
  
  
    org.springframework.web.context.ContextLoaderListener
   

  <!--  Configure Jersey in this application. -->
  
    jersey
    com.sun.jersey.spi.spring.container.servlet.SpringServlet
  

  
    jersey
    /
  

In the applicationContext.xml file, I enable Spring’s component scanning support so that it will find my Jersey resources automagically and enable them for me. Because I bound the servlet to the root, all web requests will go through it.

So what is a Jersey resource, and what does it look like? A Jersey resource is an endpoint for a web request. In other words, it’s like a JSP page or a Struts Action – it’s the class that’s going to receive the web request.

Let’s go for a simple example:

package net.twasink.jersey.example.resource;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import org.springframework.stereotype.Service;

import net.twasink.jersey.example.model.HotelModel;

@Service @Path("/hotels/")
public class Simple {

    @GET @Path("{hotelId}/")
    @Produces({ MediaType.TEXT_HTML, MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public HotelModel doSomethingSimple(@PathParam("hotelId") String hotelId) {  

        Hotel hotel = ... // look up the hotel.
        
        return new HotelModel(hotel);
    }
}

(I should point out here that after four years working at Wotif.com, most of the examples that come to mind easily these days involve things like Hotels and Rooms and Rates; there is no implication that this in any way resembles real Wotif code, folks)

Anyway, let’s break that down.

  • Line 13 – the @Service annotation marks this class as a component to be managed by Spring. The @Path annotation tells Jersey that this covers the “/hotels/” directory.
  • Line 16 – the @GET says that this is only for HTTP GETs. The @Path is relative to the earlier path, and means that the first directory down from “/hotels/” will be used as the hotel ID. So a web request like http://mysite.org/hotels/12345/ will try to load hotel 12345. This gets bound to the method parameter in line 18.
  • Line 17 – the @Produces lists the media types this method produces. This is handy, because in an AJAX style app, I probably wouldn’t want to load the hotel from the database for a HTML request
  • Line 22 – here we return the Model object (in a Model-View-Controller context). This is a JAXB-configured class.

Jersey will map incoming requests to this resource, and to the method, based on what occurs during “conneg” – short for “Content Negotiation”. Out of the box, this will use the Accept header in the HTTP request; we added support for using extensions[1] as well, as it’s easier to test.

Once it’s got the model, it will pass it to a “>MessageBodyWriter; Jersey comes out-of-the-box with decent support for XML (based on JAXB), some support for JSON (also based on JAXB), and we dropped in support for HTML based off Freemarker templates. Jersey also has support for implicit and explicit views, as well as JSP templating, but I don’t know much about that part yet.

Overall Impressions

First – it’s a terrible name. Whenever I google anything about it, I get back hits for the state of New Jersey, or the Isle of Jersey. Folks – when you start a new framework, please please please pick names that don’t already have massive Google-presence.

Second – the framework itself looks pretty powerful, especially once it’s integrated with Spring to leverage its features (such as security and transaction AOP). It looks like it will be able to do what we want with it pretty well.

Third – I love the cleanliness of using the one model class and transforming it via views into different representation. This means, for example, that providing a CSV extract is as simple as coding (once) the rules for turning a generic Java class into a CSV file. Annotations help a lot here, and the MessageBodyWriters are smart enough to be able to chain up, and only write the body out if they support the model. Personally, I like this clean pattern (of using models that populate themselves from the domain to drive views), and Jersey’s support of it suits me.

Fourth – I don’t like the level of documentation. The actual provided docs are very sketchy; most of the knowledge-base is embedded in the mailing lists and the blogs of the main Jersey developers. This, combined with point one, can make information hard to find. If the Jersey folks really want to push it as a mainstream choice, they’ll need to lift their game here a bit.

Fifth – I think it’s great that they’ve got JSON support. It’s fantastic that it uses the JAXB annotations. I’d be happier if the resulting JSON was friendly to work with on the Javascript side. In many ways, their JSON formats (you have a choice of three) seem to be written with an eye to parsing Java side, and they all have quirks. For example, in the default “mapped” format, a one-element list gets rendered like this:

"foo": "bar"

instead of like this:

"foo": ["bar"]

This means you can’t just loop over it in Javascript properly. D’oh! As I don’t care about Java clients parsing the JSON (they can use the XML format), I’m in the process of writing a JSON writer that still leverages the JAXB annotations but is friendlier to the Javascript clients.

Sixth – integration with other frameworks. Still working on this one. Integration with Spring is fine, and from that we get a lot (such as Hibernate support), but I’m curious to see how well I can get other frameworks (e.g. ehcache for page caching) to work. I also had some neat tricks intended to reduce boilerplate in Spring MVC that I’d like to introduce; for example, in Spring MVC I would be able to replace the hotelId parameter shown in the code snippet above with an actual Hotel object that was magically fetched from the repository – this isn’t a big deal when you have one endpoint, but when you start having a few different end points (say – “update”, or “add room”, or “upload photo”), reducing this boilerplate saves a few lines.

Seventh – performance. No idea on this one yet. OTH, we take performance testing pretty seriously at Wotif (we actually do some!), so this one will get address over time.

Overall, it looks interesting, if somewhat not-quite-ready for prime time (due to the doco flaws, mostly – featurewise it’s pretty complete).


[1] the implementation we use is pretty similar to this suggestion on the Jersey forums.

Author: Robert Watkins

My name is Robert Watkins. I am a software developer and have been for over 20 years now. I currently work for people, but my opinions here are in no way endorsed by them (which is cool; their opinions aren’t endorsed by me either). My main professional interests are in Java development, using Agile methods, with a historical focus on building web based applications. I’m also a Mac-fan and love my iPhone, which I’m currently learning how to code for. I live and work in Brisbane, Australia, but I grew up in the Northern Territory, and still find Brisbane too cold (after 22 years here). I’m married, with two children and one cat. My politics are socialist in tendency, my religious affiliation is atheist (aka “none of the above”), my attitude is condescending and my moral standing is lying down.

11 thoughts on “Jersey – a review”

  1. I haven’t tried XStream as a JSON writer – we have used it for XML in the past, and found it a little awkward. For the work I’m currently doing, I’m using json-lib; we’d used that on an earlier project and I was very happy with the resulting JSON.

    In fact, the biggest problem with it I’m having right now is that JAXB doesn’t understand maps properly. This means my model classes look just a little odd when I want a map effect.

  2. how do you find the practice of embedding the request path straight into the class via an annotation? personally, this pattern seems to violate principles of keeping configuration separate from code, especially when you might otherwise be able to use the same class on another path? maybe that’s unneccesary in this case?

    Also perhaps you can elucidate on the basis of the comment about line 17? Is this related to the mapping of the views from the model class that you mention as being the third thing you like about it? Would this mean, a different method per view rendering even if it’s the same object being rendered?

    How does it allow for discoverable ‘next actions’?

  3. I got used to the request path from using annotated Spring MVC. There it wasn’t a problem. It’s worth pointing out that you can do these mappings via descriptors as well (including overriding the annotation), so the mapping is more of a convenience than a necessity.

    Personally, I’m heading down the path of saying that relative URLs within the app aren’t really configuration.

    Re: line 17. In an AJAX app, I may want a HTML request to, say, /hotel/1234/ to bring back the content (HTML, CSS, javascript). This content could be entirely static, with all the dynamic behaviour coming from the javascript interpreting the JSON data. Being able to do content negotiation at that level means I can avoid hitting a method that will load content from the database, if that’s not necessary. Of course, in the example above, I don’t do that.

    As for discoverable “next actions” – that comes down to what you have in your outgoing model. So, for example, you’d want to put in “link” elements – which presumably would be relative (and another good reason to have the URLs within the app not treated as configuration). You also get some “next actions” via convention – e.g to update a hotel, do a POST.

  4. @Robert
    The issue with “foo”: “bar” (list of one) has a workaround. You can configure (see JSONJAXBContext javadoc the JSON processor with a list of array representing elements, and it will do the right thing. I have also recently added a new (NATURAL) notation to Jersey, which takes advantage of better integration with the newest JAXB RI. Then you do not need to configure anything and still get your array serialized correctly as “foo”:[“bar”]

  5. Jakub, thanks for the reply. I was aware of the option of passing through element names that should be arrays, but found it too cumbersome. In addition, that was one of several issues we had with the defined formats.

    My solution is not superior, or even suitable for Jersey generally as the JSON it produces would be hard to parse back to Java code, but it’s a bit easier to deal with as a Javascript client. The natural notation sounds interesting, and I will definitely check it out.

  6. Hi, Robert, I’m working on some documentation for Jersey, so I’m hoping to get more detail on what didn’t work for you with regard to what currently exists. We wanted to use the Jersey wiki page (http://wikis.sun.com/display/Jersey/Main) as the primary location for Jersey documentation so that community members could easily add or edit the existing documentation, and this page has links to all of the other documentation, such as the RESTful Web Services Developer’s Guide, which ships with GlassFish. Anyway, if you could provide a bit more info on what made the docs “sketchy”, I’ll work to improve them. Thanks for the feedback.

  7. Thanks for the post. I’ve played with Jersey a bit and find it quite interesting and useful. I have one question tho…is there a compelling reason for the introduction of JAXB-annotated model classes, as opposed to just adding the JAXB annotations to your domain classes? I found the latter sufficient, but haven’t really thought about why I might want to take your approach instead. Do you feel it important to not clutter (or tie) your domain classes to JAXB due to other factors?

    Dave

  8. Dave,

    I put a layer of Model classes between my domain and my external views because it allows me to refactor with greater safety.

    My domain classes tend to evolve over time, as my understanding of the problem domain improves. Automated refactoring tools, combined with the compiler and extensive unit tests, help ensure that this transformation is safe – within the Java code.

    If I exposed the domain classes directly, then this refactoring would alter the outgoing XML and JSON (or, in a JSP world, alter the beans injected into the JSP scope). These changes are harder to detect and test to be sure they are safe. In addition, if I’m using the XML as an integration point, I’ll have to go and check other pieces of software.

    So I put the model classes in between to allow me the freedom to change the domain without this overhead; refactoring the model classes still requires the discipline mentioned above.

  9. Debbie – I’ll send through a list of problem areas we had with the documentation later today. Thanks for dropping by and extending the invite.

  10. @Robert

    Hi, I am struggling to find documentation for even the simplest example of wiring jersey, spring and hibernate together. Can you point me to a simple example? I don’t mind whether it places the rest annotations in the domain, or uses an intermediate model, any help would be greatly appreciated. Thanks!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: