Spring with Stripes

Stripes is a web application framework that was designed to be easy to use and increase developer productivity. Spring is, primarily, a lightweight component container (although nowadays it is many other things too) designed to be easy to use and increase developer productivity. Naturally you might think to use the two together.

Stripes integrates with Spring to provide your ActionBean classes access to Spring resources in the form of configured Spring beans. It does this using a simple, behind the scenes, injecting of Spring beans into ActionBeans. You'll need to perform a little configuration to get things started, but once that's done you can use your Spring beans in your Stripes web application without writing another line of XML!

Installing and Configuring Spring

This section provides a basic overview of how to install and configure Spring so that you can use it with your Stripes application. It will not cover all the possible ways of doing this, but simply one that works. Start off by downloading the latest version of Spring from http://www.springframework.org/download. Expand the downloaded file and find the file dist/spring.jar. Copy this file into your application's classpath, probably under WEB-INF/lib.

Slimming down your classpath

If having lots of classes in your classpath that you may not be using bothers you, you may want to take the time to go through the various spring-* jars in the dist directory and assemble the minimal set of classes you need to use Spring. Me, I'm too lazy to do that so I just use spring.jar which includes everything I need and a bunch more too.

Once you have the jar or jars in place you'll need to create a spring context file, and configure a web application context in your web.xml file. Let's start by taking a look at the web.xml:

Configuring Spring in the web.xml
<listener> 
	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> 
</listener> 

<context-param> 
	<param-name>contextConfigLocation</param-name> 
	<param-value>/WEB-INF/spring-context.xml</param-value> 
</context-param> 

The listener stanza simply configures a Spring listener class, which is used to bootstrap Spring in the web application environment. The second stanza provides Spring with the context parameter it uses to locate the context file being used. In this case we are using /WEB-INF/spring-context.xml. Any name can be used, but it is generally a good idea to put it in WEB-INF or a subdirectory thereof, as this will ensure it is not served up to clients of your web application!

The second thing we need to do is configure the Spring context. For more details on this you are better off reading the Spring documentation at http://www.springframework.org/docs/reference/index.html. Below is a sample context that we might use to access the various manager components in the Bugzooky application as Spring beans.

/WEB-INF/spring-context.xml
<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> 

<beans> 
	<bean name="/bugzooky/BugManager" class="net.sourceforge.stripes.examples.bugzooky.biz.BugManager"/>
	<bean name="/bugzooky/PersonManager" class="net.sourceforge.stripes.examples.bugzooky.biz.PersonManager"/>
	<bean name="/bugzooky/ComponentManager" class="net.sourceforge.stripes.examples.bugzooky.biz.ComponentManager"/>
</beans> 

Configuring Stripes for Spring

Now that we have Spring all set up we need to do a couple of things to make Stripes a bit more Spring aware. In your web.xml file locate the initialization parameters for the Stripes Filter. If you do not already have the Interceptor.Classes parameter defined, add the following:

Using the Spring Interceptor (Stripes 1.5 and up)
<init-param> 
	<param-name>Interceptor.Classes</param-name> 
	<param-value> 
		net.sourceforge.stripes.integration.spring.SpringInterceptor 
	</param-value> 
</init-param> 

The parameter tells Stripes to use the Spring interceptor. This will work with Stripes 1.5 and up; if you are still using an older version, you also need to tell Stripes not to stop using the Before/After method interceptor (which is configured by default):

