Behind the Spring Security Namespace

Luke Taylor

With the introduction of the security schema in Spring Security 2, it became much easier to get a simple secured application up and running. In older versions, users had to declare and wire-up all the implementation beans individually, resulting in large and complicated Spring application context files which were difficult to understand and maintain. There was a pretty steep learning curve and I can still remember that it took me some time to get my head round it all when I started working on the project (then Acegi Security), back in 2004. On the positive side, this exposure to the basic building blocks of the framework meant that once you had managed to put together a working configuration, it was almost impossible not to have gained at least some awareness of the important classes and how they work together. This knowledge in turn put you in a good position to take advantage of the opportunities for customization that are one of the biggest benefits of using Spring Security.

We now have many Spring Security users who have started out using the namespace and have benefited from the simplicity and rapid development opportunities that it offers, but things get more difficult when you want to move beyond the features offered by the namespace. At that point you have to start to understand the framework architecture and how your custom classes will fit in. You have to know which classes to extend, which strategy interfaces to implement and where to plug them in. The learning curve is still there, it has just moved. The namespace intentionally provides a high-level view of the problem domain addressed by Spring Security and as such it actually hides the implementation details, making it difficult to know what is really going on. It does provide a lot of extension points, but for whatever reason you may feel you need to dig deeper.

In this article we'll take a look at a simple namespace configuration for a web application and what it would look like as a full-blown Spring bean configuration. We won't go into all the details of what the beans do, but you can find more information on specific classes and interfaces in the reference manual and Javadoc. The material here is mainly targeted at existing users who are already familiar with the basics, so if you haven't used Spring Security before you should at least read the namespace chapter in the reference manual and spend some time looking at the sample applications.

Namespace Configuration

First let's look at the namespace configuration we want to replace.

<beans:beans xmlns="http://www.springframework.org/schema/security"
    xmlns:beans="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
      http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
      http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd">

    <http>
        <intercept-url pattern="/secure/extreme/**" access="ROLE_SUPERVISOR" />
        <intercept-url pattern="/secure/**" access="IS_AUTHENTICATED_FULLY" />
        <intercept-url pattern="/login.htm" access="IS_AUTHENTICATED_ANONYMOUSLY" />
        <intercept-url pattern="/images/*" filters="none" />
        <intercept-url pattern="/**" access="ROLE_USER" />
        <form-login login-page="/login.htm" default-target-url="/home.htm" />
        <logout logout-success-url="/logged_out.htm" />
    </http>

    <authentication-manager>
        <authentication-provider>
            <password-encoder hash="md5"/>
            <user-service>
                <user name="bob" password="12b141f35d58b8b3a46eea65e6ac179e" authorities="ROLE_SUPERVISOR, ROLE_USER" />
                <user name="sam" password="d1a5e26d0558c455d386085fad77d427" authorities="ROLE_USER" />
            </user-service>
        </authentication-provider>
    </authentication-manager>

</beans:beans>

This is a pretty straightforward example, similar to what you'll see in online examples and the sample applications that come with the project. It defines an in-memory list of user accounts to authenticate against, with a list of authorities (in this case simple roles) for each user. It also configures a set of protected URL patterns within the web application, a form-based authentication mechanism and support for a basic logout URL.

How would we reproduce this with an old-fashioned bean configuration? Time for a trip down memory lane for those who were around in the Acegi Security days.

Spring Bean Version

Authentication Beans

Let's look at the <authentication-manager> element first, which (as of Spring Security 3.0) must be declared in any namespace-based configuration. In this example, the <http> part depends on this element (the form-login authentication mechanism uses it to authenticate against). The actual dependency is on the interface AuthenticationManager, which encapsulates the authentication services provided by a Spring Security configuration. You could provide your own implementation at this level, but most people use the default, ProviderManager, which delegates to a list of AuthenticationProvider instances. The configuration could look like this:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:sec="http://www.springframework.org/schema/security"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
      http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
      http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd">

    <bean id="authenticationManager" class="org.springframework.security.authentication.ProviderManager">
        <property name="providers">
            <list>
                <ref bean="authenticationProvider" />
                <ref bean="anonymousProvider" />
            </list>
        </property>
    </bean>

    <bean id="authenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
        <property name="passwordEncoder">
            <bean class="org.springframework.security.authentication.encoding.Md5PasswordEncoder" />
        </property>
        <property name="userDetailsService" ref="userService" />
    </bean>

    <bean id="anonymousProvider" class="org.springframework.security.authentication.AnonymousAuthenticationProvider">
        <property name="key" value="SomeUniqueKeyForThisApplication" />
    </bean>

    <sec:user-service id="userService">
        <sec:user name="bob" password="12b141f35d58b8b3a46eea65e6ac179e" authorities="ROLE_SUPERVISOR, ROLE_USER" />
        <sec:user name="sam" password="d1a5e26d0558c455d386085fad77d427" authorities="ROLE_USER" />
    </sec:user-service>

