Version 2.0.3
July 2008
Table of Contents
Many web applications require the same sequence of steps to execute in different contexts. Often these sequences are merely components of a larger task the user is trying to accomplish. Such a reusable sequence is called a flow.
Consider a typical shopping cart application. User registration, login, and cart checkout are all examples of flows that can be invoked from several places in this type of application.
Spring Web Flow is the module of Spring for implementing flows. The Web Flow engine plugs into the Spring Web MVC platform and provides declarative flow definition language. This reference guide shows you how to use and extend Spring Web Flow.
This guide covers all aspects of Spring Web Flow. It covers implementing flows in end-user applications and working with the feature set. It also covers extending the framework and the overall architectural model.
Professional from-the-source support on Spring Web Flow is available from SpringSource, the company behind Spring, and Ervacon, operated by Web Flow project co-founder Erwin Vervaet
You can help make Web Flow best serve the needs of the Spring community by interacting with developers at the Spring Community Forums.
Report bugs and influence the Web Flow project roadmap using the Spring Issue Tracker.
Subscribe to the Spring Community Portal for the latest Spring news and announcements.
Each jar in the Web Flow distribution is available in the SpringSource Enterprise Bundle Repository. These jars may be accessed using Maven or Ivy dependency managers.
To access jars using Maven, add the following repositories to your Maven pom:
<repository> <id>com.springsource.repository.bundles.release</id> <name>SpringSource Enterprise Bundle Repository - SpringSource Releases</name> <url>http://repository.springsource.com/maven/bundles/release</url> </repository> <repository> <id>com.springsource.repository.bundles.external</id> <name>SpringSource Enterprise Bundle Repository - External Releases</name> <url>http://repository.springsource.com/maven/bundles/external</url> </repository>
Then declare the following dependencies:
<dependency> <groupId>org.springframework.webflow</groupId> <artifactId>org.springframework.binding</artifactId> <version>2.0.3.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.webflow</groupId> <artifactId>org.springframework.js</artifactId> <version>2.0.3.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.webflow</groupId> <artifactId>org.springframework.webflow</artifactId> <version>2.0.3.RELEASE</version> </dependency>
And if using JavaServerFaces:
<dependency> <groupId>org.springframework.webflow</groupId> <artifactId>org.springframework.faces</artifactId> <version>2.0.3.RELEASE</version> </dependency>
To access jars using Ivy, add the following repositories to your Ivy config:
<url name="com.springsource.repository.bundles.release"> <ivy pattern="http://repository.springsource.com/ivy/bundles/release/ [organisation]/[module]/[revision]/[artifact]-[revision].[ext]" /> <artifact pattern="http://repository.springsource.com/ivy/bundles/release/ [organisation]/[module]/[revision]/[artifact]-[revision].[ext]" /> </url> <url name="com.springsource.repository.bundles.external"> <ivy pattern="http://repository.springsource.com/ivy/bundles/external/ [organisation]/[module]/[revision]/[artifact]-[revision].[ext]" /> <artifact pattern="http://repository.springsource.com/ivy/bundles/external/ [organisation]/[module]/[revision]/[artifact]-[revision].[ext]" /> </url>
Then declare the following dependencies:
<dependency org="org.springframework.webflow" name="org.springframework.binding" rev="2.0.3.RELEASE" conf="compile->runtime" /> <dependency org="org.springframework.webflow" name="org.springframework.js" rev="2.0.3.RELEASE" conf="compile->runtime" /> <dependency org="org.springframework.webflow" name="org.springframework.webflow" rev="2.0.3.RELEASE" conf="compile->runtime" />
And if using JavaServerFaces:
<dependency org="org.springframework.webflow" name="org.springframework.faces" rev="2.0.3.RELEASE" conf="compile->runtime" />
Each jar in the Web Flow distribution is also available in the Maven Central Repository.
To access Web Flow jars from Maven Central, declare the following dependencies in your pom:
<dependency> <groupId>org.springframework.webflow</groupId> <artifactId>org.springframework.binding</artifactId> <version>2.0.3.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.webflow</groupId> <artifactId>org.springframework.js</artifactId> <version>2.0.3.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.webflow</groupId> <artifactId>org.springframework.webflow</artifactId> <version>2.0.3.RELEASE</version> </dependency>
And if using JavaServerFaces:
<dependency> <groupId>org.springframework.webflow</groupId> <artifactId>org.springframework.faces</artifactId> <version>2.0.3.RELEASE</version> </dependency>
Nightly snapshots of the Web Flow trunk are available at the SpringSource Bundle Repository. To access snapshots, add the following repository to your pom:
<repository> <id>com.springsource.repository.bundles.snapshot</id> <name>SpringSource Enterprise Bundle Repository - Nightly Snapshots</name> <url>http://repository.springsource.com/maven/bundles/snapshot</url> </repository>
Then declare the following dependencies:
<dependency> <groupId>org.springframework.webflow</groupId> <artifactId>org.springframework.binding</artifactId> <version>2.0.3.CI-###</version> </dependency> <dependency> <groupId>org.springframework.webflow</groupId> <artifactId>org.springframework.js</artifactId> <version>2.0.3.CI-###</version> </dependency> <dependency> <groupId>org.springframework.webflow</groupId> <artifactId>org.springframework.webflow</artifactId> <version>2.0.3.CI-###</version> </dependency>
And if using JavaServerFaces:
<dependency> <groupId>org.springframework.webflow</groupId> <artifactId>org.springframework.faces</artifactId> <version>2.0.3.CI-###</version> </dependency>
Where '###' above represents the build number like 2.0.3.CI-500. Get the latest build number by browsing the nightly build area.
This chapter begins the Users Section. It shows how to implement flows using the flow definition language. By the end of this chapter you should have a good understanding of language constructs, and be capable of authoring a flow definition.
A flow encapsulates a reusable sequence of steps that can execute in different contexts. Below is a Garrett Information Architecture diagram illustrating a reference to a flow that encapsulates the steps of a hotel booking process:

