Spring Finance > Part 5: Spring Web Flow Integration

In this fifth instalment of the Spring Finance Manager series I am integrating a form wizard into the application. So far, to properly use the Spring Finance Manager application, you had to know the sequence in which you needed to create resources which are exposed through Spring MVC. You first needed to create a bunch of people, then a few financial products and finally you could create accounts. This sequence was important because certain web forms depended on the existence of data in the system. For example, a loan required you to specify lenders (people) and an account required you to specify owners, and a manager. So the resulting sequence becomes aparent:

  1. create person records
  2. create financial products
  3. create accounts
    • reference people as manager and account owners
    • reference products when assigning investments to accounts

As part of the last release of the Spring Finance Manager application I have included a Selenium script which took care of creating resources in the correct sequence. However, it does make sense to guide users through the steps in the correct sequence. This is where Spring Web Flow comes into play. It integrates nicely with Spring MVC so that I do not have to choose between the two technologies in my project. I could for example just remove access to the Spring MVC forms from my view tier (remove them from the menu) and still allow access to the application through the REST interface supported by the sample application thanks to the new REST features in Spring 3.0.

For this article I have tagged a new version (v0.3) of the Spring Finance Manager sample application which is now available at the project hosting site.

So let’s start with the flow definition:

Spring Web Flow

Flow definitions are written in XML and effectively replace Spring MVC controller Servlets (although the XML definitions is translated to a Spring MVC Servlet in the background). Here is an extract of a flow definition for our wizard (see wizard-flow.xml):

<?xml version="1.0" encoding="UTF-8"?>
<flow xmlns="http://www.springframework.org/schema/webflow"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/webflow 

http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">

	<persistence-context />

	<view-state id="createNewPerson" model="person">
		<on-entry>
			<evaluate expression="personBean" result="viewScope.person" />
		</on-entry>
		<transition on="person" to="createNewPerson">
			<evaluate expression="personService.persist(person)" />
		</transition>
		<transition on="product" to="createNewProduct">
			<evaluate expression="personService.persist(person)" />
		</transition>
		<transition on="cancel" to="cancel" bind="false" />
	</view-state>
[...]
        <end-state id="cancel" view="cancel"/>
</flow>

As you can see most of the time you use view states which correspond to view pages in your flow. You can assign model objects to a view (just like in Spring MVC) and you can initialize resources for each view state or globally. Apart from view states, you also have action states, decision states, end states and subflow states. As shown above, action and decision states can be integrated into transitions defined in the view states.

In my case I am making sure to persist newly created resources after each step since the resources are independently useful. If you have a multi-page form which populates only one domain object you can configure Spring Web Flow to commit the form data only after the last page has been submitted.

As for the actual view code I have just recycled my JSP pages which I have used for the previous Spring MVC samples. JSP is generally not very nice to code but it relieves me from having to introduce yet another technology such as FreeMarker, Tiles, JSF, etc. There is plenty of documentation out there which shows how to use these technologies if you are interested.

There are many more options available to configure Spring Web Flow but I’ll spare you the details. Take a look at the reference documentation for further information.

There is also tooling support available to configure your flow definitions using a UI:

Configuration

To integrate Spring Web Flow into your application you don’t need to change the web.xml file from your Spring MVC project. However the Spring MVC servlet configuration needs to be made aware that certain URIs in your application are to be handled by Spring Web Flow rather than Spring MVC:

<!-- Maps request paths to flows in the flowRegistry;
       e.g. a path of /wizard looks for a flow with id "wizard" -->
<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerMapping">
	<property name="order" value="0" />
	<property name="flowRegistry" ref="flowRegistry" />
</bean>

<!-- Maps request paths to @Controller classes; e.g. a path of /person
      looks for a controller named PersonController -->
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
	<property name="order" value="1" />
</bean>

<!-- Dispatches requests mapped to flows to FlowHandler implementations -->
<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerAdapter">
	<property name="flowExecutor" ref="flowExecutor"/>
</bean>

Note, the handlers are ordered so that Spring Web Flow always checks first weather this is a flow URI before the Spring container passes the request on to Spring MVC. Then we need some further configuration to get Spring Web Flow up and running:

<!-- Executes flows: the entry point into the Spring Web Flow system -->
<webflow:flow-executor id="flowExecutor"/>

<!-- The registry of executable flow definitions -->
<webflow:flow-registry id="flowRegistry" flow-builder-services="flowBuilderServices" base-path="/WEB-INF/jsp">
	<webflow:flow-location-pattern value="/**/*-flow.xml" />
</webflow:flow-registry>

<!-- Plugs in a custom creator for Web Flow views -->
<webflow:flow-builder-services id="flowBuilderServices"  view-factory-creator="mvcViewFactoryCreator" conversion-service="conversionService" development="true" />