</beans>

At this stage we've left the <user-service> element in place to illustrate that it can be used in isolation to create a UserDetailsService instance which is injected into the DaoAuthenticationProvider. We've also switched to using "beans" as the default XML namespace. We'll assume this from now on. UserDetailsService is an important interface in the framework and is just a DAO for user information. Its only responsibility is to load the data for a named user account. The bean equivalent would be

<bean id="userService" class="org.springframework.core.userdetails.memory.InMemoryDaoImpl">
    <property name="userMap">
        <value>
            bob=12b141f35d58b8b3a46eea65e6ac179e,ROLE_SUPERVISOR,ROLE_USER
            sam=d1a5e26d0558c455d386085fad77d427,ROLE_USER
        </value>
    </property>
</bean>

In this case, the namespace syntax is clearer, but you may want to use your own UserDetailsService implementation. Spring Security also has standard JDBC and LDAP-based versions. We've also added in an AnonymousAuthenticationProvider which is purely there to support the AnonymousAuthenticationFiter which appears in the web configuration below.

Web Beans

Now we'll take a look at how the <http> block can be expanded. This is more complicated as the created beans do not not map as obviously to the element names used in the namespace.

The FilterChainProxy

As you probably already know, Spring Security's web functionality is implemented using servlet filters. It maintains its own chain of filters in the application context and delegates to this using an instance of Spring's DelegatingFilterProxy defined in the web.xml file. The class which implements this delegate filter chain (or potentially multiple chains) is called FilterChainProxy. You can think of the <http> block as creating the FilterChainProxy bean. FilterChainProxy has a property called filterChainMap, which is a map of patterns to lists of filter beans. So for example, you might have something like this:

    <bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy">
        <property name="matcher">
            <bean class="org.springframework.security.web.util.AntUrlPathMatcher"/>
        </property>
        <property name="filterChainMap">
            <map>
                <entry key="/somepath/**">
                    <list>
                      <ref local="filter1"/>
                    </list>
                </entry>
                <entry key="/images/*">
                    <list/>
                </entry>
                <entry key="/**">
                    <list>
                      <ref local="filter1"/>
                      <ref local="filter2"/>
                      <ref local="filter3"/>
                    </list>
                </entry>
            </map>
        </property>
    </bean>

where filter1, filter2 etc. are the names of other beans in the application context which implement the javax.servlet.Filter interface.

So FilterChainProxy matches incoming requests to lists of filters and passes a request through the first matching chain it finds. Note that with the exception of the "/images/*" pattern (which maps to an empty filter chain), these are not connected with the patterns in the <intercept-url< namespace elements. The <http> configuration is currently only capable of maintaining a single list of filters mapped to all requests (except those which are configured to bypass the filter chain completely).

Since the configuration above is a bit on the verbose side, there is a more compact namespace syntax which can be used to configure a FilterChainProxy map, without losing any functionality. The equivalent of the above would then be:

    <bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy">
        <sec:filter-chain-map path-type="ant">
            <sec:filter-chain pattern="/somepath/**" filters="filter1"/>
            <sec:filter-chain pattern="/images/*" filters="none"/>
            <sec:filter-chain pattern="/**" filters="filter1, filter2, filter3"/>
        </sec:filter-chain-map>
    </bean>

The filter chains are now specified as an ordered list of bean names, in the order the filters will be applied. So what filters would our original namespace configuration create? In this case the FilterChainProxy would be:

    <alias name="filterChainProxy" alias="springSecurityFilterChain"/>

    <bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy">
        <sec:filter-chain-map path-type="ant">
            <sec:filter-chain pattern="/images/*" filters="none"/>
            <sec:filter-chain pattern="/**" filters="securityContextFilter, logoutFilter, formLoginFilter, requestCacheFilter,
                     servletApiFilter, anonFilter, sessionMgmtFilter, exceptionTranslator, filterSecurityInterceptor" />
        </sec:filter-chain-map>
    </bean>

