Handling Generified Collections in Jersey JAX-RS

It is quite possible many developers have run into this problem with Jersey, not really a problem, but limitations of a programming language. I remember from school days where C++ Templates had quite a few reference books and it always kept me away from using STL containers 🙂

In Java, we have Generics since 1.5 which looks lot like C++ Templates, but they are not the same. I am not going to cover the details here, just google it. But, Generics have grown in such complexity that it has dedicated 500+ pages FAQ written and maintained by Angelika Langer for years (JLS 3rd edition is only 684 pages).

Long story short: Generics provides compile time type safety and thus eliminating the need for casts. It is achieved through a compile time phenomenon called type erasure. The Generics FAQ explains everything in detail and it is the Java Generics Bible at least for me.

There are cases when we need to return parameterized types from a JAXRS resource method in the Response. Due to type erasure, it requires special handling in Jersey runtime to determine the generic type that is required to select a suitable MessageBodyWriter.

There are couple options available for JAX-RS developers using Jersey and I am going discuss each one of them in detail.

Let us consider a simple domain model, Employee.

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "employee")
public class EmployeeBean {
    private Long id;
    private String firstName;
    private String lastName;

    public EmployeeBean() {
        // required for JAXB
    }

    public EmployeeBean(Long id, String firstName, String lastName) {
        this.id = id;
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
}

The employee resource shows an example implementation.

@Path("/employees")
public class EmployeeResource {

    @GET
    public Collection<EmployeeBean> getEmployees() {
        EmployeeBean emp = new EmployeeBean(1L, "John", "Doe");
        return Collections.singletonList(emp);
    }
}

In this case, we return the Collection of EmployeeBean from the resource method. The following XML is produced on accessing this resource at http://localhost:9998/employees.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<employeeBeans>
    <employee>
        <firstName>John</firstName>
        <id>1</id>
        <lastName>Doe</lastName>
    </employee>
</employeeBeans>

I would expect to see the list of employees inside <employees> tag instead of <employeeBeans> tag. Hmm, it requires some tweaks to produce that format. So, lets write Employees POJO which embeds the Collection.

@XmlRootElement
public class Employees {
    private List<EmployeeBean> employee;

    public Employees(List<EmployeeBean> employee) {
        this.employee = employee;
    }

    public Employees() {
        // required for JAXB
    }

    public List<EmployeeBean> getEmployee() {
        return employee;
    }

    public void setEmployee(List<EmployeeBean> employee) {
        this.employee = employee;
    }
}

Let us add couple methods to the EmployeeResource using Customers POJO that produces a more relevant XML.

    @GET
    @Path("test1")
    public Employees getEmployees1() {
        EmployeeBean emp = new EmployeeBean(1L, "John", "Doe");
        Employees employees = new Employees(Collections.singletonList(emp));
        return employees;
    }

    @GET
    @Path("test2")
    public Response getEmployees2() {
        EmployeeBean emp = new EmployeeBean(1L, "John", "Doe");
        Employees employees = new Employees(Collections.singletonList(emp));
        return Response.ok(employees).build();
    }

Now, accessing http://localhost:9998/employees/test1 or http://localhost:9998/employees/test2 should produce the following XML.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<employees>
    <employee>
        <firstName>John</firstName>
        <id>1</id>
        <lastName>Doe</lastName>
    </employee>
</employees>

But, do we need this silly logic to produce this output? Not anymore, this has been improved in Jersey 1.2 release. Enabling FEATURE_XMLROOTELEMENT_PROCESSING feature in resource configuration should produce this format out of the box. So, accessing http://localhost:9998/employees/test1 should produce this format of XML. This property is disabled by default.

Now, lets dive into our actual problem of type erasure in case of a parameterized type returned in the JAX-RS Response. I have added another method to our EmployeeResource.