Using the Spring Interceptor (Stripes 1.4.x and earlier
<init-param> 
	<param-name>Interceptor.Classes</param-name> 
	<param-value> 
		net.sourceforge.stripes.integration.spring.SpringInterceptor, 
		net.sourceforge.stripes.controller.BeforeAfterMethodInterceptor
	</param-value> 
</init-param> 

We're done installing things and monkeying with configurations. Now let's do some coding!

Accessing Spring Beans in your ActionBean

Stripes uses the SpringInterceptor to inject Spring beans into ActionBeans after the ActionBean is instantiated. To do this, it must be told how to inject beans and what to inject. As opposed to pushing the linkage out to an XML file, a simple annotation is used. The standard case would look something like this:

Injecting a Spring bean into an ActionBean
/** Setter to allow the BugManager to be injected. */ 
@SpringBean("/bugzooky/BugManager") 
public void injectBugManager(BugManager bm) { 
	this.bm = bm; 
} 

Specifying the exact name and path of the Spring bean you want injected is probably the safest way to go. There are, however, two other options available to you; we'll call auto-wire by name and auto-wire by type. If you omit the value of the @SpringBean annotation thusly:

Auto-wiring of Spring beans in an ActionBean
/** Setter to allow the BugManager to be injected. */ 
@SpringBean 
protected void setBugManager(BugManager bm) { 
	this.bm = bm; 
} 

then Stripes will first attempt to auto-wire by name, and then by type. First the name of the desired bean is derived from the method name - if the method name starts with 'set' then it is removed and the next character down-cased (following standard JavaBean rules), otherwise the entire method name is taken verbatim. In the example above this yields bugManager. The Spring context is then queried to see if it contains a bean called bugManager. This query is case insensitive and path insensitive, resulting in a match to the /bugzooky/BugManager bean in the example above.

If the context does not contain a bean with a matching name then auto-wiring by type is a last ditch effort. In this case if a single bean exists in the context that is of the type required by the annotated method (BugManager in this case) then that bean will be selected and injected into the ActionBean.

It's worth noting that although ActionBeans themselves are not Spring beans, using this approach, the beans injected from the Spring context are completely managed by Spring and have access to all the Spring services any other Spring bean would.

Public Get/Set Methods for Spring Beans are Dangerous

Because Stripes maps parameters from the request into properties on ActionBeans by name, it is extremely unwise to provide public methods matching JavaBean property semantics for Spring Beans (e.g. public void setBugManager(...)). Doing so would allow malicious users to set your Spring beans to null, and possibly even set nested properties on them if a getter method is present for the Spring bean.

The possibility of this happening is quite remote - a malicious user would have to successfully guess that you were using Spring, the name of the methods used to access Spring Beans, and the properties of your Spring Beans. But it is theoretically possible, and as such you should mitigate the risk (especially since it's easy).

Stripes provides several alternatives. If you are explictly naming your Spring beans with @SpringBean("/some/bean") then you can simply name your method differently, e.g. injectBugManager(). If auto-wiring by name, simply drop the set from the method name, e.g. public void bugManager(BugManager mgr). Or alternatively, as discussed below, use protected or private methods and/or field access to inject your Spring Beans.

Method vs. Field Access

Stripes can inject Spring beans through both method access and field access. The access type is determined by the placement of the @SpringBean annotation. If the annotation is directly on the field, e.g.:

 @SpringBean private BugManager bugManager;

then Stripes will use field access. If the annotation is on a method, e.g.:

 @SpringBean protected void setBugManager(BugManager bm) { ... }

then Stripes will use the method to inject the bean.

If the JVM's security manager will permit it, Stripes will even inject Spring beans, when requested, into protected, package access and private fields and methods. If the JVM's security manager does not permit this, an exception will be raised explaining the problem.

Using Spring Beans in Other Types of Objects

It's sometimes the case that ActionBeans are not the only type of class you'll write that are managed by Stripes but need Spring beans injected into them. Interceptors are a common example, but also if you end up overriding any of Stripes' ConfigurableComponents you may wish to have Spring beans injected.

Fortunately this is fairly easy. In the case of interceptors there is a base class that can be extended. SpringInterceptorSupport is a simple base class that provides Spring bean injection at initialization time.

For other classes, the solution is almost as simple. A class called SpringHelper provides methods to apply the same Spring bean injection mechanism to any object provided you have access to either the ActionBeanContext, ServletContext or Spring ApplicationContext. In most cases this can be acheived by writing:
    SpringHelper.injectBeans(obj, StripesFilter.getConfiguration().getServletContext());