So there are nine filters in there, some of which are optional and some of which are essential. At this point you can see that you are now exposed to many of the the details which the namespace protects you from. You control both the filters used and the order they are invoked in, both of which are crucial.

We've also added alias for the bean, to match the name used previously in web.xml. Alternatively, you could just use "filterChainProxy" directly.

The Filter Beans

We'll now look at those nine filter beans and the other beans that are required to support them.

<bean id="securityContextFilter" class="org.springframework.security.web.context.SecurityContextPersistenceFilter" >
    <property name="securityContextRepository" ref="securityContextRepository" />
</bean>

<bean id="securityContextRepository"
        class="org.springframework.security.web.context.HttpSessionSecurityContextRepository" />

<bean id="logoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
    <constructor-arg value="/logged_out.htm" />
    <constructor-arg>
        <list><bean class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler" /></list>
    </constructor-arg>
</bean>

<bean id="formLoginFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
    <property name="authenticationManager" ref="authenticationManager" />
    <property name="authenticationSuccessHandler">
        <bean class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
            <property name="defaultTargetUrl" value="/index.jsp" />
        </bean>
    </property>
    <property name="sessionAuthenticationStrategy">
        <bean class="org.springframework.security.web.authentication.session.SessionFixationProtectionStrategy" />
    </property>
</bean>

<bean id="requestCacheFilter" class="org.springframework.security.web.savedrequest.RequestCacheAwareFilter" />

<bean id="servletApiFilter" class="org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter" />

<bean id="anonFilter" class="org.springframework.security.web.authentication.AnonymousAuthenticationFilter" >
    <property name="key" value="SomeUniqueKeyForThisApplication" />
    <property name="userAttribute" value="anonymousUser,ROLE_ANONYMOUS" />
</bean>

<bean id="sessionMgmtFilter" class="org.springframework.security.web.session.SessionManagementFilter" >
    <constructor-arg ref="securityContextRepository" />
</bean>

<bean id="exceptionTranslator" class="org.springframework.security.web.access.ExceptionTranslationFilter">
    <property name="authenticationEntryPoint">
        <bean class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
            <property name="loginFormUrl" value="/login.htm"/>
        </bean>
    </property>
</bean>

<bean id="filterSecurityInterceptor" class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
    <property name="securityMetadataSource">
        <sec:filter-security-metadata-source>
            <sec:intercept-url pattern="/secure/extreme/*" access="ROLE_SUPERVISOR"/>
            <sec:intercept-url pattern="/secure/**" access="IS_AUTHENTICATED_FULLY" />
            <sec:intercept-url pattern="/login.htm" access="IS_AUTHENTICATED_ANONYMOUSLY" />
            <sec:intercept-url pattern="/**" access="ROLE_USER" />
        </sec:filter-security-metadata-source>
    </property>
    <property name="authenticationManager" ref="authenticationManager" />
    <property name="accessDecisionManager" ref="accessDecisionManager" />
</bean>

Again, we've used a convenient namespace element, filter-security-metadata-source, to create the SecurityMetadataSource instance which is used by the FilterSecurityInterceptor, but you can insert your own bean here (see this FAQ for an example). The filter-security-metadata-source element creates an instance of DefaultFilterInvocationSecurityMetadataSource.

SecurityContextPersistenceFilter

This filter must be included in any filter chain. It is responsible for storing authentication information (a SecurityContext instance) between requests. It also for sets up the thread-local variable in which it is stored during a request and clears it when the request completes. Its default strategy is to store the SecurityContext in the HTTP session, hence the use of the HttpSessionSecurityContextRepository bean.

Access Control

FilterSecurityInterceptor sits at the end of the stack and applies the configured security contraints to incoming requests. If the request is not authorized (either because the user isn't authenticated, or because they don't have the required authorities) it will raise an exception. This will be handled by the ExceptionTranslationFilter which will either send the user an access denied message, or start the authentication process by invoking the configured AuthenticationEntryPoint. In this case, a LoginUrlAuthenticationEntryPoint is in use, which redirects the user to a login page. Before doing this, ExceptionTranslationFilter will cache the current request information, so that it can be restored after authentication, if required.

The Authentication Process

