5 minutes with – Jersey Producers

Have you ever tried to have to produce a multiple rappresentation of the same resources? I’m speaking about produce a common json and xml and, also, custom rappresentation like text/html which is not very common for data exchanging.

In this article I’d like to show you how produce multiple response format of Jersey resources. The type of response format will depend from resource extension request.

Let’s start to configure the project and then we’ll go on to see how it works.

I used Maven, so the pom.xml file is the following

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>it.samplejerseymultipleproduces</groupId>
	<artifactId>SampleJerseyMultipleProduces</artifactId>
	<version>1.0.0</version>
	<packaging>war</packaging>

	<dependencies>
		<dependency>
			<groupId>asm</groupId>
			<artifactId>asm</artifactId>
			<version>3.3.1</version>
		</dependency>
		<dependency>
			<groupId>com.sun.jersey</groupId>
			<artifactId>jersey-json</artifactId>
			<version>1.14</version>
		</dependency>
		<dependency>
			<groupId>com.sun.jersey</groupId>
			<artifactId>jersey-bundle</artifactId>
			<version>1.14</version>
		</dependency>
	</dependencies>

</project>

A very useful notes is how run this project in eclipse with Tomcat embedded. Briefly, you have to edit the Web Deployment Assembly into your eclipse project.

More details are available here http://horrikhalid.wordpress.com/2011/01/24/you-want-to-debug-your-maven-project-with-embedded-tomcat-in-eclipse-now-its-easy/.
As I said before, We need to associate the url request extension with the response resource type. To make it possible, we must extend a com.sun.jersey.api.core.PackagesResourceConfig class


package it.samplejerseymultipleproduces.config;

import java.util.HashMap;
import java.util.Map;

import javax.ws.rs.core.MediaType;

import com.sun.jersey.api.core.PackagesResourceConfig;

public class OrderResourceConfig extends PackagesResourceConfig {
	public OrderResourceConfig(Map<String, Object> props) {
		super(props);
	}

	@Override
	public Map<String, MediaType> getMediaTypeMappings() {
		Map<String, MediaType> mediaTypeMap = new HashMap<String, MediaType>();

		mediaTypeMap.put("json", MediaType.APPLICATION_JSON_TYPE);
		mediaTypeMap.put("xml", MediaType.APPLICATION_XML_TYPE);
		mediaTypeMap.put("js", MediaType.valueOf("text/javascript"));
		mediaTypeMap.put("html", MediaType.TEXT_HTML_TYPE);

		return mediaTypeMap;
	}
}

We’ve associated the url extension json, xml, js and html with the relative response mime type. You can see that configured into the web.xml


