Monday, May 20, 2013

Handling different subresources with JAX-RS subresource locator

1   Introduction


 In this article I won't explain what a resource or subresource is, since there are several pages that explain perfectly well its meaning. For example, you can check Oracle tutorial or Jersey documentation. I will focus on implementing a RESTful service with a subresource locator which will decide at runtime what type of subresource will be returned.

You can check the source code on github.


2   Configuration


You need to include the JAX-RS reference implementation libraries:
The first two dependencies are necessary if you want to develop services, while the third contains the implementation to convert your classes to JSON.

Next step is to define the Jersey servlet that will handle requests to our services. Include the content below on your web.xml file:
I have included two init parameters:

  • com.sun.jersey.config.property.packages: Required. It must define one or more packages separated by ";". These are the packages where your resource classes must be located.

  • com.sun.jersey.api.json.POJOMappingFeature: Activates POJO support, which means that it will use the Jackson library to convert your Java Objects to JSON and the other way back.

We are done with the configuration, let's implement the resources.


3   The root resource


To declare a root resource you must annotate your class with @Path.
The @GET annotated getWarehouseInfo() method will handle requests to the root resource, so when the user enters the following URI:

http://localhost:8080/rest-subresources/rest/warehouse


It will return the warehouse information.

Take into account that if I had included the @Path annotation in conjunction with @GET, I would be declaring a subresource method.

The getItem method is annotated with @Path but not with any request method designator (get, post...). This is because I'm declaring a subresource locator. This subresource locator will return an object  that will be capable of handling the HTTP request, but which one? It will depend on the id parameter. Both the TypeAResource and TypeBResource implement the same interface ItemResource, so we can return any of them.


4   Subresources


These subresources have a method with a request method designator, but no @Path annotation has been included.
The following requests will return a different subresource:

http://localhost:8080/rest-subresources/rest/warehouse/items/1: Will return a Type B resource:

{"id":1,"price":33.5,"type":"B"}

http://localhost:8080/rest-subresources/rest/warehouse/items/12: Will return a Type A resource:

{"id":12,"price":35.5,"type":"A","code":"12SS34"}


5   Possible mistakes


I'm including some errors that may occur if you fail to configure the application properly:

Fail on deploy. com.sun.jersey.api.container.ContainerException: The ResourceConfig instance does not contain any root resource classes

You did not define the packages init-param, so it will fail to find nor load your resource classes.
Fail on runtime. com.sun.jersey.api.MessageException: A message body writer for Java class [yourModelClass], and Java type class [yourModelClass], and MIME media type application/json was not found.

You did not define the JSON feature on web.xml. It won't be able to convert your object to JSON.
Fail on runtime. com.sun.jersey.api.MessageException: A message body writer for Java class [yourModelClass], and Java type class [yourModelClass], and MIME media type application/json was not found.

This error might also be produced because the JSON maven dependency was not included.
Fail on deploy. java.lang.ClassNotFoundException: com.sun.jersey.spi.container.servlet.ServletContainer.

The jersey-servlet maven dependency was not included. Older versions of Jersey included this class in jersey-core library, but in newer versions they have put it in a separate jar.

1 comment: