Integration with Spring

Integration modes

During the development of the 1.0 version of the API, several users attempted to integrate Restlet with Spring. They were especially trying to use the XML-based bean wiring feature of Spring. This resulted in several examples available on the community Wiki.

In order to facilitate this integration, two dedicated Spring extension were added to the Restlet. It allows us to provide several integration modes.

Restlet as main container

In the first mode, the goal is to leverage the concept of Restlet Application and all the associated services, as well as the transparent deployment to either a Servlet container (using the adapter ServerServlet extension class) or using a standalone HTTP server connector. For this, you can leverage the SpringContext class which is a Spring's GenericApplicationContext subclass. You can associate a list of XML or property configuration URIs (file:/// or war:/// URIs) in order to have Spring auto-instantiate and wire your Restlet beans.

Spring as main container

In the second mode, the goal is to leverage the concept of Spring Web Application as an alternative to the Restlet Application. This is sometimes required when the Restlet code is part of a larger Spring-based Web application, with dependencies on the Servlet API for example.

Initially, it was hard to achieve this integration because the Servlet extension, and especially the ServerServlet adapter class was assuming the usage of a Restlet Application. Later we added a lighter adapter based on the ServletConverter class that  lets you directly instantiate Restlet Routers, Finders and Resources from your existing Servlet-based Spring code. You can check the Javadocs for details.

Finally, there is also a SpringFinder class available in the Spring extension. It hasn't any specific dependency to Spring, but the addition of a parameter-less createResource() method allows the usage of the Spring's "lookup-method" mechanism.

In Restlet 1.1, the Spring extensions received several contributions, increasing the number of ways to integrate Restlet with Spring. There is now a RestletFrameworkServlet, a SpringServerServlet, SpringBeanFinder and SpringBeanRouter. Please check the Javadocs of the org.restlet.ext.spring and com.noelios.restlet.ext.spring modules for more details.

Configuring Restlet beans

Passing the parent context

One frequent issue that developers encounter when configuring their Restlet beans with Spring XML is that it is not easy to find a way to pass the Context instance to the Restlet subclasses such as Application, Directory or Router. What we actually need to do is to extract the context property from the parent Restlet (typically a Component or an Application) and pass it by reference to the constructor method.

Spring provides two mechanism to achieve this: either using the PropertyPathFactoryBean class to create a context bean such as:

<!-- Restlet Component bean -->
<bean id="component" class="org.restlet.ext.spring.SpringComponent">
	...
</bean>

<!-- Component's Context bean -->
<bean id="component.context" class="org.springframework.beans.factory.config.PropertyPathFactoryBean"/>

<!-- Application bean -->
<bean id="application" class="org.restlet.Application">
	<constructor-arg ref="component.context" />
	...
</bean>

The second mechanism is based on the Spring utilities schema and is actually more compact:

<!-- Restlet Component bean -->
<bean id="component" class="org.restlet.ext.spring.SpringComponent">
	...
</bean>

<!-- Application bean -->
<bean id="application" class="org.restlet.Application">
	<constructor-arg>
		<util:property-path path="component.context" />
	</constructor-arg>
	...
</bean>

You also have to make sure that the util namespace is properly declared in your XML configuration header. Here is a snippet for Spring 2.5:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd">

<!-- Add you <bean/> definitions here -->

</beans>

This utilities mechanism is quite powerful and flexible, for more information check this page.

A complete example

This example is a Spring-enabled but otherwise functionally equivalent version of the bookmarks example from chapter 7 of RESTful Web Services by Richardson and Ruby. The complete code for this version is available through CVS from :pserver:anonymous@cvs.cs.luc.edu:/root/laufer/433, module BookmarksRestletSpring. Project dependencies are managed using Apache Maven, and the example illustrates standalone and servlet-container deployment.

In a nutshell, Spring handles the configuration of the top-level Restlet Component and Router beans. The Restlet Resources had to be modified to support the init method and the injection of the dependency on the db4o ObjectContainer, which is also configured in Spring. As expected, the domain objects User and Bookmark remained unchanged.

