Wednesday, September 18, 2013

Manual Dependency Injection with Jersey and embedded Jetty

I wrote a little application demonstrating how to manually (through constructors) inject dependencies into Jersey resources in an embedded container like Jetty. 

The benefits are:
- lightweightness (no need to use Spring, Guice, etc.),
- more control over your application (less magic behind the scenes),
- controlling dependencies in tests (via Dependency Injection)
- running a web app with a simple Java main method (via embedded Jetty)

The example application is called Time Expert. It exposes the current time via a RESTful web service, so when I run it in the production (via Main.java) I can use it as following:
curl -v -H "Accept: text/plain" http://localhost:6666/time
HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 21
Server: Jetty(9.0.5.v20130815)
It's currently 20:59
view raw gistfile1.txt hosted with ❤ by GitHub

Here's how I test it (TimeAcceptanceTest.java) :
@Test
public void saysTheCurrentTime() {
givenTheCurrentTimeIs("20:15");
whenAUserChecksTheTime();
thenTheUserSees("It's currently 20:15!");
}
view raw gistfile1.java hosted with ❤ by GitHub

However,  I cannot rely here on the real time because it changes every time I run tests . The problem can be easily solved with the Clock pattern. My tests require a FixedClock which allows me to set the current time to any value:

public class FixedClock implements Clock {
private Date fixedNow;
@Override
public Date now() {
return fixedNow;
}
public void setNow(Date fixedNow) {
this.fixedNow = fixedNow;
}
}
view raw gistfile1.java hosted with ❤ by GitHub


 So I start my server with a fixed clock:
@Before
public void startApplication() {
clock = new FixedClock();
timeExpert = new TimeExpertServer(clock);
timeExpert.start();
}
view raw gistfile1.java hosted with ❤ by GitHub


... and then set it to, say, 20:15:

@Test
public void saysTheCurrentTime() throws Exception {
givenTheCurrentTimeIs("20:15");
whenAUserChecksTheTime();
thenTheUserSees("It's currently 20:15!");
}
private void givenTheCurrentTimeIs(String currentTime) throws ParseException {
Date currentTimeAsDate = new SimpleDateFormat("HH:mm").parse(currentTime);
clock.setNow(currentTimeAsDate);
}
private void whenAUserChecksTheTime() {
actualApplicationTime = timeExpertUser.checkCurrentTime();
}
private void thenTheUserSees(String expectedApplicationTime) {
assertEquals(expectedApplicationTime, actualApplicationTime);
}
view raw gistfile1.java hosted with ❤ by GitHub

Since my Jersey resource class is only aware of the clock interface, it will display 20:15:

@Path("time")
public class TimeResource {
private final Clock clock;
public TimeResource(Clock clock) {
this.clock = clock;
}
@GET
@Produces(MediaType.TEXT_PLAIN)
public String getCurrentTime() {
return String.format("It's currently %s!", format(clock.now()));
}
private String format(Date now) {
return new SimpleDateFormat("HH:mm").format(now);
}
}
view raw gistfile1.java hosted with ❤ by GitHub

In order to create your Jersey resources with manually injected dependencies you have to register org.glassfish.jersey.servlet.ServletContainer with a customized org.glassfish.jersey.server.ResourceConfig. In that config Jersey resources can be newed up with their dependencies:
public void start() {
server = new Server(6666);
ServletContextHandler handler = new ServletContextHandler();
handler.setContextPath("");
// adds Jersey Servlet with a customized ResourceConfig
handler.addServlet(new ServletHolder(new ServletContainer(resourceConfig())), "/*");
server.setHandler(handler);
try {
server.start();
} catch (Exception e) {
throw new RuntimeException("Could not start the server", e);
}
}
private ResourceConfig resourceConfig() {
// manually injecting dependencies (clock) to Jersey resource classes
return new ResourceConfig().register(new TimeResource(clock));
}
view raw gistfile1.java hosted with ❤ by GitHub

Of course the production provides a real clock (which returns new Date()):

public class Main {
public static void main(String[] args) {
new Main().startTimeExpert();
}
private void startTimeExpert() {
final TimeExpertServer timeExpert = new TimeExpertServer(new RealClock());
try {
timeExpert.start();
timeExpert.join();
} finally {
timeExpert.stop();
}
}
}
view raw gistfile1.java hosted with ❤ by GitHub

Code is available under: https://github.com/unclejamal/TimeMaster

Enjoy!

Tested using:
- Java 1.7,
- Gradle 1.7 (easily convertible to Maven :)),
- Jersey 2.2,
- Jetty 9.0.5.