0
0
0
40,189

Spring WebFlow Tutorial

Spring WebFlow Tutorial

Abstract

SpringWebflow This is a comprehensive tutorial on Spring WebFlow - a leading technology to implement stateful navigation contexts and constraints in web applications. We review the set of configuration options available and show case common use cases of the technology with practical examples. The tutorial is intended to work as useful complement to the reference documentation of the framework, and help developers to get started easily and at the same time have a deep understanding of the concepts and issues of involved in implementing webflows.

Introducing Spring Web Flow

Spring WebFlow is a web framework implemented on top of Spring CoreFramework designed specially to address the issues of implementing stateful navigation contexts and navigation constraints in web application.

Webflow is the name given to structured and constrained navigation in a web environment. In standalone applications, the traditionally used and somewhat equivalent term is a wizard - a pattern of human-computer interaction which guides the human-user throw a set of well defined steps. Due to the hyperlinked characteristic of web content, webflows may offer more navigation flexibility than traditional wizards but less than the unconstrained navigation of simpler (dynamic-content) web applications. Due to specifics of web-application web-flows are harder to implement than traditional wizards. And since the "default" state-management model of web applications is state-less, webflows are also considered harder to implement that other sorts of web-application. For this reason, framework support - such as provided by Spring WebFlow - is in many cases highly welcome by developers.

Key Features of Spring Webflow

  • Implements web navigation stateful "flows"
  • Integration with Spring @MVC Framework and JSF
  • Check that users follow the meaningful navigation paths
  • Manages multiple windows issues by supporting multiple simulataneous webflow context inside the same session context
  • Provides scopes beyond request and session (conversation scope)
  • Addresses the double-submit problem elegantly
  • Give a consistent and expected "semantics" to the browser back-button
  • Use of declarative XML domain-specific language (Flow DSL) to define web-flows as (deterministic finite) state-transaction diagram or graph, where:
    • Graph states are mapped web pages or decision nodes
    • transitions between states are triggered by user actions (typically involving form actions)

Flow Definition Language - Sample Flow

Web-Flows are defined in a XML DSL:
<flow ...>
	<view-state id="state0">
		<transition on="action0" to="state1" />
	</view-state>
	
	<view-state id="state1">
		<transition on="action1-1" to="state2" />
		<transition on="action1-2" to="xstate" />
	</view-state>
	
	<view-state id="state2">
		<transition on="action2-1" to="xstate" />
	</view-state>
	
	<end-state id="xstate"/>
</flow>
On-line demos and sample applications:

Webflow Design-Patterns

Some navigation structures or design-patterns are recurrent when modeling web-flows. Becoming familiar with these Web-Flow design-patterns provides us with a rough guide to help in the implementation of real-world web applications based on web-flows. Essentially, the same way that Object-Oriented design-patterns guide in designing effective object-oriented architectures, and Enterprise Integration Patterns guide in implementing pipe&filter architectures.

We have identified several of these recurrent web-flow design-pattern, which are summarized in the sub-sections below. Naturally, others could be identified and included in this list.

Multi-Step Edit Webflow Design-Pattern

In the Multi-Step Edit Webflow Design-Pattern, form details are introduce in stages. Mandatory data is introduced in the first step of the webflow, and additional complementary data is introduced in separated steps. This design is motivated by the fact that users tend to be reluctant to spend time filling up forms, and may also have privacy concerns about providing extensive data to a website.

In order to keep user engagement with a site high is preferred to require the user to provide only minimal information. Additional details may be provided by some users from the very start, while others may prefer to provide that data later on or not at all. An obvious example where this flow pattern shows up is in user registration pages - where the business model benefits from having users provide extensive information (e.g. for later marketing purposes), while the user simply wants to start using the site as quickly as possible.

Figure below shows a generic diagram for the Multi-Step Edit Webflow Design-Pattern:

Multi-Step Edit Web-Flow Design-Pattern

This general pattern would be modeled in Spring WebFlow XML Flow Definition Language roughly as follows:

<flow ...>
	<view-state id="enterBasicInfo">
		<transition on="next" to="enterDetails" />
		<transition on="done" to="completed" />
	</view-state>
	
	<view-state id="enterDetails">
		<transition on="next" to="enterMoreDetails" />
		<transition on="done" to="completed" />
	</view-state>

	<view-state id="enterMoreDetails">
		<transition on="done" to="completed" />
	</view-state>
	
	<end-state id="completed" />
</flow>

Operation with Confirmation Webflow Design-Pattern

Most operations that have sensitive real world consequences mandate that the user be careful with the correctness of the details entered in forms. Common examples of this include: transfer of money, making an order, or introduce payment details. Having a confirmation page before completing the operation is the frequent approach to give confidence to user about the operation, and is an almost mandatory feature for a website.

A webflow can be used to model this with the Operation with Confirmation webflow design pattern. State-transition diagram below illustrates this pattern:

Operation with Confirmation Web-Flow Design-Pattern

This pattern is modeled in Spring WebFlow XML Flow Definition Language as follows:

<flow ...>
	<view-state id="enterDetails">
		<transition on="next" to="reviewDetails" />
	</view-state>
	
	<view-state id="reviewDetails">
		<transition on="confirm" to="completed" />
		<transition on="back" to="enterDetails" />
	</view-state>
	
	<end-state id="completed" />
</flow>

Operation with Optional-Step Webflow Design-Pattern

Some operations have a main required flow of steps, with some other steps are optional. A classical example in the webflow world is the flight check-in, where a entering passenger details and review the itinerary is mandatory, while reviewing and changing the seat is optional - and may be perform only be aisle-or-window or otherwise conscious users.

The Operation with Optional-Step Webflow design-pattern characterize these interaction scenarios. It is illustrated in a generic way in the diagram below:

Operation with Optional Step Web-Flow Design-Pattern

This pattern is modeled in Spring WebFlow XML Flow Definition Language as follows:

<flow ...>
	<view-state id="enterDetails">
		<transition on="next" to="reviewDetails" />
	</view-state>
	
	<view-state id="reviewDetails">
		<transition on="finish" to="completed" />
		<transition on="refine" to="optionalOperation" />
	</view-state>

	<view-state id="optionalOperation">
		<transition on="finish" to="completed" />
	</view-state>
	
	<end-state id="completed" />
</flow>

Defining Flows

A webflow is fundamentally a navigation graph in a web application. A webflow is roughly defined by a set of nodes or states - usually view states - and a set of transitions between states triggered by the user's GUI events.

Flow XML Namespace

A flow is commonly defined in a XML file importing a dedicated XML namespace. Flow definitions have the element <flow> as 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>

View States

