Skip to content

Your First RESTful Application

Peter edited this page May 9, 2014 · 5 revisions

This framework provides a way to build services that sit inside standard HTTP Servlet containers. Due to the somewhat verbose nature of Java (and to prevent abstracting your code too much from Guice and the other environment), some amount of boilerplate code is necessary. We try to keep this to a minimum.

This page will describe the pieces necessary to build a simple application. Behind the scenes the application will use RESTEasy for the JAX-RS implementation (and, of course, Guice for dependency injection).

Dependencies

To build a project, first add the required modules as dependencies to your project. The following maven dependency XML can be used to pull the libraries in:

<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">
	...
	<properties>
		<stdlib.version>6.0.2</stdlib.version>
	</properties>
	...

	<dependencies>
		<dependency>
			<groupId>com.peterphi.std.guice</groupId>
			<artifactId>stdlib-guice-common</artifactId>
			<version>${stdlib.version}</version>
		</dependency>
		<dependency>
			<groupId>com.peterphi.std.guice</groupId>
			<artifactId>stdlib-guice-webapp</artifactId>
			<version>${stdlib.version}</version>
		</dependency>
		<dependency>
			<groupId>com.peterphi.std</groupId>
			<artifactId>stdlib</artifactId>
			<version>${stdlib.version}</version>
		</dependency>

		<!-- Required only if using thymeleaf templating -->
		<dependency>
			<groupId>com.peterphi.std.guice</groupId>
			<artifactId>stdlib-guice-thymeleaf</artifactId>
			<version>${stdlib.version}</version>
		</dependency>

		<!-- Required only if using hibernate -->
		<dependency>
			<groupId>com.peterphi.std.guice</groupId>
			<artifactId>stdlib-guice-hibernate</artifactId>
			<version>${stdlib.version}</version>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>servlet-api</artifactId>
			<version>2.5</version>
			<scope>provided</scope>
		</dependency>
	</dependencies>

	...
</project>

web.xml

With this set up, in your webapp project, set up a web.xml file to nominate the framework's filter to handle all HTTP requests (it can be assigned a subfolder if required):

src/main/webapp/WEB-INF/web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" 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">
	
	<display-name>Webapp</display-name>
	
	<filter>
        <filter-name>resteasy</filter-name>
        <filter-class>com.peterphi.std.guice.web.rest.resteasy.ResteasyDispatcher</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>resteasy</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

Service configuration

This ResteasyDispatcher will try to initialise your service once the webapp is loaded; if this initialisation fails then it will attempt to reinitialise every time the webapp receives an HTTP request. It loads configuration values from service.properties in the environment (in fact, the property file loading mechanism is a bit more complicated to simplify automated deployment with configuration management tools like Puppet - full details can be found at [The Service Configuration page](Service Configuration)). This property file can contain arbitrary config values which are bound into the Guice environment (using the guice bindProperties mechanism). A minimal example is shown below.

src/main/resources/service.properties

guice.bootstrap.class=com.me.myservice.guice.MyServiceSetup

Setup class

The setup class nominated by guice.bootstrap.class will be loaded as soon as the webapp loads; it should extend AbstractRESTGuiceSetup and implement the addModules method, which will set up a list of guice Modules to load. The AbstractRESTGuiceSetup base class provides a level of base functionality and core REST services. An example is shown below:

public class MyServiceSetup extends AbstractRESTGuiceSetup
{
	@Override
	public void addModules(final List<Module> modules, final PropertyFile config)
	{
		// Nominate our guice module
		modules.add(new MyServiceModule());

		// Only needed if using Thymeleaf templating
		modules.add(new ThymeleafModule());
	}


	@Override
	public void injectorWasCreated(final Injector injector)
	{
		// No code necessary, provides a hook for lifecycle management (will be removed as a mandatory override in a future version)
	}
}

Guice Module

In MyServiceModule we will register our REST services:

public class MyServiceModule extends AbstractModule
{
	@Override
	protected void configure()
	{
		RestResourceRegistry.register(MyRestService.class);
	}
}

To set up additional services, simply pass them to the RestResourceRegistry.register call. The code expects that these classes being passed are JAX-RS interfaces. We are not binding an implementation of this service in the Module because we will use the Guice @ImplementedBy annotation on the interface (however we could certainly bind in this module with bind(MyRestService.class).to(MyRestServiceImpl.class);).

REST Service

@Path("/")
@ImplementedBy(MyRestServiceImpl.class)
public interface MyRestService
{
	@Path("/")
	@GET
	@Provides("text/html")
	@Doc("Retrieve the index page of this sample service")
	public String getIndex();
}

In MyRestServiceImpl we are running in an environment created by Guice and with method calls controlled by JAX-RS.

public class MyRestServiceImpl implements MyRestService
{
	@Inject
	Templater templater;

	@Override
	public String getIndex()
	{
		return "<html><body><h1>Hello, world!</h1></body></html>";
	}
}

Replacing static return with Thymeleaf

We can use a templating engine to return the page by changing the service implementation to:

public class MyRestServiceImpl implements MyRestService
{
	@Inject
	Templater templater;

	@Override
	public String getIndex()
	{
		return templater.template("index").process();
	}
}

This will look for a resource called "WEB-INF/template/index.html" in the webapp (which should be written to "src/main/webapp/WEB-INF/template/index.html" in the project structure). For information on how to use Thymeleaf, see their documentation online.

Core REST services

The application we have just built has a special /list resource, which provides an easy-to-read view of the JAX-RS service interfaces registered as well as their resources.

Summary

In this walkthrough we have shown how to build a simple RESTful webapp to expose a simple JAX-RS application which can take advantage of Guice (and optionally Thymeleaf).