Spring Web Flow 2 Reference Guide

Reference Documentation

Keith Donald

Erwin Vervaet

Jeremy Grelle

Scott Andrews

Rossen Stoyanchev

Version 2.0.3

Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically.

July 2008


Table of Contents

Preface
1. Introduction
1.1. What this guide covers
1.2. What Web Flow requires to run
1.3. Where to get support
1.4. Where to follow development
1.5. How to obtain Web Flow artifacts from the SpringSource Bundle Repository
1.5.1. Accessing Web Flow with Maven
1.5.2. Accessing Web Flow with Ivy
1.6. How to obtain Web Flow artifacts from Maven Central
1.7. How to Obtain Nightly Builds
2. Defining Flows
2.1. Introduction
2.2. What is a flow?
2.3. What is the makeup of a typical flow?
2.4. How are flows authored?
2.5. Essential language elements
2.5.1. flow
2.5.2. view-state
2.5.3. transition
2.5.4. end-state
2.5.5. Checkpoint: Essential language elements
2.6. Actions
2.6.1. evaluate
2.6.1.1. Assigning an evaluate result
2.6.1.2. Converting an evaluate result
2.6.2. Checkpoint: flow actions
2.7. Input/Output Mapping
2.7.1. input
2.7.1.1. Declaring an input type
2.7.1.2. Assigning an input value
2.7.1.3. Marking an input as required
2.7.2. output
2.7.2.1. Specifying the source of an output value
2.7.3. Checkpoint: input/output mapping
2.8. Variables
2.8.1. var
2.9. Calling subflows
2.9.1. subflow-state
2.9.1.1. Passing a subflow input
2.9.1.2. Mapping subflow output
2.9.2. Checkpoint: calling subflows
3. Expression Language (EL)
3.1. Introduction
3.2. Supported EL implementations
3.2.1. Unified EL
3.2.2. OGNL
3.3. EL portability
3.4. EL usage
3.4.1. Expression types
3.4.1.1. Standard eval expressions
3.4.1.2. Template expressions
3.5. Special EL variables
3.5.1. flowScope
3.5.2. viewScope
3.5.3. requestScope
3.5.4. flashScope
3.5.5. conversationScope
3.5.6. requestParameters
3.5.7. currentEvent
3.5.8. currentUser
3.5.9. messageContext
3.5.10. resourceBundle
3.5.11. flowRequestContext
3.5.12. flowExecutionContext
3.5.13. flowExecutionUrl
3.5.14. externalContext
3.6. Scope searching algorithm
4. Rendering views
4.1. Introduction
4.2. Defining view states
4.3. Specifying view identifiers
4.3.1. Flow relative view ids
4.3.2. Absolute view ids
4.3.3. Logical view ids
4.4. View scope
4.4.1. Allocating view variables
4.4.2. Assigning a viewScope variable
4.4.3. Manipulating objects in view scope
4.5. Executing render actions
4.6. Binding to a model
4.7. Suppressing binding
4.8. Specifying bindings explicitly
4.9. Validating a model
4.9.1. Programmatic validation
4.9.1.1. Implementing a model validate method
4.9.1.2. Implementing a Validator
4.9.2. Declarative validation
4.10. Suppressing validation
4.11. Transition actions
4.12. Handling events
4.12.1. Rendering partials
4.12.2. Handling global events
4.13. Working with messages
4.13.1. Adding plain text messages
4.13.2. Adding internationalized messages
4.13.3. Using message bundles
4.14. Displaying popups
4.15. View backtracking
4.15.1. Discarding history
4.15.2. Invalidating history
5. Executing actions
5.1. Introduction
5.2. Defining action states
5.3. Defining decision states
5.4. Action outcome event mappings
5.5. Action execution examples
5.5.1. on-start
5.5.2. on-entry
5.5.3. on-exit
5.5.4. on-end
5.5.5. on-render
5.5.6. on-transition
6. Flow Managed Persistence
6.1. Introduction
6.2. Data access patterns
6.3. FlowScoped PersistenceContext
6.4. ConversationScoped PersistenceContext
6.5. ViewState PersistenceContext
7. Securing Flows
7.1. Introduction
7.2. How do I secure a flow?
7.3. The secured element
7.3.1. Security attributes
7.3.2. Matching type
7.4. The SecurityFlowExecutionListener
7.4.1. Custom Access Decision Managers
7.5. Configuring Spring Security
7.5.1. Spring configuration
7.5.2. web.xml Configuration
8. Flow Inheritance
8.1. Introduction
8.2. Is flow inheritance like Java inheritance?
8.3. Types of Flow Inheritance
8.3.1. Flow level inheritance
8.3.2. State level inheritance
8.4. Abstract flows
8.5. Inheritance Algorithm
8.5.1. Mergeable Elements
8.5.2. Non-mergeable Elements
9. System Setup
9.1. Introduction
9.2. webflow-config.xsd
9.3. Basic system configuration
9.3.1. FlowRegistry
9.3.2. FlowExecutor
9.4. flow-registry options
9.4.1. Configuring custom FlowBuilder services
9.4.1.1. conversion-service
9.4.1.2. expression-parser
9.4.1.3. view-factory-creator
9.4.2. Configuring FlowRegistry hierarchies
9.4.3. Specifying flow locations
9.4.4. Assigning custom flow identifiers
9.4.5. Assigning flow meta-attributes
9.4.6. Registering flows using a location pattern
9.5. flow-executor options
9.5.1. Attaching flow execution listeners
9.5.2. Tuning FlowExecution persistence
9.5.2.1. max-executions
9.5.2.2. max-execution-snapshots
10. Spring MVC Integration
10.1. Introduction
10.2. Configuring web.xml
10.3. Mapping URLs to Flows
10.4. Flow Handlers
10.4.1. Example FlowHandler
10.4.2. Registering the FlowHandlerAdapter
10.4.3. FlowHandler Redirects
10.5. Flow Controller
10.6. View Resolution
11. Spring JavaScript Quick Reference
11.1. Introduction
11.2. Serving Javascript Resources
11.3. Including Spring Javascript in a Page
11.4. Spring Javascript Decorations
11.5. Handling Ajax Requests
11.5.1. Handling Ajax Requests with Spring MVC Controllers
11.5.2. Handling Ajax Requests with Spring MVC + Spring Web Flow
12. JSF Integration
12.1. Introduction
12.2. Spring-centric Integration Approach
12.3. Configuring web.xml
12.4. Configuring Web Flow to render JSF views
12.5. Configuring faces-config.xml
12.6. Replacing the JSF Managed Bean Facility
12.6.1. Using Flow Variables
12.6.2. Using Scoped Spring Beans
12.6.3. Manipulating The Model
12.7. Handling JSF Events With Spring Web Flow
12.7.1. Handling JSF In-page Action Events
12.7.2. Handling JSF Action Events
12.7.3. Performing Model Validation
12.7.4. Handling Ajax Events
12.8. Enhancing The User Experience With Rich Web Forms
12.8.1. Validating a Text Field
12.8.2. Validating a Numeric Field
12.8.3. Validating a Date Field
12.8.4. Preventing an Invalid Form Submission
12.9. Third-Party Component Library Integration
12.9.1. Rich Faces Integration
12.9.2. Apache MyFaces Trinidad Integration
13. Portlet Integration
13.1. Introduction
13.2. Configuring web.xml and portlet.xml
13.3. Configuring Spring
13.3.1. Flow Handlers
13.3.2. Handler Mappings
13.3.3. Flow Handler Adapter
13.4. Portlet Views
13.5. Portlet Modes and Window States
13.5.1. Window State
13.5.2. Portlet Mode
13.6. Issues in a Portlet Environment
13.6.1. Redirects
13.6.2. Switching Portlet Modes
13.6.3. Portlets and JSF
14. Testing flows
14.1. Introduction
14.2. Extending AbstractXmlFlowExecutionTests
14.3. Specifying the path to the flow to test
14.4. Registering flow dependencies
14.5. Testing flow startup
14.6. Testing flow event handling
14.7. Mocking a subflow
15. Upgrading from 1.0
15.1. Introduction
15.2. Flow Definition Language
15.2.1. Flow Definition Updater Tool
15.2.1.1. Flow Definition Updater Tool Warnings
15.2.2. EL Expressions
15.3. Web Flow Configuration
15.3.1. Web Flow Bean Configuration
15.3.2. Web Flow Schema Configuration
15.3.2.1. flow-executor
15.3.2.2. flow-execution-listeners
15.3.2.3. flow-registry
15.3.3. Flow Controller
15.3.4. Flow Request URL Handler
15.3.5. View Resolution
15.4. New Web Flow Concepts
15.4.1. Automatic Model Binding
15.4.2. OGNL vs EL
15.4.3. Flash Scope
15.4.4. Spring Faces
15.4.5. External Redirects
A. Flow Definition Language 1.0 to 2.0 Mappings

Preface

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.

1. Introduction

1.1. What this guide covers

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.

1.2. What Web Flow requires to run

Java 1.4 or higher

Spring 2.5.4 or higher

1.3. Where to get support

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

1.4. Where to follow development

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.

1.5. How to obtain Web Flow artifacts from the SpringSource Bundle Repository

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.

1.5.1. Accessing Web Flow with Maven

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> 

			

1.5.2. Accessing Web Flow with Ivy

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" />

			

1.6. How to obtain Web Flow artifacts from Maven Central

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> 

		

1.7. How to Obtain Nightly Builds

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.

2. Defining Flows

2.1. Introduction

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.

2.2. What is a flow?

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

2.3. What is the makeup of a typical 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

2.4. How are flows authored?

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.

2.5. Essential language elements

2.5.1. flow

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.

2.5.2. view-state

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.

2.5.3. transition

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.

2.5.4. end-state

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.

2.5.5. Checkpoint: Essential language elements

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>	
			

2.6. Actions

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.

2.6.1. evaluate

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)" />		
			

2.6.1.1. Assigning an evaluate result

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" />
				

2.6.1.2. Converting an evaluate result

If the expression returns a value that may need to be converted, specify the desired type using the result-type attribute:

<evaluate expression="bookingService.findHotels(searchCriteria)" result="flowScope.hotels"
          result-type="dataModel"/>
				

2.6.2. Checkpoint: flow actions

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.

2.7. Input/Output Mapping

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();
}
		

2.7.1. input

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.

2.7.1.1. Declaring an input type

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.

2.7.1.2. Assigning an input value

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.

2.7.1.3. Marking an input as required

Use the required attribute to enforce the input is not null or empty:

<input name="hotelId" type="long" value="flowScope.hotelId" required="true" />
				

2.7.2. output

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.

2.7.2.1. Specifying the source of an output value

Use the value attribute to denote a specific output value expression:

<output name="confirmationNumber" value="booking.confirmationNumber" />  
				

2.7.3. Checkpoint: input/output mapping

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.

2.8. Variables

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.

2.8.1. var

Use the var element to declare a flow variable:

<var name="searchCriteria" class="com.mycompany.myapp.hotels.search.SearchCriteria"/>
			

Make sure your variable's class implements java.io.Serializable, as the instance state is saved between flow requests.

2.9. Calling subflows

A flow may call another flow as a subflow. The flow will wait until the subflow returns, then respond to the subflow outcome.

2.9.1. subflow-state

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.

2.9.1.1. Passing a subflow input

Use the input element to pass input to the subflow:

<subflow-state id="addGuest" subflow="createGuest">
    <input name="booking" />
    <transition to="reviewBooking" />
</subfow-state>
				

2.9.1.2. Mapping subflow output

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.

2.9.2. Checkpoint: calling subflows

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.

3. Expression Language (EL)

3.1. Introduction

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.

3.2. Supported EL implementations

3.2.1. Unified EL

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]Note
The el-api dependency is typically provided by your web container. Tomcat 6 includes it, for example.

3.2.2. OGNL

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.

3.3. EL portability

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.

3.4. EL usage