All states of the flow are defined within the <flow> root element. The most commonly used kind of state is a view-state - defined with element <view-state> . A view-state defines a step in the flow where a view is rendered to the user. A number of <view-state> is defined for a flow. The first defined <view-state> is considered the start-state of the flow.

<flow ...>

	<view-state id="enterBookingDetails" />

	<view-state id="confirmBookings" />
	
</flow>

Each <view-state> should be assigned a unique identifier with attribute id . By default, a view-state maps its id to a view resource file in the same directory where the flow is located. For example, if the above flow is located in /WEB-INF/hotels/booking/bookings-flow.xml then the first view-state of the flow above maps to a view file located in /WEB-INF/hotels/booking/enterBookingDetails.jsp .

An explicit view can also be assigned to a view-state with attribute view , as follow:

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

Logical view names are also supported. In Spring MVC this view names are mapped using the installed ViewResolver .

State Transitions

Each state can defined one or more transitions with XML element <transition> . For each transition, the attribute on specifies the name of the event that trigger the transition. The attribute to specifies the state that the flow navigates too when the event and transition occurs.

<view-state id="enterBookingDetails">
	<transition on="submit" to="reviewBooking" />
</view-state>

The collection of transitions of a state drives the navigations for the view where the transition is declared. However, it is the end-user that cause the events triggering the transitions.

End States

End-states are states whose navigation causes the flow to complete. The XML attribute <end-state defines an individual end-state. A flow can have any number of end-states.

<end-state id="bookingCancelled" />

When a flow transitions to an end-state it terminates and the outcome is processed or returned.

Putting it all Together

Putting together view-states, transitions, and end-states, becomes possible to express a complete view navigation graph. Below, I show the sample flow putting all these elements together.

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

A common development methodology for WebFlow based applications, is to start by defining a flow layout and mocking up views for the application GUI. Once the basic layout of the flow has been agreed and accepted, flow behavior is added - such as invoking service beans and/or repositories to get persistente data.

Scoped Variables

Variables can be defined in one of several scopes. For example, flow-scoped variables are allocated when the flow starts. Likewise, view-scoped are allocated when a view is first rendered and remain available while the flow is on that same view. An arbitrary number of variable can be defined for each scope.

Defining Variables

To define flow-scoped or view-scoped variable the <var> XML element should be used. The attribute name specifies the names of the variable, while the attribute class specifies the Java type of the variable.

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

When a variable is instantiated, any @Autowired transient references the variable type holds are (re)wired.

The scope of the variable is defined by the place where the element <var> is defined. For flow-scoped variables are defined just inside <flow> , as follows:

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

For view-scoped variables the the element <var is defined inside <view-state> , as follows:

<flow>

	<view-state id="searchHotel">
		<var name="searchCriteria" class="com.acme.myapp.hotels.search.SearchCriteria" />
		...
	</view-state>
	...
</flow>

Variable Scopes

A variable are defined with a scope and always exist in that specific scope. The scope defines the context and time-frame of existence of the variable and its value. When the scope of the variable is exited the the value of the variable is discarded, and the variable can no longer be accessed. If the same scope is entered again, then a new instance of the variable is created with a new intialized value.

Scopes follow roughly a order of search in resolving variable names. More transient scopes are searched first, followed by more durable scopes.

Table below list the variable scopes available in Spring WebFlow. Scopes listed at the bottom are more transient and search first. So the search order for scopes is as follows: Request → View → Flash → Flow → Conversation.

ScopeScope VariableDescription
Conversation conversationScope current flow and all sub-flows
Flow flowScope current flow
Flash flashScope current view and next view
View viewScope current view (including refreshes)
Request requestScope current HTTP request
Variable scopes in Spring WebFlow.

When assigning a value to a variable the scope should be explicit by reference to the scope variable. This is the case when assigning a value to the variable from a <evaluate action with the result attribute, and with the assignment element <set> .

In the example below, the assignment to variable hotels is specified a view scope with viewScope.hotels .

<flow ...>
	<var name="searchCriteria" class="org.springframework.webflow.samples.booking.SearchCriteria" />

	<view-state id="reviewHotels">
		<on-render>
			<evaluate expression="bookingService.findHotels(searchCriteria)" result="viewScope.hotels" />
		</on-render>
	</view-state>
	
</flow>

When accessing the value of an existing variable the reference of scope is optional, since all scopes are searched in order. An example is show below, where a property of variable searchCriteria is referenced without scope.

<flow ...>

	<var name="searchCriteria" class="org.springframework.webflow.samples.booking.SearchCriteria" />

	<view-state id="reviewHotels">
		<transition on="sort">
			<set name="searchCriteria.sortBy" value="requestParameters.sortBy" />
		</transition>
	</view-state>

</flow>

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

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 be Serializable (since session state may be persisted).

<evaluate expression="searchService.findHotel(hotelId)" result="conversationScope.hotel" />

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 can only be referenced 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>

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

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

Predefined Variables

There are several predefined (implicit) variables that flow can reference. Some of these variables are the scope variables mentioned earlier.

Table below lists other implicitly variables (excluding scope variable).

Scope Scope Variable Description
Conversation conversationScope current flow and all sub-flows
Flow flowScope current flow
Flash flashScope current view and next view
View viewScope current view (including refreshes)
Request requestScope current HTTP request
Implicit variable in Spring WebFlow (excluding scope variables).

requestParameters

Use requestParameters to access a HTTP request parameters. Request parameter are always extracted from the URL query string (the part after the "?").

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

currentEvent

The variable currentEvent is used to access attributes of the current view Event:

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

currentUser

The variable currentUser is used to access information about the authenticated Principal .

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

Flow Context Variables

The variable flowRequestContext is used to represen the context of the current flow request.

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

The variable flowExecutionUrl is used to access the context-relative URI for the current flow execution view-state. A typical use of this variable is in authoring views, such as link to the view itself.

The variable externalContext is used to access the client environment, including user session attributes.
<evaluate expression="searchService.suggestHotels(externalContext.sessionMap.userProfile)" 
		result="viewScope.hotels" />

Messages and Resource Boundle

The messageContext variable hold an object that can be passed to service POJO to get text messages for the flow views. The resourceBundle can be used inside a flow to get text messages. These two variable are discussed in a dedicate section on Messages and Internationalization.

Flow Actions

Actions are operations that can be performed by flows. Actions are defined using a scripting or expression language. Spring Web Flow uses the Spring EL, as default. Possible actions include invoking business services, repositories, and other application beans, get property values from a bean or variable, assigning value to variable, and so forth.

Defining Actions

The XML element <evaluate> is the most commonly used kind of action. It simple evaluates the EL expression specified in attribute expression , performing the operations specified.
<evaluate expression="entityManager.persist(order)" />		