<web-app id="WebApp_ID" version="2.4"
	xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
	http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
	<display-name>JerseyMultipleProduces</display-name>

	<servlet>
		<servlet-name>jersey-multipleproduces</servlet-name>
		<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
		<init-param>
			<param-name>com.sun.jersey.config.property.packages</param-name>
			<param-value>it.samplejerseymultipleproduces.rest;it.samplejerseymultipleproduces.config</param-value>
		</init-param>
		<init-param>
			<param-name>com.sun.jersey.config.property.resourceConfigClass</param-name>
			<param-value>it.samplejerseymultipleproduces.config.OrderResourceConfig</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<servlet-mapping>
		<servlet-name>jersey-multipleproduces</servlet-name>
		<url-pattern>/rest/*</url-pattern>
	</servlet-mapping>

</web-app>

The core code of the article is the implementation of MessageBodyWriter for both text/javascript and text/html. Let’s see how it works.

package it.samplejerseymultipleproduces.config;

import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

import it.samplejerseymultipleproduces.entity.Order;

import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;

import org.codehaus.jackson.map.ObjectMapper;

@Produces("text/javascript")
@Provider
public class JsWriterProvider implements MessageBodyWriter<Order> {

	public boolean isWriteable(Class<?> type, Type genericType,
			Annotation[] annotations, MediaType mediaType) {
		return true;
	}

	public long getSize(Order t, Class<?> type, Type genericType,
			Annotation[] annotations, MediaType mediaType) {
		return -1;
	}

	public void writeTo(Order order, Class<?> type, Type genericType,
			Annotation[] annotations, MediaType mediaType,
			MultivaluedMap<String, Object> httpHeaders,
			OutputStream entityStream) throws IOException,
			WebApplicationException {

		ObjectMapper mapper = new ObjectMapper();
		String jsonOrder = "";

	    try {
	    	jsonOrder = mapper.writeValueAsString(order);
	    } catch (Exception e) {
	      e.printStackTrace();
	    }
		entityStream.write(jsonOrder.getBytes());
	}
}

The interface javax.ws.rs.ext.MessageBodyWriter contains three methods. From Java documentation:

  • getSize: Called before writeTo to ascertain the length in bytes of the serialized form of the object.
  • isWriteable: Ascertain if the MessageBodyWriter supports a particular type.
  • writeTo: Write a type to an HTTP response.

Now, let’s see the Jersey service class. Into that file we’ve configured only one resource (getOrder()) with multiple response type.

package it.samplejerseymultipleproduces.rest;

import it.samplejerseymultipleproduces.entity.Order;

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

@Path("/service")
public class OrderService {

	@GET
	@Path("/order")
	@Produces({
		MediaType.TEXT_XML ,
		MediaType.APPLICATION_XML,
		MediaType.APPLICATION_JSON,
		MediaType.TEXT_HTML,
		"text/javascript" })
	public Order getOrder() {

		Order order = new Order();
		order.setOrderId("cb123");
		order.setInvoice(120);
		order.setItemNumber(100);
		return order;
	}
}

The last codes are about text/html response type and Order object return.


package it.samplejerseymultipleproduces.config;

import it.samplejerseymultipleproduces.entity.Order;

import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;

@Produces(MediaType.TEXT_HTML)
@Provider
public class HtmlWriterProvider implements MessageBodyWriter<Order> {

	public boolean isWriteable(Class<?> arg0, Type arg1, Annotation[] arg2,
			MediaType arg3) {
		return true;
	}

	public long getSize(Order t, Class<?> type, Type genericType,
			Annotation[] annotations, MediaType mediaType) {

		return -1;
	}

	public void writeTo(Order order, Class<?> type, Type genericType,
			Annotation[] annotations, MediaType mediaType,
			MultivaluedMap<String, Object> httpHeaders,
			OutputStream entityStream) throws IOException,
			WebApplicationException {

		String result = "<html>\n" + "<body>\n" + "<table border=\"1\">\n"
				+ "<tr>\n" + "<td>Id Order</td>\n" + "<td>"
				+ order.getOrderId() + "</td>\n" + "</tr><tr>\n"
				+ "<td>Number Of Item</td>\n" + "<td>" + order.getItemNumber()
				+ "</td>\n" + "</tr><tr>\n" + "<td>Invoice</td>\n" + "<td>"
				+ order.getInvoice() + "</td>\n" + "</tr>\n" + "</table>\n"
				+ "</body>\n" + "</html>";

		entityStream.write(result.getBytes());
	}
}

The Order object:

package it.samplejerseymultipleproduces.entity;

import java.io.Serializable;
import javax.xml.bind.annotation.XmlRootElement;
import org.codehaus.jackson.annotate.JsonProperty;

@XmlRootElement(name = "orders")
public class Order implements Serializable {

	private static final long serialVersionUID = -3631611117467960241L;

	@JsonProperty("id")
	private String orderId;

	@JsonProperty("invoice")
	private double invoice;

	@JsonProperty("number")
	private int itemNumber;

	public String getOrderId() {
		return orderId;
	}

	public void setOrderId(String orderId) {
		this.orderId = orderId;
	}

	public double getInvoice() {
		return invoice;
	}

	public void setInvoice(double invoice) {
		this.invoice = invoice;
	}

	public int getItemNumber() {
		return itemNumber;
	}

	public void setItemNumber(int itemNumber) {
		this.itemNumber = itemNumber;
	}

}

Now, run tomcat and browse the Urls:
<host>/SampleJerseyMultipleProduces/rest/service/order.js
<host>/SampleJerseyMultipleProduces/rest/service/order.json
<host>/SampleJerseyMultipleProduces/rest/service/order.xml
<host>/SampleJerseyMultipleProduces/rest/service/order.html

You should receive a text/javascript, application/json, application/xml and text/html response.

That’s all.

Advertisements

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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s