Site Map illustrating a reference to a flow
In Spring Web Flow, a flow consists of a series of steps called "states". Entering a state typically results in a view being displayed to the user. On that view, user events occur that are handled by the state. These events can trigger transitions to other states which result in view navigations.
The example below shows the structure of the book hotel flow referenced in the previous diagram:

Flow diagram
Flows are authored by web application developers using a simple XML-based flow definition language. The next steps of this guide will walk you through the elements of this language.
Every flow begins with the following root element:
<?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"> </flow>
All states of the flow are defined within this element. The first state defined becomes the flow's starting point.
Use the view-state element to define a step of the flow that renders a view:
<view-state id="enterBookingDetails" />
By convention, a view-state maps its id to a view template in the directory where the flow is located.
For example, the state above might render /WEB-INF/hotels/booking/enterBookingDetails.xhtml
if the flow itself was located in the /WEB-INF/hotels/booking directory.
Use the transition element to handle events that occur within a state:
<view-state id="enterBookingDetails"> <transition on="submit" to="reviewBooking" /> </view-state>
These transitions drive view navigations.
Use the end-state element to define a flow outcome:
<end-state id="bookingCancelled" />
When a flow transitions to a end-state it terminates and the outcome is returned.
With the three elements view-state, transition, and end-state, you can quickly express your view navigation logic.
Teams often do this before adding flow behaviors so they can focus on developing the user interface of the application with end users first.
Below is a sample flow that implements its view navigation logic using these elements:
<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"> <view-state id="enterBookingDetails"> <transition on="submit" to="reviewBooking" /> </view-state> <view-state id="reviewBooking"> <transition on="confirm" to="bookingConfirmed" /> <transition on="revise" to="enterBookingDetails" /> <transition on="cancel" to="bookingCancelled" /> </view-state> <end-state id="bookingConfirmed" /> <end-state id="bookingCancelled" /> </flow>
Most flows need to express more than just view navigation logic. Typically they also need to invoke business services of the application or other actions.
Within a flow, there are several points where you can execute actions. These points are:
On flow start
On state entry
On view render
On transition execution
On state exit
On flow end
Actions are defined using a concise expression language. Spring Web Flow uses the Unified EL by default. The next few sections will cover the essential language elements for defining actions.
The action element you will use most often is the evaluate element.
Use the evaluate element to evaluate an expression at a point within your flow.
With this single tag you can invoke methods on Spring beans or any other flow variable.
For example:
<evaluate expression="entityManager.persist(booking)" />
If the expression returns a value, that value can be saved in the flow's data model called flowScope:
<evaluate expression="bookingService.findHotels(searchCriteria)" result="flowScope.hotels" />
Now review the sample booking flow with actions added:
<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"> <input name="hotelId" /> <on-start> <evaluate expression="bookingService.createBooking(hotelId, currentUser.name)" result="flowScope.booking" /> </on-start> <view-state id="enterBookingDetails"> <transition on="submit" to="reviewBooking" /> </view-state> <view-state id="reviewBooking"> <transition on="confirm" to="bookingConfirmed" /> <transition on="revise" to="enterBookingDetails" /> <transition on="cancel" to="bookingCancelled" /> </view-state> <end-state id="bookingConfirmed" /> <end-state id="bookingCancelled" /> </flow>
This flow now creates a Booking object in flow scope when it starts. The id of the hotel to book is obtained from a flow input attribute.
Each flow has a well-defined input/output contract. Flows can be passed input attributes when they start, and can return output attributes when they end. In this respect, calling a flow is conceptually similar to calling a method with the following signature:
FlowOutcome flowId(Map<String, Object> inputAttributes);
... where a FlowOutcome has the following signature:
public interface FlowOutcome { public String getName(); public Map<String, Object> getOutputAttributes(); }
Use the input element to declare a flow input attribute:
<input name="hotelId" />
Input values are saved in flow scope under the name of the attribute.
For example, the input above would be saved under the name hotelId.
Use the type attribute to declare the input attribute's type:
<input name="hotelId" type="long" />
If an input value does not match the declared type, a type conversion will be attempted.
Use the value attribute to specify an expression to assign the input value to:
<input name="hotelId" value="flowScope.myParameterObject.hotelId" />
If the expression's value type can be determined, that metadata will be used for type coersion if no type attribute is specified.
Use the output element to declare a flow output attribute.
Output attributes are declared within end-states that represent specific flow outcomes.
<end-state id="bookingConfirmed"> <output name="bookingId" /> </end-state>
Output values are obtained from flow scope under the name of the attribute.
For example, the output above would be assigned the value of the bookingId variable.
Now review the sample booking flow with input/output mapping:
<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">; <input name="hotelId" /> <on-start> <evaluate expression="bookingService.createBooking(hotelId, currentUser.name)" result="flowScope.booking" /> </on-start> <view-state id="enterBookingDetails"> <transition on="submit" to="reviewBooking" /> </view-state> <view-state id="reviewBooking"> <transition on="confirm" to="bookingConfirmed" /> <transition on="revise" to="enterBookingDetails" /> <transition on="cancel" to="bookingCancelled" /> </view-state> <end-state id="bookingConfirmed" > <output name="bookingId" value="booking.id"/> </end-state> <end-state id="bookingCancelled" /> </flow>
The flow now accepts a hotelId input attribute and returns a bookingId output attribute
when a new booking is confirmed.
A flow may declare one or more instance variables.
These variables are allocated when the flow starts.
Any @Autowired transient references the variable holds are also rewired when the flow resumes.
A flow may call another flow as a subflow. The flow will wait until the subflow returns, then respond to the subflow outcome.
Use the subflow-state element to call another flow as a subflow:
<subflow-state id="addGuest" subflow="createGuest"> <transition on="guestCreated" to="reviewBooking"> <evaluate expression="booking.guests.add(currentEvent.attributes.guest)" /> </transition> <transition on="creationCancelled" to="reviewBooking" /> </subfow-state>
The above example calls the createGuest flow, then waits for it to return.
When the flow returns with a guestCreated outcome, the new guest is added to the booking's guest list.
Use the input element to pass input to the subflow:
<subflow-state id="addGuest" subflow="createGuest"> <input name="booking" /> <transition to="reviewBooking" /> </subfow-state>
Simply refer to a subflow output attribute by its name within a outcome transition:
<transition on="guestCreated" to="reviewBooking"> <evaluate expression="booking.guests.add(currentEvent.attributes.guest)" /> </transition>
In the above example, guest is the name of an output attribute returned by the guestCreated outcome.
Now review the sample booking flow calling a subflow:
<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"> <input name="hotelId" /> <on-start> <evaluate expression="bookingService.createBooking(hotelId, currentUser.name)" result="flowScope.booking" /> </on-start> <view-state id="enterBookingDetails"> <transition on="submit" to="reviewBooking" /> </view-state> <view-state id="reviewBooking"> <transition on="addGuest" to="addGuest" /> <transition on="confirm" to="bookingConfirmed" /> <transition on="revise" to="enterBookingDetails" /> <transition on="cancel" to="bookingCancelled" /> </view-state> <subflow-state id="addGuest" subflow="createGuest"> <transition on="guestCreated" to="reviewBooking"> <evaluate expression="booking.guests.add(currentEvent.attributes.guest)" /> </transition> <transition on="creationCancelled" to="reviewBooking" /> </subfow-state> <end-state id="bookingConfirmed" > <output name="bookingId" value="booking.id" /> </end-state> <end-state id="bookingCancelled" /> </flow>
The flow now calls a createGuest subflow to add a new guest to the guest list.
Web Flow uses EL to access its data model and invoke actions. This chapter will familiarize you with the EL syntax, and special EL variables you can reference from your flow definition.
Web Flow attempts to use the Unified EL by default.
jboss-el is currently the default EL implementation.
When found in your classpath along with the el-api, it will be used automatically.
You can find the JBoss EL jar in the SpringSource Bundle Repository.
![]() | Note |
|---|---|
The el-api dependency is typically provided by your web container. Tomcat 6 includes it, for example.
|
OGNL is the other EL supported by Web Flow 2.
OGNL is the EL most familiar to Web Flow version 1.0 users.
To use ognl, simply include ognl in your classpath instead of jboss-el.
Please refer to the OGNL language guide for specifics on its EL syntax.
In general, you will find the Unified EL and OGNL have a very similar syntax. For basic variable resolution, property access, and method invocation the syntax is identical. We recommend adhering to Unified EL syntax whenever possible, and only relying on proprietary EL features when needed.
EL is used for many things within a flow, including:
Accessing data provided by the client, such as flow input attributes and request parameters.
Accessing internal data structures such as flowScope.
Invoking methods on Spring beans.
Resolving constructs such as state transition criteria, subflow ids, and view names.
Views rendered by flows typically access flow data structures using EL as well.
There are basically two types of expressions in Web Flow.
The first, and most common, type of expression, is the standard eval expression.
Such expressions are dynamically evaluated by the EL and should not be enclosed in delimiters like ${} or #{}.
For example:
<evaluate expression="searchCriteria.nextPage()" />
The expression above is a standard expression that invokes the nextPage method on the searchCriteria variable when evaluated.
Attempting to enclose this expression in special eval delimiters like ${} or #{} will result in an IllegalArgumentException.
![]() | Note |
|---|---|
We view use of special eval delimiters as redundant in this context, as the only acceptable value for the expression attribute is a single eval expression string.
|
The second type of expression is a "template" expression.
Such expressions allow a mixing of literal text with one or more eval blocks.
Each eval block is explictly delimited with the ${} delimiters.
For example:
<view-state id="error" view="error-${externalContext.locale}.xhtml" />
The expression above is a template expression.
The result of evaluation will be a string that concatenates the literal text error- with the result of evaluating externalContext.locale.
As you can see, explicit delimiters are necessary here to demarcate eval blocks within the template.
See the Web Flow XML schema for a complete listing of the XML attributes that accept standard expressions and template expressions.
There are several implicit variables you may reference from within a flow. These variables are discussed in this section.
Use flowScope to assign a flow variable.
Flow scope gets allocated when a flow starts and destroyed when the flow ends. With the default
implementation, any objects stored in flow scope need to be Serializable.
<evaluate expression="searchService.findHotel(hotelId)" result="flowScope.hotel" />
Use viewScope to assign a view variable.
View scope gets allocated when a view-state enters and destroyed when the state exits.
View scope is only referenceable from within a view-state. With the
default implementation, any objects stored in view scope need to be Serializable.
<on-render> <evaluate expression="searchService.findHotels(searchCriteria)" result="viewScope.hotels" result-type="dataModel" /> </on-render>
Use requestScope to assign a request variable.
Request scope gets allocated when a flow is called and destroyed when the flow returns.
<set name="requestScope.hotelId" value="requestParameters.id" type="long" />
Use flashScope to assign a flash variable.
Flash scope gets allocated when a flow starts, cleared after every view render, and destroyed when the
flow ends. With the default implementation, any objects stored in flash scope need to be Serializable.
<set name="flashScope.statusMessage" value="'Booking confirmed'" />
Use conversationScope to assign a conversation variable.
Conversation scope gets allocated when a top-level flow starts and destroyed when the top-level flow ends.
Conversation scope is shared by a top-level flow and all of its subflows. With the default
implementation, conversation scoped objects are stored in the HTTP session and should generally be
Serializable to account for typical session replication.
<evaluate expression="searchService.findHotel(hotelId)" result="conversationScope.hotel" />
Use requestParameters to access a client request parameter:
<set name="requestScope.hotelId" value="requestParameters.id" type="long" />
Use currentEvent to access attributes of the current Event:
<evaluate expression="booking.guests.add(currentEvent.guest)" />
Use currentUser to access the authenticated Principal:
<evaluate expression="bookingService.createBooking(hotelId, currentUser.name)" result="flowScope.booking" />
Use messageContext to access a context for retrieving and creating flow execution messages, including error and success messages.
See the MessageContext Javadocs for more information.
<evaluate expression="bookingValidator.validate(booking, messageContext)" />
Use resourceBundle to access a message resource.
<set name="flashScope.successMessage" value="resourceBundle.successMessage" />
Use flowRequestContext to access the RequestContext API, which is a representation of the current flow request.
See the API Javadocs for more information.
Use flowExecutionContext to access the FlowExecutionContext API, which is a representation of the current flow state.
See the API Javadocs for more information.
Use flowExecutionUrl to access the context-relative URI for the current flow execution view-state.
When assigning a variable in one of the flow scopes, referencing that scope is required. For example:
<set name="requestScope.hotelId" value="requestParameters.id" type="long" />
When simply accessing a variable in one of the scopes, referencing the scope is optional. For example:
<evaluate expression="entityManager.persist(booking)" />
If no scope is specified, like in the use of booking above, a scope searching algorithm will be employed.
The algorithm will look in request, flash, view, flow, and conversation scope for the variable.
If no such variable is found, an EvaluationException will be thrown.
This chapter shows you how to use the view-state element to render views within a flow.
Use the view-state element to define a step of the flow that renders a view and waits for a user event to resume:
<view-state id="enterBookingDetails"> <transition on="submit" to="reviewBooking" /> </view-state>
By convention, a view-state maps its id to a view template in the directory where the flow is located.
For example, the state above might render /WEB-INF/hotels/booking/enterBookingDetails.xhtml
if the flow itself was located in the /WEB-INF/hotels/booking directory.
Below is a sample directory structure showing views and other resources like message bundles co-located with their flow definition:

