Jersey + Guice + Scala

At Pongr, our RESTful web services are built using Jersey, Guice and Scala (among many other technologies). Here’s a quick post that shows how to set up an example project that uses all three. By the end we’ll be able to declare Guice bindings in our own custom module and have them injected into Jersey resource classes.

We’ll manage everything with Maven, so first create your pom.xml. I know it looks like a lot, but only has three Jersey dependencies (and one on the Servlet 2.5 API to make things compile). The rest of this junk is mainly configuring plugins for Scala – don’t worry about it. The upshot is that you can a) run this all using mvn jetty:run, and b) can generate an Eclipse project using mvn eclipse:eclipse.

<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/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.pongr</groupId>
    <artifactId>jerseyguicescala</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>jerseyguicescala</name>
    <properties>
        <jersey-version>1.1.1-ea</jersey-version>
    </properties>
    <dependencies>
        <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>
        <dependency>
            <groupId>com.sun.jersey.contribs</groupId>
            <artifactId>jersey-scala</artifactId>
            <version>${jersey-version}</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <!-- Run with "mvn jetty:run" -->
            <plugin>
                <groupId>org.mortbay.jetty</groupId>
                <artifactId>maven-jetty-plugin</artifactId>
                <version>6.1.19</version>
                <configuration>
                    <contextPath>/</contextPath>
                </configuration>
            </plugin>
            <!-- Begin scala plugins, inspired by: http://scala-tools.org/mvnsites/maven-scala-plugin/usage_java.html -->
            <plugin>
                <groupId>org.scala-tools</groupId>
                <artifactId>maven-scala-plugin</artifactId>
                <executions>
                    <execution>
                        <id>scala-compile-first</id>
                        <phase>process-resources</phase>
                        <goals>
                            <goal>add-source</goal>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>scala-test-compile</id>
                        <phase>process-test-resources</phase>
                        <goals>
                            <goal>testCompile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
                <executions>
                    <execution>
                        <phase>compile</phase>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <artifactId>maven-eclipse-plugin</artifactId>
                <configuration>
                    <sourceIncludes>
                        <sourceInclude>**/*.scala</sourceInclude>
                    </sourceIncludes>
                    <buildcommands>
                        <buildcommand>ch.epfl.lamp.sdt.core.scalabuilder</buildcommand>
                    </buildcommands>
                    <additionalProjectnatures>
                        <!-- This nature gets put after org.eclipse.jdt.core.javanature in .project so Eclipse has a J badge on the project instead of an S -->
                        <projectnature>ch.epfl.lamp.sdt.core.scalanature</projectnature>
                    </additionalProjectnatures>
                    <classpathContainers>
                        <classpathContainer>org.eclipse.jdt.launching.JRE_CONTAINER</classpathContainer>
                        <classpathContainer>ch.epfl.lamp.sdt.launching.SCALA_CONTAINER</classpathContainer>
                    </classpathContainers>
                </configuration>
            </plugin>
            <!-- http://groups.google.com/group/liftweb/browse_thread/thread/3dac7002f9e59546/3918bba2f7a92cd3?pli=1 -->
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>build-helper-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <id>add-source</id>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>add-source</goal>
                        </goals>
                        <configuration>
                            <sources>
                                <source>src/main/scala</source>
                            </sources>
                        </configuration>
                    </execution>
                    <execution>
                        <id>add-test-source</id>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>add-test-source</goal>
                        </goals>
                        <configuration>
                            <sources>
                                <source>src/test/scala</source>
                            </sources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <!-- End scala plugins -->
        </plugins>
    </build>
</project>

Next up, create the src/main/webapp/WEB-INF/web.xml file. This just registers our special GuiceConfig class that, uh, configures Guice.

<?xml version="1.0" encoding="UTF-8"?>
<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_5.xsd" metadata-complete="false" version="2.5">
    <listener>
        <listener-class>com.pongr.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>
</web-app>

We’ll create an example Guice module in the src/main/java/com/pongr/ExampleModule.java file. It just binds a message String that we’ll inject into our resource classes later.

package com.pongr;

import com.google.inject.*;
import com.google.inject.name.*;

public class ExampleModule extends AbstractModule
{
    @Override
    protected void configure()
    {
        bindConstant().annotatedWith(Names.named("message")).to("Hello, World!");
    }
}

Next up is src/main/java/com/pongr/GuiceConfig.java where we connect Jersey to Guice. This is where we create the Guice Injector using our ExampleModule and configure any Jersey properties, like the package(s) that contain our resource classes.

package com.pongr;

import java.util.*;

import com.google.inject.*;
import com.google.inject.servlet.*;
import com.sun.jersey.api.core.*;
import com.sun.jersey.guice.spi.container.servlet.*;

public class GuiceConfig extends GuiceServletContextListener
{
    @Override
    protected Injector getInjector()
    {
        final Map<String, String> params = new HashMap<String, String>();
        params.put(PackagesResourceConfig.PROPERTY_PACKAGES, "com.pongr");
        return Guice.createInjector(new ExampleModule(), new ServletModule()
        {
            @Override
            protected void configureServlets()
            {
                serve("/*").with(GuiceContainer.class, params);
            }
        });
    }
}

Now for the fun! This src/main/java/com/pongr/JavaResource.java file gets the message String injected and displays it.

package com.pongr;

import javax.ws.rs.*;
import javax.ws.rs.core.*;

import com.google.inject.*;
import com.google.inject.name.*;

@Path("java")
public class JavaResource
{
    private String message;

    @Inject
    public JavaResource(@Named("message") String message)
    {
        this.message = message;
    }

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String get()
    {
        return "From Java: " + message;
    }
}

And for good measure here’s src/main/scala/com/pongr/ScalaResource.scala. Same as JavaResource, except in Scala. The Guice @Inject and @Named annotations can be a bit tricky for constructor injection, so here’s how it’s done.

package com.pongr

import javax.ws.rs._
import javax.ws.rs.core._

import com.google.inject._
import com.google.inject.name._

@Path("scala")
class ScalaResource @Inject() (@Named("message") message: String) {
  @GET
  @Produces(Array(MediaType.TEXT_PLAIN))
  def get(): String = "From Scala: " + message
}

That’s it! Now just run mvn jetty:run on your command line and hit http://localhost:8080/java and http://localhost:8080/scala in your browser to see these resources in action. As always, you can also visit http://localhost:8080/application.wadl to get an overview of the available resources. Enjoy!

Advertisements

6 thoughts on “Jersey + Guice + Scala

  1. Thanks for the post.

    Changed my non-working setup to yours because I thought my setup is wrong. But still get

    2009-09-23 07:31:35.389::WARN: Error starting handlers
    java.lang.NoSuchMethodError: com.google.inject.Scopes.isSingleton(Lcom/google/inject/Binding;)Z

    Any idea? What Guice version are you using? From which repo?

    Thanks
    Stephan

  2. Pingback: Twitter Trackbacks for Jersey + Guice + Scala « Zach's Blog [zcox.wordpress.com] on Topsy.com

  3. Pingback: Guice / Reference to WebApplication : 14376

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