The result of evaluation an expression can be assigned to a variable in some scope. This is done by specifying the name of the variable in the result attribute.

<evaluate expression="orderManager.getOrder(id)" result="flowScope.order" />

The result type can also be cast before the assignment is done. This is done with attribute result-type , as follows:

<evaluate expression="orderManager.getOrder(id)" result="flowScope.order"
		result-type="Order"/>

Executing Actions

Within a flow, there are several points where you can execute actions.

Executing Actions on Flow Start

To execute an action on the start of a flow, the XML element <on-start> should be used:

<flow ...>
	<on-start>
		<evaluate expression="orderManager.createOrder(orderId)" 
			result="flowScope.order" />
	</on-start>
	...
</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.

Executing Actions on Flow End

To execute an action on the end of a flow, the XML element <on-end should be used:

<flow ...>
	<on-end>
		<evaluate expression="..." result="..." />
	</on-end>
	...
</flow>

Executing Actions on State Entry

To execute an action on entry of a view-state, the XML element <on-entry should be used:

<view-state id="viewOrders" view="viewOrders.xhtml" popup="true">
	<on-entry>
		<render fragments="orderTable" />
	</on-entry>
</view-state>

The example above renders a partial fragment of the view when the view-state is entered.

Executing Actions on State Exit

To execute an action on exist of a view-state, just before a transitions mode the flow to another stte, the XML element <on-exist should be used:

<view-state id="editOrder">
	<transition on="update" to="viewOrders">
		<evaluate expression="orderManager.update(order)" />
	</transition>
	<on-exit>
		<evaluate expression="orderManager.commit()" />
	</on-exit>
</view-state>

Executing Actions on View Render

To execute an action when a view is rendered, the XML element <on-render should be used. Actions are executed before the view is rendered, including in the initial render as well as any subsequent refreshes, and partial re-renderings of the view.

<view-state id="searchResults">
	<on-render>
	<evaluate expression="bookingService.findHotels(searchCriteria)"
			result="viewScope.hotels" />
	</on-render>
	...
</view-state>

Executing Actions on Transitions

It is also possible to execute action on state transitions. This is explained in the next section.

Types of Actions

There are several options when defining the Java side of action to be called from a flow <evaluate> action.

Plain POJO Actions

Plain POJO class can be called by using the EL syntax for method invocation. The POJO should be registered as Spring bean, either with the <bean> element in a XML file or as class annotated with @Component .

<evaluate expression="orderManager.createOrder(flowRequestContext)" />

Java methods can also get information about the invocation context from the flow by specifying a parameter of type FlowRequestContext. This is illustrated below.

public class OrderManager {
	public String createOrder(RequestContext context) {
		Order order = (Order) context.getFlowScope().get("order");
		OrderConfirmation confirmation = orderService.create(order);
		return "success";
	}
	...
}

The Action interface

The Action interface is defined by WebFlow as a type-safe way to defines Java actions. The interface specification is show below:

package org.springframework.webflow.execution;

public interface Action {
	public Event execute(RequestContext context) throws Exception;
}

The method execute() takes a RequestContext as parameter and return an Event . Java class can implement this interface, for custom application Action .

An Action should be registered as Spring bean, either with the <bean> element in a XML file or as class annotated with @Component . Action implementations can be invoked from a flow definition file by simply writing the named of the bean.

<evaluate expression="createOrder" />

Below, I show an example of an Action to create Order objects.

public class CreateOrder implements Action {
	public Event execute(RequestContext context) {
		Order order = (Order) context.getFlowScope().get("order");
		OrderConfirmation confirmation = orderService.create(order);
		return new Event(this, "success");
	}
}

The MultiAction Class

The class MultiAction can also be extended to implement one or more action methods.

A MultiAction should also registered as Spring bean. MultiAction methods can be invoked from a flow definition file by simply writing the name of the bean and the method.

<evaluate expression="orderMultiAction.createOrder" />

Below, I show an example of a MultiAction used to manage Order objects.

public class OrderMultiAction extends MultiAction {
	
	public Event createOrder(RequestContext context) {
		Order order = (Order) context.getFlowScope().get("order");
		OrderConfirmation confirmation = orderService.create(order);
		return success();
	}

	public Event updateOrder(RequestContext context) {
		...
	}

	public Event getOrder(RequestContext context) {
	...
	}

	public Event deleteOrder(RequestContext context) {
	...
	}
}

View Events

Transitions between view-state occur when the user triggers events in a view. Events are signaled by commands in GUI components, such as pressing buttons, links, and other. Firing an event results in a HTTP request being sent to the server. On the server-side, the flow handles decoding the event from within its current view-state. The details of how events are decoded on the server-side is specific to the view technology in use. This section shows how events are decoded from HTML-based views (e.g. generated by templating engines such as JSP, Velocity, or Freemarker.)

(X)HTML Buttons

HTML buttons encode events by using the convention that the name attribute of a <button> should be prefixed with _eventId_, and the suffixed with the name of the event. When a button is pressed WebFlow looks for a request parameter name beginning with prefix _eventId_ and treats the remaining substring as the event ID.

The example below shows two buttons on the same form that signal proceed and cancel events when clicked, respectively.

<input type="submit" name="_eventId_proceed" value="Proceed" />
<input type="submit" name="_eventId_cancel" value="Cancel" />		

In this example, pressing and submitting with button _eventId_proceed will generate the event proceed.

This style should be considered when there are several different events that can be signaled from the same form.

Single Buttons

An alternative approach to encode events in (X)HTML is to have an HTTP request parameter named _eventId to explicitly encode the event name. This is specially useful when their is a single submit button.

<button type="submit" name="_eventId" value="confirm">Confirm</button>
A hidden input element can also be used to setup the event ID, as shown below:
<input type="submit" value="Proceed" />
<input type="hidden" name="_eventId" value="proceed" />	

WebFlow detects the special _eventId parameter and uses its value as the event id. If there are more than one form submit button, the Javascript code should be used to set the _eventId before posting.

Links

An event can also be encoded explicitly in the URL of a (X)HTML link.

<a href="${flowExecutionUrl}&_eventId=cancel">Cancel</a>		

The general strategy for event decoding in Spring MVC view is first to look for a HTTP request parameter named _eventId. If no _eventId parameter is found, then WebFlow looks for a parameter that starts with _eventId_ and will use the remaining substring as the event id. If neither cases exist, no flow event is triggered and the current view is re-rendered.

State Transitions

Define one or more transition elements to handle user events that may occur on the view. A transition may take the user to another view, or it may simply execute an action and re-render the current view. A transition may also request the rendering of parts of a view called "fragments" when handling an Ajax event. Finally, "global" transitions that are shared across all views may also be defined.

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 from exiting the current view-state. If an error result occurs, the view will be re-rendered. and should display an appropriate message to the user.

If the transition action invokes a Java boolean method, and the invoked method returnsfalse, then the transition is prevented executing.

<transition on="submit" to="bookingConfirmed">
	<evaluate expression="bookingAction.makeBooking(booking, messageContext)" />
</transition>

Exception thrown by a service-layer method can be handled with this approach.

public class BookingAction {
	public boolean makeBooking(Booking booking, MessageContext context) {
	try {
		bookingService.make(booking);
		return true;
	} catch (RoomNotAvailableException e) {
		context.addMessage(new MessageBuilder().error().
			.defaultText("No room is available at this hotel").build());
		return false;
	}
}

When there is more than one action defined on a transition, if one returns an error result the remaining actions in the set will not be executed. If you need to ensure that an error in one action does not prevent other from executing, them a possible approach it to define a single transition action that invokes a Java method that encapsulates the logic of all the actions.

Global transitions

Use the flow's global-transitions element to create transitions 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>

Event handlers

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.

Rendering Fragments

Use the render element within a transition to request partial re-rendering of the current view after handling the 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.

Action States and Decision States

In addition to view-states that render a view, flow definitions can also specifiy states that do not render views but are used to execute actions. An action-state encapsulates an action plus a set of transitions. A decision-state is a specialized form of an action-state for simple if-then-else decisions.

Action States

The XML element <action-state defines a state that encapsulate an action and transitions. An <evaluate action is used to evaluate an expression. Transitions branch in the flow depending on the result of the expression evaluation. Using an action-state allow actions and branching logic to be shared across several points in the flow definition.

<action-state id="moreAnswersNeeded">
	<evaluate expression="interview.moreAnswersNeeded()" />
	<transition on="yes" to="answerQuestions" />
	<transition on="no" to="finish" />
</action-state>

Decision States

For branching based on a boolean value, the element <decision-state> may be used as a less verbose alternative to an action-state. The (sub-)element <if> specifies both the expression to evaluate and the name of the transition states. The attribute test specifies the boolean expression, attribute then specifies the state to branch for true return value, and the attribute else the state to branch for a false return value.

<decision-state id="moreAnswersNeeded">
	<if test="interview.moreAnswersNeeded()" then="answerQuestions" else="finish" />
</decision-state>

Action Results

The execution of action that evaluate expressions is used in action-states and decision-states to trigger transitions. Since the expressions can invoke an arbitrary Java method and/or have arbitrary types, some conventions are used to map the value of the expression to an event ID.

Table below specifies how event IDs are generate from return values by Java type.

Expression Type Event ID
java.lang.String the literal value of the String
java.lang.Boolean|boolean "yes" (for true); "no" (for false)
java.lang.Enum Enum name (value as text)
other "success"
Mapping between action return type/value and event ID.

Flow Input and Output

Flows can have input parameters and a return/output value, similarly to Java methods. Input parameters can be passed in when a flow is called from another flow. Likewise, a calling flow uses the output value of a flow to generate the event ID that triggers transactions to other state.

Flow Input

The XML element <input> is used to specify an input parameter of a flow. A flow can have an arbitrary number of input parameters. The <input> elements should be specified just inside the <flow> element.

Input parameters are saved in the flow scope, and can be accessed the same way as declared variables. The attribute name specifies the attribute name by which the flow input paramater is available.

<flow ...>

	<input name="customerId" />
	...
</flow>

Input parameters can be typed by setting the attribute type. When an input parameter is typed, and the concrete input value does not match the declared type, a type conversion will be attempted.

<flow ...>
	<input name="costumerId" type="long" />
	...
</flow>

A default input value can also be specified with attribute value. The value is an expression whose evaluation provides the initialization value for the input parameter. The type of the expression implicitly assigns a type to the input parameter, if it not specified explicitly with attribute type.

<input name="costumerId" value="flowScope.flowArgs.costumerId" />

To force an input parameter to have a non null and non empty value, the attribute required="true" can be used.

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

Flow Output

Flow output is specified with XML element <output>. An <output> element can be specified inside each <end-state>, corresponding to the output of the flow for that particular outcome. The attribute value takes the expression for the output value.

<end-state id="reservationConfirmed">
	<output name="reservationId" value="reservation.confirmationNumber" /> 
</end-state>

Sample Flow with Input and Output

Below, I show an example of a flow for managing a shooping cart with a input parameters and an output value:

<flow ...>

	<input name="userId" />

	<on-start>
		<evaluate expression="shopService.createShoopingCart(userId, currentUser.name)" 
					result="flowScope.shoopingCart" />
	</on-start>

	<view-state id="enterProducts">
		...
		<transition on="submit" to="reviewPurcase" />
	</view-state>
	
	<view-state id="reviewPurchase">
		<transition on="confirm" to="purchaseConfirmed" />
		<transition on="revise" to="enterProducts" />
		<transition on="cancel" to="purchaseCancelled" />
	</view-state>
	
	<end-state id="purchaseConfirmed" >
		<output name="orderId" value="shoopingCart.orderId"/>
	</end-state>