UsernamePasswordAuthenticationFilter is responsible for processing a submitted login form (it's created by the <form-login> namespace element). The bean has been configured with a SavedRequestAwareAuthenticationSuccessHandler which means that it will redirect the user to the URL that they originally requested before they were asked to authenticate. The original request is then restored by the RequestCacheFilter which using a request wrapper, allowing the user to continue from the point they left off.

Other Miscellaneous Filters

LogoutFilter is simply responsible for processing a logout link (/j_spring_security_logout by default), clearing the security context and invalidating the session. AnonymousAuthenticationFilter is responsible for populating the security context for anonymous users, making it easier to apply default security restrictions which are relaxed for certain URLs. For example, in the above configuration, the IS_AUTHENTICATED_ANONYMOUSLY attribute implies that anonymous users can access the login page (but nothing else). Check out the chapter on this in the manual for more information. Its use is optional, and you can remove the extra AnonymousAuthenticationProvider if you aren't using it.

SecurityContextHolderAwareRequestFilter provides the standard servlet API security methods, using a request wrapper which accesses the SecurityContext. If you don't need these methods, you can omit this filter. SessionManagementFilter is responsible for applying a session-related strategy if the user is authenticated during the current request (for example, by remember-me authentication). In it's default configuration it will create a new session (copying the attributes from the existing one) with the aim of changing the session identifier, and providing a defence against session-fixation attacks. It is also used when Spring Security's concurrent session control is being used. In this configuration, the UsernamePasswordAuthenticationFilter is the only authentication mechanism in place, and is also injected with a SessionFixationProtectionStrategy. This means we can safely remove the session-management filter.

The AccessDecisionManager

If you've been paying close attention, you'll notice that we are still missing a bean reference from the above configuration. The security interceptor needs to be configured with an AccessDecisionManager. If you're using the namespace, then one is created internally, though you can also plug in a custom bean. Without the namespace, we need to supply one explicitly. The equivalent of the namespace internal version would look like this:

    <bean id="accessDecisionManager" class="org.springframework.security.access.vote.AffirmativeBased">
        <property name="decisionVoters">
            <list>
                <bean class="org.springframework.security.access.vote.RoleVoter"/>
                <bean class="org.springframework.security.access.vote.AuthenticatedVoter"/>
            </list>
        </property>
    </bean>

WebInvocationPrivilegeEvaluator

This is another bean which is registered by the namespace, even though it it isn't directly required (it may be used in some JSP tags). It allows you to query whether the current user is allowed to invoke a particular URL. It can be useful in your controller beans to establish what information or navigation links should be made available in a presented view.

    <bean id="webPrivilegeEvaluator" class="org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator">
        <constructor-arg ref="filterSecurityInterceptor" />
    </bean>
Conclusion

Once again, this article isn't intended to explain in detail how all these beans work, but mainly to provide a reference to help to move on from a basic namespace configuration and to understand what lies underneath. As you can see, it's quite complicated! But remember that it's possible to plug quite a few of these beans into the namespace configuration itself, and you can now see where they're actually going. And now that you know which classes are involved, you know where to look for further information in the Spring Security Reference Manual, the Javadoc and of course the source code itself.

Spring Framework 3.0.1 released

Juergen Hoeller

After two months of incorporating valuable feedback, it is my pleasure to announce the first Spring 3.0 maintenance release – addressing more than 170 reported issues. Get it from our download page.

Since quite a few users asked for a dependencies distribution (as an alternative to grabbing dependencies via Maven or Ivy), we are providing a third download now: containing an Ivy repository with common third-party jar files. Note that the core framework is separate from the dependencies; the latter are just provided as an additional convenience and do not constitute an inherent part of the framework distribution. You may of course keep using any supported version of the third-party libraries of your choice.

Talking about third-party library versions, Spring 3.0.1 includes support for two important new third-party releases: Tiles 2.2 (with 2.2.1 as the latest release) and Hibernate 3.5 (with 3.5 CR1 as the current release candidate). You may keep using Tiles 2.1 and Hibernate 3.2/3.3, respectively; we are just actively tracking the emerging next generations of those libraries so that you may choose to upgrade whenever you wish. FYI, in line with Spring 3.0's JPA 2.0 support, Hibernate 3.5 is the first Hibernate generation to implement JPA 2.0.

Spring 3.0.1 introduces several core refinements, addressing limitations that were reported against 3.0 GA:

  • ApplicationListener detection has been revised for improved robustness and consistency, e.g. with respect to proxies and factory methods, and in particular with respect to Spring 3.0's support for declaring a specific event through the use of generics.
  • Pointcut-based proxies and also EntityManager proxies and @Transactional proxies are fully serializable now. This completes the BeanFactory serialization capabilities in 3.0 GA, in particular for use in web application environments.
  • Generic interfaces such as FactoryBean and HttpMessageConverter have relaxed declarations of Class parameters now, for more convenient use in practice. (You may have to adapt your source signatures in some cases if you implement those interfaces with use of generics; binary compatibility should not be affected.)
  • Spring's JdbcTemplate features overloaded query methods with full use of varargs, as previously known from the SimpleJdbcTemplate class. This turns the standard JdbcTemplate class into an equally convenient Java 5 based delegate for most query use cases.

We recommend upgrading to Spring 3.0.1 for the best possible Spring 3 API experience!

As a bonus, 3.0.1 also introduces an addition to Spring's JSP tag library: The <spring:eval> tag allows for evaluating a SpEL (Spring EL) expression and embedding its result into a JSP page, properly formatted through Spring 3.0's new formatting system. This is basically a Spring variant of JSTL's <c:out>, with <fmt:*> capabilities integrated out of the box. Watch out for up-to-date sample applications, demonstrating the use of Spring 3.0's web feature set – just around the corner!

Quick update (Feb 19th): Due to an accidental breakage in the OSGi manifest, we released a 3.0.1.A update (called "3.0.1.RELEASE-A" for OSGi naming convention reasons). If you are going to use 3.0.1 in an OSGi environment, make sure to use those revised artifacts.

P.S.: A good opportunity to hear about the latest Spring 3 features – and about the latest releases in the overall SpringSource portfolio – is the SpringSource S2G Forum – Munich on March 18th, part of this year's S2G conference series in Europe. See you there!

Practical Use of Spring Batch and Spring Integration

Dave Syer

There are some common concerns of users of Spring Batch and Spring Integration, and we get asked a lot about how they fit together. Spring Batch Admin 1.0.0.M2 was released recently, and it makes heavy use of Spring Integration, so it is a good vehicle for looking at some specific use cases, and that is what we plan to do in this article.

Spring Batch Integration

Part of the 1.0.0.M2 release was the Spring Batch Integration module, recently migrated from Spring Batch and given a new home with Batch Admin. Many of the Batch-Integration cross over use cases are either implemented or demonstrated in Spring Batch Integration. The reason for the new home is that Batch Admin uses a lot of the features of Batch Integration, and so aligning the release cycle of those projects makes more sense.

Spring Batch Admin

Spring Batch Admin is an open source project from SpringSource. It aims to provide developers with a Web UI and tools for building their own UI to interact with Spring Batch jobs (launching, stopping, investigating causes of failure etc.). The recent milestone release is fairly complete as far as planned functionality goes for 1.0, but if you have ideas or contributions to make, please visit the forum and the issue tracker and get involved in the community.

The target runtime out of the box is a single instance of a servlet container (e.g. SpringSource tc Server), and in that container the system works with zero or no configuration. But we want to be able to support customisations and extensions of the basic use cases, including scaling the deployment up to a cluster of servers, and Spring Integration is proving to be the key to a lot of the extension points.

Combining Batch and Integration

The line between Spring Batch and Spring Integration is not always clear, but there are guidelines that one can follow. Principally, these are: think about granularity, and apply common patterns. Some of those common patterns are described in this article. More are implemented (and may be the subject of future articles) in Spring Batch Integration and Spring Batch Admin.

Adding messaging to a batch process enables automation of operations, and also separation and strategising of key concerns. For example a message might trigger a job to execute, and then the sending of the message can be exposed in a variety of ways. Or when a job completes or fails that might trigger a message to be sent, and the consumers of those messages might have operational concerns that have nothing to do with the application itself.

The other way round works too: messaging can also be embedded in a job, but that is out of the scope of this article. For example: reading or writing items for processing via channels.

Here are some use cases that are implemented in Batch Admin using Spring Integration and Spring Batch Integration.

Pattern: Message Trigger

The beauty of Spring Integration is the separation of concerns between message producers and message consumers, and a good concrete example of that is the ability for a message to trigger a Job execution. In this case the consumer is completely generic, and a very thin wrapper around a standard Spring Batch JobLauncher (code here is from the Spring Batch Integration JobLaunchingMessageHandler):

@ServiceActivator
public JobExecution launch(JobLaunchRequest request) {

    Job job = request.getJob();
    JobParameters jobParameters = request.getJobParameters();

    return jobLauncher.run(job, jobParameters);
   
}

As you can see from the code snippet above, the wrapper is so thin it hardly merits a mention, but its virtue is that it has a very clear and obvious reponsibility and is easily testable in isolation. The JobLaunchRequest object is a special wrapper for the input parameters to a JobLauncher, so that they can form the payload of a message in Spring Integration.

The JobLaunchingMessageHandler is hooked up to a MessageChannel in Spring Batch Admin (in the Manager jar /META-INF/bootstrap/integration/launch-context.xml):

<service-activator input-channel="job-requests">
    <beans:bean class="org.springframework.batch.integration.launch.JobLaunchingMessageHandler">
        <beans:constructor-arg ref="jobLauncher" />
    </beans:bean>
</service-activator>

That's the consumer side of this integration pattern completed. It is a local approach, in that it isn't particularly well suited to remote invocation because the JobLaunchRequest is intentionally not Serializable (because the Job is not).

To launch a Job locally all we need to do is create a producer and use it to send a JobLaunchRequest to the job-requests channel. There is an integration test in the Batch Admin Manager module that does just this, but the real power of the integration approach here is the ability to strategise the requests and have them come from a variety of different producers.

Pattern: Channel Reuse

A message can be sent to the job-requests channel in Batch Admin in a number of ways. To break out of the local invocation and expose the job remotely all that is needed is to adapt an incoming request in some other form to a JobLaunchRequest, and Spring Integration makes this really easy. This is the basic scenario for a pattern we call Channel Reuse. Examples are:

HTTP: Browser

The Spring Integration HTTP adapter module can be used to accept an input message over HTTP:

<http:inbound-channel-adapter name="/job-requests" channel="job-launches"
    request-mapper="bodyInboundRequestMapper" view="reload-job-executions" />

This snippet can be found in the Batch Admin Manager module (META-INF/servlet/integration-servlet.xml). It exposes an endpoint URL http://.../batch/job-requests which we can use to send a request for a job execution by submiting a form in a browser.

The request in principle can be in any form we like because we can transform the message downstream of this adapter and upstream of the JobLaunchingMessageHandler. In Spring Batch Admin the transformation is done on the output from the adapter by another POJO message handler (StringToJobLaunchRequestAdapter).

HTTP: Command line

The same HTTP adapter that was used above can be used to launch a job remotely from a UN*X command line. This is a really great way to use Spring Integration HTTP adapters: you can automate a lot of operations using simple shell scripts. E.g. this would work if the application was deployed locally with a job called "staging":

$ echo staging[input.file=foo] | curl -v -d @- -H "Content-Type: text/plain" \
  http://localhost:8080/springone-web-demo/batch/job-requests

The job named "staging" is launched with one parameter (input.file=foo), where foo is the absolute path of a file to read as input. The job is configured with an item reader like this:

<bean id="reader" class="org.springframework.batch.item.file.FlatFileItemReader" scope="step">
    <property name="linesToSkip" value="1" />
    <property name="lineMapper">
        <bean class="org.springframework.batch.item.file.mapping.PassThroughLineMapper"/>
    </property>
    <property name="resource" value="#{jobParameters[input.file]}" />
</bean>

(This snippet is not in the Spring Batch Admin sample, but it was in the demo that we gave at Spring One Americas 2009.)

File polling

Spring Integration can poll for files in a directory (using the file adapters module). The message generated just needs to be adapted for the job-requests channel. Spring Batch Admin does this in a simple message handler (FileToJobLaunchRequestAdapte):

public JobLaunchRequest adapt(File file) throws NoSuchJobException {
    JobParameters jobParameters = new JobParametersBuilder().addString(
            "input.file", file.getAbsolutePath()).toJobParameters();
    return new JobLaunchRequest(job, jobParameters);
}

This simple POJO method is declared as a @ServiceActivator (it could have been a @Transformer), and so it can be inserted in a message handling chain just before the JobLaunchingMessageHandler to tranform a File into a JobLaunchRequest.

Restart

A failed job can usually be restarted in Spring Batch, and this feature is available through a web browser in the Spring Batch Admin UI. It is also available on the command line, or for anyone who can send a message to a Spring Integration channel called job-restarts:

<channel id="job-restarts" />
    <service-activator input-channel="job-restarts" output-channel="job-requests">
    <beans:bean class="org.springframework.batch.admin.integration.JobNameToJobRestartRequestAdapter">
        <beans:property name="jobLocator" ref="jobRegistry" />
        <beans:property name="jobExplorer" ref="jobExplorer" />
    </beans:bean>
</service-activator>

All this channel requires is the job name, and it has been exposed as an HTTP inbound endpoint, so from a UN*X command line you might do this:

$ echo staging | curl -v -d @- -H "Content-Type: text/plain" \
  http://localhost:8080/springone-web-demo/batch/job-restarts

Retry

If a job fails repeatedly with a recoverable error (like a timeout or network glitch in a call to a remote service) maybe you would like to have it restarted automatically. Retry can be dealt with at a low level inside the job using some features of Spring Batch, but to retry the whole job requires some manipulation of the runtime. This could be accomplished simply with Spring Integration, now that the job-requests channel is accepting launch requests. The endpoint for this would act as a filter, looking for failure conditions in the job that are known to be retryable, and then as a restart trasformer (like the example above). So a chain like this would work:

<chain input-channel="input-files" output-channel="job-requests"
        xmlns="http://www.springframework.org/schema/integration">

    <filter>
        <bean class="…RetryableJobExecutionFilter"
            xmlns="http://www.springframework.org/schema/beans">

            <property name="pattern" value="(&amp;s).*TimeoutException.*" />
        </bean>
        </filter>
    <service-activator>
        <bean class="org.springframework.batch.admin.integration.FileToJobLaunchRequestAdapter"
            xmlns="http://www.springframework.org/schema/beans">

            <property name="job" ref="job1" />
        </bean>
    </service-activator>
</chain>

where the RetryableJobExecutionFilter might be implemented like this

public boolean isRetryable(JobExecution jobExecution) {
    boolean retryable = false;
    for (StepExecution stepExecution : jobExecution.getStepExecutions()) {
        if (stepExecution.getStatus().isLessThan(BatchStatus.STOPPED)) {
            continue;
        }
        if (stepExecution.getExitStatus().getExitDescription().matches(pattern)) {
            retryable = true;
            break;
        }
    }
    return retryable;
}

This example was in our Spring One demo; it is not in Spring Batch Admin, although it is trivial to implement for any specific filter that you need.

Input File Uploads

File uploads into the application are supported directly through the Spring Batch Admin UI. It isn't recommended to use an HTTP POST to upload large files, mainly because the application has to buffer the contents in memory, but this is a nice feature for uploading small or medium sized datasets for processing by Spring Batch.

The sample application actually doesn't use the file poller from Spring Integration (but it is available for clients who want to configure it as described above); rather it uses a direct message trigger once the file is uploaded. The strategy is for the Manager module to upload a file and then send a message to a publish-subscribe channel (input-files).

Any job that can use the input file just has to have an upstream component subscribe to that channel and pass on the file if it is of interest. This is done in the sample by filtering the file by its parent directory name:

<chain input-channel="input-files" output-channel="job-requests"
        xmlns="http://www.springframework.org/schema/integration">

    <filter expression="payload.parent.name=='sample'" />
    <service-activator>
        <bean class="org.springframework.batch.admin.integration.FileToJobLaunchRequestAdapter"
            xmlns="http://www.springframework.org/schema/beans">

            <property name="job" ref="job1" />
        </bean>
    </service-activator>
</chain>

If the input file has a parent directory (which can be set in the Web UI) "sample" then it is piped into the service activator which converts it to a JobLaunchRequest and sends it on for processing by the JobLaunchingMessageHandler as already discussed).

