Sunday, May 29, 2011

Using Guice-ified Jersey in Embedded Jetty

While IoC is a terrific way to promote software modularity, someone still has to write that bootstrap code to connect all the dots. Even though there is abundant information on how to use Guice, Jersey and Jetty respectively in a servlet environment, little is known about using all three together to programmatically configure servlets in Java. Here I will use an example to demonstrate how to write RESTful servlet using all three:
  1. Assemble framework libraries using Maven.
  2. Create a POJO interface and an implementation.
  3. Write a JAX-RS resoure to use Guice constructor injection and JAX-RS annotations.
  4. Write the Guice bootstrap code.
  5. Write the embedded Jetty start up code.
  6. Run and test.
First, assemble all necessary parts in a Maven pom.xml like this:
   
       7.4.1.v20110513
       1.7
       3.0
   

   
    
        org.eclipse.jetty
        jetty-servlet
        ${jetty.version}
    
 
     com.google.inject
     guice
     ${guice.verion}
 
 
     com.sun.jersey
     jersey-server
     ${jersey.version}
 
 
     com.sun.jersey.contribs
     jersey-guice
     ${jersey.version}
  
    
      junit
      junit
      ${junit.version}
      test
    
   
     
  
 
     maven2-repository.java.net
     Java.net Repository for Maven
     http://download.java.net/maven/2/
     default
  
  

We start with a simple POJO interface:
public interface GuicyInterface {
   String get();
}
And a simple implementation:
public class GuicyInterfaceImpl implements GuicyInterface {

   public String get() {
      return GuicyInterfaceImpl.class.getName();
   }
}
Now, write a JAX-RS resource to use both Guice and JAX-RS annotated injections:
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;

import com.google.inject.Inject;@Path("/helloguice")

public class HelloGuice {
   private final GuicyInterface gi;
   
   @Inject
   public HelloGuice(final GuicyInterface gi) {
      this.gi = gi;
   }
   @GET
   @Produces("text/plain")
   public String get(@QueryParam("x") String x) {
      return "Howdy Guice. " + "Injected impl " + gi.toString() + ". Injected query parameter "+ (x != null ? "x = " + x : "x is not injected");
   }
}

Next, compose POJO bindins in a JerseyServletModule. This module will setup the Jersey-based JAX-RS framework for use with Guide injection. The GuiceServletContextListener is used to bootstrap Guice when the servet context is initialized.
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.servlet.GuiceServletContextListener;
import com.sun.jersey.guice.JerseyServletModule;
import com.sun.jersey.guice.spi.container.servlet.GuiceContainer;

public class HelloGuiceServletConfig extends GuiceServletContextListener {
   @Override
   protected Injector getInjector() {
      return Guice.createInjector(new JerseyServletModule() {
         @Override
         protected void configureServlets() {
            // Must configure at least one JAX-RS resource or the 
            // server will fail to start.
            bind(HelloGuice.class);
            bind(GuicyInterface.class).to(GuicyInterfaceImpl.class);
            
            // Route all requests through GuiceContainer
            serve("/*").with(GuiceContainer.class);
         }
      });
   }
}

Finally, write the main method using embedded Jersey to start Guice and Jersey together.
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.ServletContextHandler;

import com.google.inject.servlet.GuiceFilter;

public class GuiceLauncher {
   public static void main(String[] args) throws Exception {
      // Create the server.
      Server server = new Server(8080);
      
      // Create a servlet context and add the jersey servlet.
      ServletContextHandler sch = new ServletContextHandler(server, "/");
      
      // Add our Guice listener that includes our bindings
      sch.addEventListener(new HelloGuiceServletConfig());
      
      // Then add GuiceFilter and configure the server to 
      // reroute all requests through this filter. 
      sch.addFilter(GuiceFilter.class, "/*", null);
      
      // Must add DefaultServlet for embedded Jetty. 
      // Failing to do this will cause 404 errors.
      // This is not needed if web.xml is used instead.
      sch.addServlet(DefaultServlet.class, "/");
      
      // Start the server
      server.start();
      server.join();
   }
}

If you run the main program on your local host, you can test the servlet using this URL:
http://localhost:8080/helloguice?x=q
Then, you should see a response like this:
Howdy Guice. Injected impl GuicyInterfaceImpl@3aaa3518. Injected query parameter x = q
Congratulations! You have now mastered the three most popular IoC frameworks for programming RESTful servlets!

Thursday, May 5, 2011

Singleton Injection in JAX-RS

JAX-RS defines a Java API for implementing RESTful server-side functions in Java. Being a post-Spring framework, it uses annotations extensively to denotate "resource" injection points. One of these DI annotations is called Provider. A provider is an application supplied class used to extend a JAX-RS run-time such as Jersey. A JAX-RS implementation is required to load only one instance of a provider class in a JAX-RS run-time instance. An application developer can leverage this feature to inject singletons, e.g. a database manager class, into JAX-RS resources. Here is an example on how to do this.

First, define the singleton class.
public class MyDBManager{
   public void store(final String key, final String value) {
      // Store the key-value pair in a DB.
   }
}
As you can see, this class is just a POJO. No special singleton marking anywhere in the class definition.

Next, we will use ContextResolver and @Provider annotatoin to turn MyDBManager into a JAX-RS provider.
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;

@Provider
public class DBResolver implements ContextResolver<MyDBManager> {
   private MyDBManager db;
   
   // A provider must have at least a zero-arg constructor.
   public DBResolver() {
      db = new MyDBManager();
   }
   public MyDBManager getContext(Class type) {
      return db;
   }
}

Now we can inject MyDBManager into a resource class through @Context like this:
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.ext.ContextResolver;

@Path("/myresrc")
public class MyResource {
   @POST
   @Path("/{id}")
   @Consumes("text/plain")
   @Produces("text/plain")
   public String post(final @Context ContextResolver myDbManager,
                      final @PathParam("id") String id) {
      myDbManager.getContext(MyResource.class).store(id, "someValue");
      return "OK";
   }
}

Two observations from this method of injecting a singleton:
  • The singleton instance of DBManager is not instantiated until MyResource is invoked the first time.
  • The type T in ContextResolver<T> can be an interface so an implementation of the ContextResolver<T> can return an implementation of the T. But, the implementation of T can only be determined by a Class object.
Therefore, this @Provider-based injection method should not be taken as a general purpose DI mechanism because JAX-RS is not a general purpose DI framework. It is not only ugly but also comes with a reflection overhead when the resource is invoked. Using Guice or Spring instead for general purpose DI.