EL is used for many things within a flow, including:

  1. Accessing data provided by the client, such as flow input attributes and request parameters.

  2. Accessing internal data structures such as flowScope.

  3. Invoking methods on Spring beans.

  4. 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.

3.4.1. Expression types

There are basically two types of expressions in Web Flow.

3.4.1.1. Standard eval expressions

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]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.

3.4.1.2. Template expressions

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.

3.5. Special EL variables

There are several implicit variables you may reference from within a flow. These variables are discussed in this section.

3.5.1. flowScope

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" />
			

3.5.2. viewScope

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>
			

3.5.3. requestScope

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" />
			

3.5.4. flashScope

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'" />				
			

3.5.5. conversationScope

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" />
			

3.5.6. requestParameters

Use requestParameters to access a client request parameter:

<set name="requestScope.hotelId" value="requestParameters.id" type="long" />
			

3.5.7. currentEvent

Use currentEvent to access attributes of the current Event:

<evaluate expression="booking.guests.add(currentEvent.guest)" />
			

3.5.8. currentUser

Use currentUser to access the authenticated Principal:

<evaluate expression="bookingService.createBooking(hotelId, currentUser.name)" 
          result="flowScope.booking" />
			

3.5.9. messageContext

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)" />
			

3.5.10. resourceBundle

Use resourceBundle to access a message resource.

<set name="flashScope.successMessage" value="resourceBundle.successMessage" />
			

3.5.11. flowRequestContext

Use flowRequestContext to access the RequestContext API, which is a representation of the current flow request. See the API Javadocs for more information.

3.5.12. flowExecutionContext

Use flowExecutionContext to access the FlowExecutionContext API, which is a representation of the current flow state. See the API Javadocs for more information.

3.5.13. flowExecutionUrl

Use flowExecutionUrl to access the context-relative URI for the current flow execution view-state.

3.5.14. externalContext

Use externalContext to access the client environment, including user session attributes. See the ExternalContext API JavaDocs for more information.

<evaluate expression="searchService.suggestHotels(externalContext.sessionMap.userProfile)" 
          result="viewScope.hotels" />
			

3.6. Scope searching algorithm

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.

4. Rendering views

4.1. Introduction

This chapter shows you how to use the view-state element to render views within a flow.

4.2. Defining view states

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

4.3. Specifying view identifiers

Use the view attribute to explictly specify the id of the view to render.

4.3.1. Flow relative view ids

The view id may be a relative path to view resource in the flow's working directory:

<view-state id="enterBookingDetails" view="bookingDetails.xhtml">
			

4.3.2. Absolute view ids

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">
			

4.3.3. Logical view ids

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.

4.4. View scope

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.

4.4.1. Allocating view variables

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" />
			

4.4.2. Assigning a viewScope variable

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>
			

4.4.3. Manipulating objects in view scope

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>
			

4.5. Executing render actions

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>
	    

4.6. Binding to a model

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:

  1. View-to-model binding. On view postback, user input values are bound to model object properties for you.

  2. 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.

4.7. Suppressing binding

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>
		

4.8. Specifying bindings explicitly

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.

4.9. Validating a model

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.

4.9.1. Programmatic validation

There are two ways to perform model validation programatically.

4.9.1.1. Implementing a model validate method

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());
    }
}
				

4.9.1.2. Implementing a Validator

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.

4.9.2. Declarative validation

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.

4.10. Suppressing validation

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.

4.11. Transition actions

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;
       }
   }
}
		

4.12. Handling events

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.

4.12.1. Rendering partials

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.

4.12.2. Handling global events

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>
			

4.13. Working with messages

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.

4.13.1. Adding plain text 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());
			

4.13.2. Adding internationalized messages

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());
			

4.13.3. Using message bundles

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}" />
			

4.14. Displaying popups

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.

4.15. View backtracking

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.

4.15.1. Discarding history

Set the history attribute to discard to prevent backtracking to a view:

<transition on="cancel" to="bookingCancelled" history="discard">
			

4.15.2. Invalidating history

Set the history attribute to invalidate to prevent backtracking to a view as well all previously displayed views:

<transition on="confirm" to="bookingConfirmed"