First, we show the configuration of the Restlet Component and top-level Router beans. The top-level Router is necessary only if an non-root context path is required for standalone deployment.

<bean id="top" class="org.restlet.ext.spring.SpringComponent">
	<property name="server">
		<bean class="org.restlet.ext.spring.SpringServer">
			<constructor-arg value="http" />
			<constructor-arg value="3000" />
		</bean>
	</property>
	<property name="defaultTarget" ref="default" />
</bean>

<bean id="default" class="org.restlet.ext.spring.SpringRouter">
	<property name="attachments">
		<map>
			<entry key="/v1" value-ref="root" />
		</map>
	</property>
</bean>

As a result, the main method has become very simple. It loads a Spring context based on two configuration metadata files, one for the preceding top-level beans, and one for the application-specific beans shown below. It then starts up the top-level Restlet Component.

    public static void main(String... args) throws Exception {
	// load the Spring application context
	ApplicationContext springContext = new ClassPathXmlApplicationContext(
		new String[] { "applicationContext-router.xml", "applicationContext-server.xml" });

	// obtain the Restlet component from the Spring context and start it
	((Component) springContext.getBean("top")).start();
    }

Next, we look at the configuration of the application-specific Router. We use a SpringRouter for this purpose, which is configured using a map of URI patterns to resources. The SpringFinder beans provide the extra level of indirection required to create Resource instances lazily on a per-request basis.

In this example, the last URI pattern has to be customized to accept complete URIs (possibly including slashes) as the last component of the pattern. We use Spring's nested properties to drill into the configuration of the URI pattern along with Spring's mechanism for accessing a static field in a class.

<bean id="root" class="org.restlet.ext.spring.SpringRouter">
	<property name="attachments">
		<map>
			<entry key="/users/{username}">
				<bean class="org.restlet.ext.spring.SpringFinder">
					<lookup-method name="createResource"
						bean="userResource" />
				</bean>
			</entry>
			<entry key="/users/{username}/bookmarks">
				<bean class="org.restlet.ext.spring.SpringFinder">
					<lookup-method name="createResource"
						bean="bookmarksResource" />
				</bean>
			</entry>
			<entry key="/users/{username}/bookmarks/{URI}">
				<bean class="org.restlet.ext.spring.SpringFinder">
					<lookup-method name="createResource"
						bean="bookmarkResource" />
				</bean>
			</entry>
		</map>
	</property>
	<property name="routes[2].template.variables[URI]">
		<bean class="org.restlet.util.Variable">
			<constructor-arg ref="org.restlet.util.Variable.TYPE_URI_ALL" />
		</bean>
	</property>
</bean>

<bean id="org.restlet.util.Variable.TYPE_URI_ALL"
	class="org.springframework.beans.factory.config.FieldRetrievingFactoryBean" />

Unlike the preceding singleton beans, we define the Resources as prototype beans so that they get instantiated separately for each request. All of the Resource beans depend on the db4o ObjectContainer and are configured analogously, so we show only UserResource here.

<bean id="userResource"
	class="org.restlet.example.book.rest.ch7.spring.UserResource"
	scope="prototype">
	<property name="container" ref="db4oContainer" />
</bean>

Using the db4o Spring Module, configuring the ObjectContainer is straightforward.

<bean id="db4oContainer"
	class="org.springmodules.db4o.ObjectContainerFactoryBean">
	<property name="configuration" ref="db4oConfiguration" />
	<property name="databaseFile" value="file://${user.home}/restbook.dbo" />
</bean>

<bean id="db4oConfiguration"
	class="org.springmodules.db4o.ConfigurationFactoryBean">
	<property name="updateDepth" value="2" />
	<property name="configurationCreationMode" value="NEW" />
</bean>

As mentioned above, we added the following elements to each application-specific Resource:

  • An empty default constructor.
  • An init method containing the code originally in the non-default constructor. That constructor now simply invokes the init method, although it is no longer used in this context.
  • An instance variable and getter/setter pair for the db4o ObjectContainer.

