Posts‎ > ‎

Jersey and Guice: a perfect combination for writing RESTful APIs

posted Mar 29, 2013, 12:30 PM by Renato Athaydes   [ updated Aug 6, 2013, 10:56 PM ]
There's an incredible number of web frameworks out there. I sometimes can't help but ask myself: do we really need these many options???
Not to mention the variety of really cool languages (Groovy, Scala, Python, Ruby, Node.js, Clojure....) everyone, including myself, is eager to try!

However, I would like to go a little bit against the flow and show how a pure-Java application (with a little JavaScript/HTML/CSS on the client-side, which is inevitable unless you're willing to adopt GWT or some other heavy-handed solution) can, in fact, look really cool, simple and effective!

To do that, I will use just these 2 kick-ass Java technologies:
  • Jersey (implementation of JAX-RS (Java API for RESTful Web Services)
  • Guice (Dependency Injection magic)
These are quite new, but already well-established technologies, so you can expect them to just work!

I will show you how to build a Java application to provide a HTTP REST API with minimum effort.

We are going to use Jersey to implement our REST web services because it is the default implementation of JAX-RS/JSR-311. Also, due to an important technical reason: a high level of awesomeness!

Guice will be used as our DI framework because it is simple to get started with, uses (type-safe) Java code to do the bindings, and provides everything we're likely to need in a project such as this one.

The only XML we will be needing here is the Maven POM (I was tempted to give Gradle a try, but will leave that for a next post) and, in case you want to deploy your app on an existing server (instead of just bootstrapping your own, as I'll show here for our unit-tests), the web.xml file.

So, let's write some code!


Creating a simple Jersey RESTful API with Guice

Required dependencies

First off, let's have a look at the Maven dependencies we will need.

<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>

<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-server</artifactId>
<version>${jersey.version}</version>
</dependency>

<dependency>
<groupId>com.sun.jersey.contribs</groupId>
<artifactId>jersey-guice</artifactId>
<version>${jersey.version}</version>
</dependency>

        <!--  Test Dependencies  -->

        <dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-client</artifactId>
<version>${jersey.version}</version>
            <scope>test</scope>
</dependency>

        <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.9</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-grizzly2</artifactId>
<version>${jersey.version}</version>
<scope>test</scope>
</dependency>

The first one is the Servlet API. Although we will not directly use the Java Servlet API, we need it as Jersey/Guice will be using it under the hoods.

Obviously, we need Jersey itself, so we import the jersey-server artifact. We also add the Jersey-Guice dependency so we can get all of Guice's functionality in a well-integrated union with Jersey.

I am using the latest stable version of Jersey at the time of writing: 1.17.1.

Finally, we have our unit tests dependencies, which for now include only the Jersey client package (to generate API calls), JUnit4 and the Grizzly 2 web server, which is really, really fast and extremely simple to start up, perfect for us to deploy our simple app and see if things are working as expected.

Defining our REST Web Service

Now we can get to what really matters. Let's write a REST Service, hopefully with as little boiler-plate as possible.
With Jersey, that's a breeze, as you'll see.
Although I always strive to keep things simple, I also can't stand bad design... so let's not throw good software design practices out the window just yet, and keep our application's different concerns tidily separated.

The first thing a Service needs to work is a way to get data, which basically means we need a DAO (Data Access Object) definition:

public interface Dao<T> {

List<? extends T> getAll();

T getById(String id);

}

This is a generic DAO which can be used to retrieve either all of the items of a certain type T, or just a single one if we know its ID.
For simplicity, for now we will not worry about updating/saving/deleting stuff. Just reading stuff! We also don't care about the implementation of this interface... we just take it for granted that we will be able to get data from somewhere.

Now we can get to the Service class itself:

package com.athaydes.web.server;

import static javax.ws.rs.core.MediaType.TEXT_HTML;

import com.athaydes.web.dao.Dao;

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;

// this class will handle any request similar to "server.com/stuff"
@Path( "stuff" )
public class StuffService {

private Dao<String> dao;

    // let Guice inject the DAO
@Inject
public StuffService( Dao<String> dao ) {
this.dao = dao;
}
    
    // this method will handle GET requests for text/html 
@GET 
@Produces( TEXT_HTML )
public String getAll() {
String html = "<h2>All stuff</h2><ul>";
for ( String stuff : dao.getAll() ) {
html += "<li>" + stuff + "</li>";
}
html += "</ul>";
return html;
}

    // requests which include a sub-path (expected to be the ID) are handle here
@GET
@Path( "{id}" )
@Produces( TEXT_HTML )
public String getById( @PathParam( "id" ) String id ) {
String stuff = dao.getById( id );
if ( stuff == null ) return notFound();
else return "<html><body><div>" + stuff + "</div></body></html>";
}

    // when an ID is not found, do this
private String notFound() {
return "<html><body><div>Not Found</div></body></html>";
}

}

I hope you will forgive me for the apparent breach of separation of concerns I have just committed! Generating HTML directly from the service class is not a great idea, but I hope the reader will appreciate the fact that the important here is to demonstrate how to use the JSR-311 API.
Later on, we would need to modify this to actually provide pure data (in Json or XML format), which would be really simple to do with Jersey without even writing any mappings directly! But that would require use to define an Object Model, which for now, we don't really need to do...

By the way, notice our imports... No mention at all of Jersey or Guice so far! Only javax.* and our own packages.

It would take too much space to explain the JSR-311 specification, but I hope you can see how easy it is to write pure Java code which maps to a REST API using only annotations.

For example, the request mappings look like the following ( assuming we're running our app in localhost:8080 ):

http://localhost:8080/stuff             maps to    StuffService#getAll()
http://localhost:8080/stuff/some_id     maps to    StuffService#getById( some_id )

For a detailed explanation of the specification please check the Jersey documentation.

Gluing everything together ( DI )

The next thing we need to do is create our dependency injection ( Guice, in this case ) module, which will put all parts of the application together:

package com.athaydes.web;

import com.athaydes.web.dao.Dao;
import com.athaydes.web.dao.StuffDao;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.TypeLiteral;
import com.google.inject.servlet.GuiceServletContextListener;
import com.google.inject.servlet.ServletModule;
import com.sun.jersey.api.core.PackagesResourceConfig;
import com.sun.jersey.api.core.ResourceConfig;
import com.sun.jersey.guice.spi.container.servlet.GuiceContainer;

public class Main extends GuiceServletContextListener {

@Override
protected Injector getInjector() {
return Guice.createInjector( new ServletModule() {
@Override
protected void configureServlets() {
bind( new TypeLiteral<Dao<String>>() {
} ).to( StuffDao.class );

                ResourceConfig rc = new PackagesResourceConfig( "com.athaydes.web.server" );
for ( Class<?> resource : rc.getClasses() ) {
bind( resource );
}

serve( "/services/*" ).with( GuiceContainer.class );
}
} );
}
}

You may notice that now we have finally added vendor-specific code to our application. There are some Guice and Jersey classes. But this is basically the only place where we define the actual implementation of our Dependency Injection framework (Guice) and REST Web-Service framework (Jersey). Changing the implementations used would be a matter of changing only this one class (and maybe web.xml if you have one)!

This is an extremely simple module, but I think it captures well what a real-world application would look like. Being Java code, this just scales! You could have hundreds of bindings, separating them perhaps by application layer in different Java classes.

When we create the Guice Injector, we pass a ServletModule as an argument. This class contains the abstract method configureServlets(), which we must define. Inside this method, we bind the Dao<String>, which was injected into our Service class above, to an implementation ( StuffDao ). Notice the use of Guice's TypeLiteral, which allows us to bind to a parameterized type.

We must also bind the Service classes in the context. To do that, we use the handy Jersey PackagesResourceConfig class, which allows us to get all classes in the given package which Jersey detects as being a "resource" ( by calling getClasses() ). It uses the class annotations to determine whether a class is a resource. That means that to add a new Service, all you need to do is write another Java class within the specified package... you don't even need to add a mapping in some hidden XML file or even in the Java Guice Module!!!! That's what I meant when I said Jersey is awesome!

Finally, we declare that we want to serve requests to "/services/*" with the GuiceContainer.
Note that this changes slightly our mappings from the examples given previously. Now, the path specified in the Service class will be pre-pended by "services/", so our mappings become:

http://localhost:8080/services/stuff             maps to    StuffService#getAll()
http://localhost:8080/services/stuff/some_id     maps to    StuffService#getById( some_id )

The reason for me to add "services" to the path is that I wanted to allow our server to still provide static content (index.html, css, js files, etc), which would be harder if we let the GuiceContainer handle all requests.

Running and Testing all this stuff

Now we can have some fun!!! Let's run this code.
If you're like me, you don't just want to see your code running, you want to make sure it works in a number of different scenarios.
To do this, we should write unit tests...

You may think it would be a little difficult to test this... we seem to require a Web Server and a Servlet container just to get off the ground.
Well, you do need those, but it's not nearly as hard as you might think.

See for yourself below.

Here's the code required to start our Grizzly server, including configuring our Guice Module (imports omitted for brevity):

static final URI BASE_URI = getBaseURI();
HttpServer server;

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

@Before
public void startServer() throws IOException {
Injector injector = Guice.createInjector( new ServletModule() {
@Override
protected void configureServlets() {
bind( new TypeLiteral<Dao<String>>() {
} ).to( TestDao.class );
}
} );

ResourceConfig rc = new PackagesResourceConfig( "com.athaydes.web.server" );
IoCComponentProviderFactory ioc = new GuiceComponentProviderFactory( rc, injector );
server = GrizzlyServerFactory.createHttpServer( BASE_URI + "services/", rc, ioc );
}

Notice that we have a test Guice module that looks really similar to the real one. This is important because we want our test to be as close as possible to our real environment. However, it cannot be the same because we need to "mock" some dependencies (the Dao in this case) which we are not testing here. Another difference is that we don't need to do the binding of the Service classes from within the ServletModule... that's because we can pass the PackagesResourceConfig object (also used in the real module) directly into the IoC Factory and Server.

Now we're ready to run some tests (@Before and @Test are plain JUnit4 annotations so these tests can be run in your IDE or Maven comand-line without any setup):

@Test
public void testGetAll() throws IOException {
Client client = Client.create( new DefaultClientConfig() );
WebResource service = client.resource( getBaseURI() );

ClientResponse resp = service.path( "services" ).path( "stuff" )
.accept( MediaType.TEXT_HTML )
.get( ClientResponse.class );
String text = resp.getEntity( String.class );

assertEquals( 200, resp.getStatus() );
assertEquals( "<h2>All stuff</h2><ul>" +
"<li>stuff1</li>" +
"<li>stuff2</li>" +
"<li>stuff3</li></ul>", text );

}

@Test
public void testGetById() throws IOException {
Client client = Client.create( new DefaultClientConfig() );
WebResource service = client.resource( getBaseURI() );

ClientResponse resp = service.path( "services" )
.path( "stuff" ).path( "id1" )
.accept( MediaType.TEXT_HTML )
.get( ClientResponse.class );
String text = resp.getEntity( String.class );

assertEquals( 200, resp.getStatus() );
assertEquals( "<html><body><div>stuff1</div></body></html>", text );

String text2 = service.path( "services" )
.path( "stuff" ).path( "non_existent_id" )
.accept( MediaType.TEXT_HTML )
.get( String.class );

assertEquals( 200, resp.getStatus() );
assertEquals( "<html><body><div>Not Found</div></body></html>", text2 );

}

Lastly, let's not forget to stop the server after each test is run.

@After
public void stopServer() {
server.stop();
}

You may also want to try hitting the Grizzly server from your browser... add a main method like this to the test class:

public static void main( String[] args ) throws Exception {
ServerTest test = new ServerTest();
test.startServer();
System.in.read(); // hit enter to stop the server
test.server.stop();
}

Then access this URL with your browser:

http://localhost:9998/services/stuff


Deploying to (almost) any server


It seems our code is working, thanks to our unit-tests... but to be sure of that, there's only one way: to deploy it to a couple of web servers/servlet containers and hit it from an external program. After all, we haven't even used our "real" Guice module in the unit tests.

So we need to implement a Dao (I just wrote a fake dao, StuffDao mentioned above, to get some data into the Service), write a web.xml deployment descriptor file, and use curl or a browser (I wanted to use a browser to test some stuff like real HTML/style-sheets requests also work, that's part of the reason why the REST API written here actually provides HTML instead of XML or Json) to manually verify the web app can indeed be deployed to any web server/servlet container. If you want to automate this kind of test, you may want to check out SoapUI, which allows you to easily write functional tests which can verify your actual production environment.

The implementation of a fake Dao is left as an exercise to the reader :)

Here's the web.xml needed:


<web-app 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"
         version="2.4">

<display-name>My Awesome REST API App</display-name>

<listener>
<listener-class>com.athaydes.web.Main</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>

</web-app>

That's it for the web.xml file. Really... forever... you will never have to change this file again unless you want to specify some static resource (welcome-file-list, error-page etc). Because the resources are all managed/configured in the Java code, the XML file is used just to make the server happy.

Now to deploy it to a Jetty server, for example, you can add this snippet to your POM:

<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<version>7.0.0pre3</version>
<configuration>
<scanIntervalSeconds>10</scanIntervalSeconds>
<connectors>
<connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
<port>8080</port>
<maxIdleTime>60000</maxIdleTime>
</connector>
</connectors>
<stopKey/>
<stopPort/>
</configuration>
</plugin>

After doing this, you get the application running in Jetty by typing the following:

mvn jetty:run

Impossible to get any simpler.

Knowing how popular Tomcat is, you probably want to also try your app in it.... easy! Supposing you have a Tomcat instance running in your local machine, just copy the war file generated by maven into your Tomcat's webapps directory and you're done. Tomcat will hot-deploy it for you, no re-start required.

Conclusion

That's it for now!
I hope you enjoyed reading and had as much fun as I had writing it.
We saw how easy it is to write a web service using Jersey, and gluing different parts of the application together with Guice.

As a side-note, you may be interested to know you can actually access an automatically-generated WADL of your API by running the app in your server of choice (as shown above) and hitting this URL:

http://localhost:9998/services/application.wadl

This can be extremely useful! You can use this WADL in your functional and load tests later. You can use SoapUI/LoadUI for those! Maybe I will write about that some time... but I must mention that both tools are developed by my employer Smart Bear (I currently work in the LoadUI team).

The code is on Github:


Please leave your comments below!

Comments