We define a flow executor, a flow registry, which scans for all files ending with -flow.xml (in our case just wizard-flow.xml), and a flow builder service. The flow builder service requires a view factory which in my case is just a simple MvcViewFactoryCreator which let’s us use plain JSP. Optionally you can define a conversion service which takes care of type conversion much like Property editors in Spring MVC. Spring Web Flow comes with its own type conversion API and some of this is now being integrated into Spring 3 core.

Apart from the numerous converters already shipped with Spring Web Flow you still need to develop your own to achieve conversion from an ID submitted from a form to a domain object:

public class LongToPerson implements TwoWayConverter {

	private PersonService personService;

	public LongToPerson(PersonService personService) {
		this.personService = personService;
	}	

	public Object convertSourceToTargetClass(Object source, Class targetClass) throws Exception {
		Long longSource = (Long) source;
		if (longSource != null && longSource > 0) {
			return personService.find(longSource);
		} else {
			return null;
		}
	}
[...]
}

Again, these converters work in the same way as PropertyEditors in Spring MVC so I’ll spare you the details.

Spring Finance Manager Configuration Changes

Up until the last version of the Spring Finance Manager application I have used two Spring application context configuration files. One was located in src/main/resources/FinanceManager-infrastructure.xml and the other one in was located in src/main/webapps/WEB-INF/FinanceManager-servlet.xml. Now that there are more configuration files needed (I am planning to add a separate security config file in the next part of this series) it makes sense to place them all in a central location. Since we are using a Web based application I have decided to place the files into src/main/webapps/WEB-INF/config:

  • FinanceManager-config.xml
  • FinanceManager-infrastructure.xml
  • FinanceManager-mvc.xml
  • FinanceManager-webflow.xml

The file names should be quite self explaining. The FinanceManager-config.xml file acts as the parent config which imports the others and does the component scanning which is required by all other configs. This way I only need to reference the FinanceManager-config.xml file in the web.xml file for context loading.

In addition there is now a new src/test/resources/FinanceManager-test.xml which allows us to separate the test infrastructure from the production infrastructure if so desired.

Conclusion

In this article I showed how to Spring Web Flow into a Spring MVC application. I think the integration in Spring MVC applications is a good way to use Spring Web Flow. Some people prefer to use MVC or Web flow exclusively but in my experience it makes more sense to develop stateless Spring MVC applications and then only use flows where multi-page forms, or navigation flows make sense. This way you get the best of both worlds :-) .

In the next part of this series, I am now planning to write about the integration of Spring Security to safeguard the Spring Finance Manager sample application using authentication and authorisation.

  • Share/Bookmark

6 Responses Subscribe to comments


  1. Daily del.icio.us for April 13th through April 15th | Vinny Carpenter's blog

    [...] Spring Finance > Part 5: Spring Web Flow Integration | StSMedia – In this fifth instalment of the Spring Finance Manager series I am integrating a form wizard into the application. So far, to properly use the Spring Finance Manager application, you had to know the sequence in which you needed to create resources which are exposed through Spring MVC [...]

    Apr 16, 2009 @ 17:01


  2. Niel Eyde

    I’m not sure if this would be helpful to other readers, but I created an alternative diagram of the flow.
    http://www.skywayperspectives.org/images/swf/swfdiagram1.jpg

    May 01, 2009 @ 08:02


  3. Pablo

    Great blog post Stephan.
    Great diagram Niel. Makes Web Flow easier.

    May 01, 2009 @ 23:07


  4. Jim

    Given your use of Web Flow and MVC, is the browser’s back button supported. I know SWF supports the back button no problem, but wasn’t sure if MVC does, or if you use both?

    May 11, 2009 @ 14:05


  5. sschmidt

    @Jim

    Spring MVC is stateless so would need to deal with this manually. As far as Spring Web Flow goes – as soon as the flow (within the application) has started the back button functionality should actually work without any problems. But just combining MVC and Web Flow does not introduce ‘new features’ to MVC per se if that is what you are asking ;) .

    May 13, 2009 @ 12:28


  6. Greg Ederer

    The ContextLoaderListener in RC1 requires that a bean with the name “conversionService” be of type org.springframework.core.convert.ConversionService; however, FlowBuilderServices will not accept an object of this type. Even webflow 3.0.0M1 does not accept this (will this be aligned?)

    I worked around this by changing the name in the @Component annotation in ApplicationConversionService to “conversionServiceOld”. Made the same change to the webflow context XML.

    Thanks for this terrific series! –G

    Oct 20, 2009 @ 11:04

Reply

Gallery
_mg_8226.jpg _mg_2664.jpg 02240021.jpg _mg_2653.jpg