Search This Blog

Sunday, February 19, 2012

Jersey JAX-RS MVC Killed the Spring MVC Star

JAX-RS is arguably the de-facto standard for creating RESTful web services in Java. Most JAX-RS providers have a way to implement a de-coupled MVC paradigm; much like Spring MVC. This BLOG will demonstrate how Jersey JAX-RS can be used as an MVC web framework and will spend some time discussing the difference between Jersey MVC and Spring MVC. As part of the investigation, the Flight Application that I have previously demonstrated using Spring MVC will be translated to use Jersey MVC.

Some of the additional goals of this BLOG are:
1. Integrate with Spring Security for authentication/authorization
2. Use Spring for the Dependency injection
3. Use Twitter Bootstrap for the view part

Front Controller:
Both Spring MVC and Jersey have an implementation of the Front Controller design pattern with the DispatcherServlet from Spring MVC and ServletContainer from Jersey. The former dispatches to Controller classes while the later dispatches to Resource classes.

Controller/Resource:
In a typical Spring MVC Controller we have a ModelAndView object returned with data for the view tier to digest:
@Controller
@Request("/airports.html")
public class AirportsController {
  @RequestMapping(method = RequestMethod.GET)
  public ModelAndView getAirports() {
    List<Airport> airports = airportService.getAirports();

    ModelAndView mav = new ModelAndView("/airports");
    mav.addObject("airports", airports);

    return mav;
  }
}
Jersey provides a class equivalent to ModelAndView called Viewable that defines the view to be used and also houses the model objects:
@Path("/airports.html")
public class AirportsResource {
  @GET
  public Viewable getAirports() {
    List<Airport> airports = airportService.getAirports();
    Map<String, List<Airports>> modelMap = Maps.newHashMap();
    modelMap.put("airports", airports);

    return new Viewable("/airports", modelMap);
  }
}
View Processing:
Spring MVC provides View Resolvers to resolve the template name to a template reference.
In the Jersey MVC land, implementations of the ViewProcessor interface are used to resolve a template name to a template reference. In addition, they are also responsible for processing the template and the results of which are written to the output stream. One such processor is available in the Jersey code base for JSP's, i.e., the JSPTemplateProcessor.  The processor requires the location of the JSP files and the same is made available via the argument of JSTTemplatesBasePath.  The Viewable interface lends itself to providing alternate View Processors for other templating languages like Free Marker and Velocity but the same would need to be built:
<filter>
  <filter-name>jersey</filter-name>
  <filter-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</filter-class>
    <init-param>
      <param-name>com.sun.jersey.config.property.JSPTemplatesBasePath</param-name>
      <param-value>/WEB-INF/pages</param-value>
    </init-param>
</filter>
View:
In the JSP, all the model objects provided by the Resource tier are easily accessed by using a prefix of "it".  For example
${it.airports} as shown below:
<display:table name="${it.airports}" class="table table-condensed"
     requestURI="" id="a" export="true" pagesize="10">
   <display:column title="Code" property="code" sortable="true"/>
   <display:column title="Name" property="name" sortable="true" />
   <display:column title="City" property="city" sortable="true"/>
</display:table>
With the above, one has the exact same functionality between Spring MVC and Jersey. It is important to note that with Spring MVC however, there are a bunch of options for the return type by which the view is resolved. Spring MVC follows a convention based approach for its Controllers and return types can be quite a few:
  • ModelAndView object
  • Model object, for example, a List of Airports
  • Map of Model objects
  • A View object
  • A String that represents a View name
I will not get into details of the how the above are achieved by Spring MVC as it is beyond the scope of this BLOG. It is sufficient to accept that Spring MVC does support these alternate return types and follows a convention based approach to view resolution.

Accessing Request Data:
Web applications need to have access to request information like HTTP Headers, Cookies, Query Parameters and Form data (discussed later).  Both Spring MVC and Jersey JAX-RS have annotations that help extract the same.
@RequestMapping(method=GET)
public void foo(@RequestParam("q") String q, @CookieValue("c") String c, 
                @RequestHeader("h") String h) {
    // Spring MVC
}

@GET 
@Path
public void foo(@QueryParam("q") String q, @CookieParam("c") String c,
    @HeaderParam("h") String h) {
    // JAX-RS
}

Form Data Management:
Handling Form data and validating the same is one area where every MVC framework has to be tested against.

From a binding perspective, when a form is submitted, one would like to have the form details map to a POJO. With Spring MVC, a form POJO is automatically populated with the details of the HTML Form coupled with the ability of having the contents of the Form being validated and results made available via a BindingResult object as shown below:
public class FlightController {
  @RequestMapping(method=POST)
  public ModelAndView searchFlights(FlightSearchCriteria criteria, BindingResult result) {
     ...
     if (result.hasErrors()) { ... }
     ..
  }
}
In the JAX-RS domain, there are a few ways to access Form parameters:

1. Using the Form object or a MultiValuedMap and explicitly extracting the form data by name:
    public Viewable searchFlights(Form form) {
      String fromAirport = form.getFirst("from");
      String toAirport = form.getFirst("to");
      ...
    }
2. Using @FormParam annotation for auto injection of Form parameters:
public Viewable searchFlights(@FormParam("from") String fromAirport, @FormParam("to") String toAirport) {
   ...
}
3. Using @InjectParam with a Java POJO whose attributes are annotated with @FormParam:

Using @FormParam when there are  a few Form parameters works well but fails when there are multiple parameters as the same translates to a very long method signature. In such cases a single object can house the individual parameters as shown below with the Object being injected automatically to the controller method due to the magic of Jersey's Injectable annotation. Read more about the same from Valotas BLOG:
public class FlightSearchCriteria {
  @FormParam("from")
  String fromAirport;

  @FormParam("to")
  String toAirport;
  ...
}

public class FlightResource {
  public Viewable searchFlights(@InjectParam FlightSearchCriteria criteria) {
    ....
  }
}
With the above, Jersey's MVC is able to map a form object to a POJO but it does involve adding annotations of @FormParam for each form parameter. With the Spring MVC binding, one did not have to deal with the same. In addition, the above examples of Jersey MVC are missing validation of the Form. To achieve the same, one might be able to create a custom Injection Provider to validate and provide the validation results as part of the method signature. I did not find a ready way to do the automatic validation and thus leave that effort for the making of a future BLOG post. That said, validation can easily be performed using JSR 303 validation or a custom validator easily enough programatically within the Jersey MVC resource method:
public Viewable searchFlights(@InjectParam FlightSearchCriteria criteria) {
    ...
    Set<ConstraintViolation<FlightSearchCriteria>> violations = validator.validate(criteria);

    if (violations.size() > 0) {
      modelMap.put("errors", violations);

      return ...;
    }
    ...
}
On the view, displaying the errors on a global level is pretty easy using JAX-RS, as one can simply loop over the violations and display the violation message. Displaying an error message corresponding to each violation explicitly, well Spring MVC has tag support to do the same and although achievable by writing a custom tag, it is an added effort for a Jersey MVC application.

Http Session Variables:
If an application wishes to use the Http Session for managing state, Spring MVC has annotated convention based support for the same. Consider the following example from the petclinic app where managing the lifecycle of the pet in the HTTP session is shown:
@Controller
@SessionAttributes("pet")
public class PetController {
  @RequestMapping(method=GET)
  public Pet get(@RequestParam int id) {
   Pet pet = petService.getPet(id);
   ..
   return pet; // Pet added to Session
  }
  
  @RequestMapping(method=POST)
  public String update(Pet pet, BindingResult result, SessionStatus status) {
    ..
    petService.update(pet);
    
    status.setComplete(); // Remove Pet from session
    ...
  }
}

I did not see an equivalent offering with Jersey MVC.

Security:
As the Spring Security framework is not tightly coupled to Spring MVC, integrating the framework to support the Jersey MVC application is trivial.  On the view tier, Spring Security Tags readily provide access control. If one does not wish to use Spring Security on the web framework, one can easily roll out their own with access being restricted by Jersey filters or Servlet Filters.

Thoughts on Jersey JAX-RS MVC versus Spring MVC:
JAX-RS is a great framework tailored toward creating RESTful web services and Jersey is a solid implementation of the same. As Jersey is a JAX-RS implementation, one could swap out Jersey for another provider like RestEasy if desired without much effort. Spring MVC on the other hand has REST web service support but the same is not a JAX-RS implementation and therefore one is tied to Spring MVC. Read more about a RESTful comparison of Spring MVC and JAX-RS on a very nice INFOQ BLOG

From the perspective of an MVC application, as shown, creating a web application using Jersey is clearly achievable.  It is important to note that one is leaving the JAX-RS specifications and heading into custom Jersey land while doing so though. On the controller tier, I find that the ease of binding Form objects to POJOs is not as full fledged as Spring MVC's offering. The lack of JSR 303 support via automatic validation of Form objects with Jersey MVC is a bit of a downer but not a show stopper. Spring MVC's controller and convention based routing allow for a multitude of return types from a Controller method that Jersey does not seem to have out of the box. Whether the same is a benefit or a pitfall is debatable. 

On the view tier, Spring MVC has strong support for the different templating languages. Be it Free Marker, Velocity, Excel, Jasper Reports, what have you. In addition, Spring MVC out of the box provides a way to chain view resolvers something that Jersey MVC does not have. Spring MVC also comes built in with a rich set of tag libraries supporting JSP, Free Marker and Velocity. There is no such support automatically available via Jersey JAX-RS. Spring MVC also has a strong user community and support in the form of books, forums and blogs working in its favor.