    @GET
    @Path("test3")
    @Produces(MediaType.APPLICATION_XML)
    public Response getEmployees3() {
        EmployeeBean emp = new EmployeeBean(1L, "John", "Doe");
        List<EmployeeBean> list = new ArrayList<EmployeeBean>();
        list.add(emp);
        return Response.ok(list).build();
    }

Now, accessing this method at http://localhost:9998/employees/test3 should spit the following exception. I believe this exception trace is familiar to most Jersey/JAX-RS users.

SEVERE: A message body writer for Java class java.util.ArrayList, and Java type class java.util.ArrayList, and MIME media type application/xml was not found
Jul 24, 2010 11:58:55 PM com.sun.jersey.spi.container.ContainerResponse write
SEVERE: The registered message body writers compatible with the MIME media type are:
application/xml ->
  com.sun.jersey.core.impl.provider.entity.XMLJAXBElementProvider$App
  com.sun.jersey.core.impl.provider.entity.DocumentProvider
  com.sun.jersey.core.impl.provider.entity.SourceProvider$SourceWriter
  com.sun.jersey.core.impl.provider.entity.XMLRootElementProvider$App
  com.sun.jersey.core.impl.provider.entity.XMLListElementProvider$App
*/* ->
  com.sun.jersey.core.impl.provider.entity.FormProvider
  com.sun.jersey.server.impl.template.ViewableMessageBodyWriter
  com.sun.jersey.core.impl.provider.entity.StringProvider
  com.sun.jersey.core.impl.provider.entity.ByteArrayProvider
  com.sun.jersey.core.impl.provider.entity.FileProvider
  com.sun.jersey.core.impl.provider.entity.InputStreamProvider
  com.sun.jersey.core.impl.provider.entity.DataSourceProvider
  com.sun.jersey.core.impl.provider.entity.XMLJAXBElementProvider$General
  com.sun.jersey.core.impl.provider.entity.ReaderProvider
  com.sun.jersey.core.impl.provider.entity.DocumentProvider
  com.sun.jersey.core.impl.provider.entity.StreamingOutputProvider
  com.sun.jersey.core.impl.provider.entity.SourceProvider$SourceWriter
  com.sun.jersey.core.impl.provider.entity.XMLRootElementProvider$General
  com.sun.jersey.core.impl.provider.entity.XMLListElementProvider$General

Jul 24, 2010 11:58:55 PM com.sun.jersey.spi.container.ContainerResponse traceException
SEVERE: Mapped exception to response: 500 (Internal Server Error)
javax.ws.rs.WebApplicationException

To fix this, we need to somehow tell the JAX-RS runtime the type of the response entity, in this case a Collection of Employees. JAX-RS API GenericEntity comes to the rescue. GenericEntity can be used to represent a response entity of a generic type. The EmployeeResource method is updated to use the GenericEntity class when returning a Collection type.

    @GET
    @Path("test4")
    @Produces(MediaType.APPLICATION_XML)
    public Response getEmployees4() {
        EmployeeBean emp = new EmployeeBean(1L, "John", "Doe");
        List<EmployeeBean> list = new ArrayList<EmployeeBean>();
        list.add(emp);
        GenericEntity entity = new GenericEntity<List<EmployeeBean>>(list) {};
        return Response.ok(entity).build();
    }

Accessing http://localhost:9998/employees/test4 should produce the desired output.

In addition to this approach, Jersey 1.2 introduced a new API JResponse to support this better. JResponse is a type safe alternative to Response that preserves the type information of response entity thus it is not necessary to utilize GenericEntity.

The updated Employee resource using JResponse is shown below.

    @GET
    @Path("test5")
    @Produces(MediaType.APPLICATION_XML)
    public JResponse<List<EmployeeBean>> getEmployees5() {
        EmployeeBean emp = new EmployeeBean(1L, "John", "Doe");
        List<EmployeeBean> list = new ArrayList<EmployeeBean>();
        list.add(emp);
        return JResponse.ok(list).build();
    }

Accessing http://localhost:9998/employees/test5 should produce the desired output.

Both these approaches are easy to implement. The major difference is GenericEntity is a JAX-RS API while JResponse is a Jersey API, which may not work with other JAX-RS implementations and thus not portable. If you are just using Jersey, then JResponse is the preferred way as it is type safe and provides all capabilities of the Response.

Here is the Server code that uses Grizzly (provided for completion) :

import com.sun.grizzly.http.SelectorThread;
import com.sun.jersey.api.container.grizzly.GrizzlyWebContainerFactory;
import com.sun.jersey.core.util.FeaturesAndProperties;

import javax.ws.rs.core.UriBuilder;
import java.io.IOException;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;

public class Main {

    private static int getPort(int defaultPort) {
        String port = System.getenv("JERSEY_HTTP_PORT");
        if (null != port) {
            try {
                return Integer.parseInt(port);
            } catch (NumberFormatException e) {
            }
        }
        return defaultPort;
    }

    private static URI getBaseURI() {
        return UriBuilder.fromUri("http://localhost/").port(getPort(9998)).build();
    }

    public static final URI BASE_URI = getBaseURI();

    protected static SelectorThread startServer() throws IOException {
        final Map<String, String> initParams = new HashMap<String, String>();

        initParams.put("com.sun.jersey.config.property.packages", "com.employee.resources");
        initParams.put(FeaturesAndProperties.FEATURE_XMLROOTELEMENT_PROCESSING, "true");

        System.out.println("Starting grizzly...");
        SelectorThread threadSelector = GrizzlyWebContainerFactory.create(BASE_URI, initParams);
        return threadSelector;
    }

    public static void main(String[] args) throws IOException {
        SelectorThread threadSelector = startServer();
        System.out.println(String.format("Jersey app started with WADL available at "
                + "%sapplication.wadlnTry out %shelloworldnHit enter to stop it...",
                BASE_URI, BASE_URI));
        System.in.read();
        threadSelector.stopEndpoint();
    }
}

I hope this clarifies some of the underlying behavior of handling parameterized types in JAX-RS applications.

3 thoughts on “Handling Generified Collections in Jersey JAX-RS

  1. Thanks for the post. It provided *exactly* what I needed. I was in a time crunch and it was such a help to find material that addressed my immediate need with nothing more than a little copy/paste/modify.

    Like

  2. Hi,

    I still have the same problem after the use of GenericEntity with the MediaType application/json. With xml, no problem. Do you have any idea why ?

    Thanks.

    Like

    1. Did you include the Jersey-Json dependency, which is required for JSON rendering? Adding this to your POM should fix this problem.

      [xml]
      <dependency>
      <groupId>com.sun.jersey</groupId>
      <artifactId>jersey-json</artifactId>
      <version>1.3</version>
      </dependency>
      [/xml]

      -Arul

      Like

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