Up and running on AppEngine with Maven, Jersey and Guice - Part 3

This is part three of my post on getting a web application up and running on Google AppEngine with Maven, Jersey and Guice.

Part 2 got us to the point of having a working web application running locally and deployed on AppEngine. Part 3 will get Guice & Jersey up and running.

Guice up

After the relative nightmare of some of the other things we’ve done in earlier parts, Guice is remarkably easy to get working. Some of it is due to good documentation, but mostly it’s just a great framework that’s very easy to use.

The first step is to include the appropriate dependencies in our pom.xml. Here’s the modified pom.xml that includes Guice and the Guice-Servlet packages. Here’s the reworked file. See lines 24-33 for changes:

pom.xmlview raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<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>com.listerly</groupId>
<artifactId>listerly-server</artifactId>
<version>0.1</version>
<packaging>war</packaging>

<name>Listerly Main Server</name>
<url>http://www.listerly.com</url>

<dependencies>
<dependency>
<groupId>com.google.appengine</groupId>
<artifactId>appengine-api-1.0-sdk</artifactId>
<version>1.9.0</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>com.google.inject.extensions</groupId>
<artifactId>guice-servlet</artifactId>
<version>3.0</version>
</dependency>
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>3.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>9.1.3.v20140225</version>
</plugin>
<plugin>
<groupId>com.google.appengine</groupId>
<artifactId>appengine-maven-plugin</artifactId>
<version>1.8.7</version>
</plugin>
</plugins>
</build>

</project>

Next, we setup our Guice modules. First, we extend ServletModule to help serve our HelloWorldServlet:

ListerlyServletModule.javaview raw
1
2
3
4
5
6
7
8
9
10
11
12
13
package com.listerly.config.guice;

import com.google.inject.servlet.ServletModule;
import com.listerly.HelloWorldServlet;

public class ListerlyServletModule extends ServletModule {

@Override
protected void configureServlets() {
serve("/hi").with(HelloWorldServlet.class);
}

}

Then we extend GuiceServletContextListener to tell Guice about our new ServletModule:

ListerlyGuiceServletContextListener.javaview raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.listerly.config.guice;

import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.servlet.GuiceServletContextListener;

public class ListerlyGuiceServletContextListener extends
GuiceServletContextListener {


@Override
protected Injector getInjector() {
return Guice.createInjector(new ListerlyServletModule());
}

}

Finally, we have to load Guice and our ServletContentListener in the web.xml. Notice we now take out our Servlet definition.

web.xmlview raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

<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>

<listener>
<listener-class>com.listerly.config.guice.ListerlyGuiceServletContextListener</listener-class>
</listener>
</web-app>

Make a quick change to HelloWorldServlet to prove that you’re actually hitting the new Servlet (I’ve now got Hello, handrolled, guiced AppEngine! as my message). Then run the devserver and hit the server. You should be looking good:

Great, Guice is clearly serving our Servlet, but let’s make absolutely sure by injecting a class. I’ve created a class called TestClass. I’m marking it as a @Singleton to ensure that Guice only creates it once:

TestClass.javaview raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.listerly;

import java.util.Date;

import javax.inject.Inject;
import javax.inject.Singleton;

@Singleton
public class TestClass {

@Inject
private Date date;

public TestClass() {
}

public Date getDate() {
return date;
}
}

Then in my HelloWorldServlet I inject the TestClass object, and print out the date it is loaded with:

HelloWorldServlet.javaview raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.listerly;

import java.io.IOException;
import java.text.SimpleDateFormat;

import javax.inject.Inject;
import javax.inject.Singleton;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@SuppressWarnings("serial")
@Singleton
public class HelloWorldServlet extends HttpServlet {

@Inject
TestClass theClass;

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {

resp.setContentType("text/plain");
resp.getWriter().println("Hello, handrolled, guiced AppEngine!");

SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
resp.getWriter().println("Date is: " + sdf.format(theClass.getDate()));
}

}

Reload the page a few times. Things are behaving as you would expect: you get the time at which you first loaded the page. Hopefully Jersey will be that easy!

Jersey? Sure.

There are a bunch of really good articles on how to use Jersey with Guice. Unfortunately they’re all based on Jersey 1.X and Jersey 2.0 is now out. Jersey 2.0 seems to be a different story judging by this, this, this and this.

It seems like Jersey’s inclusion of the HK2 dependency injection framework means lots of problems with getting Guice up and running. There are three options to proceed: (1) Revert back to better understood Jersey 1.X library, (2) Switch from Guice to HK2, or (3) Try to get something going with the help of a couple of working samples.

I decided to push through and try #3. I’m a big fan of Guice and don’t think HK2 is as mature so option 2 was out. Option 1 is tempting, but seemed like the fallback option since I figure at some stage Jersey 1.X is going to be too outdated to continue with so better to eat the pain now rather than after I have 100s of services.

The following instructions get things working. I’m not sure they’re the perfect (or best) way to do things, but after a day of struggling I’m going to settle for what I have. Things do at least seem to be working.

First, lets start with the dependencies. Here’s my final working pom.xml:

pom.xmlview raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
<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>com.listerly</groupId>
<artifactId>listerly-server</artifactId>
<version>0.1</version>
<packaging>war</packaging>

<name>Listerly Main Server</name>
<url>http://www.listerly.com</url>

<properties>
<appengine.sdk.version>1.9.0</appengine.sdk.version>
<jersey.version>2.5.1</jersey.version>
<guice.version>3.0</guice.version>
<servlet.api.version>2.5</servlet.api.version>
</properties>
<dependencies>
<dependency>
<groupId>com.google.appengine</groupId>
<artifactId>appengine-api-1.0-sdk</artifactId>
<version>${appengine.sdk.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>${servlet.api.version}</version>
</dependency>
<dependency>
<groupId>com.google.inject.extensions</groupId>
<artifactId>guice-servlet</artifactId>
<version>${guice.version}</version>
</dependency>
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>${guice.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-server</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-mvc-jsp</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-mvc-freemarker</artifactId>
<version>${jersey.version}</version>
<scope>compile</scope>
<exclusions>
<exclusion>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.glassfish.hk2</groupId>
<artifactId>guice-bridge</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker-gae</artifactId>
<version>2.3.20</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>9.1.3.v20140225</version>
</plugin>
<plugin>
<groupId>com.google.appengine</groupId>
<artifactId>appengine-maven-plugin</artifactId>
<version>${appengine.sdk.version}</version>
</plugin>
</plugins>
</build>

</project>

Notice the new dependencies for Jersey, Freemarker (my template language of choice with Jersey) and the HK2-Guice bridge.

Next we have to change our web.xml to use Jersey:

web.xmlview raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

<listener>
<listener-class>com.listerly.config.guice.ListerlyGuiceServletContextListener</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>

<filter>
<filter-name>jersey-filter</filter-name>
<filter-class>org.glassfish.jersey.servlet.ServletContainer</filter-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>com.listerly.config.jersey.JerseyConfiguration</param-value>
</init-param>
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>org.glassfish.jersey.server.mvc.freemarker.FreemarkerMvcFeature</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>jersey-filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>

This now has a filter defined for Jersey (lines 19-30), which is configured with (a) our Application (more on that in a second) and (b) the Freemarker mumbo-jumbo. The filter, like the Guice filter, is applied to all URLs.

The next file to go through is the “Application” for Jersey, which defines how Jersey is set up:

JerseyConfiguration.javaview raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.listerly.config.jersey;

import static java.util.logging.Logger.getLogger;

import java.util.logging.Logger;

import javax.inject.Inject;
import javax.servlet.ServletContext;

import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.mvc.freemarker.FreemarkerMvcFeature;
import org.jvnet.hk2.guice.bridge.api.GuiceBridge;
import org.jvnet.hk2.guice.bridge.api.GuiceIntoHK2Bridge;

import com.google.inject.Injector;

public class JerseyConfiguration extends ResourceConfig {
private final Logger log = getLogger(getClass().getName());

@Inject
public JerseyConfiguration(ServiceLocator serviceLocator, ServletContext servletContext) {
log.info("Creating JerseyConfiguration");
packages("com.listerly.resources");

register(FreemarkerMvcFeature.class);

GuiceBridge.getGuiceBridge().initializeGuiceBridge(serviceLocator);
GuiceIntoHK2Bridge guiceBridge = serviceLocator.getService(GuiceIntoHK2Bridge.class);
guiceBridge.bridgeGuiceInjector((Injector) servletContext.getAttribute(Injector.class.getName()));
}

}

Three important parts to note here: (1) Line 24 tells Jersey which package will contain our Resources - the POJOs that will respond to requests. (2) Line 26 tells Jersey we will be using Freemarker. (3) Lines 28-30 set up the HK2-Guice bridge.

Next, lets create a simple POJO to respond to requests:

HeyResource.javaview raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.listerly.resources;

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

@Path("/Hey")
public class HeyResource {
@GET
@Produces(MediaType.TEXT_PLAIN)
public String get() {
return ("Hey there");
}
}

Lets fire up the devserver and load http://localhost:8080/Hey

That all looks good. But let’s make absolutely sure injection is working correctly.

Lets add a second injectable class to begin. Notice that this one is RequestScoped, as opposed to our previous one which was a Singleton:

SecondTest.javaview raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.listerly;

import java.util.Date;

import javax.inject.Inject;

import com.google.inject.servlet.RequestScoped;

@RequestScoped
public class SecondTest {

@Inject
private Date date;

public SecondTest() {
}

public Date getDate() {
return date;
}

}

We can now add a second method and inject a couple of objects into our class:

HeyResource.javaview raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package com.listerly.resources;

import java.text.SimpleDateFormat;

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

import javax.inject.Inject;
import com.listerly.SecondTest;
import com.listerly.TestClass;

@Path("/Hey")
public class HeyResource {

@Inject TestClass first;
@Inject SecondTest second;

@GET
@Produces(MediaType.TEXT_PLAIN)
public String get() {
return ("Hey there");
}

@GET
@Produces(MediaType.TEXT_PLAIN)
@Path("/foo")
public String foo(@QueryParam("test") String test) {
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
StringBuilder builder = new StringBuilder();

builder.append("Hi there. You entered ").append(test).append("\n");
builder.append("The first date is:").append(sdf.format(first.getDate())).append("\n");
builder.append("The second date is:").append(sdf.format(second.getDate())).append("\n");

return builder.toString();
}
}

Run the server, and everything looks good:

Push everything to AppServer and make sure everything is fine. We’ll be using more of Jersey and Guice in the next couple of steps as we get started on templates and persistence. Part 4 will build on today, getting Jersey set up with JSON and Freemarker.