Jersey + Guice on Google App Engine Java

This tutorial will demonstrate how to setup Google Guice with JAX-RS Jersey on top of Google App Engine Java platform. I have just started learning dependency injection and was looking for a light weight DI framework. There are a few Java based DI container in the market like Spring, Guice, and Pico. I found Guice to be easy and light weight enough for my need. First, I will show how to configure Jersey to make it work with Guice using Guice’s Servlet module. I will also show how you can inject your own business level service classes using Guice. My Jersey resource class PlayerResource.java is using a few third party libraries like Objectify, Simple XML , and Jackson. Objectify is being used to persistence to GAEJ Datastore. Simple and Jackson are being used to transform Java objects to XML and JSON stream respectively. PlayerResource will use Guice to get references to all these third party libraries.

Sandbox Environment:

  • OS : Windows XP
  • App Engine SDK : 1.3.0 with Google Eclipse Plugin
  • IDE : Eclipse Galieleo
  • 3rd Party Libraries & Frameworks :
    • Simple XML : http://simple.sourceforge.net/
    • Objectify : http://code.google.com/p/objectify-appengine/
    • Jackson
  • Jersey 1.1.5-ea-SNAPSHOT
  • Guice 2.0

web.xml

You will need to prepare you web.xml file with a Guice listener class as bellow.