The following code fragment summarizes these changes.

public class UserResource extends Resource {

    private ObjectContainer container;
	
    // other instance variables

    public UserResource() { }
    
    @Override
    public void init(Context context, Request request, Response response) {
    	super.init(context, request, response);
        // code originally in non-default constructor
    }

    public UserResource(Context context, Request request, Response response) {
        super(context, request, response);
        init(context, request, response);
    }

    public ObjectContainer getContainer() {
        return container;
    }
    
    public void setContainer(ObjectContainer container) {
    	this.container = container;
    }

    // other methods
}

Configuration of Restlet Resources in Spring

Configuration of basic properties

Restlet Resources support only limited configuration beyond injecting custom dependencies such as the ObjectContainer in the example above. To make specific Resource classes more reusable, it would be helpful if their basic properties could be configured through Spring:

  • available
  • modifiable
  • negotiateContent
  • readable

Currently, the init method resets these properties to their default values but, in the Spring component life cycle, is invoked after Spring sets the properties. An obvious workaround is to refine the init method like so:

    @Override
    public void init(Context context, Request request, Response response) { 
        final ResourcePropertyHolder backup = new ResourcePropertyHolder();  
        BeanUtils.copyProperties(this, backup);
        super.init(context, request, response);
        BeanUtils.copyProperties(backup, this);
    }

Configuration of representation templates

In addition, it would be quite useful if one could map media types to representation templates in Spring. In the following example, we explore this idea further by mapping different media types to different Freemarker and JSON representation factories. Whenever a Resource creates a concrete representation, it passes a uniform data model to the representation factory, which then instantiates the template with the data model and returns the resulting representation. (The Freemarker configuration is also handled by Spring.)

<bean id="resource" class="helloworldrestlet.HelloWorldResource"
	scope="prototype">
	<property name="available" value="true" />
	<property name="representationTemplates">
		<map>
			<entry key-ref="org.restlet.data.MediaType.TEXT_PLAIN"
				value-ref="hwFreemarkerTextPlain" />
			<entry key-ref="org.restlet.data.MediaType.TEXT_HTML"
				value-ref="hwFreemarkerTextHtml" />
			<entry key-ref="org.restlet.data.MediaType.APPLICATION_JSON"
				value-ref="jsonRepresentationFactory" />
		</map>
	</property>
</bean>

<bean id="hwFreemarkerTextPlain"
	class="edu.luc.etl.restlet.spring.FreemarkerRepresentationFactory">
	<property name="templateName" value="hw-plain.ftl" />
	<property name="freemarkerConfig" ref="freemarkerConfig" />
</bean>

<bean id="hwFreemarkerTextHtml"
	class="edu.luc.etl.restlet.spring.FreemarkerRepresentationFactory">
	<property name="templateName" value="hw-html.ftl" />
	<property name="freemarkerConfig" ref="freemarkerConfig" />
</bean>

<bean id="jsonRepresentationFactory"
	class="edu.luc.etl.restlet.spring.JsonRepresentationFactory" />

<!-- omitted beans for specific MediaType static fields --> 

<bean id="freemarkerConfig"
	class="freemarker.template.Configuration">
	<property name="directoryForTemplateLoading"
		value="src/test/resources/presentation" />
	<property name="objectWrapper">
		<bean class="freemarker.template.DefaultObjectWrapper" />
	</property>
</bean>

When using this approach, the Resources themselves become very simple, for example:

public class HelloWorldResource extends ConfigurableRestletResource {
    @Override
    public Representation represent(Variant variant) {
	final Map<String, Object> dataModel = Collections.singletonMap("DATE", (Object) new Date());
	return createTemplateRepresentation(variant.getMediaType(), dataModel);
    }
}

A working proof-of-concept for this approach is available through CVS from :pserver:anonymous@cvs.cs.luc.edu:/root/laufer/433, module ConfigurableRestletResource. Support for the missing configuration of representations tied to responses to non-GET requests is in the works.

Comments (2)
Navigation doc: view