Flow Packaging
Use the view attribute to explictly specify the id of the view to render.
The view id may be a relative path to view resource in the flow's working directory:
<view-state id="enterBookingDetails" view="bookingDetails.xhtml">
The view id may be a absolute path to a view resource in the webapp root directory:
<view-state id="enterBookingDetails" view="/WEB-INF/hotels/booking/bookingDetails.xhtml">
With some view frameworks, such as Spring MVC's view framework, the view id may also be a logical identifier resolved by the framework:
<view-state id="enterBookingDetails" view="bookingDetails">
See the Spring MVC integration section for more information on how to integrate with the MVC ViewResolver infrastructure.
A view-state allocates a new viewScope when it enters.
This scope may be referenced within the view-state to assign variables that should live for the duration of the state.
This scope is useful for manipulating objects over a series of requests from the same view, often Ajax requests.
A view-state destroys its viewScope when it exits.
Use the var tag to declare a view variable.
Like a flow variable, any @Autowired references are automatically restored when the view state resumes.
<var name="searchCriteria" class="com.mycompany.myapp.hotels.SearchCriteria" />
Use the on-render tag to assign a variable from an action result before the view renders:
<on-render> <evaluate expression="bookingService.findHotels(searchCriteria)" result="viewScope.hotels" /> </on-render>
Objects in view scope are often manipulated over a series of requests from the same view. The following example pages through a search results list. The list is updated in view scope before each render. Asynchronous event handlers modify the current data page, then request re-rendering of the search results fragment.
<view-state id="searchResults"> <on-render> <evaluate expression="bookingService.findHotels(searchCriteria)" result="viewScope.hotels" /> </on-render> <transition on="next"> <evaluate expression="searchCriteria.nextPage()" /> <render fragments="searchResultsFragment" /> </transition> <transition on="previous"> <evaluate expression="searchCriteria.previousPage()" /> <render fragments="searchResultsFragment" /> </transition> </view-state>
Use the on-render element to execute one or more actions before view rendering.
Render actions are executed on the initial render as well as any subsequent refreshes, including any partial re-renderings of the view.
<on-render> <evaluate expression="bookingService.findHotels(searchCriteria)" result="viewScope.hotels" /> </on-render>
Use the model attribute to declare a model object the view binds to.
This attribute is typically used in conjunction with views that render data controls, such as forms.
It enables form data binding and validation behaviors to be driven from metadata on your model object.
The following example declares an enterBookingDetails state manipulates the booking model:
<view-state id="enterBookingDetails" model="booking">
The model may an object in any accessible scope, such as flowScope or viewScope.
Specifying a model triggers the following behavior when a view event occurs:
View-to-model binding. On view postback, user input values are bound to model object properties for you.
Model validation. After binding, if the model object requires validation that validation logic will be invoked.
For a flow event to be generated that can drive a view state transition, model binding must complete successfully. If model binding fails, the view is re-rendered to allow the user to revise their edits.
Use the bind attribute to suppress model binding and validation for particular view events.
The following example suppresses binding when the cancel event occurs:
<view-state id="enterBookingDetails" model="booking"> <transition on="proceed" to="reviewBooking"> <transition on="cancel" to="bookingCancelled" bind="false" /> </view-state>
Use the binder element to configure the exact set of model bindings usable by the view.
This is particularly useful in a Spring MVC environment for restricting the set of "allowed fields" per view.
<view-state id="enterBookingDetails" model="booking"> <binder> <binding property="creditCard" /> <binding property="creditCardName" /> <binding property="creditCardExpiryMonth" /> <binding property="creditCardExpiryYear" /> </binder> <transition on="proceed" to="reviewBooking" /> <transition on="cancel" to="cancel" bind="false" /> </view-state>
If the binder element is not specified, all public properties of the model are eligible for binding by the view. With the binder element specified, only the explicitly configured bindings are allowed.
Each binding may also apply a converter to format the model property value for display in a custom manner. If no converter is specified, the default converter for the model property's type will be used.
<view-state id="enterBookingDetails" model="booking"> <binder> <binding property="checkinDate" converter="shortDate" /> <binding property="checkoutDate" converter="shortDate" /> <binding property="creditCard" /> <binding property="creditCardName" /> <binding property="creditCardExpiryMonth" /> <binding property="creditCardExpiryYear" /> </binder> <transition on="proceed" to="reviewBooking" /> <transition on="cancel" to="cancel" bind="false" /> </view-state>
In the example above, the shortDate converter is bound to the
checkinDate and checkoutDate properties.
Custom converters may be registered with the application's ConversionService.
Each binding may also apply a required check that will generate a validation error if the user provided value is null on form postback:
<view-state id="enterBookingDetails" model="booking"> <binder> <binding property="checkinDate" converter="shortDate" required="true" /> <binding property="checkoutDate" converter="shortDate" required="true" /> <binding property="creditCard" required="true" /> <binding property="creditCardName" required="true" /> <binding property="creditCardExpiryMonth" required="true" /> <binding property="creditCardExpiryYear" required="true" /> </binder> <transition on="proceed" to="reviewBooking"> <transition on="cancel" to="bookingCancelled" bind="false" /> </view-state>
In the example above, all of the bindings are required. If one or more blank input values are bound, validation errors will be generated and the view will re-render with those errors.
Model validation is driven by constraints specified against the model object.
These constraints may be specified declaratively, or enforced using a programmatic validation routine or external Validator.
There are two ways to perform model validation programatically.
The first way is to define a validate method on the model object class.
To do this, create a public method with the name validate${state}, where state is the id of your view-state.
The method must declare a MessageContext parameter for recording validation error messages.
For example:
public void validateEnterBookingDetails(MessageContext context) { Calendar calendar = Calendar.getInstance(); if (checkinDate.before(today())) { context.addMessage(new MessageBuilder().error().source("checkinDate").defaultText( "Check in date must be a future date").build()); } else if (!checkinDate.before(checkoutDate)) { context.addMessage(new MessageBuilder().error().source("checkoutDate").defaultText( "Check out date must be later than check in date").build()); } }
The second way is to define a separate object, called a Validator, which validates your model object.
To do this, create a class that defines a public method with the name validate${state}, where state is the id of your view-state.
The method must declare a parameter to accept your model object, and a MessageContext parameter for recording validation error messages.
For example:
@Component public class BookingValidator { public void validateEnterBookingDetails(Booking booking, MessageContext context) { if (booking.getCheckinDate().before(today())) { context.addMessage(new MessageBuilder().error().source("checkinDate").defaultText( "Check in date must be a future date").build()); } else if (!booking.getCheckinDate().before(checkoutDate)) { context.addMessage(new MessageBuilder().error().source("checkoutDate").defaultText( "Check out date must be later than check in date").build()); } } }
A Validator can also accept a Spring MVC Errors object, which is required for invoking existing Spring Validators.
Validators must be registered as Spring beans employing the naming convention ${model}Validator to be detected and invoked automatically.
In the example above, Spring 2.5 classpath-scanning would detect the @Component and automatically register it as a bean with the name bookingValidator.
Then, anytime the booking model needs to be validated, this bookingValidator instance would be invoked for you.
Spring Web Flow does not yet ship integration with a declarative validation framework such as Hibernate Validator. It is expected that integration will be provided in a future Web Flow release. This integration will allow declarative validation constraints to be defined against model properties.
Use the validate attribute to suppress model validation for particular view events:
<view-state id="chooseAmenities" model="booking"> <transition on="proceed" to="reviewBooking"> <transition on="back" to="enterBookingDetails" validate="false" /> </view-state>
In this example, data binding will still occur on back but validation will be suppressed.
A view-state transition can execute one or more actions before executing. These actions may return an error result to prevent the transition itself from executing. If an error result occurs, the view will re-render and should typically display an appropriate error message to the user.
If the transition action invokes a plain Java method, the invoked method may return false to prevent the transition from executing. This technique can be used to handle exceptions thrown by service-layer methods. The example below invokes an action that calls a service and handles an exceptional situation:
<transition on="submit" to="bookingConfirmed"> <evaluate expression="bookingAction.makeBooking(booking, messageContext)" /> </transition>
public class BookingAction { public boolean makeBooking(Booking booking, MessageContext context) { try { bookingService.make(booking); return true; } catch (RoomNotAvailableException e) { context.addMessage(builder.error(). .defaultText("No room is available at this hotel").build()); return false; } } }
From a view-state, transitions without targets can also be defined. Such transitions are called "event handlers":
<transition on="event"> <!-- Handle event --> </transition>
These event handlers do not change the state of the flow. They simply execute their actions and re-render the current view or one or more fragments of the current view.
Use the render element to request partial re-rendering of a view after handling an event:
<transition on="next"> <evaluate expression="searchCriteria.nextPage()" /> <render fragments="searchResultsFragment" /> </transition>
The fragments attribute should reference the ID(s) of the view element(s) you wish to re-render. Specify multiple elements to re-render by separating them with a comma delimiter.
Such partial rendering is often used with events signaled by Ajax to update a specific zone of the view.
Use the flow's global-transitions element to create event handlers that apply across all views.
Global-transitions are often used to handle global menu links that are part of the layout.
<global-transitions> <transition on="login" to="login"> <transition on="logout" to="logout"> </global-transitions>
Spring Web Flow's MessageContext is an API for recording messages during the course of flow executions.
Plain text messages can be added to the context, as well as internationalized messages resolved by a Spring MessageSource.
Messages are renderable by views and automatically survive flow execution redirects.
Three distinct message severities are provided: info, warning, and error.
In addition, a convenient MessageBuilder exists for fluently constructing messages.
MessageContext context = ... MessageBuilder builder = new MessageBuilder(); context.addMessage(builder.error().source("checkinDate") .defaultText("Check in date must be a future date").build()); context.addMessage(builder.warn().source("smoking") .defaultText("Smoking is bad for your health").build()); context.addMessage(builder.info() .defaultText("We have processed your reservation - thank you and enjoy your stay").build());
MessageContext context = ... MessageBuilder builder = new MessageBuilder(); context.addMessage(builder.error().source("checkinDate").code("checkinDate.notFuture").build()); context.addMessage(builder.warn().source("smoking").code("notHealthy") .resolvableArg("smoking").build()); context.addMessage(builder.info().code("reservationConfirmation").build());
Internationalized messages are defined in message bundles accessed by a Spring MessageSource.
To create a flow-specific message bundle, simply define messages.properties file(s) in your flow's directory.
Create a default messages.properties file and a .properties file for each additional Locale you need to support.
#messages.properties
checkinDate=Check in date must be a future date
notHealthy={0} is bad for your health
reservationConfirmation=We have processed your reservation - thank you and enjoy your stay
From within a view or a flow, you may also access message resources using the resourceBundle EL variable:
<h:outputText value="#{resourceBundle.reservationConfirmation}" />
Use the popup attribute to render a view in a modal popup dialog:
<view-state id="changeSearchCriteria" view="enterSearchCriteria.xhtml" popup="true">
When using Web Flow with the Spring Javascript, no client side code is necessary for the popup to display. Web Flow will send a response to the client requesting a redirect to the view from a popup, and the client will honor the request.
By default, when you exit a view state and transition to a new view state, you can go back to the previous state using the browser back button.
These view state history policies are configurable on a per-transition basis by using the history attribute.
Set the history attribute to discard to prevent backtracking to a view:
<transition on="cancel" to="bookingCancelled" history="discard">