<?xml version="1.0" encoding="utf-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	version="2.5">

    <!-- Guice integration example -->
    	<listener>
			<listener-class>ausbdsoccer.server.resources.GuiceConfig</listener-class>
		</listener>

		<filter>
			<filter-name>guiceFilter</filter-name>
			<filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
		</filter>

		<filter-mapping>
			<filter-name>guiceFilter</filter-name>
			<url-pattern>/*</url-pattern>
		</filter-mapping>
     <!-- -->

	<!-- Default page to serve -->
	<welcome-file-list>
		<welcome-file>admin.html</welcome-file>
	</welcome-file-list>

</web-app>

GuiceConfig

This is the place where you create Guice injectors for Jersey and for your own business service objects. A parameter has been defined to expose all Jersey annotated resources in “ausbdsoccer.server.resources” package. Guice ServletModule has been overridden to intercept all incoming request with a url pattern of http://<yourdomain.com>/rest/*, that will be directed to your Jersey resources. Also note that I am referencing GuiceModule class. This GuiceModule defines all Guice bindings for my business objects.

package ausbdsoccer.server.resources;

import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;

import ausbdsoccer.server.services.GuiceModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.servlet.GuiceServletContextListener;
import com.google.inject.servlet.ServletModule;
import com.sun.jersey.guice.spi.container.servlet.GuiceContainer;

public class GuiceConfig extends GuiceServletContextListener {
	Logger log = Logger.getLogger(GuiceConfig.class.getName());
	
	protected Injector getInjector() {
		
		final Map&lt;String, String&gt; params = new HashMap&lt;String, String&gt;();
		/*
		 * The following line will scan ausbdsoccer.server.resources package for Jersey Resources
		 */
		//params.put(&quot;com.sun.jersey.config.property.packages&quot;,&quot;ausbdsoccer.server.resources&quot;);
		
		/*
		 * The following line will use MainJerseyApplication.java to define Jersey resources
		 */
		params.put(&quot;javax.ws.rs.Application&quot;, &quot;ausbdsoccer.server.resources.MainJerseyApplication&quot;);
		
		return Guice.createInjector(
				new GuiceModule(),
				new ServletModule() {
					@Override
					protected void configureServlets() {
						//bind(PlayersResource.class); //Works
						//bind(MainJerseyApplication.class); //Does not work 
						serve(&quot;/rest/*&quot;).with(GuiceContainer.class, params);
					}
				});
	}
}

MainJerseyApplication

This is where we define Jersey resources.

package ausbdsoccer.server.resources;

import java.util.HashSet;
import java.util.Set;

import javax.ws.rs.core.Application;

import ausbdsoccer.server.services.MessageBodyWriterJSON;
import ausbdsoccer.server.services.MessageBodyWriterXML;


public class MainJerseyApplication extends Application {
	public Set&lt;Class&lt;?&gt;&gt; getClasses() {
		Set&lt;Class&lt;?&gt;&gt; s = new HashSet&lt;Class&lt;?&gt;&gt;();
		s.add(PlayersResource.class);
		s.add(AuthorizationResource.class);
		s.add(MessageBodyWriterXML.class);
		s.add(MessageBodyWriterJSON.class);
		return s;
	}
}

Object Serialization to XML and JSON for Response (Updated)

These set of objects are used to convert Java value objects to XML and JSON streams. I’ve omitted import statements to save space. {Update: as per Andrew Booth’s suggestion I’ve refactored code to use MessageBodyWriter for XML and JSON response serialization. I got it to work by adding my two implementations (i.e. MessageBodyWriterXML and MessageBodyWriterJSON) by registering them in my MainJerseyApplication.java class. Thanks Andrew 🙂 .}


MessageBodyWriterXML

For detail description of how to implement MessageBodyWriter interface for custom serializer in Jersey please read section
3.5.5. Java instances and types for representations from Jersey User Guide. Also, if you have the book Restful Java with Jax-RS ~ Bill Burke , you can read section titled “Custom Marshalling” on Chapter 6 for a great description of MessageBodyWriter.

MessageBodyWriterXML.java is an implementation to serialize my Java types to XML responses using Simple XML. This is how you can implement your own custom serializer in Jersey.

package ausbdsoccer.server.services;

import java.io.IOException;
import java.io.OutputStream;
import java.io.StringWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.logging.Logger;

import javax.ws.rs.Produces;
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.simpleframework.xml.Root;
import org.simpleframework.xml.Serializer;
import org.simpleframework.xml.core.Persister;


@Provider
@Produces(&quot;application/xml&quot;)
public class MessageBodyWriterXML implements MessageBodyWriter {
	Logger log = Logger.getLogger(MessageBodyWriterXML.class.getName());

	@Override
	public long getSize(Object obj, Class type, Type genericType,
			Annotation[] annotations, MediaType mediaType) {
		return -1;
	}

	@Override
	public boolean isWriteable(Class type, Type genericType,
			Annotation annotations[], MediaType mediaType) {
		//Make sure that class to be serialized using Simple XML has org.simpleframework.xml.Root annotation
		//TODO: Come up with our own Annonation class as marker to identify if a class is usable by Siimple XML
		return type.isAnnotationPresent(Root.class);
		//return true;
	}

	@Override
	public void writeTo(Object target, Class type, Type genericType,
			Annotation[] annotations, MediaType mediaType,
			MultivaluedMap httpHeaders, OutputStream outputStream)
			throws IOException {
		log.info(target.getClass().getName());

		Serializer serializer = new Persister();
		StringWriter stringWriter = new StringWriter();
		try {
			serializer.write(target, outputStream);
		} catch (Exception e) {
			log.severe(e.getMessage());
		}
	}
}

MessageBodyWriterJSON

This class acts as a custom serializer my Java types to JSON using Jackson by implementing MessageBodyWriter interface.

package ausbdsoccer.server.services;

import java.io.IOException;
import java.io.OutputStream;
import java.io.StringWriter;
import java.lang.reflect.Type;
import java.util.logging.Logger;
import java.lang.annotation.Annotation;

import javax.ws.rs.Produces;
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.JsonGenerationException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;

@Provider
@Produces(&quot;application/json&quot;)
public class MessageBodyWriterJSON implements MessageBodyWriter{
	Logger log = Logger.getLogger(MessageBodyWriterJSON.class.getName());

	@Override
	public long getSize(Object obj, Class type, Type genericType,
			Annotation[] annotations, MediaType mediaType) {
		return -1;
	}

	@Override
	public boolean isWriteable(Class type, Type genericType,
			Annotation annotations[], MediaType mediaType) {
		
		//TODO: Come up with our own Annonation class as marker to identify if a class can be serialized using Jackson
		return true;
	}

	@Override
	public void writeTo(Object target, Class type, Type genericType,
			Annotation[] annotations, MediaType mediaType,
			MultivaluedMap httpHeaders, OutputStream outputStream)
			throws IOException {
		log.info(target.getClass().getName());

		ObjectMapper om = new ObjectMapper();
		StringWriter sw = new StringWriter();
		try {
			om.writeValue(outputStream, target);
		} catch (JsonGenerationException e) {
			log.severe(e.getMessage());
			throw e;
		} catch (JsonMappingException e) {
			log.severe(e.getMessage());
			throw e;
		} catch (IOException e) {
			log.severe(e.getMessage());
			throw e;
		}
	}
}

DAOBase

This is a Objectify specific object that I’m using for datastore persistence. Here I’m registering all my Java objects that needs to be persisted, namely Player.class and SoccerEvent.class.

.....
......
public class DAO extends DAOBase {
	static {
		ObjectifyService.register(SoccerEvent.class);
		ObjectifyService.register(Player.class);
		ObjectifyService.setDatastoreTimeoutRetryCount(3);
	}
}
.....
...

DAOPlayer

This is a subclass specific to Player.class. All methods declared here are performing some kind of storage operation to and from datastore using Objectify framework.

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import ausbdsoccer.server.resources.Player;
import ausbdsoccer.server.resources.Players;

import com.googlecode.objectify.Objectify;
import com.googlecode.objectify.ObjectifyService;
import com.googlecode.objectify.Query;

public class DAOPlayer extends DAO {
	Logger log = Logger.getLogger(DAOPlayer.class.getName());

	public Player findAPlayer(String name){
		Player found = ofy().find(Player.class,name);
		if (found == null)
			return null;
		else
			return found;
	}

	public Player createAPlayer(String name, String email, String telephone){
		Player player = ofy().find(Player.class,name);
		if (player == null){
			player =new Player(name,email,telephone);
			ofy().put(player);
			return player;
		}
		else {
			return player;
		}
	}

...........
..............
..........

	public Players findAllPlayers(){

		Objectify ofy = ObjectifyService.begin();
		Query&lt;Player&gt; q = ofy.query(Player.class);
		log.info(&quot;Count of players in query: &quot; + q.countAll());
		Iterable&lt;Player&gt; playerItbl = q.fetch();
		ArrayList&lt;Player&gt; playersList = new ArrayList&lt;Player&gt;();
		for (Player p : playerItbl) {
			playersList.add(p);
		}
		log.info(&quot;Count of players in List&lt;Player&gt; : &quot; + playersList.size());
		Players players = new Players(playersList);
		//players.setPlayers(playersList);
		return players;
	}

}

Player

Just for the completeness of this tutorial I’m listing my vo object Player here. @Entity and @Id annotations are needed for Objectify framework. @Root and @Element annotations are getting used by Simple XML framework. For Jackson (JSON) library I did not need any annotations but my Player class needed getters and setters for Jackson to make Player serialized into JSON objects.

package ausbdsoccer.server.resources;

import javax.persistence.Entity;
import javax.persistence.Id;

import org.simpleframework.xml.Element;
import org.simpleframework.xml.Root;


@Entity
@Root
public class Player {

	@Element 	@Id 	String name;
	@Element 	String email;
	@Element	String phoneNumber;
	
	private Player() {
	}
	
	public Player(String name, String email, String phoneNumber) {
		this.name = name;
		this.email = email;
		this.phoneNumber = phoneNumber;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public String getPhoneNumber() {
		return phoneNumber;
	}

	public void setPhoneNumber(String phoneNumber) {
		this.phoneNumber = phoneNumber;
	}

	@Override
	public String toString() {
		return &quot;Player [name=&quot; + name + &quot;, email=&quot; + email + &quot;,  phoneNumber=&quot;
				+ phoneNumber + &quot;]&quot;;
	}
}

GuiceModule

You define your binding in GuiceModule. The bind statement is mapping my DAO class.

package ausbdsoccer.server.services;

import com.google.inject.AbstractModule;
import com.google.inject.Singleton;
import com.google.inject.name.Names;

public class GuiceModule extends AbstractModule {
	public GuiceModule() {
	}

	@Override
	public void configure() {
		bind(DAO.class).annotatedWith(Names.named(&quot;objectify.dao.Player&quot;)).to(
				DAOPlayer.class).in(Singleton.class);
	}
}

PlayerResource

This is a Jersey resource for Player. In respect to Guice this class is also a client class where I’m using all the business services that I defined in GuiceModule. Instead of creating DAOPlayer with “new” key word, I’m using Guice to get references to DAOPlayer.


package ausbdsoccer.server.resources;

//Import statements ommited
//........
//...........

@Path(&quot;/players&quot;)
public class PlayersResource {
	Logger log = Logger.getLogger(PlayersResource.class.getName());
	private Injector injector;
	
	
	@GET
	@Path(&quot;{name}/xml&quot;)
	@Produces(&quot;application/xml&quot;)
	public Player getPlayerByNameInXML(@PathParam(&quot;name&quot;) String name){
		DAOPlayer daop = getDAOPlayer();
		Player player = daop.findAPlayer(name);
		if(player!=null){
			return player;
		}else {
			return new Player(&quot;NULL&quot;,&quot;NULL&quot;,&quot;NULL&quot;);
		}

	}
	
	@GET
	@Path(&quot;{name}/json&quot;)
	@Produces(&quot;application/json&quot;)
	public Player getPlayerByNameInJSON(@PathParam(&quot;name&quot;) String name){
		DAOPlayer daop = getDAOPlayer();
		Player player = daop.findAPlayer(name);
		if(player!=null){
			return player;
		}else {
			return new Player(&quot;NULL&quot;,&quot;NULL&quot;,&quot;NULL&quot;);
		}

	}


	private DAOPlayer getDAOPlayer() {
		injector = getInjectorInstance();
		DAOPlayer daop = (DAOPlayer) injector.getInstance(Key.get(DAO.class,Names.named(&quot;objectify.dao.Player&quot;)));
		return daop;
	}
	
	private Injector getInjectorInstance() {
		if(injector == null){
			injector = Guice.createInjector(new GuiceModule());
		}
		return injector;
	}
}

Conclusion

Main purpose of this tutorial is to show how to configure Guice. The way I have used Guice may not be the optimal way. There may be other best practices and design pattern to use Guice with Jersey. I would love to hear comments from the Guice and Jersey gurus on ways to best use Guice on Jersey.

Resources:

Guice : Google’s Dependency Injection framework

JAX-RS reference implementation Jersey for Java.

Objectify: Light weight object persistence framework for Google App Engine Java.

Simple XML: Simple light weight Java to and from XML framework.

Jackson: Simple Java to and from JSON framework.

11 Comments

  1. Andy March 10, 2010 Reply
  2. iqbalyusuf March 10, 2010 Reply
  3. Andy March 11, 2010 Reply
    • iqbalyusuf March 12, 2010 Reply
  4. Stan Berka August 5, 2010 Reply
  5. Jeff Schnitzer August 5, 2010 Reply
  6. Stan Berka August 8, 2010 Reply
    • iqbalyusuf August 8, 2010 Reply
  7. Chris January 11, 2011 Reply
    • Chris January 11, 2011 Reply
  8. Sam February 4, 2013 Reply

Add a Comment

Your email address will not be published. Required fields are marked *