I think back at analogy of designing a bedroom. One can go to a furniture store and simply buy a bedroom set where all components mesh well together, i.e., Spring MVC or one can choose to make the same by buying and building out the components individually, i.e., Jersey MVC. Both approaches will lead one to the same bedroom but how hard was it to get in? When you are in a hurry, the fastest path is welcomed, if you know what I mean ;-) All puns definitely intended ! A colleague of mine also pointed out that Jersey JAX-RS would be driven to make steads in further releases on its core offering, i.e, RESTful services but Spring MVC would make strides on enhancing its web application  support. This is definitely a factor one must consider when deciding between Jersey MVC and Spring MVC.

On the other hand, arguably, if ones application is not very big and a lot of the benefits provided by Spring MVC can be ignored in favor of a light weight MVC application, Jersey MVC can easily fit that role. In particular Jersey MVC will work quite well for web applications that are heavy on Ajax, use client side validation and with partial round trips to the server as they can utilize the strong RESTful support (JSON, XML etc) from Jersey and use the MVC part of Jersey for page transitions.

Condensing, I think Jersey JAX-RS is getting close on the MVC support but for now the Spring MVC star is not extinguished by the Jersey JAX-RS/MVC Juggernaut. If I have not understood the features of Jersey's MVC support correctly, please do comment and clarify the same.

The Jersey MVC Example:
The Jersey MVC Example can be downloaded from HERE. After extracting the file,  execute a "mvn jetty:run" from the root of the project and access the application at http://localhost:8080. The user credentials are the same as in my Spring MVC Security example. Once you get in, play with the validation and scrutinize the source. The example uses Spring for DI.

The styling itself is using Twitter Bootstrap, thanks to a good friend of mine pushing me to try it. I am sadly an average front end developer and therefore offer my thanks to the supremo of web development, Matt Raible, and his customization of App Fuse, based of which, I have been able to change the style of the Flight Application. On Twitter Bootstrap, I did like the ease it presented of creating a decent looking web application but I must lean on Matt's BLOG and his assessment that although Twitter Bootstrap is a great starter template, it is by no means a substitute for a template created by a solid CSS developer.

11 comments:

Anonymous said...

This is a great article; I've been using jersey mvc and been hesitant to continue using it due to spring's pojo binding and form validation, but I will give what you suggest a try.

Thanks!

Sanjay Acharya said...

Thanks much. I am curious, what view technology have you been using Jersey MVC with, JSP/FreeMarker/Velocity or something else?

Unknown said...

Interesting article, but the conclusion does not seem to match the title. Title implies that you prefer Jersey while the rest of the article seems to indicate that you still prefer Spring MVC and Jersey is maybe approaching parity. Did I misunderstand your conclusion or was the title intended to be the reverse?

Sanjay Acharya said...

@Dmitry, you are right, it does not. I was the questioning the need for Spring MVC at the time of writing the article and thus the title. As I went on investigating into Jersey MVC as a full fledged replacement, as you noticed I did not feel that it had reached parity as yet with Spring MVC. So yes the title does not match the final thoughts I left.

Abhishek S Oza said...

Mr Acharya,
I cannot get any reference to @Request annotation, you have used in the code above. Can you please tell us which jar/package it belongs to? It is neither anywhere in this class heirarchy tree: http://static.springsource.org/spring/docs/3.1.x/javadoc-api/overview-tree.html , nor can I find any reference on the net. The only webpage that had something about @Request was this https://gist.github.com/1294957 . Have you used the same Request interface ? Thanks in advance.
-Abhishek Oza

Sanjay Acharya said...

@Abhishek, you are right. I have a typo. The annotation should be @RequestMapping for the Spring Controller. Thanks for pointing it out.

Adam Gent said...

What will be interesting is how the async (aka web-sockets-like) work for Spring 3.2 and Jersey 2.0 will be different.

Right now for both Jersey (< 2.0) and Spring (< 3.2) you need to use Atmosphere. Atmosphere's support for Jersey's is a little cleaner than Spring but Spring 3.2's version looks nice also.

Like many of the other posters I use Spring most of the time. It does more than Jersey and most cases I already have it as a dependency for DI.

Anonymous said...

Hey,

nice walkthrough and works fine for me. Maybe you can ad some security features to avoid XSS because if you add something like "script alert("XSS"); script" (with angle brackets) to the "Create Ariport" form than at the next refresh the javascript alert gets executed.

Ryan said...

Have you been able to use the spring-test-mvc to integration test your jax-rs 2.0 resources?

Jakub said...
This comment has been removed by the author.
Jakub said...

Where does airportService come from in case of Jersey controller? Can it be injected as spring service (in jsf it is possible)? How does jersey integrate with spring ?