Pattern: POJO Message Handling

Sending a file to the input-files channel is done in the Batch Admin Manager, in the best tradition of Spring applications, through simple POJO and interface-based components. There is a FileService interface and a local implementation which uses a temporary directory to marshal the files coming in over HTTP. Once a file is uploaded it is sent by the service through a simple messaging gateway with a custom interface:

public interface FileSender {
   
    void send(File file);

}

The interface has no implementation (except stubs in unit tests) because Spring Integration can provide one:

<gateway id="fileSender"
    service-interface="org.springframework.batch.admin.service.FileSender"
    default-request-channel="input-files" />

<beans:bean class="org.springframework.batch.admin.service.LocalFileService">
    <beans:property name="fileSender" ref="fileSender" />
</beans:bean>

Configuration File Uploads

Spring Batch Admin allows the user to upload Spring configuration files for jobs to launch and manage from the UI. This is really useful for re-parameterising a job at run time, for instance when running a suite of performance tests to measure the effect of various performance tweaks, like changing the commit interval in a step.

To accept the confuration files for input we use a message channel, so that it can be re-used by multiple different input methods. The configurations come in on a channel called job-configurations:

<service-activator input-channel="job-configurations" output-channel="job-registrations">
        <beans:bean class="org.springframework.batch.admin.integration.JobConfigurationResourceLoader">
                <beans:property name="jobRegistry" ref="jobRegistry" />
        </beans:bean>