	<end-state id="purchaseCancelled" />

</flow>

Sub-Flows

A flow may call another flow as a subflow. This is done by defining a subflow-state, that passes control to the subflow. The calling flow execution is suspended until the called subflow ends. Subflow can also call other subflow in a recursive manner.

Subflow States

The XML element subflow-state is used to call another flow as a subflow. The attribute subflow specifies the name of the flow to call as specified in the FlowRegistry. Transition on the calling flow are triggered by the output value returned by the called flow.

Below, I show an example of a flow that call a subflow:

<flow ...>
	<subflow-state id="addProduct" subflow="selectProduct">
		<transition on="productAdded" to="reviewPurchase">
			<evaluate expression="shoppingCart.add(currentEvent.attributes.product)" /> 
		</transition>
		<transition on="productCancelled" to="reviewPurchase" />
	</subflow-state>
</flow>

The main flow calls a suflow named addProduct. On return, the event productAdded triggers an action to add a product to a shopping-cart.

Subflow Input

Effective call of a subflow often requires that input parameter values be specified. This is done with XML element <input>.

<subflow-state id="addProduct" subflow="selectProduct">
	<input name="shoppingCart" value="shoppingCart" />
	<transition to="reviewPurchase" />
</subflow-state>

Subflow Output

The output of a called subflow is available a named attributed of the currentEvent. The name of the attribute is the same as the name of the output parameter as defined in the end-state of the called flow.

Flow Inheritance

TODO

Data Binding

Dynamic views, such as forms, depend on some model object to provide the values for the dynamic parts of the view. The model object can be initialized by invoking a Java service or repository layer, or by initialization from HTTP parameters. The process by which a model object is initialized is designated as data binding. It also possible to put validation constraints on bound objects.

Binding to a View model

In Spring WebFlow a model object is bound to a view-state by setting the attribute model with a scoped variable. Below, I show an example of how to define the model for a view-state.

<view-state id="enterBookingDetails" model="booking"> ... </view-state>

The model variable can be defined in any scope, such as flowScope or viewScope. The model varable is initialzed from user input values specified as HTTP request parameters. Request parameter names should match the names of the properties of the model object.

Before an event in a view generates a transitons to some other state, the binding to the view model must complete successfully. If the binding fails, no transition from events is generated and the view is re-rendered. This should allow the user to revise their edit and generate the same event.

Suppressing binding

Usually all events and transitions in a view-state produce an attempted bind of request parameters to the model object. It is, however, possible to supress model binding (and validation) for particular events and transitions. This is done by setting the attribute bind="false".

<view-state id="enterBookingDetails" model="booking">
	<transition on="proceed" to="reviewBooking">
	<transition on="cancel" to="bookingCancelled" bind="false" />
</view-state>

Specifying Bindings Explicitly

It possible to control the details of the binding process, by specifying which propoerties of the model object should be bound for a view. This is done with XML element <binder>, and with one (sub-)element <binding> for each model property to bind. Below, I show an example of a model with severel explicitly bound proporties:

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

Each <binding element specifies a model propoerty to be bound. When a binder</b> element is not specified in a view-state, all the public properties of the model object are eligible for binding. On the other hand, when the <b><![CDATA[<binder element is specified, only the explicitly defined properties are bound.

Property Converters

A <binding element may explicitly specify a converter object 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 is used.

The example below show a model bound property with an explicit converter.

<view-state id="enterBookingDetails" model="booking">
	<binder>
		<binding property="checkinDate" converter="dateConverter" />
		...
	</binder>
	...
</view-state>

Custom default converters may be registered as a ConversionService in the FlowBuilderServices of the application. This can be done with <flow-builder-services> element, as explained in a later section.

Required Bindings

A simple kind of validation of a bound model object is to check if a property is null. This can be performed by setting the attribute required="true" in each model object property defined with <binding>. When a model property is set as required and no value is provided by the user input, a validation error is generated. Validation errors prevents a events to be processed so that the same view is re-rendered.

Below, I show an example of a view-state with a model object with a required property:

<view-state id="enterBookingDetails" model="booking">
	<binder>
	<binding property="creditCard" required="true" />
	...
	</binder>
		<transition on="proceed" to="reviewBooking">
...
</view-state>

Data Validation

Data validation is the process of checking constraints on object. In WebFlow data validation applies to view-state model objects, after they are bound to HTTP request parameters. Web Flow supports programatic valiation, and declarative validation using JSR-303 Bean Validation annotations.

JSR-303 Bean Validation

WebFlow supports JSR-303 Bean Validation, based on the same support provided by Spring MVC. To enable JSR-303 validation configure the <flow-builder-services< element should be set with the attribute validator with a reference to the Spring MVC's LocalValidatorFactoryBean.

<webflow:flow-registry flow-builder-services="flowBuilderServices" />

<webflow:flow-builder-services id="flowBuilderServices" validator="validator" />

<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />

Programmatic Validation

There are two approaches in WebFlow to perform model validation programatically. In the first approach, the validation logic is implemented in the model object class. In the second approach, a Validator class is implemented. Both approaches make available a ValidationContext to record error messages and access information about the current user.

Implementing Model Validation Methods

To implement validation logic packed with the model class, one or more methods with prefix validate*() should be defined. The method name suffix is the ID of the view-state for which the validation method is invoked. Whenever an event occurs in a view-state (postback lifecycle), the WebFlow executor invokes the respective validation method.

Any number of validation methods cab be defined in a model class. Typically, a flow edits a model object over a series of state-views. In that case, a validate method should be defined for each view-state where validation is needed.

The example below show a model class with a validation method:

public class Booking {
	private Date checkinDate;
	private Date checkoutDate;
	...
	
	public void validateEnterBookingDetails(ValidationContext context) {
		MessageContext messages = context.getMessageContext();
		if (checkinDate.before(today())) {
			messages.addMessage(new MessageBuilder().error().source("checkinDate").
			defaultText("Check in date must be a future date").build());
		} else if (!checkinDate.before(checkoutDate)) {
			messages.addMessage(new MessageBuilder().error().source("checkoutDate").
			defaultText("Check out date must be later than check in date").build());
	}
}

The above validation method is invoked in a enterBookingDetails view-state, if its model object is of type Booking. An example of such view-state is shown below:

<view-state id="enterBookingDetails" model="booking">
		<transition on="proceed" to="reviewBooking" />
</view-state>

Implementing a Validator

The second approach to programmatic validation way is to define a dedicate Validator class. The class does not need to implement any specific interface or extends specific class. The convention is that a class validating a model type named Booking should be named BookingValidator. Methods can have the same signature as the validation methods defined in the model class, except that the first parameter should have the type of the model class being validated.

A Validator class can also define a default validation method called validate() that is not associated with any specific view-state. If both a view-state specific and a default validation methods are available, both are called -- first the view-state specific then the default method.

Below, I show an example of a model Validator declared as Spring bean with annotation @Component.

@Component
public class BookingValidator {
	public void validateEnterBookingDetails(Booking booking, ValidationContext context) {
	MessageContext messages = context.getMessageContext();
	if (booking.getCheckinDate().before(today())) {
		messages.addMessage(new MessageBuilder().error().source("checkinDate").
		defaultText("Check in date must be a future date").build());
	} else if (!booking.getCheckinDate().before(booking.getCheckoutDate())) {
		messages.addMessage(new MessageBuilder().error().source("checkoutDate").
		defaultText("Check out date must be later than check in date").build());
	}
	public void validate(Booking booking, ValidationContext context) {
		//...
	}	
}

The defined method validateEnterBookingDetails() is invoked in a enterBookingDetails view-state with a model attribute of type Booking. validate() is invoked in all view-state of the flow with a model attribute of type Booking.

A Validator can also accept a Spring MVC Errors object, which is required for invoking existing Spring Validators.

ValidationContext

A ValidationContext allows you to obtain a MessageContext to record messages during validation. It also exposes information about the current user, such as the signaled userEvent and the current user's Principal identity. This information can be used to customize validation logic based on what button or link was activated in the UI, or who is authenticated. See the API Javadocs for ValidationContext for more information.

Messages and Internationalization

Spring WebFlow support an API to record text messages to be rendered in views and accessed from flows.

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.

Building Messages and MessageContext

A MessageContext the WebFlow abstraction to record messages. MessageContext is simple a collection of messages that can be setup in Java and be used in a flow or view.

A Message is characterized by a severity level (info, warning, and error), a source such as a field name, a code to resolve to locale-specific messages, and a default text. A convenient MessageBuilder class is available to simplify the construction of Message.

Below, I show an example of code snippet using a MessageBuilder for creating messages that are added to a MessageContext.

MessageContext context = ...
MessageBuilder builder = new MessageBuilder();
context.addMessage(builder.error()
	.source("name")
	.defaultText("Name must be specified")
	.build());
context.addMessage(builder.warn()
	.source("dateOfBirtday")
	.defaultText("dateOfBirtday should be in the past")
	.build());
context.addMessage(builder.info()
	.defaultText("Order confirmed")
	.build());

Internationalized Messages

For internationalized messages a code should also be specified that is has a key to search in ResourceBundle propery files.

MessageContext context = ...
MessageBuilder builder = new MessageBuilder();
context.addMessage(builder.error()
	.source("dateOfBirtday")
	.code("dateOfBirtday.notFuture")
	.build());

For internalized messages to be resolved a bean implementing the MessageSource interface should be setup the the application context. See the Spring MVC tutorial for details on configuring a MessageSource and background information on ResourceBundle and Locale.

In WebFlow message bundles specific to a flow should be packaged in the same directory as the flow definition file. The default bundle should be named messages.properties, and bundle specific to a Locale should be named with an appropriate sufix. For example, for the english language the property file should be named messages_en.properties.

Each property file should resolve (a sub-set of) the message codes saved in a MessageContext.

#messages.properties
dateOfBirtday.notFuture=dateOfBirtday should be in the past

Model Binding and Validation Messages

WebFlow automatically add messages to the MessageContext in several instances, such as when binding and/or validation error occur.

Implicit Variables

The implicit variable messageContext hold an object of type MessageContext. It provides a context for adding creating flow execution messages, including error and success messages. It can be used to pass to Java methods.

<evaluate expression="bookingValidator.validate(booking, messageContext)" />

A MessageContext is also available from method ValidationContext.getMessageContext(). A ValidationContext is injected in validation methods if the method has a parameter of type ValidationContext.

The implicit variable resourceBundle can also be used to get message resource from within a flow.

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

The resourceBundle variable is also available from within views:

<html>
...
	<p>${resourceBundle.orderConfirmation}"</p>
</html>			

Configuration Overview

Webflow configuration consists of two basic step. First, several infrastructure and view technology neutral beans are configured. This includes a registry for the flows, and a flow execution engine. Secondly, beans specific to the web framework or view tecnology in use should be registered. Spring WebFlow support both Spring MVC integration and JSF integration.

WebFlow Configuration XML Namespace

WebFlow basic configuration can be conviniently done using a Spring XML namespace. This namespace, spring-webflow-config, can be used along side with the beans namespace and/or mvc namespace. Below, I show how this namespace can be imported along side with the beans namespace.

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

<!--	<webflow:*> elements available -->

</beans>

Note that the XML namespace used for WebFlow configuration is different from the one used to author individual flows.

Webflow Common Configuration

Two beans are required in all webflow installation. This is a flow registry bean, and a flow executor bean. Details of parsing and processing flow definition files can also be optionally configured. Next two section discuss the configuration of these common infrastructure beans.

Webflow Techology-Specific Configuration

In adddition to common configuration beans, Spring WebFlow also required specific integration with a specificweb framework or view tecnology. For integration with Spring MVC and a HandlerMapping and a HandlerAdapter dedicated to map and invoke flows should be configured. JSF required other approach for integration. A separated section is dedicated to the issue of integration with Spring MVC, and another to integration with JSF.

FlowRegistry

The FlowRegistry is registry for flows available in the application. A single FlowRegistry can be used to register all the flows, althought its also possible to setup an hierarchy with multiple FlowRegistry. A FlowRegistry is a required dependency for other infrastructure beans, therefore its definition is mandatory.

The XML element <webflow:flow-registry> can be used to install FlowRegistry as a spring bean. The XML id attribute specifies the Spring bean ID for the FlowRegistry. Below, I show an example:

<webflow:flow-registry id="flowRegistry">
	<webflow:flow-location path="/WEB-INF/flows/booking/booking.xml" />
</webflow:flow-registry>

Flow Registration

Individual flows are be registered using the XML (sub-)element <webflow:flow-location>. The attribute path specifies the path location of the XML file with the flow specification. Each XML resource should defined a single resource. Views for the flow should be available in the same directory as the flow.

When left unspecified a flow is registered with the same identifier as is filename minus extention. A custom flow ID can also be made explict with XML attribute ID, as show below:

<webflow:flow-location path="/WEB-INF/flows/booking/booking.xml" id="bookHotel" />

Flow Base Location

The base-path attribute can also be used in <webflow:flow-registry> to define a base location for all flows in the application. All flow locations are then relative to defined base location.

<webflow:flow-registry id="flowRegistry" base-path="/WEB-INF">
	<webflow:flow-location path="/hotels/booking/booking.xml" />
</webflow:flow-registry>

The base path can be a resource path such as /WEB-INF/ or a location on the classpath like classpath:flows.

When a base-path is specified the ID assigned to flow includes only the part that is specified in the path attribute. In the example above, the flow ID will be hotels/booking.

Flow Registration Patterns

The XML element <webflow:flow-location-pattern> can also be used to register multiple flows. The attribute value specifies the resource location pattern:

<webflow:flow-location-pattern value="/WEB-INF/flows/**/*-flow.xml" />

The pattern above matches all XML files with filename suffix -flow.xml located in directory /WEB-INF/flows/ or any other directory under this directory at any arbitrary deep.

When using location patterns the ID assigned to flow is the directory where it is located (not including the base-path). For example:

<webflow:flow-registry id="flowRegistry" base-path="/WEB-INF">
	<webflow:flow-location-pattern value="/**/*-flow.xml" />
</webflow:flow-registry>

If two flows are located in /WEB-INF/user/registration and /WEB-INF/hotels/booking, then their ID are user/registration and hotels/booking, respectively.

Resource Packaging

Its considered a best practice to package each flow definition in a unique directory. Dependent resources such as JSP or JSF views, and property files for messages should also be package in the same directory as the flow definition. This prevents flows from having the same identifiers, and to have view names conflicts.

Flow Attributes

The XML element <flow-definition-attributes should be used to assign custom attributes to a registered flow:

<webflow:flow-location path="/WEB-INF/flows/booking/booking.xml">
	<webflow:flow-definition-attributes>
	<webflow:attribute name="caption" value="Books a hotel" />
	</webflow:flow-definition-attributes>
</webflow:flow-location>

FlowRegistry Hierarchies

FlowRegistry instances can be setup in a parent-child hierarchy. The parent attribute should be used to link two flow registries together in a hierarchy. When the child registry is queried, if it cannot find the requested flow it will delegate to its parent.

<!-- shared-config.xml -->
<webflow:flow-registry id="sharedFlowRegistry">
	<!-- Global flows shared by several applications -->
</webflow:flow-registry>

<!-- my-system-config.xml -->
<webflow:flow-registry id="flowRegistry" parent="sharedFlowRegistry">
	<webflow:flow-location path="/WEB-INF/flows/booking/booking.xml" />
</webflow:flow-registry>

FlowExecutor

A FlowExecutor is the bean responsible for the execution of all flows in the application. It should be declared with XML element <webflow:flow-executor>). The attribute flow-registry specifies the bean id for the FlowRegistry.

<webflow:flow-executor id="flowExecutor" flow-registry="flowRegistry"/>

A FlowExecutor is a dependency for FlowHandlerAdaptor used for integration with Spring MVC.

This remaning parts of this section explore the configuration options for <flow-executor>.

FlowExecution Listeners

Flow execution listeners are object that can get notification about flow life-cycle events. The interface FlowExecutionListener defines the methods and events that can be listen for. These events include: flow is about to start or has just started, flow is about to end or just ended, after or before a state is entered, and after or before a view is rendered.

Flow execution listeners are registered with a FlowExecutor. This is done by setting a single XML element <flow-execution-listeners> inside the <flow-executor></b>. Each individual <b>FlowExecutionListener</b> is registred with a <b><![CDATA[<listener element refering the bean implementing the FlowExecutionListener interface.

<webflow:flow-executor id="flowExecutor" flow-registry="flowRegistry">
	<webflow:flow-execution-listeners>
		<webflow:listener ref="securityListener"/>
		<webflow:listener ref="persistenceListener"/>
	</webflow:flow-execution-listeners>
</webflow:flow-executor>

It also possible to configure a listener to observe only a selected set of flows. This is done with attribute criteria . A comma separated list of flow names should be specified, corresponding to the flows that the listeners should get notifications from.

<webflow:listener ref="securityListener" criteria="securedFlow1,securedFlow2"/>

Table below shows the list of FlowExecutionListener shipped in with Spring WebFlow:

Listener Description
FlowExecutionListenerAdapter Abstract class to support implementations of FlowExecutionListener
JpaFlowExecutionListener listener to manage a JPA EntityManager
HibernateFlowExecutionListener listener to manage a Hibernate Session
SecurityFlowExecutionListener Spring Security integration listener
FlowFacesContextLifecycleListener JSF integration listener
FlowExecutionListener implementations shipped in with Spring WebFlow.

Persistence of FlowExecution

Use the flow-execution-repository element to tune flow execution persistence settings:

<webflow:flow-executor id="flowExecutor" flow-registry="flowRegistry">
	<webflow:flow-execution-repository max-executions="5" max-execution-snapshots="30" />
</webflow:flow-executor>

Tune the max-executions attribute to place a cap on the number of flow executions that can be created per user session. When the maximum number of executions is exceeded, the oldest execution is removed.

The max-executions attribute is per user session, i.e. it works across instances of any flow definition.

Tune the max-execution-snapshots attribute to place a cap on the number of history snapshots that can be taken per flow execution. To disable snapshotting, set this value to 0. To enable an unlimited number of snapshots, set this value to -1.

History snapshots enable browser back button support. When snapshotting is disabled pressing the browser back button will not work. It will result in using an execution key that points to a snapshot that has not be recorded.

FlowBuilder Services

Use the flow-builder-services attribute of <flow-registry> to customize the services and settings used to build flows in a flow-registry. If no flow-builder-services tag is specified, the default service implementations are used. When the tag is defined, you only need to reference the services you want to customize.

<webflow:flow-registry id="flowRegistry" flow-builder-services="flowBuilderServices">
...
</webflow:flow-registry>

<webflow:flow-builder-services id="flowBuilderServices" ... />

The configurable services are defined with the attributes conversion-service, expression-parser, and view-factory-creator. These services are configured by referencing custom beans you define. For example:

<webflow:flow-builder-services id="flowBuilderServices"
	conversion-service="conversionService"
	expression-parser="expressionParser"
	view-factory-creator="viewFactoryCreator" />

<bean id="conversionService" class="..." />
<bean id="expressionParser" class="..." />
<bean id="viewFactoryCreator" class="..." />

Type Conversion Service

When HTTP request parameters are used to populate a model object (data binding), type conversion is required to parse the paramater String-values to the type of the model object properties. Default type conversion is available for several Java types such as primitive types, numbers, enums, and java.util.Date. It is also possible to define and register a custom type converter, that extends or overrides the default converters.

The conversion-service attribute of flow-builder-services can be used to customize the default type conversion. The default conversion service is used throughout WebFlow, such as when invoking actions and assigning values to variables. A converter can be set on a per model object property.

Defining a Conversion Service

A type converter in Spring implement the interface org.springframework.binding.convert.converters.Converter.

A convertion service is specified with interface org.springframework.core.convert.ConverterRegistry A custom ConversionService is usually defined by extending the DefaultConversionService class, which in turn extends the GenericConversionService.

The constructor of a ConversionService should register converters for all the types that are supported.

public class ApplicationConversionService extends DefaultConversionService {	
	public ApplicationConversionService() {
		addDefaultConverters();
		addDefaultAliases();
		addConverter("customConverter", new CustomConverter());
	}
}

See the tutorial on Spring MVC to learn how to define custom converter and converter services.

Note: The ConversionService were introduced in Spring 3. Before this, the PropertyEditor abstraction was used for type conversion. Earlier Spring WebFlow used is own converstion abstraction, but since Spring 3 the same abstractions are used in Spring WebFlow and other modules such as Spring MVC.

Formatting and Conversion Services in Spring MVC

A Formatter is comparable to a Converter except that it is specialized to convert value to and from String values in a localized manner. A FormatterRegistry is the interface that defines a formatting service. The FormattingConversionService is the a support class for this interface.

In Spring MVC an instance of a FormattingConversionService is created automatically. It can also be configured by setting the XML the attribute conversion-service in element <mvc:annotation-driven/>.

To defined a custom FormattingConversionService the class FormattingConversionServiceFactoryBean can be overriden, or alternativelly an instance be create as a Spring bean that is condigured with properties to register a list of Formatter and/or Converter.

<beans ...>

	<mvc:annotation-driven conversion-service="applicationConversionService" />

	<bean id="applicationConversionService" class="somepackage.ApplicationConversionServiceFactoryBean">

</beans>

It also possible to reuse the conversion services configured in Spring MVC into WebFlow.

<webflow:flow-registry id="flowRegistry" flow-builder-services="flowBuilderServices" .../>

		
<webflow:flow-builder-services id="flowBuilderServices" conversion-service="defaultConversionService" .../>

		
<bean id="defaultConversionService"
	class="org.springframework.binding.convert.service.DefaultConversionService">
		<constructor-arg ref="applicationConversionSevice"/>
</bean>

Expression Parser

Use the expression-parser attribute to customize the ExpressionParser used by the Web Flow system. The default ExpressionParser uses the Unified EL if available on the classpath, otherwise OGNL is used.

View Factory Creator

Use the view-factory-creator attribute to customize the ViewFactoryCreator used by the Web Flow system. The default ViewFactoryCreator produces Spring MVC ViewFactories capable of rendering JSP, Velocity, and Freemarker views.

Development Setttings

The attribute development is use activate the development mode for the project. Development mode switches on hot-reloading of changes in XML flow definition files, including changes to dependent flow resources such as message bundles.

Spring MVC Integration

Spring WebFlow depends on Spring MVC for execution. In particular, the FlowExecutor takes the role of a flow Controller to where HTTP request are dispatched.

MVC Configuration

Spring MVC standard configuration should be used as a first step in setting up WebFlow. This is done by setting up the MVC DispatcherServlet in the application descriptor file /WEB-INF/web.xml.

<servlet>
	<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
	<init-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>/WEB-INF/application-config.xml</param-value>
	</init-param>
</servlet>
	
<servlet-mapping>
	<servlet-name>Spring MVC Dispatcher Servlet</servlet-name>
	<url-pattern>/*</url-pattern>
</servlet-mapping>

Mapping to Flows

The Spring MVC DispatcherServlet is used to map requests to flows and to deliver requests to be handled by a flow. This is configured in two steps. First, the request URLs should be mapped to flows. This is done by setting up a mapping strategy with bean FlowHandlerMapping , with a dependcy on the FlowRegistry.

<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerMapping">
	<property name="flowRegistry" ref="flowRegistry"/>
</bean>
Since we want other application controllers to be handleded by MVC, in addition to flows, we can use the XML element <mvc:annotation-driven /> for register the default HandlerMapping. The optional property order can be setup to specify the order of mappings between different HandlerMapping.
<mvc:annotation-driven />

<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerMapping">
	<property name="flowRegistry" ref="flowRegistry"/>
	<property name="order" value="0"/>
</bean>

Configuring the FlowHandlerMapping allows the DispatcherServlet to map application resource paths to flows in a flow registry. Flow are accessed by using their ID as URL location. If a flow is found with that id, that flow will handle the request. If no flow is found, the request is passed to the next configured HandlerMapping in the ordered chain. If a URL does not match a flow nor other handler the exception NoHandlerFound is thrown.

Dispatching to Flows

The second step in configuring Spring WebFlow is to setup a FlowHandlerAdapter strategy, with a dependency to the FlowExecutor. The FlowHandlerAdapter is responsible for the actual execution of the flow according the flow specification.

<bean class="org.springframework.webflow.mvc.servlet.FlowHandlerAdapter">
	<property name="flowExecutor" ref="flowExecutor" />
</bean>

When a valid flow mapping is found, the FlowHandlerAdapter figures out whether to start a new execution of that flow or resume an existing execution based on information present the HTTP request.

Custom FlowHandler

TODO

Persistence in Flows

Spring WebFlow allows for the persistence context of application objects to be bound to the duration of a flow. This means all operations inside a flow be executed in the scope of a single transaction. Spring WebFlow integrates with Spring transaction managament infrastructure. This means the same the infrastructure beans need to be setup for persistence in Spring WebFlow and in other application components, such as service or data-access objects. Thus persistence operation done by these components of will ocurr in the transactional context managed by the flow.

Flow Managed Persistence

To specify that a flow should manage a persistence context the XML element <persistence-context /> should be used inside the flow definition.

<flow xmlns="http://www.springframework.org/schema/webflow" ...>

	<persistence-context />

	<!-- flow definition -->
	
	<end-state id="done" commit="true"
		view="externalRedirect:contextRelative:account/show?id=#{account.id}" />
	
</flow>

Moreover, the end states of a flow should specify that the current transaction should be commited when the end state is reached. This is done with attribute commit="true" in the <end-state> element defining the state.

Managing JPA Transactions

The FlowExecutor should also be configured with a listener to interact with a persistence provider. For JPA this is the JpaFlowExecutionListener. The configuration for the JpaFlowExecutionListener include two constructor arguments with references to a EntityManager factory and to a transaction manager.

<webflow:flow-executor id="flowExecutor" flow-registry="flowRegistry">
	<webflow:flow-execution-listeners>
	<webflow:listener ref="jpaFlowExecutionListener" />
	</webflow:flow-execution-listeners>
</webflow:flow-executor>
	
<bean id="jpaFlowExecutionListener"	class="org.springframework.webflow.persistence.JpaFlowExecutionListener">
	<constructor-arg ref="entityManagerFactory" />
	<constructor-arg ref="transactionManager" />
</bean>

Managing Hibernate Transactions

Configuration for Hibernate is similar to JPA, this time using the listener HibernateFlowExecutionListener.

JPA Persistence with Spring

EntityManager Factory

JPA Transaction Manager

Hibernate Persistence with Spring

EntityManager Factory

Securing Flows

TODO

Testing Flows

TODO

JSF Integration

TODO

Resource Bundles

From within a view or a flow, you may access message resources using the resourceBundle EL variable:

Spring JavaScript

TODO

6 Comments

7/2/14

This is the best tutorial on Spring WebFlow I found on the net.
Specially in a topic that is not easy to find information about it.

I usually do not post comments..but you man are a genius!!

Thanks,
Tomas!
7/25/14

Agree with Iutomas , best tutorial by a genius . Hats Off !!!

8/11/14

Awesome tutorial on WebFlow! - I have been looking all over for a WebFlow tutorial. Can you post the code?

 

Thanks

Steve

9/1/14

Great tutorial!!, the best one about spring webflow so far. You did a great job. Thanks so much :)

9/12/14

Great one, can you provide an example program as well !!! Thank a ton!!

1/29/15

So good! Thank you!

Post Your Comment

Login (or Register)
Contribute Feedback