</service-activator>

The service activator here just accepts a Spring Resource and treats it as a configuration file: loading an ApplicationContext, scanning it for Job components and registering them in the registry provided. Once in the registry the jobs can be launched from the main Jobs menu in the UI, or via the job-requests channel, as described above.

Just like input files, the configuration files can come over HTTP, in this case as file attachments or as plain text parameters, and also through file polling. The polling use case is implemented in the Manager module, so its worth having a quick look at that to see how it works. In META-INF/bootstrap/integration/configuration-context.xml we find this:

<file:inbound-channel-adapter directory="target/config" channel="job-configuration-files"
        filename-pattern=".*\.xml">

        <poller max-messages-per-poll="1">
                <cron-trigger expression="5/1 * * * * *" />
        </poller>
</file:inbound-channel-adapter>

The adapter is going to poll a directory (here hard-coded for demo purposes to "target/config" but would be parameterised in a real application) and look for files whose name ends in ".xml". When a file matching that pattern arrives it is sent (as a java.io.File) to the job-configuration-files channel. The message is transformed from there so that the File becomes a Resource and it can be sent to the job-configurations channel.

Pattern: Informational Messages

Once you start using Spring Integration messages for driving a lot of application features, it is often useful to be able to tap into the flows of messages for informational or reporting purposes. For instance it would be useful to send a message when a job starts, stops (completes or fails). This is easy to do with a MessagePublishingInterceptor from Spring Integration. In the Spring Batch Admin Manager the interceptor is configured to send job execution messages:

<aop:config>
        <aop:advisor advice-ref="jobMessagePublishingInterceptor" pointcut="execution(* *..Job+.execute(..))" />
</aop:config>

<bean id="jobMessagePublishingInterceptor" class="org.springframework.integration.aop.MessagePublishingInterceptor"
        xmlns="http://www.springframework.org/schema/beans">

        <constructor-arg index="0">
                <bean class="org.springframework.batch.admin.integration.TrivialExpressionSource" p:payload="#args[execution]" />
        </constructor-arg>
        <property name="defaultChannel" ref="job-operator" />
</bean>

Every time a Job is executed the AOP advisor passes the argument value (a JobExecution) to the job-operator channel. Interested parties can then subscribe to that channel and pick up the information about recently executed messages. Spring Batch Admin does nothing with those messages out of the box, except log them on teh console, and list them in the UI so they can be inspected. Clients who build their own application on top of Spring Batch Admin might find the messages useful to notify operators or a reporting system about the outcome of the job.

Setting up informational messages can have the side effect of opening up new application features: the Job retry feature described above was implemented for Spring One by hooking an endpoint up to listen to the job-operator channel.

What Next?

We hope this article has given you some insight into some of the ways that Spring Integration can be used in a Batch application. Nearly all of the code sample above are in Spring Batch Admin in some form, but this is by no means the end of the story, and there are plenty more examples in the Spring Batch Integration and Spring Batch Admin projects. Visit the Batch Admin website for more information and to find out where to get the code to play with. There is also a video on InfoQ of some of the topics in this article presented at Spring One by the Spring Batch and the Spring Integration leads (Dave Syer and Mark Fisher).

Latest comments across all posts

Recent Team Posts

Keith Donald

post Ajax Simplifications in Spring 3.0

In my last entry, I walked you through several enhancements in Spring 3 for web application development. A number of you expressed interest in a follow-up entry focused on Ajax remoting. Spring 3 provides a lot in this area to take advantage of. Read on, and I'll walk you through it.
Spring and [...]


Stefan Schmidt

post Introduction To Spring Roo Screencast

After our mad dash to the final release of Spring Roo 1.0.0 on New Year's Eve, many users have asked for an introductory screencast.

In this 5 minute screencast you will see how to:

Develop a simple "contact manager application" using the Roo shell
Import and edit the project our free IDE, SpringSource Tool Suite (STS)
Run the Roo-provided [...]


Andy Wilkinson

post dm Server 2.0.0 released

As Adrian mentioned, today is the day for dm Server 2.0 and I'm delighted to announce that dm Server 2.0 is now available. Thank you for all of the feedback that we've had during the development of 2.0, it's helped to shape it into what we believe is a big step forward for enterprise OSGi. [...]

Older Posts

dm Server project moves to Eclipse.org

Task Scheduling Simplifications in Spring 3.0

Spring Roo 1.0.0 Released

Grails 1.2 Released