Spring Webflow Reference
Spring Webflow Reference
Version 2.0-m3
December 2007
Copyright 2004-2007 Keith Donald, Erwin Vervaet, Rossen Stoyanchev, Jeremy Grelle
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.
Table of Contents
Preface ................................................................................................................................................ 1. Introduction .................................................................................................................................. 1.1. Overview ............................................................................................................................. 1.2. Architecture overview .......................................................................................................... 1.3. Architectural layers .............................................................................................................. 1.4. Layer descriptions ................................................................................................................ 1.4.1. The Execution Core Layer (Bottom Layer) .................................................................. 1.4.2. The Execution Engine Layer ...................................................................................... 1.4.3. The Test Layer .......................................................................................................... 1.4.4. The Executor Layer ................................................................................................... 1.4.5. The System Configuration Layer (Top Layer) ............................................................. 1.5. Support ................................................................................................................................ 2. Flow definition .............................................................................................................................. 2.1. Introduction ......................................................................................................................... 2.2. FlowDefinition ..................................................................................................................... 2.2.1. XML-based Flow template ......................................................................................... 2.2.2. Java Flow API example ............................................................................................. 2.3. StateDefinition ..................................................................................................................... 2.4. Transitionable State .............................................................................................................. 2.4.1. XML-based state template .......................................................................................... 2.4.2. Java state API example .............................................................................................. 2.5. TransitionDefinition ............................................................................................................. 2.5.1. Transition XML template ........................................................................................... 2.5.2. Transition Java API example ...................................................................................... 2.5.3. Action transition execution criteria ............................................................................. 2.5.4. Dynamic transitions ................................................................................................... 2.5.5. Global transitions ...................................................................................................... 2.5.6. Transition executing exception handlers ...................................................................... 2.5.7. Custom exception handlers ......................................................................................... 2.6. Concrete state types .............................................................................................................. 2.6.1. ViewState ................................................................................................................. 2.6.2. ActionState ............................................................................................................... 2.6.3. DecisionState ............................................................................................................ 2.6.4. SubflowState ............................................................................................................. 2.6.5. EndState ................................................................................................................... 3. Flow execution .............................................................................................................................. 3.1. Introduction ......................................................................................................................... 3.2. FlowExecution ..................................................................................................................... 3.2.1. Flow execution creation ............................................................................................. 3.2.2. Flow execution startup ............................................................................................... 3.2.3. Flow execution resume .............................................................................................. 3.2.4. Flow execution lifecycle ............................................................................................ 3.2.5. Flow execution properties .......................................................................................... 3.2.6. Flow execution impl creation ..................................................................................... 3.3. Flow execution context ......................................................................................................... 3.4. Flow execution scopes .......................................................................................................... 3.5. Flow execution testing .......................................................................................................... 3.5.1. Flow execution test example ...................................................................................... Spring Web Flow Version 2.0-m3 1 1 1 2 2 4 4 4 5 5 7 7 8 9 9 10 10 11 11 12 12 12 12 12 14 15 16 17 21 30 31 34 38 38 38 38 39 39 39 40 41 43 44 44 ii
Spring Web Flow 4. Flow execution repositories ........................................................................................................... 4.1. Introduction ......................................................................................................................... 4.2. Repository architecture overview .......................................................................................... 4.3. Flow execution identity ........................................................................................................ 4.3.1. Conversation identifier ............................................................................................... 4.3.2. Continuation identifier ............................................................................................... 4.3.3. Flow execution key .................................................................................................... 4.4. Conversation ending ............................................................................................................. 4.5. Flow execution repository implementations ........................................................................... 4.5.1. Simple flow execution repository ................................................................................ 4.5.2. Continuation flow execution repository ....................................................................... 4.5.3. Client continuation flow execution repository .............................................................. 5. Flow executors .............................................................................................................................. 5.1. Introduction ......................................................................................................................... 5.2. FlowExecutor ...................................................................................................................... 5.2.1. FlowExecutorImpl ..................................................................................................... 5.2.2. A typical flow executor configuration with Spring 2.0 ................................................. 5.2.3. A flow executor using a simple execution repository .................................................... 5.2.4. A flow executor using a client-side continuation-based execution repository ................. 5.2.5. A flow executor using a single key execution repository .............................................. 5.2.6. A flow executor setting custom conversation management attributes ............................. 5.2.7. A flow executor setting system execution attributes ..................................................... 5.2.8. A flow executor setting custom execution listeners ...................................................... 6. Spring Faces .................................................................................................................................. 6.1. Introduction ......................................................................................................................... 6.2. Web Flow Java Server Faces (JSF) integration ....................................................................... 6.2.1. Adding Spring Web Flow extensions to a JSF application. ........................................... 6.2.2. Configuring the Web Flow system .............................................................................. 6.2.3. Configuring the FacesServlet ...................................................................................... 6.2.4. Launching a flow execution - normal HTML anchor .................................................... 6.2.5. Launching a flow execution - JSF command link component ........................................ 6.2.6. Flow definitions in a JSF environment ........................................................................ 6.2.7. Resuming a flow execution - form bound to flow execution variables ........................... 6.2.8. Spring Faces JSF Components .................................................................................... 6.2.9. Spring Web Flow JSF Integration Samples .................................................................. 7. Practical Use of Spring Web Flow ................................................................................................. 7.1. Sample applications .............................................................................................................. 7.2. Running the Web Flow sample applications ........................................................................... 7.2.1. Building from the Command Line .............................................................................. 7.2.2. Importing Projects into Eclipse ................................................................................... 7.2.3. Deploying projects inside Eclipse using Eclipse Web Tools (WTP) .............................. 7.2.4. Other IDE's ............................................................................................................... 7.3. Sellitem Example ................................................................................................................. 7.3.1. Overview .................................................................................................................. 7.3.2. Web.xml ................................................................................................................... 7.3.3. Services-config.xml ................................................................................................... 7.3.4. Spring MVC Context ................................................................................................. 7.3.5. Sellitem-beans.xml .................................................................................................... 7.3.6. Sellitem-flow Flow Definition .................................................................................... 7.3.7. Sellitem-simple-flow Flow Definition ......................................................................... 7.3.8. Sellitem-conversation-scope-flow Flow Definition ...................................................... 7.4. Sellitem-JSF Example ..........................................................................................................
47 47 48 48 49 49 49 49 49 50 50 51 51 51 52 52 52 52 53 53 53 55 55 55 55 57 57 57 57 58 58 60 61 61 61 62 62 62 62 63 63 64 65 66 67 68 68 69
iii
Spring Web Flow 7.4.1. Overview .................................................................................................................. 7.4.2. Web.xml ................................................................................................................... 7.4.3. Web Flow JSF Setup in faces-config.xml .................................................................... 7.4.4. Web Flow System Setup in webflow-config.xml ......................................................... 7.4.5. Launching the sellitem-flow ....................................................................................... 7.5. Shippingrate Example ........................................................................................................... 7.5.1. Overview .................................................................................................................. 7.5.2. Web.xml ................................................................................................................... 7.5.3. Spring MVC Context ................................................................................................. 7.5.4. Ajax Requests ........................................................................................................... 7.5.5. getRate Web Flow ..................................................................................................... 7.6. Numberguess Example ......................................................................................................... 7.6.1. Overview .................................................................................................................. 7.6.2. Web.xml ................................................................................................................... 7.6.3. Spring MVC Context ................................................................................................. 7.6.4. Higherlower Flow ...................................................................................................... 7.6.5. Mastermind Flow ...................................................................................................... 7.7. Flowlauncher Example ......................................................................................................... 7.7.1. Overview .................................................................................................................. 7.7.2. Web.xml ................................................................................................................... 7.7.3. Spring MVC Context ................................................................................................. 7.7.4. Sample A Web Flow .................................................................................................. 7.7.5. Sample B Web Flow .................................................................................................. 7.8. Itemlist Example .................................................................................................................. 7.8.1. Overview .................................................................................................................. 7.8.2. Web.xml ................................................................................................................... 7.8.3. Spring MVC Context ................................................................................................. 7.8.4. Itemlist Web Flow ..................................................................................................... 7.8.5. Itemlist-alternate Web Flow ....................................................................................... 7.9. Fileupload Example .............................................................................................................. 7.9.1. Overview .................................................................................................................. 7.9.2. Web.xml ................................................................................................................... 7.9.3. Spring MVC Context ................................................................................................. 7.9.4. Fileupload Web Flow ................................................................................................. 7.10. Birthdate Example .............................................................................................................. 7.10.1. Overview ................................................................................................................ 7.10.2. Web.xml ................................................................................................................. 7.10.3. Struts Configuration ................................................................................................. 7.10.4. Birthdate Web Flow ................................................................................................. 7.10.5. Birthdate-alternate Web Flow ................................................................................... 7.11. Phonebook-Portlet Example ................................................................................................ 7.11.1. Overview ................................................................................................................ 7.11.2. Portal/Portlet Related Software Used in the Sample ................................................... 7.11.3. Portlet.xml Configuration ......................................................................................... 7.11.4. Web.xml Configuration ............................................................................................ 7.11.5. Portlet MVC Configuration ...................................................................................... 69 69 70 70 71 71 71 72 72 73 74 75 75 75 76 76 77 78 78 78 78 79 80 80 80 80 81 82 83 83 83 83 84 84 85 85 86 86 87 89 89 89 90 91 91 92
iv
Preface
Many web applications consist of a mix of free browsing, where the user is allowed to navigate a web site as they please, and controlled navigations where the user is guided through a series of steps towards completion of a business goal. Consider the typical shopping cart application. While a user is shopping, she is freely browsing available products, adding her favorites to her cart while skipping over others. This is a good "free browsing" use case. However, when the user decides to checkout, a controlled workflow begins--the checkout process. Such a process represents a single user conversation that takes place over a series of steps, and navigation from step-to-step is controlled. The entire process represents an discrete application transaction that must complete exactly once or not at all. Consider some other good examples of "controlled navigations": applying for a loan, paying your taxes on-line, booking a trip reservation, registering an account, or updating a warehouse inventory. Traditional approaches to modeling and enforcing such controlled navigations or "flows" fall flat, and fail to express the Flow as a first class concept. Spring Web Flow (SWF) is a component of the Spring Framework's web stack focused on solving this problem in a productive and powerful manner.
Chapter 1. Introduction
1.1. Overview
Spring Web Flow (SWF) is a component of the Spring Framework's web stack focused on the definition and execution of UI flow within a web application. The system allows you to capture a logical flow of your web application as a self-contained module that can be reused in different situations. Such a flow guides a single user through the implementation of a business task, and represents a single user conversation. Flows often execute across HTTP requests, have state, exhibit transactional characteristics, and may be dynamic and/or long-running in nature. Spring Web Flow exists at a higher level of abstraction, integrating as a self-contained flow engine within base frameworks such as Struts, Spring MVC, Portlet MVC, and JSF. SWF provides you the capability to capture your application's UI flow explicitly in a declarative, portable, and manageable fashion. SWF is a powerful controller framework based on a finite-state machine, fully addressing the "C" in MVC.
Most users will embed SWF as a component within a larger web application development framework, as SWF is a focused controller technology that expects a calling system to care for request mapping and response rendering. In this case, those users will depend on a thin integration piece for their environment. For example, those executing flows within a Servlet environment might use the Spring MVC integration to care for dispatching requests to SWF and rendering responses for SWF view selections. Spring Web Flow ships convenient Spring MVC, Struts Classic, and JSF integration out of the box.
Note
Spring Web Flow, like Spring, is a layered framework, packaged in a manner that allows teams to use the parts they need and nothing else. For example, one team might use Spring Web Flow in a Servlet environment with Spring MVC and thus require the Spring MVC integration. Another team might use SWF in a Portlet environment, and thus require the Portlet MVC integration. Another team might mix and match. A major benefit of SWF is that it allows you to define reusable, self-contained controller modules that can execute in any environment.
Introduction
Spring Web Flow is a layered framework. A diagram of Spring Web Flow's layered architecture is shown below:
Foundational, generic types usable core, by all other subsystems. Contains core.collection the default expression parser (OGNL-based) and core collection types (AttributeMap and company). Spring Web Flow Version 2.0-m3
Introduction Subsystem name Util Flow Definition Description Packages Subsystem interfaces None FlowDefinition Internal dependencies None Core
Low level utilities used by all other util parts of the system. Central abstractions for modeling definition flow definitions. These abstractions include FlowDefinition, StateDefinition, and TransitionDefinition that form the domain language for describing flows.
Flow Definition Support for working with registries definition.registry FlowDefinitionRegistry, Core, Flow Registry of flow definitions. Flow FlowDefinitionLocator Definition definitions eligible for execution are typically stored in a registry that provides lookup services. External Context Provides normalized access to a context, client environment that has called context.servlet, into Spring Web Flow. context.portlet ExternalContext Core, context.servlet requires Servlet API 2.4, context.portlet requires Portlet API 1.0 in addition to Servlet API 2.4 Util,
Conversation
Manages the creation and cleanup conversation, ConversationManager Core, of conversational state. Used by conversation.impl External the execution repository system to Context begin new user conversations and track execution state. Stable runtime abstractions that execution, FlowExecution define the flow definition execution.support, execution model. For executing execution.factory flow definitions and representing execution state.
Flow Execution
Flow Execution For persisting paused flow execution.repository, FlowExecutionRepository Core, Util, Flow Repository executions beyond a single request execution.repository.support, Definition, into the server. execution.repository.continuation Conversation, Flow Execution Action Reusable action implementations. action, action.portlet None Core, Util, Flow Definition, External Context, Flow Execution
Introduction
The implementation of the flow engine, execution engine based on a finite engine.support, state machine. engine.impl
Flow Definition Abstractions used at engine.builder, FlowBuilder Builder configuration-time for building and engine.builder.xml assembling Flow definitions executable by this engine implementation. Flows are typically defined in externalized resources such as XML files.
Engine Artifact Support for unit testing test Unit Test implementations such as Actions Support in isolation. Flow Execution Support for testing Flow test.execution Test Support Executions out-of-container.
None
Introduction
Table 1.4. Executor Subsystems Subsystem name Core Spring MVC Description Packages Subsystem interfaces Internal dependencies None Core, Spring Web MVC 2.0 Core, Struts 1.1
Generic flow executor abstractions executor, FlowExecutor and support. executor.support The integration between Spring executor.mvc Web Flow and the Spring MVC framework. The integration between Spring executor.struts Web Flow and the Struts Classic framework. None
Struts
None
For configuring Spring Web Flow config using Spring 1.x and 2.x.
spring-webflow-config-1.0
Note
As described above, some subsystem packages are optional depending on your use of the subsystem. For example, use of Spring Web Flow in a Servlet environment entails use of the ExternalContext context.servlet package which requires the Servlet API to be in the classpath. In this case, the context.portlet package is not used and the Portlet API is not required. For the exact list of dependencies, as well as supported product usage configurations, see the Ivy dependency manager descriptor located within the SWF distribution.
1.5. Support
Spring Web Flow 1.x is supported on Spring Framework 1.2.7 or > for the 1.x series, and supported on 2.0 or > for the 2.x series. Spring Web Flow Version 2.0-m3 5
Introduction
XML-based flow building requires Xerces 2 or JDK 5.0 (for XSD support). Our active community support forum is located at http://forum.springframework.org.
2.2. FlowDefinition
A flow definition is a instance of org.springframework.webflow.definition.FlowDefinition. This is the central domain artifact representing the definition of a user dialog or task. A flow definition consists of a set of one or more states, where each state defines a step in the flow that when entered executes a behavior. What behavior is executed is a function of the state's type and configuration. The outcome of a state's execution, called an event, is used by the flow to drive a state transition. Exactly one of a flow's states is the startState that defines the starting point of the flow. Optionally, a flow can have one or more end states defining the ending points of the flow. An example definition of a simple flow to carry out a search process is shown graphically below:
org.springframework.webflow.engine.Flow.
implementation in Spring Web Flow Its configurable properties are summarized below:
is
Flow definition
Table 2.1. Flow properties Property name id Description Cardinality Default value
The identifier of the flow definition, 1 typically unique to all other flows of the application. Additional custom attributes about the 0..* flow. The steps of the flow. The starting point of the flow. 1..* 1 Empty None
The set of flow instance variables to create 0..* each time an execution of the flow is started. The service responsible for mapping flow 0..1 input provided by the client each time an execution of the flow is started. The list of actions to execute each time an 0..* execution of the flow is started. The list of actions to execute each time an 0..* execution of the flow ends. The service responsible for mapping flow 0..1 output to expose to the client each time an execution of the flow ends. The set of transitions shared by all states 0..* of the flow. An ordered set of handlers to be applied 0..* when an exception is thrown within a state of the flow.
inputMapper
Null
globalTransitions exceptionHandlers
Empty Empty
Below is a high level example of how these properties can be configured in XML form or directly in Java code.
Flow definition
</start-actions> <start-state idref="yourStartingStateId"/> <-- your state definitions go here --> <global-transitions> ... </global-transitions> <end-actions> ... </end-actions> <output-mapper .../> <exception-handler .../> </flow>
A Flow is typically built by a FlowBuilder rather than assembled by hand. The flow building subsystem is contained within the org.springframework.webflow.engine.builder package. The XML Flow Builder and spring-webflow.xsd schema are located within the org.springframework.webflow.engine.builder.xml package. The XML-based format is currently the most popular way to define flows, though Groovy-based and Java-based formats are emerging.
2.3. StateDefinition
A StateDefinition defines the behavior for a step of a FlowDefinition. The base implementation class for all Flow state types is org.springframework.webflow.engine.State. This abstract class defines common properties applicable to all state types, which include: Table 2.2. State properties Property name id owner attributes Description Cardinality Default value
The id of the state, unique to its containing 1 flow definition. The owning flow definition. 1 None
Flow definition Property name entryActions exceptionHandlers Description Cardinality Default value Empty Empty
The list of actions to execute each time the 0..* state is entered. An ordered set of handlers to be invoked 0..* when an exception is thrown within the state.
The list of actions to execute each time 0..* this state is exited.
Below is a mock flow definition snippet showing how properties may be configured for a TransitionableState in XML and in Java code:
10
Flow definition
A State is typically constructed by a FlowArtifactFactory, used by a FlowBuilder during flow assembly. The flow building subsystem is contained within the org.springframework.webflow.engine.builder package.
2.5. TransitionDefinition
A transition takes a flow from one state to another, defining a path through the flow. This is modeled using a TransitionDefinition. Recall that all TransitionableStates have a set of one or more transitions, each defining a path to another state in the flow (or a recursive path back to the same state). When a transitionable state is entered, it executes a behavior. For example, a transitionable state called "Display Form" may display a form to the user and wait for user input. The outcome of the state's execution, called an event, is used to drive execution of one of the state's transitions. For example, the user may press the form submit button which signals a submit event that matches the transition to the "Process Submit" state. This event-driven transition execution process is shown graphically below:
Transition execution The transition definition implementation is defined by an Its properties are summarized below: instance of
org.springframework.webflow.engine.Transition.
Table 2.4. Transition properties Property name attributes matchingCriteria Description Additional transition. attributes describing Cardinality the 0..* Default value None Always matches
The strategy that determines if the 1 transition matches on an event occurrence. Spring Web Flow Version 2.0-m3
11
Flow definition Property name executionCriteria Description Cardinality Default value Always allowed
The strategy that determines if the 1 transition, once matched, is allowed to execute. The strategy that resolves the target state 1 of the transition. Most transitions always resolve to the same target state. This strategy allows for dynamic resolution.
targetStateResolver
Below is a high-level example of how a Transition can be configured in XML form or directly in Java code.
This will transition the flow to the state resolved by evaluating the flowScope.lastViewStateId expression.
12
Flow definition
As outlined, one or more transitions are added to all TransitionableState types, attached at the state-level. Optionally, transitions may also be added at the flow-level where they are shared by all states. These shared transitions are called global transitions. When an event is signaled in a transitionable state, the state will first try and match one of its own transitions. If there is no match at the state level, the set of global transitions will be tested. If there still is no match, a NoMatchingTransitionException will be thrown. Global transitions are useful in situations where many states of the flow share the same transitional criteria. For example, consider a navigation menu that displays alongside each view of a flow. Logic to process navigation menu events is needed by all view states. This is the problem global transitions are designed to solve. 2.5.5.1. Global transitions - XML example The following example shows transitions defined at the state level, as well as global transitions defined at the flow level.
<?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"> <start-state idref="state1"/> <xxx-state id="state1"> <transition on="localEvent1" to="state2"/> </xxx-state> <xxx-state id="state2"> <transition on="localEvent1" to="state1"/> </xxx-state> <global-transitions> <transition on="globalEvent1" to="state1"/> <transition on="globalEvent2" to="state2"/> </global-transitions> </flow>
In this mock example, state1 defines one transition and also inherits the two others defined within the global-transitions element. Any other states defined within this flow would also inherit those global transitions. This example is shown graphically below:
13
Flow definition
Global transitions
In this example, state1 defines one transition and an exception handler which executes a transition to state3 if a MyBusinessException is thrown within the state. The handled exception will be put into flash scope under the key stateException, where it will be automatically exposed to the next view (typically an error view). 2.5.6.2. Flow exception handling - XML example The following example illustrates a flow-level transition executing exception handler:
14
Flow definition
<?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"> <start-state idref="state1"/> <xxx-state id="state1"> <transition on="event1" to="state2"/> </xxx-state> <xxx-state id="state2"/> <global-transitions> <transition on-exception="example.MyBusinessException" to="state3"/> </global-transitions> ... </flow>
In this example, the exception handler is defined as a global transition. This reads "any time a MyBusinessException occurs during flow execution, transition the flow to state3". Exception handlers attached at the state level take precedence over those defined at the flow level. This is illustrated by the following example:
<?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"> <start-state idref="state1"/> <xxx-state id="state1"> <transition on="event1" to="state2"/> <transition on-exception="example.MyBusinessException" to="state4"/> </xxx-state> <xxx-state id="state2"/> <global-transitions> <transition on-exception="example.MyBusinessException" to="state3"/> </global-transitions> ... </flow>
In this example, if MyBusinessException is thrown in state1 the flow will transition to state4. For any other state, the flow will transition to state3.
15
Flow definition
<?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"> <start-state idref="state1"/> <xxx-state id="state1"> <transition on="event1" to="state2"/> <exception-handler bean="myCustomStateExceptionHandler"/> </xxx-state> <xxx-state id="state2"/> <global-transitions> <exception-handler bean="myCustomFlowExceptionHandler"/> </global-transitions> <import resource="flow-beans.xml"/> </flow>
2.5.7.1. flow-beans.xml
<bean id="myCustomStateExceptionHandler" class="example.CustomFlowExecutionExceptionHandler"/> <bean id="myCustomFlowExceptionHandler" class="example.AnotherCustomFlowExecutionExceptionHandler"/>
Custom
exception
handlers
must
the
built-in concrete state types, all contained within the package. These states execute common controller behaviors
including: 1. 2. 3. 4. 5. allowing the user to participate in a flow (ViewState) executing business application code (ActionState) making a flow routing decision (DecisionState) spawning another flow as a subflow (SubflowState) terminating a flow (EndState)
Each of these state types, with the exception of EndState, is transitionable. This hierarchy is illustrated below:
16
Flow definition
FlowDefinition class diagram As you will see, with these five basic state types you can develop rich controller modules.
2.6.1. ViewState
When entered, a view state allows the user (or other external client) to participate in a flow. This participation process goes as follows: 1. 2. 3. The entered view state renters a view to issue a response to the caller. The flow execution 'pauses' in this state, and control is returned to the calling system. After some 'think time', the user signals an input event to resume the flow execution from the 'paused' point.
Spring Web Flow gives you full control over the view selection process and, on resume, how a view state responds to a user input event. The properties of a org.springframework.webflow.engine.ViewState are summarized below: Table 2.5. ViewState properties Property name viewFactory Description Cardinality Default value Null
The strategy that makes the view selection 0..1 when this state is entered. Spring Web Flow Version 2.0-m3
17
Flow definition Property name renderActions Description Cardinality Default value Empty
The list of actions to execute each time a 0..* renderable view selection is made. Allows for execution of pre-render logic.
The org.springframework.webflow.execution.ViewFactory type is an interface, encapsulating knowledge about a particular view rendering technology. with the flow. Concrete subtypes exist for each of the supported view technologies. These subtypes are summarized below: Table 2.6. Concrete ViewFactory types Type JsfViewFactory MvcViewFactory Description Requests the rendering of Java Server Faces (JSF) view. Requests the rendering of a Spring MVC view.
2.6.1.1. ViewState class diagram The class diagram below shows the ViewState and the associated types used to carry out the view selection process (TODO - needs updating for 2.0):
ViewState class diagram 2.6.1.2. ViewState XML - application view selection The following example shows a view-state definition in XML that makes an application view selection when entered, selecting the searchForm view for display and, on resume, responding to two possible user input events (submit and cancel) in different ways:
<?xml version="1.0" encoding="UTF-8"?> <flow xmlns="http://www.springframework.org/schema/webflow"
18
Flow definition
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"> <start-state idref="displaySearchForm"/> <view-state id="displaySearchForm" view="searchForm"> <transition on="submit" to="processFormSubmission"/> <transition on="cancel" to="processCancellation"/> </view-state> ... </flow>
View name expressions may also be specified for the view attribute to achieve runtime view name calculation. For example, view="${requestScope.calculatedViewName}". 2.6.1.3. ViewState API - application view selection The following example shows the equivalent view state definition using the FlowBuilder API:
public class SearchFlowBuilder extends AbstractFlowBuilder { public void buildStates() { addViewState("displaySearchForm", "searchForm", new Transition[] { transition(on("submit"), to("processFormSubmission")), transition(on("cancel"), to("processFormCancellation")) } ); ... } }
2.6.1.4. ViewState XML - flow execution redirect The following example illustrates a view-state definition in XML that makes an flow execution redirect selection when entered, redirecting to the yourList view for display.
<?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"> <start-state idref="displayList"/> <view-state id="displayList" view="redirect:yourList"> <transition on="add" to="addListItem"/> </view-state> ... </flow>
This example is called a flow execution redirect because the application view selected is rendered only after a redirect to the flow execution. The redirect request is sent to a URL that refreshes the flow execution paused in the displayList view state. Refresh then triggers the rendering of the yourList application view on the next request into the server. Spring Web Flow Version 2.0-m3 19
Flow definition
2.6.1.6. ViewState XML - null view The following example illustrates a view-state definition in XML that makes a null view selection when entered. This causes no additional response to be issued.
<?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"> <start-state idref="displayPdf"/> <view-state id="displayPdf"> <render-actions> <action bean="pdfWriter" method="write"/> </render-actions> </view-state> ... </flow>
2.6.1.7. FlowDefinitionRedirect and ExternalRedirect The FlowDefinitionRedirect and ExternalRedirect are not generally used by a view state. They are typically used by an end state, either to start a new independent flow or redirect to an arbitrary external URL. Examples are provided in the discussion of the end state. 2.6.1.8. ViewState XML - form state behavior The following example illustrates a view-state definition in XML that encapsulates typical "form state" behavior.
20
Flow definition
Consider the requirements of typical input forms. Most forms require pre-render or setup logic to execute before the form is displayed. For example, such logic might load the backing form object from the database, install formatters for formatting form field values, and pull in supporting form data needed to populate drop-down menus. In addition, most forms require post-back or submission logic to execute when the form is submitted. This logic typically involves binding form input to the backing form object and performing type conversion and data validation. This "form state" behavior of form setup, display, and post-back is handled elegantly in Spring Web Flow by the capabilities of the view-state construct. See below:
<?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"> <start-state idref="displayForm"/> <view-state id="displayForm" view="form"> <render-actions> <action bean="formAction" method="setupForm"/> <action bean="formAction" method="loadFormReferenceData"/> </render-actions> <transition on="submit" to="saveForm"> <action bean="formAction" method="bindAndValidate"/> </transition> </view-state> ... </flow>
This reads "when this flow starts enter the displayForm state to execute the setupForm and loadFormReferenceData methods before rendering the form view. On submit, transition to the saveForm state if the bindAndValidate method executes successfully."
2.6.2. ActionState
When entered, an action state executes business application code, then responds to the result of that execution by deciding what state in the flow to enter next. Specifically: 1. The an ordered list of one or more org.springframework.webflow.execution.Action instances. This Action interface is the central abstraction that encapsulates the execution of a logical unit of application code. The state determines if the outcome of the first action's execution matches a transition. If there is a match, the transition is executed. If there is no match, the next action in the list is executed. This process continues until a transition is matched or the list of actions is exhausted. entered action state executes
2.
Spring Web Flow gives you full control over implementing your own actions and configuring when they should be invoked within the lifecycle of a flow. The system can also automatically adapt methods on your existing application objects (POJOs) to the Action interface in a non-invasive manner. This means in many cases you can implement your flows without needing to develop custom glue code to bind SWF to your service layer operations.
21
Flow definition
The properties of a org.springframework.webflow.engine.ActionState are summarized below: Table 2.7. ActionState properties Property name actions Description Cardinality Default value
The ordered list of actions to execute 1..* when the state is entered.
2.6.2.1. Action execution points As outlined, the ActionState is the dedicated state type for invoking one or more actions and responding to their result to drive a state transition. There are also other points within the lifecycle of a flow where a chain of actions can be executed. At all of these points, the only requirement is that these actions implement the central org.springframework.webflow.execution.Action interface. Table 2.8. Other points in a Flow where an Action can be executed, and how those points can be defined in a XML-based Flow definition. Point on flow start on state entry on transition on state exit before rendering on flow end Description Each time a new flow session starts. Each time a state enters. XML Configuration Element A flow's <start-actions/> A state's <entry-actions/>
Each time a state transition is matched but A transition <action/> before it is executed. Each time a transitionable state exits. A transitionable state's <exit-actions/>
view Each time a renderable view selection is A view state's <render-actions/> made. Each time a flow session terminates. A flow's <end-actions/>
Note
The other points above where actions may be executed do not allow you to execute a state transition in response to the action result event. If you need such flow control you must execute the action from within an action state. 2.6.2.2. Action attributes An Action may be annotated with attributes by wrapping the Action in a decorator, an instance of org.springframework.webflow.engine.AnnotatedAction. These attributes may provide descriptive characteristics, or may be used to affect the action's execution in a specific usage context. Support for setting several common attributes is provided for convenience. These include: Table 2.9. Common Action attributes
22
Flow definition Attribute name caption description name Description A short description about the action, suitable for display as a tool-tip. A long description about the action, suitable for display in a text box. The name of the action, used to qualify the action's result event. For example, an Action named placeOrder that returns success would be assigned a result event identified by placeOrder.success. This allows you to distinguish logical execution outcomes by action, useful when invoking multiple actions as part of a chain. The name of the target method on the Action instance to invoke to carry out execution. This facilitates multiple action methods per Action instance, supported by the org.springframework.webflow.action.MultiAction.
method
2.6.2.3. ActionState class diagram The class diagram below shows the ActionState and the associated types used to carry out the action execution process:
ActionState class diagram 2.6.2.4. ActionState XML - simple action execution The following example constructs an ActionState definition from XML that executes a single action when entered and then responds to its result:
<?xml version="1.0" encoding="UTF-8"?> <flow xmlns="http://www.springframework.org/schema/webflow"
23
Flow definition
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"> <start-state idref="executeSearch"/> <action-state id="executeSearch"> <action bean="searchAction"/> <transition on="success" to="displayResults"/> </action-state> ... </flow>
This state definition reads "when the executeSearch state is entered, execute the searchAction. On successful execution, transition to the displayResults state." The binding between the searchAction id and an Action implementation is made at Flow build time by querying a service locator, typically a Spring BeanFactory. For example:
<beans> <bean id="searchAction" class="example.webflow.SearchAction"/> </beans>
... binds the searchAction action identifier to a singleton instance of the example.webflow.SearchAction class. A simple SearchAction implementation might look like this:
public class SearchAction implements Action { private SearchService searchService; public SearchAction(SearchService searchService) { this.searchService = searchService; } public Event execute(RequestContext context) { // lookup the search criteria in "flow scope" SearchCriteria criteria = (SearchCriteria)context.getFlowScope().get("criteria"); // execute the search Collection results = searchService.executeSearch(criteria); // set the results in "request scope" context.getRequestScope().put("results", results); // return "success" return new Event(this, "success"); } }
2.6.2.5. ActionState API - standard action The following example constructs the equivalent action state definition using the FlowBuilder API:
public class SearchFlowBuilder extends AbstractFlowBuilder { public void buildStates() { ... addActionState("executeSearch", action("searchAction"), transition(on("success"), to("displayResults"))); ...
24
Flow definition
} }
2.6.2.6. ActionState XML - multi action The next example constructs an ActionState definition from XML that executes a single action method on a org.springframework.webflow.action.MultiAction and then responds to its result:
<?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"> <start-state idref="executeSearch"/> <action-state id="executeSearch"> <action bean="searchAction" method="executeSearch"/> <transition on="success" to="displayResults"/> </action-state> ... </flow>
This state definition reads "when the executeSearch state is entered, call the executeSearch method on the searchFlowAction. On successful execution, transition to the displayResults state." A SearchAction implementation containing multiple action methods might look like this:
public class SearchAction extends MultiAction { private SearchService searchService; public SearchAction(SearchService searchService) { this.searchService = searchService; } public Event executeSearch(RequestContext context) { // lookup the search criteria in "flow scope" SearchCriteria criteria = (SearchCriteria)context.getFlowScope().get("criteria"); // execute the search Collection results = searchService.executeSearch(criteria); // set the results in "request scope" context.getRequestScope().put("results", results); // return "success" return success(); } public Event someOtherRelatedActionMethod(RequestContext context) { ... return success(); } public Event yetAnotherRelatedActionMethod(RequestContext context) { ... return success(); } }
As you can see, this allows you to define one to many action methods per Action class. With this approach,
25
Flow definition there are two requirements: 1. Your Action class must extend from org.springframework.webflow.MultiAction, or another class that extends from MultiAction. The multi action cares for the action method dispatch that is based on the value of the method property. Each action method must conform to the signature illustrated above:
public Event
2.
${method}(RequestContext) { ... }
MultiActions are useful for centralizing command logic on a per-flow definition basis, as a flow definition typically carries out execution of a single application use case. 2.6.2.7. ActionState API - multi action The following example constructs the equivalent action state definition using the FlowBuilder API:
public class SearchFlowBuilder extends AbstractFlowBuilder { public void buildStates() { ... addActionState("executeSearch", invoke("executeSearch", action("searchAction")), transition(on("success"), to("displayResults"))); ... } }
2.6.2.8. ActionState XML - bean action The next example constructs an ActionState definition from XML that executes a single method on a Plain Old Java Object (POJO) and then responds to the result:
<?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"> <start-state idref="executeSearch"/> <action-state id="executeSearch"> <bean-action bean="searchService" method="executeSearch"> <method-arguments> <argument expression="${flowScope.criteria}"/> </method-arguments> <method-result name="results"/> </bean-action> <transition on="success" to="displayResults"/> </action-state> ... </flow>
This state definition reads "when the executeSearch state is entered, call the executeSearch method on the searchService passing it the object indexed by name criteria in flowScope. On successful execution, expose the method return value in the default scope (request) under the name results and transition to the displayResults state." In this example the referenced bean searchService would be your application object, typically a transactional business service. Such a service implementation must have defined the the Collection Spring Web Flow Version 2.0-m3 26
Flow definition
executeSearch(SearchCriteria)
With this approach there are no requirements on the signature of the methods that carry out action execution, nor is there any requirement to extend from a Web Flow specific base class. Basically, you are not required to write a custom Action implementation at all--you simply instruct Spring Web Flow to call your business methods directly. The need for custom "glue code" to bind your web-tier to your middle-tier is eliminated. Spring Web Flow achieves this by automatically adapting the method on your existing application object to the Action interface and caring for exposing any return value in the correct scope. This adaption process is shown graphically below:
Bean->Action adapter 2.6.2.9. ActionState XML - decision bean action The following example constructs an ActionState from XML that executes an action whose execution result forms the basis for the transition decision:
<?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"> ... <action-state id="shippingRequired"> <bean-action bean="shippingService" method="isShippingRequired"> <method-arguments> <argument expression="${flowScope.purchase}"/> </method-arguments>
27
Flow definition
</bean-action> <transition on="yes" to="enterShippingDetails"/> <transition on="no" to="placeOrder"/> </action-state> ... </flow>
This state definition reads "if the isShippingRequired method on the shippingService returns true, transition to the enterShippingDetails state, otherwise transition to the placeOrder state."
Note
Note how the boolean return value of the isShippingRequired method is converted to the event identifiers yes or no. This conversion process is handled by the action adapter responsible for adapting the method on your application object to the org.springframework.webflow.execution.Action interface. By default, this adapter applies a number of rules for creating a result event from a method return value. These conversion rules are: Table 2.10. Default method return value to Event conversion rules Return type boolean java.lang.Enum org.springframework.core.enum.LabeledEnum org.springframework.webflow.execution.Event java.lang.String any other type Event identifier yes or no this.name() this.getLabel() this.getId() the string success
You may customize these default conversion policies by setting a custom ResultEventFactory instance on the bean invoking action performing the adaption. Consult the JavaDoc documentation for more details on how to do this. 2.6.2.10. ActionState XML - decision bean action with enum return value The following example constructs an ActionState from XML that executes a action that invokes a method on an application object that returns a java.lang.Enum:
<?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"> ... <action-state id="shippingRequired"> <bean-action bean="shippingService" method="calculateShippingMethod"/>
28
Flow definition
<method-arguments> <argument expression="${flowScope.order}"/> </method-arguments> </bean-action> <transition on="BASIC" to="enterBasicShippingDetails"/> <transition on="EXPRESS" to="enterExpressShippingDetails"/> <transition on="NONE" to="placeOrder"/> </action-state> ... </flow>
This state definition reads "if the calculateShippingMethod method on the shippingService returns BASIC for the current order, transition to the enterBasicShippingDetails state. If the return value is EXPRESS, transition to the enterExpressShippingDetails state. If the return value is NONE, transition to the placeOrder state." 2.6.2.11. ActionState XML - evaluate action The following example constructs an ActionState from XML that executes a action that evaluates an expression against the flow request context and exposes the evaluation result:
<?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">
<action-state id="getNextInterviewQuestion"> <evaluate-action expression="flowScope.interview.nextQuestion()"/> <evaluation-result name="question"/> </evaluate-action> <transition on="success" to="displayQuestion"/> </action-state> </flow>
This state definition reads "evaluate the flowScope.interview.nextQuestion() expression, and expose the result under name question in the default scope." The traversable This example nextQuestion method on the interview business object in flow scope.
org.springframework.webflow.execution.RequestContext.
expression
can
evaluate
any
object
from expression
2.6.2.12. ActionState XML - set action The next example constructs an ActionState from XML that executes an action on a success transition that sets an attribute in "flash scope":
<?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"> <view-state id="selectFile" view="fileUploadForm"> <transition on="submit" to="uploadFile"/>
29
Flow definition
</view-state> <action-state id="uploadFile"> <action bean="uploadAction" method="uploadFile"/> <transition on="success" to="selectFile"> <set attribute="fileUploaded" scope="flash" value="true"/> </transition> </action-state> </flow>
This flow definition reads "display the fileUploadForm. On form submit, invoke the uploadFile method on the uploadAction. On success, allow the user to select another file to upload. Report that the last file was uploaded successfully by setting the fileUploaded attribute in flash scope to true.
Note
Flash scoped attributes are preserved until the next user event is signaled into the flow execution. In this example, this means the fileUploaded attribute is preserved across a redirect to the selectFile view state and any subsequent browser refreshes. Only when the submit event is signaled will the flash scope be cleared. 2.6.2.13. When to use which kind of action? Simple action, Multi action, bean action, evaluate action, set? When to use one or the other? Table 2.11. Action implementation usage guidelines Action type Simple (extends AbstractAction) Usage scenario You have a specialized behavior that stands on its own; for creating lightweight stubs or mocks for testing purposes. To group related command logic together. Particularly useful for when there are multiple related behaviors called by a flow. When the logical behavior maps well to a method call on a service layer bean. When there is no "special" or exotic glue code required. When you need to invoke a bean in flow scope or evaluate any other flow expression. When you need to set an attribute in flow or other scope during the course of flow execution.
MultiAction
Bean action
EvaluateAction
SetAction
2.6.3. DecisionState
When entered, a decision state makes a flow routing decision. This process consists of: 1. Evaluating one or more boolean expressions against the executing flow to decide what state to transition to next.
30
Flow definition
Table 2.12. DecisionState properties Property name Description Cardinality Default value
transitions The transitions that are evaluated on an 1..* (inherited from event occurrence that forms the basis for TransitionableState) the decision.
2.6.3.1. DecisionState XML - expression evaluation The following example constructs a DecisionState from XML that evalutes a boolean expression to determine what transition to execute:
<?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"> ... <decision-state id="shippingRequired"> <if test="${flowScope.order.needsShipping}" then="enterShippingDetails" else="placeOrder"/> </decision-state> ... </flow>
This state definition reads "if the needsShipping property on the order object in flow scope is true, transition to the enterShippingDetails state, otherwise transition to the placeOrder state."
Note
Caution: flow definitions should not be vehicles for business logic. In this case the decision made was controller logic, reasoning on a pre-calculated value to decide what step of the flow to transition to next. That is the kind of logic that should be in a flow definition. In contrast, having the state itself embed the business rule defining how shipping status is calculated is a misuse. Instead, push such a calculation into business application code where it belongs and instruct the flow to invoke that code using an action.
2.6.4. SubflowState
When entered, a subflow state spawns another flow as a subflow. Recall that a flow is a reusable, self-contained controller module. The ability for one flow to call another flow gives you the ability to compose independent modules together to create complex controller workflows. Any flow can be used as subflow by any other flow, and there is a well-defined contract in play. Specifically:
1. 2.
A Flow is an instance of org.springframework.webflow.engine.Flow. A newly launched flow can be passed input attributes, which it may choose to map into its own local
31
Flow definition
scope. 3. An ending flow can return output attributes. If the ended flow was launched as a subflow, the resuming parent flow may choose to map these output attributes into its own scope.
It is helpful to think of the process of calling a flow like calling a Java method. Flows can be passed input arguments, and can produce return values just like methods can. Flows are more powerful because they are potentially long-running, as they can span more than one request into the server. The properties of a org.springframework.webflow.engine.SubflowState are summarized below: Table 2.13. SubflowState properties Property name subflow attributeMapper Description Cardinality Default value
The definition of the flow to be spawned 1 as a subflow. The strategy responsible for mapping 0..* input attributes to the subflow and mapping output attributes from the subflow. Null
When a SubflowState is entered, the following behavior occurs: 1. The state first messages its
attributeMapper,
org.springframework.webflow.engine.FlowAttributeMapper,
pass to the subflow. 2. The subflow is spawned, passing the input attributes. When this happens, the parent flow suspends itself in the subflow state until the subflow ends. When the subflow ends, a result event is returned describing the flow outcome that occurred. The parent flow resumes back in the subflow state. The resumed subflow state messages its attributeMapper to map any output attributes returned by the subflow into flow scope, if necessary. Finally, the resumed subflow state responds to the result event returned by the ended subflow by matching and executing a state transition.
3.
4.
5.
The constructs used in spawning a flow as a subflow are shown graphically below:
32
Flow definition
SubflowState class diagram 2.6.4.1. SubflowState XML - with input attribute The following example constructs an SubflowState from XML that spawns a shipping subflow:
<?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"> ... <subflow-state id="enterShippingDetails" flow="shipping"> <attribute-mapper> <input-mapper> <mapping source="flowScope.order.shipping" target="shipping"/> </input-mapper> </attribute-mapper> <transition on="finish" to="placeOrder"/> </subflow-state> ... </flow>
This subflow state definition reads "spawn the shipping flow, passing it the value of the shipping property on the order object in flow scope. When the shipping flow ends, respond to the finish result event by transitioning to the placeOrder state."
Note
The inner structure and behavior of the shipping flow is fully encapsulated within its own flow definition. A flow calling another flow as a subflow can pass that flow input and capture its output, but it cannot see inside it. Flows are black boxes. Because any flow can be used as a subflow, it can be reused in other contexts without change.
33
Flow definition 2.6.4.2. SubflowState API - input attributes The following illustrates the equivalent example using the FlowBuilder API:
public class OrderFlowBuilder extends AbstractFlowBuilder { public void buildStates() { ... addSubflowState("enterShippingDetails", flow("shipping"), shippingMapper(), transition(on("finish"), to("placeOrder"))); ... } protected FlowAttributeMapper shippingMapper() { DefaultFlowAttributeMapper mapper = new DefaultFlowAttributeMapper(); mapper.addInputMapping(mapping().source("flowScope.order.shipping").target("shipping").value()); return mapper; } }
2.6.4.3. Flow input mapping - input contract Within a flow definition, input attributes can be obtained by configuring an input-mapper. Any input attributes must be explictly mapped. The list of permitted input attributes defines the input contract for the flow.
Tip
If you think of calling a Flow as analogous to calling a Java method, it is helpful to think of input-attributes as analogous to method arguments.
<?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"> <input-mapper> <input-attribute name="shipping"/> </input-mapper> ... </flow>
This short-form input mapper declaration reads "when a new execution of this flow starts, map the provided shipping input attribute into flowScope under the name shipping."
Note
Had this input mapping not been defined the shipping attribute made available as input to this flow by a calling parent flow or external client would have been ignored.
2.6.5. EndState
When entered, an end state terminates a flow. A EndState represents exactly one logical flow outcome; for example, "finish", or "cancel". If the ended flow was acting as a top-level or root flow, the entire flow execution ends and cannot be resumed.
34
Flow definition In this case, the end state is responsible for issuing a final response (for example, a confirmation page, or a redirect request to another flow or an external URL). If the ended flow was acting as a subflow, the spawned subflow session ends and the calling parent flow resumes by responding to the end result returned. In this case, the responsibility for any final response falls on the parent flow. Once a flow ends, any attributes in flow scope go out of scope immediately and become eligible for garbage collection. As outlined, an end state entered as part of a root flow messages its finalResponseAction to make a ending view selection. Typically this is a redirect-based response type, allowing for redirect after flow completion. An end state entered as part of a subflow is not responsible for a view selection; this responsibility falls on the calling flow. 2.6.5.1. EndState result events When a EndState is entered it terminates a flow and, if used as subflow, returns a result event the parent flow uses to drive a state transition from the calling subflow state. It is the end state's responsibility to create this result event which is the basis for communicating the logical flow outcome to callers. By default, an EndState creates a result event with an identifier that matches the identifier of the end-state itself. For example, an end state with id finish returns a result event with id finish. Also, any attributes in flow scope that have been explicitly mapped as output attributes are returned as result event attributes. This allows you to return data along with the logical flow outcome. Spring Web Flow gives you full control over the ending view selection strategy, as well as what flow attributes should be exposed as output on a per EndState basis. These configurable properties are summarized below: 2.6.5.2. EndState Properties Table 2.14. EndState properties Property name Description Cardinality Default value Null
finalResponseAction The strategy that makes the ending 0..1 response when this state is entered and the flow is a root flow. outputMapper The service responsible for exposing flow 0..1 output attributes, making those attributes eligible for output mapping by a calling flow.
None
2.6.5.3. EndState XML - redirect to flow after completion The following example constructs an EndState from XML that terminates a shipping subflow and requests a redirect response to another flow:
<?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">
35
Flow definition
This end state definition reads "terminate the order flow, and redirect to a new execution of the searchFlow". 2.6.5.4. EndState XML - redirect after flow completion The following example constructs an EndState from XML that terminates a shipping subflow and requests a redirect response to an external URL:
<?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"> ... <end-state id="finish" view="externalRedirect:/orders/${flowScope.order.id}"/> </flow>
This end state definition reads "terminate the order flow, and redirect to the URL returned by evaluating the /orders/${flowScope.order.id} expression." This is an example of the familiar redirect after post pattern where after transaction completion a redirect is issued allowing the result of the transaction to be viewed (in this case using a REST-style URL). 2.6.5.5. EndState XML - flow output attribute The following example constructs an EndState from XML that terminates a shipping subflow:
<?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"> ... <end-state id="finish"> <output-mapper> <output-attribute name="shipping"/> </output-mapper> </end-state> </flow>
This end state definition reads "terminate the shipping flow, and expose the shipping property in flow scope as an output attribute with name shipping." 2.6.5.6. EndState API - flow output attribute
36
Flow definition
The following illustrates the equivalent example using the FlowBuilder API:
public class ShippingFlowBuilder extends AbstractFlowBuilder { public void buildStates() { ... addEndState("finish", new DefaultAttributeMapper().add( mapping().source("flowScope.shipping").target("shipping").value() ); } }
Since this end-state does not make a view selection, it is expected this flow will be always used as a subflow. When this flow ends, the calling parent flow is expected to respond to the finish result, and may choose to map the shipping output attribute into its own scope. 2.6.5.7. SubflowState XML - mapping an output attribute The next example shows how a subflow-state can respond to the ending result of a subflow, and map subflow output attributes into its own scope:
<?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"> ... <subflow-state id="enterShippingDetails" flow="shipping"> <attribute-mapper> <output-mapper> <output-attribute name="shipping"/> </output-mapper> </attribute-mapper> <transition on="finish" to="placeOrder"/> </subflow-state> ... </flow>
This subflow state definition reads "spawn the shipping flow as a subflow. When the shipping flow ends, map the shipping output attribute into flow scope under the name shipping, then respond to the finish result event by transitioning to the placeOrder state."
Note
Had this output mapping not been defined, the shipping attribute made available as output to this flow by the ending subflow would have been ignored.
37
3.2. FlowExecution
A org.springframework.webflow.execution.FlowExecution is a runtime instantiation of a flow definition. Given a single FlowDefinition, any number of independent flow executions may be created. These executions are typically created by a FlowExecutionFactory. A flow execution carries out the execution of program instructions defined within its definition in response to user events. It may be helpful to think of a flow definition as analagous to a Java Class, and a flow execution as analagous to an object instance of that Class. Signaling an execution event can be considered analagous to sending an object a message.
Once created, a new flow execution is initially inactive, waiting to be started. Once started, a flow execution becomes active by entering its startState. From there, it continues executing until it enters a state where user input is required to continue or it terminates.
When a flow execution reaches a state where input is required to continue, it is said to have paused, where it waits in that state for user input to be provided. User input is provided by signaling an event that resumes the flow execution by communicating what user action was taken. Attributes of the signal event request form the basis for user input. The flow execution resumes by consuming the event.
38
Flow execution
Once a flow execution has resumed, it continues executing until it again enters a state where more input is needed or it terminates. Once a flow execution has terminated it becomes inactive and cannot be resumed.
Flow execution
The
execution implementation is org.springframework.webflow.engine.impl.FlowExecutionImpl, typically created by a FlowExecutionImplFactory (a FlowExecutionFactory implementation). The configurable properties of this flow execution implementation are summarized below: Table 3.1. Flow Execution properties Property name definition listeners attributes Description The flow definition to be executed. Cardinality 1 Empty Empty Default value
Spring
Web
Flow
flow
The set of observers observing the 0..* lifecycle of this flow execution. Global system attributes that can be used 0..* to affect flow execution behavior
The configurable constructs related to flow execution are shown graphically below:
Flow execution
40
Flow execution
A flag indicating if the flow execution is 1 active. An inactive flow execution has either ended or has never been started. The definition of the flow execution. The 1 flow definition serves as the blueprint for the program. It may be helpful to think of a flow definition as like a Class and a flow execution as like an instance of that Class. This method may always be safely called. The active flow session, tracking the flow 1 that is currently executing and what state it is in. The active session can change over the life of the flow execution because a flow can spawn another flow as a subflow. This property can only be queried while the flow execution is active. A data map that forms the basis for 1 "conversation scope". Arbitrary attributes placed in this map will be retained for the life of the flow execution and correspond to the length of the logical conversation. This map is shared by all flow sessions.
definition
activeSession
conversationScope
As a flow execution is manipulated by clients its contextual state changes. Consider how contextual state is effected when the following events occur: Table 3.3. An ordered set of events and their effects on flow execution context Flow Execution Event created started Active? false true Value of the activeSession property Throws an IllegalStateException A FlowSession whose definition is the top-level flow definition and whose state is the definition's start state. A FlowSession whose definition is the top-level Spring Web Flow Version 2.0-m3 41
state entered
true
Flow execution Flow Execution Event Active? Value of the activeSession property flow definition and whose state is the newly entered state. subflow spawned true A FlowSession whose definition is the subflow definition and whose state is the subflow's start state. A FlowSession whose definition is back to the top-level flow definition and whose state is the resuming state. Throws an IllegalStateException
subflow ended
true
ended
false
As you can see, the activeSession of a flow execution changes when a subflow is spawned. Each flow execution maintains a stack of flow sessions, where each flow session represents a spawned instance of a flow definition. When a flow execution starts, the session stack initially consists of one (1) entry, an instance dubbed the root session. When a subflow is spawned, the stack increases to two (2) entries. When the subflow ends, the stack decreases back to one (1) entry. The active session is always the session at the top of the stack. The contextual properties associated with a FlowSession are summarized below: Table 3.4. Flow Session properties Property name definition state status scope Description Cardinality Default value
The definition of the flow the session is an 1 instance of. The current state of the session. 1
A status indicator describing what the 1 session is currently doing. A data map that forms the basis for flow 1 scope. Arbitrary attributes placed in this map will be retained for the scope of the flow session. This map is local to the session. A data map that forms the basis for flash 1 scope. Attributes placed in this map will be retained until the next external user event is signaled in the session.
flashMap
The following graphic illustrates an example flow execution context and flow session stack:
42
Flow execution
Flow execution context In this illustration, a flow execution has been created for the Book Flight flow. The execution is currently active and the activeSession indicates it is in the Display Seating Chart state of the Assign Seats flow, which was spawned as a subflow from the Enter Seat Assignments state.
Note
Note how the active session status is paused, indicating the flow execution is currently waiting for user input to be provided to continue. In this case, it is expected the user will choose a seat for their flight.
Flow execution Scope type name Management Semantics eligible for garbage collection when the flow session ends. flow conversation Eligible for garbage collection when the flow session ends. Eligible for garbage collection when the root session of the governing flow execution (logical conversation) ends.
1.
Your own implementations of definitional artifacts used by a flow such as actions, attribute mappers, and exception handlers should be unit tested in isolation. Spring Web Flow ships convenient stubs to assist with this, for instance MockRequestContext. The execution of a flow should be tested as part of a system integration test. Such a test should exercise all possible paths of the flow, asserting that the flow responds to events as expected.
2.
Note
A flow execution integration test typically selects mock or stub implementations of application services called by the flow, though it may also exercise production implementations. Both are useful, supported system test configurations.
44
Flow execution
Phonebook Search Flow - State Diagram The corresponding XML-based flow definition implementation:
<?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"> <start-state idref="enterCriteria"/> <view-state id="enterCriteria" view="searchCriteria"> <render-actions> <action bean="formAction" method="setupForm"/> </render-actions> <transition on="search" to="displayResults"> <action bean="formAction" method="bindAndValidate"/> </transition> </view-state> <view-state id="displayResults" view="searchResults"> <render-actions> <bean-action bean="phonebook" method="search"> <method-arguments> <argument expression="flowScope.searchCriteria"/> </method-arguments> <method-result name="results"/> </bean-action> </render-actions> <transition on="newSearch" to="enterCriteria"/> <transition on="select" to="browseDetails"/> </view-state> <subflow-state id="browseDetails" flow="detail-flow"> <attribute-mapper> <input-mapper> <mapping source="requestParameters.id" target="id" from="string" to="long"/> </input-mapper> </attribute-mapper> <transition on="finish" to="displayResults"/> </subflow-state> </flow>
45
Flow execution
Above you see a flow with three (3) states that execute these behaviors, respectively:
1.
The first state enterCriteria displays a search criteria form so the user can enter who he or she wishes to search for. On form submit and successful data binding and validation, the search is executed. After search execution a results view is displayed. From the results view, the user may select a result they wish to browse additional details on or they may request a new search. On select, the "detail" flow is spawned and when it finishes the search is re-executed and it's results redisplayed.
2.
3.
From this behavior narrative the following assertable test scenarios can be extracted: 1. That when a flow execution starts, it enters the enterCriteria state and makes a searchCriteria view selection containing a form object to be used as the basis for form field population. That on submit with valid input, the search is executed and a searchResults view selection is made. That on submit with invalid input, the searchCriteria view is reselected. That on newSearch, the searchCriteria view is selected. That on select, the detail flow is spawned and passed the id of the selected result as expected.
2. 3. 4. 5.
To assist with writing these assertions, Spring Web Flow ships with JUnit-based flow execution test support within the org.springframwork.webflow.test package. These base test classes are indicated below: Table 3.6. Flow execution test support hierarchy Class name AbstractFlowExecutionTests AbstractExternalizedFlowExecutionTests AbstractXmlFlowExecutionTests Description The most generic base class for flow execution tests. The base class for flow execution tests whose flow is defined within an externalized resource, such as a file. The base class for flow execution tests whose flow is defined within an externalized XML resource.
46
1.
When a flow execution reaches a ViewState it is said to have paused, where it waits in that state for user input to be provided so it can continue. After pausing, the ViewSelection returned is used to issue a response to the user that provides a vehicle for collecting the required user input. User input is provided by signaling an event that resumes the flow execution in the paused view state. The input event communicates what user action was taken.
2.
Each time an active flow execution is paused, it is saved out to a repository. When the next request comes in for that flow execution, it is restored from the repository, resumed, and continued. This process continues until the flow execution reaches an end state, at which time it is removed from the repository. This process is demonstrated over the next two graphics:
47
48
The first part of a flow execution's persistent identity is a unique conversation identifier. This serves as an index into the logical conversation between the browser and the server that has just started.
Note
It is important to understand that use of this repository consistently prevents duplicate submission when using the back button. If you attempt to go back and resubmit, the continuation id stored in your browser history will not match the current continuation id needed to access the flow execution Spring Web Flow Version 2.0-m3 49
Note
This repository implementation should generally be used when you do not have to support browser navigational button use; for example, when you lock down the browser and require that all navigation events to be routed through Spring Web Flow.
Note
It is important to understand that use of this repository allows resubmission when using the back button. If you attempt to go back and resubmit while the conversation is active, the continuation id stored in your browser history will match the continuation id of a previous flow execution in the repository. Access to that flow execution representing the state of the conversation at that point in time will be granted. Like the simple implementation, this repository implementation provides support for conversation invalidation after completion where once a logical conversation completes (by one of its FlowExecutions reaching an end state), the entire conversation is invalidated. This prevents the possibility of resubmission after completion. This repository is more elaborate than the default repository, offering more power (by enabling multiple continuations to exist per conversation), but incurring more storage overhead. This repository implementation should be considered when you do have to support browser navigational button use. This implementation is the default.
This is achieved by encoding a serialized flow execution directly into the flow execution continuation key that is sent in the response. When asked to load a flow execution by its key on a subsequent request, this repository decodes and deserializes the flow execution, restoring it to the state it was in when it was serialized.
Note
This repository implementation does not currently support conversation invalidation after completion, as this capability requires tracking active conversations using some form of centralized storage, like a database table.
Note
Storing state (a flow execution continuation) on the client entails a certain security risk that should be evaluated. Furthermore, it puts practical constraints on the size of the flow execution. Spring Web Flow Version 2.0-m3 50
5.2. FlowExecutor
is the central facade interface external systems use to drive the execution of flows. This facade acts as a simple, convenient service entry-point into the Spring Web Flow system that is reusable across environments.
org.springframework.webflow.executor.FlowExecutor
Execution typically consists of either: 1. 2. Launching (start) a new execution of a flow definition. Resuming a paused flow execution by signaling an event against its current state.
The ExternalContext provides normalized access to properties of an external system that has called into Spring Web Flow. This context allows access to environment-specific request parameters as well as externally-managed request, session, and application-level attributes. implementations exist for each of the environments Spring Web Flow supports. If a flow artifact such as an Action needs to access native constructs of the calling environment it can downcast a context to its specific implementation. The need for such downcasting is considered a corner case.
ExternalContext
5.2.1. FlowExecutorImpl
The default executor implementation is org.springframework.webflow.executor.FlowExecutorImpl. It allows for configuration of a FlowDefinitionLocator responsible for loading the flow definitions to execute, as well as the FlowExecutionRepository strategy responsible for persisting flow executions that remain active beyond a single request into the server. The configurable FlowExecutorImpl properties are shown below: Table 5.1. FlowExecutorImpl properties
51
The service for loading flow definitions to be 1 executed, typically a FlowDefinitionRegistry The factory for creating new flow executions. 1
The repository for saving and loading persistent 1 (paused) flow executions
This instructs Spring to create a flow executor that can execute all XML-based flow definitions contained within the /WEB-INF/flows directory. The default flow execution repository, continuation, is used.
This executor is configured with a simple repository that manages execution state in the user session.
This executor is configured with a continuation-based repository that serializes continuation state to the client using no server-side state.
52
Flow executors
This executor is configured with a simple repository that assigns a single flow execution key per conversation. The key, once assigned, never changes for the duration of the conversation.
<web:flow-executor id="flowExecutor" registry-ref="flowRegistry"> <web:flow-repository type="continuation" max-conversations="5" max-continuations="30" conversation-manag </web:flow-executor> <bean id="conversationManager" class="example.MyCustomConversationalStateManager"/>
This executor is configured with a continuation repository configured with custom settings for: 1. 2. The maximum number of active conversations per user session (5) The maximum number of restorable flow execution snapshots (continuations) per conversation (30)
3. Where conversational state will be stored (via a custom conversationManager) The flow:repository child element is the more flexible form for configuring the flow execution repository. Use it or the convenient repository-type attribute, not both.
This executor is configured to set two flow execution system attributes alwaysRedirectOnPause=false and foo=bar.
Note
The alwaysRedirectOnPause attribute determines if a flow execution redirect occurs automatically each time an execution pauses (automated POST+REDIRECT+GET behavior). Setting this attribute to false will disable the default 'true' behavior.
53
Flow executors
54
55
Spring Faces
<url-pattern>/spring/*</url-pattern> </servlet-mapping>
The application context bootstrapped by the Spring Web Servlet should contain the Web Flow system configuration. The example webflow-config.xml below shows a typical Web Flow configuration for a JSF environment:
<?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:web="http://www.springframework.org/schema/webflow-config" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/webflow-config http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.0.xsd"> <!-- Imports the "application-layer" definining business logic and data access services --> <import resource="application-layer-config.xml"/> <web:flow-executor id="flowExecutor" flow-registry="flowRegistry"> <web:flow-execution-listeners> <web:listener ref="jpaFlowExecutionListener" criteria="*"/> </web:flow-execution-listeners> </web:flow-executor> <web:flow-registry id="flowRegistry" flow-builder-services="flowBuilderServices"> <web:flow-location path="flow/main/main.xml" /> <web:flow-location path="flow/booking/booking.xml" /> <web:flow-builder class="org.springframework.faces.ui.resource.ResourcesFlowBuilder" /> </web:flow-registry>
<bean id="flowBuilderServices" class="org.springframework.webflow.engine.builder.support.FlowBuilderServ <property name="expressionParser"> <bean class="org.springframework.webflow.core.expression.el.WebFlowELExpressionParser"> <constructor-arg > <bean class="org.jboss.el.ExpressionFactoryImpl"/> </constructor-arg> </bean> </property> <property name="viewFactoryCreator"> <bean class="org.springframework.faces.webflow.JsfViewFactoryCreator"/> </property> </bean> </beans>
A bean named flowExecutor must be configured and linked with a flow definition registry that contains the flows eligible for execution. Note the flowExecutor bean name is significant, as that is bean name the Web Flow JSF extensions will expect. Any flow executor property such as the flow execution repository type is configurable here, consistent with the other environments Spring Web Flow supports. The flowRegistry bean definition shows the registration of two XML based flow definitions, as well as a special java-based FlowBuilder that installs a special flow for serving the javascript and CSS resources needed by the Spring Faces custom JSF components. The flowBuilderServices provides a number of JSF-specific services to the flowRegistry, including the WebFlowELExpressionParser that allows Web Flow to use the Unified EL for parsing expressions in flow definitions.
56
Spring Faces
This configuration also allows for the mixing of legacy pure JSF request handling with the request handling of Spring Web Flow for easier page-by-page migration.
This link would launch the "main" flow, assuming /spring/* has been mapped to the SpringWebServlet defined within web.xml.
57
Spring Faces
<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-1.0.xsd"> <var name="myBean" class="example.ManagedBeanImpl" scope="conversation" /> <start-state idref="displayView" /> <view-state id="displayView" view="myview.jsp"> <transition on="submit" to="prepareNextView"/> </view-state> <action-state id="prepareNextView" > <bean-action bean="myService" method="loadMyModel"> <method-arguments> <argument expression="#{myBean.foo}"/> </method-arguments> </bean-action> <transition on="success" to="displayNextView"/> </action-state> <view-state id="displayNextView" view="mynextview.jsp" /> </flow>
A primary benefit of using JSF is it is a rich UI component framework, and UI components have both data and behavior. As JSF components typically handle data binding and field-level validation behaviors, the actual flow definition logic is often simpler and more focused as a result. An important difference to note in the above example is the difference in using EL expressions versus Web Flow's traditional OGNL expressions. When using the ELExpressionParser, the chain of configured resolvers will automatically resolve an expression against the correct scope, so the "conversationScope" identifier is optional in the expression when referencing "myBean". Views selected by view states are specified using paths relative to the current flow definition. In the above example, it is expected that myview.jsp and mynextview.jsp are both located in the same directory as the flow definition.
As shown above, there is nothing Spring Web Flow specific here. The flow execution key is automatically tracked by a special UI component in the view root, so there is no need to track it manually. Action outcomes are automatically mapped to Spring Web Flow event identifiers signaled against the current state.
58
Spring Faces
Spring Faces provides some lightweight JSF components that act in an "advisor" role to provide rich client-side validation capabilities to standard inputText components. These can be used in place of server-side JSF validators to provide immediate validation feedback to the end user without the overhead of another fine-grained call to the server. The default implementation of these components use the Dojo javascript library to provide this validation behavior. Dojo was chosen due to their increased attention to accessibility concerns compared to other javascript frameworks. An alternate implementation based on the Ext library is also provided. Though Ext does not address accessibility issues, it can still be attractive for use in internal corporate intranet style applications. 6.2.8.1. Spring Faces Component Configuration The Spring Faces components are currently provided as Facelets tags. In order to utilize them, the following namespace declaration must be added to the header of a Facelets view template:
xmlns:sf="http://www.springframework.org/tags/faces"
6.2.8.2. Spring Faces Resource Loading Spring Faces requires the installation of a special flow for loading javascript and CSS resources, as shown in the configuration example. This special stateless flow serves up resources corresponding to URLs such as "/spring/resources/dojo/dojo.js". This flow searches for the corresponding resource as follows: 1. 2. For convenience, the external javascript libraries that the Spring Faces components depend on are made available in seperate jar files, and will be automatically loaded by the components when needed using the proper resource URLs. Since the resource loading mechanism checks in the web app classpath first it is possible to, for example, override the provided resources with a custom build of the Dojo or Ext library that is optimized for the particular application. 6.2.8.3. Using The Spring Faces Client Side Validation Components Spring Faces provides three different client-side validator components:
1. 2.
<sf:clientTextValidator> - Provides validation with customizable error messages for text fields. <sf:clientNumberValidator> - Provides validation and input filtering with customizable error messages for numeric fields. <sf:clientDateValidator> - Provides validation and a rich popup date picker control with customizable error messages for date fields. <sf:validateAllOnClick> - When wrapped around a UICommand component such as <h:commandButton> or <h:commandLink> fires all client-side validators when the UICommand component is clicked and prevents the form from being submitted if any of the validations fail.
3.
4.
The validator components must be wrapped around an <h:inputText> component (or any other component that renders an HTML text input). For example, see the following snippet from the Booking sample application:
59
Spring Faces
<sf:clientDateValidator required="true"> <h:inputText id="checkinDate" value="#{booking.checkinDate}" required="true"> <f:convertDateTime pattern="yyyy-MM-dd" timeZone="EST"/> </h:inputText> </sf:clientDateValidator>
In general, each of the available validations has a corresponding sensible default error message. The error messages can be overridden via the component's "invalidMessage" attribute. All of the customizable message attributes are value-binding aware so that expressions may be used to bind to keys in the application message bundle if so desired. Please refer to the javadocs of the component classes to see all of the attributes for the components. More extensive taglib docs will be available with the final release of Spring Web Flow 2.0. 6.2.8.4. Using Ext Version of The Spring Faces Client Side Validation Components An alternate version of the components based on the Ext library is provided under a separate tag namespace. In order to utilize them, the following namespace declaration must be added to the header of a Facelets view template:
xmlns:sfe="http://www.springframework.org/tags/faces-ext"
The basic behavior of the Ext versions of the components is the same, but the tags have different attributes that correspond with the attributes of the underlying Ext widgets. Please refer to the javadocs of the component classes to see all of the attributes for the components. More extensive taglib docs will be available with the final release of Spring Web Flow 2.0.
60
1. 2.
Phonebook - the original sample demonstrating most core features (including subflows). Sellitem - demonstrates a wizard with conditional transitions, flow scope, flow execution redirects, and continuations. Sellitem-JSF - The sellitem sample in a JSF environment (notice how the flow definition is more concise because JSF components care for data binding and validation). Shippingrate - demonstrates Spring Web Flow together with the Prototype Javascript framework (for Ajax-style flows). NumberGuess - demonstrates use of stateful middle-tier components to carry out business logic. Flowlauncher - demonstrates all the possible ways to launch and resume flows. Itemlist - demonstrates REST-style URLs and inline flows. Fileupload - demonstrates multipart file upload. Birthdate - demonstrates Struts integration and the MultiAction.
3.
4.
5. 6. 7. 8. 9.
10. Phonebook-Portlet - the phonebook sample in a Portlet environment (notice how the flow definitions do not change).
This builds all samples preparing "target" areas within each sample project subdirectory containing webapp Spring Web Flow Version 2.0-m3 61
Practical Use of Spring Web Flow structures in both exploded and WAR archive forms. The build also provides basic helper targets for deploying to Tomcat from Ant; however these webapp structures can be copied to any servlet container, and each project is also a Eclipse Dynamic Web Project (DWP) for easy deployment inside Eclipse with the Eclipse Webtools Project (WTP).
7.2.3. Deploying projects inside Eclipse using Eclipse Web Tools (WTP)
Each Spring Web Flow sample application project is a Eclipse Dynamic Web Project (DWP), for easy deployment to a server running inside the Eclipse IDE. To take advantage of this, you must be running Eclipse 3.2 with Web Tools 1.5. To run a sample application as a webapp inside Eclipse, simply select the project, right-click, and select Run -> Run On Server. A convenient shortcut for this action is ALT + SHIFT + X (Execute menu), R (Run on Server). The first time you do this you will be asked to setup a Server, where you are expected to point Eclipse to a location where you have a Servlet Container such as Apache Tomcat installed. Once your container has been setup and you finish the deployment wizard, Eclipse will start the container and automatically publish your webapp to it. In addition, it will launch a embedded web browser allowing you to run the webapp fully inside the IDE.
7.3.1. Overview
The Sellitem example demonstrates using Web Flow to build a shopping cart wizard with a shipping rate subflow, decision states, service and data access Spring POJO beans, Spring 2.0 form tags, and a Web Flow FormAction bean for data binding, validation, and error reporting. The Sellitem example breaks down its Spring application configuration into a number of files organized according to purpose. Although the example itself uses a small number of beans you may consider organizing a real-world application (with many more beans) according to similar principles. Before going into the specifics of each individual context, use the diagram below to get a brief overview of all configuration files including location and purpose.
7.3.2. Web.xml
The web.xml configuration maps "*.htm" requests to the sellitem servlet - a Spring MVC DispatcherServlet:
<servlet> <servlet-name>sellitem</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/sellitem-servlet-config.xml /WEB-INF/sellitem-webflow-config.xml </param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>sellitem</servlet-name> <url-pattern>*.htm</url-pattern> </servlet-mapping>
63
Practical Use of Spring Web Flow The contextConifgLocation parameter for the DispatcherServlet indicates the Spring MVC web context for the sellitem servlet is spread over two xml files: sellitem-servlet-config.xml and sellitem-webflow-config.xml. The web.xml also requests an additional Spring context to be loaded from the classpath through the ContextLoaderListener:
<context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:org/springframework/webflow/samples/sellitem/services-config.xml </param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
This service layer context defines beans to be referenced from web flow definitions. The next section discusses the content of this context in more detail.
7.3.3. Services-config.xml
The services-config.xml loaded from the classpath through Spring MVC's ContextLoaderListener defines several beans for the service and data access layers of the application. For example, the service context defines a DAO bean ("saleProcessor") and injects it with a data source:
<bean id="saleProcessor" class="org.springframework.webflow.samples.sellitem.JdbcSaleProcessor"> <property name="dataSource" ref="dataSource"/> </bean> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="org.hsqldb.jdbcDriver"/> <property name="url" value="jdbc:hsqldb:mem:sellItem"/> <property name="username" value="sa"/> </bean>
The services context also declares a bean of type InMemoryDatabaseCreator set to autowire by type meaning that its fields will be compared against the types of beans available in the context and will be automatically set when a match is found. Hence the dataSource bean is used to set the dataSource property of InMemoryDatabaseCreator:
<bean id="databaseCreator" class="org.springframework.webflow.samples.sellitem.InMemoryDatabaseCreator" autowire="byType"/>
Looking inside the InMemoryDatabaseCreator, its initDao() method invoked during context initialization creates a table called T_SALES for use by the sample application. This table is created in an in-memory hsqldb database called sellitem (based on the url property of the dataSource bean). It's also worth noting the bean declarations related to declarative transaction management:
<tx:annotation-driven/> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>
The "<tx:annotation-driven>"declaration indicates transaction configuration is governed by Java 5 annotations used in bean classes such as this annotation in the SaleProcessor interface:
64
For annotated beans the Spring container automatically creates proxies according to the transaction semantics in the annotation metadata. The "<tx:annotation-driven>" tag has a transaction-manager attribute but this attribute is not required if the transaction manager bean is named "transactionManager".
and
FlowController is a web flow controller extending Spring MVC's AbstractController delegating requests (in this case for the "/pos.htm" servlet path) to the flowExecutor bean it is configured with. FlowController acts as gateway to Web Flow and a single controller instance can serve the application as most of the actual control logic is encapsulated in web flow definitions. The sellitem-webflow-config.xml defines web flow specific beans such as a flow executor, a flow registry and a flow listener beans:
<!-- Launches new flow executions and resumes existing executions --> <flow:executor id="flowExecutor" registry-ref="flowRegistry"> <flow:execution-listeners> <flow:listener ref="listener" criteria="sellitem-flow" /> </flow:execution-listeners> </flow:executor> <!-- Creates the registry of flow definitions for this application --> <flow:registry id="flowRegistry"> <flow:location path="/WEB-INF/flows/**/*-flow.xml" /> </flow:registry> <!-- Observes the lifecycle of sellitem-flow executions --> <bean id="listener" class="org.springframework.webflow.samples.sellitem.SellItemFlowExecutionListener" />
The FlowExecutor is the central entry point into the Spring Web Flow system. It drives the execution of flow definitions configured through the flowRegistry. The flowRegistry bean is configured to load definitions from files ending with "-flow.xml" in any subdirectory of /WEB-INF/flows. This matches to sellitem-flow.xml, shipping-flow.xml, sellitem-simple-flow.xml, sellitem-conversation-scope-flow.xml and shipping-conversation-scope-flow.xml. As shown here the flow executor can also be configured with a flow listener, which is a callback mechanism for flow execution lifecycle events. The SellItemFlowExecutionListener extends FlowExecutionListenerAdapter a default implementation of the FlowExecutionListener interface sparing the need to implement methods for all lifecycle events. Spring Web Flow Version 2.0-m3 65
Looking inside SellItemFlowExecutionListener, it implements the stateEntering method executed for whenever a new state is about to be entered. The logic in this method checks if the current web flow state has an attribute named "role" and if so it ensures the user has that role:
String role = nextState.getAttributes().getString("role"); if (StringUtils.hasText(role)) { HttpServletRequest request = ((ServletExternalContext)context.getExternalContext()).getRequest(); if (!request.isUserInRole(role)) { throw new EnterStateVetoException(context.getActiveFlow().getId(), context.getCurrentState().getId(), nextState.getId(), "State requires role '" + role + "', but the authenticated user doesn't have it!"); } }
Based on the above definitions - web.xml, Spring MVC controller bean, and web flow registry, the sellitem-flow can be initiated with the following URI:
/swf-sellitem/pos.htm?_flowId=sellitem-flow
Note: although it is possible to invoke the shipping-flow directly as well, it expects an input attribute and is intended to be invoked as a subflow.
7.3.5. Sellitem-beans.xml
Before tracing the sequence of states in sellitem-flow.xml notice the import declaration at the bottom of that file:
<import resource="sellitem-beans.xml"/>
The sellitem-beans.xml located in the same directory declares a web flow FormAction bean for use in the flow definition and configures it with a SaleValidator and a SellItemPropertyEditorRegistrar:
<!-- Manages setting up, binding input to, and validating a Sale "backing wizard form object" --> <bean id="formAction" class="org.springframework.webflow.action.FormAction"> <property name="formObjectName" value="sale"/> <property name="validator"> <bean class="org.springframework.webflow.samples.sellitem.SaleValidator"/> </property> <!-- Installs property editors used to format non-String fields like 'shipDate' --> <property name="propertyEditorRegistrar"> <bean class="org.springframework.webflow.samples.sellitem.SellItemPropertyEditorRegistrar"/> </property> </bean>
The SellValidator will be used to validate form input data. The SellItemPropertyEditorRegistrar is responsible for registering custom property editors. Such editors are used to bind text data from HTML form fields to server-side Objects. For example SellItemPropertyEditorRegistrar registers a custom date editor:
public void registerCustomEditors(PropertyEditorRegistry registry) { registry.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("MM/dd/yyyy"), true)); }
This editor will bind the shipDate form field in shippingDetailsForm.jsp to the shipDate property of the Sale Spring Web Flow Version 2.0-m3 66
The formAction bean will use the sale variable for form binding and validation (see sellitem-beans.xml). The start state for the flow enterPriceAndItemCount is a view state, which resolves to the JSP page /WEB-INF/jsp/priceAndItemCountForm.jsp:
<view-state id="enterPriceAndItemCount" view="priceAndItemCountForm"> <render-actions> <!-- create the backing form object and initialize a empty errors collection --> <action bean="formAction" method="setupForm"/> </render-actions> <transition on="submit" to="enterCategory"> <action bean="formAction" method="bindAndValidate"> <attribute name="validatorMethod" value="validatePriceAndItemCount"/> </action> </transition> </view-state>
The view state uses a render action to invoke the setupForm method of the formAction bean. The setupForm method prepares a form object based on the "sale" variable declared at the top of the flow definition. The priceAndItemCountForm.jsp page collects a price and an itemCount using Spring 2.0 form input tags binding form fields to properties in the form backing object "sale". When pressed, the submit button "_eventId_submit" causes a web flow transition for an event with the id of "submit" to the view state "enterCategory". Prior to transitioning the formAction's bindAndValidate method is called to perform binding and (partial) validation using the validatePriceAndItemCount method of the validator object. The next view state enterCategory (based on categoryForm.jsp) collects inputs for sale category and whether shipping is required. On submit it transitions to the requiresShipping state:
<view-state id="enterCategory" view="categoryForm"> <transition on="submit" to="requiresShipping"> <action bean="formAction" method="bind"/> </transition> </view-state>
The requiresShipping state is a decision state making flow routing decisions. It evaluates a boolean expression against the executing flow and it decides where to transition to next. Here the shipping boolean property of the "sale" form backing object is checked to decide whether to go to the enterShippingDetails subflow state or proceed directly to processSale.
<decision-state id="requiresShipping"> <if test="${flowScope.sale.shipping}" then="enterShippingDetails" else="processSale"/> </decision-state>
The enterShippingDetails subflow state is based on shipping-flow.xml located in the same directory. The form backing object "sale" is passed to it as an input attribute using an attribute mapper declaration:
67
<subflow-state id="enterShippingDetails" flow="shipping-flow"> <attribute-mapper> <input-mapper> <input-attribute name="sale"/> </input-mapper> </attribute-mapper> <transition on="finish" to="processSale"/> </subflow-state>
The shipping-flow subflow is a simple flow with one view state. It collects the shipping details, binds the data and returns to its parent flow. The id of the subflow end state "finish" is returned to the parent subflow state causing a transition to the processSale action state.
<action-state id="processSale"> <bean-action bean="saleProcessor" method="process"> <method-arguments> <argument expression="flowScope.sale"/> </method-arguments> </bean-action> <transition on="success" to="finish"/> </action-state>
The saleProcessor bean, a POJO defined in services-config.xml is invoked using a "bean-action" declaration (as opposed to the "action" declation used to invoke a web flow Action such as FormAction). The saleProcessor (an instance of JdbcSaleProcessor) performs a database update using the values of the Sale object and upon successful completion transitions to the end view state:
<end-state id="finish" view="costOverview"> <entry-actions> <action bean="formAction" method="setupForm"/> </entry-actions> </end-state>
Then end state calls FormAction's setupForm method again. This does not re-create the "sale" form object (still in flow scope) but it does ensure any custom property editors are registered for use in rendering the JSP.
68
Practical Use of Spring Web Flow Conversation scope retains attributes stored in it for the life of the flow execution and is shared by all flow sessions. For example when invoking the shipping details subflow the parent flow does not need to pass the "sale" form backing object because it is now stored in conversation scope and is accessible to both flows:
<subflow-state id="enterShippingDetails" flow="shipping-conversation-scope-flow"> <transition on="finish" to="processSale"/> </subflow-state>
Also, when the "sale" object needs to be accessed it is done by referencing conversation cope:
<decision-state id="requiresShipping"> <if test="${conversationScope.sale.shipping}" then="enterShippingDetails" else="processSale"/> </decision-state>
Note
The underlying Web Flow definitions for the Sellitem and the Sellitem-JSF examples are very similar. To avoid repetition the documentation for the Sellitem-JSF example focuses primarily on the points of integration between Web Flow and JSF. For further general information on Web Flow definitions and supporting Java classes for the Sellitem example, please refer to the Sellitem example documentation.
7.4.2. Web.xml
The web.xml contains standard JSF configuration including mappings for the JSF front servlet: it handles all requests ending with "*.faces":
<!-- Faces Servlet --> <servlet> <servlet-name>Faces Servlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>*.faces</url-pattern> </servlet-mapping>
69
In addition, the web.xml loads a Spring root web application context containing the services used by the application:
<context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:org/springframework/webflow/samples/sellitem/services-config.xml /WEB-INF/webflow-config.xml </param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
The services-config.xml contains POJO beans required for the services and data access layers of the application. These declarations are very similar to the Sellitem example (and explained in more detail there). The webflow-config.xml contains Web Flow related bean definitions. These definitions will be explained a little bit further on in the context of how they fit into the JSF phases lifecycle.
The FlowNavigationHandler delegates view navigation handling to the the Web Flow system when a flow is initiated or resumed. The DelegatingFlowVariableResolver is suitable for use along side other variable resolvers to support EL binding expressions like {#bean.property} where "bean" could be a property in any supported scope. The resolver search algorithm looks in flash scope first, then flow scope, then conversation scope. If no variable is found, this resolver delegates to the next resolver in the chain. The FlowPhaseListener invoked during beforePhase and afterPhase JSF events is responsible for managing the lifecycle of a FlowExecution and making it available to other JSF artifacts during the lifecycle of a JSF request.
70
<!-- Creates the registry of flow definitions for this application --> <flow:registry id="flowRegistry"> <flow:location path="/WEB-INF/flows/sellitem-flow.xml" /> </flow:registry>
Here the flow executor is configured to support execution of a single flow definition - sellitem-flow.xml. The executor bean has been assigned the id "flowExecutor". This id is significant and is required for the JSF artifacts to detect the executor and its services.
This causes the sellitem-flow to be initiated. Once a flow is initiated each subsequent JSP page can participate in the flow (the flow execution key is tracked for you). A few notable differences between Sellitem and Sellitem-JSF to keep in mind: 1. The JSF version of the sellitem flow definition is simpler because JSF components care for data binding and validation. In its web flow definition Sellitem-JSF uses actual JSP names (instead of the logical view names used in Sellitem) to be rendered by JSF. This is consistent with normal JSF-isms. The JSP pages in Sellitem-JSF use unified EL to access the converastion scoped Sale object - e.g. #{sale.price}. Sellitem-JSF uses JSF component tags for UI and Sellitem uses Spring form tags.
2.
3.
4. 5.
There is no need to manually track the flow execution key because it is tracked for you in the JSF view root. The combination of delegating flow variable resolution plus automatic flow execution key management means JSF views selected a flow look like standard JSF views to JSF developers. Also, JSF components help simplify flow definition logic as the flow no longer has to worry about data binding and validation. For more information and understanding on the Sellitem flow definition logic itself please refer to the documentation for the original Sellitem example.
context to perform calculations and to provide reference data such as countries and package types.
7.5.2. Web.xml
The web.xml configuration maps requests for "*.htm" to the shippingrate servlet - a regular Spring MVC DispatcherServlet:
<servlet> <servlet-name>shippingrate</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>shippingrate</servlet-name> <url-pattern>*.htm</url-pattern> </servlet-mapping>
The web.xml also ensures the following Spring context file is loaded at runtime from the web application classpath:
<context-param> <param-name>contextConfigLocation</param-name> <param-value> classpath:org/springframework/webflow/samples/shippingrate/domain/services.xml </param-value> </context-param>
The services.xml Spring context defines a "rateService" bean providing operations for making shipping rate calculations and for retrieving reference data required for display in the JSP pages of the application.
FlowController is a Web Flow controller. It is the main point of integration between Spring MVC and Spring Web Flow routing requests to one or more managed web flow executions. The FlowController is injected with flowExecutor and flowRegistry beans:
<!-- Launches new flow executions and resumes existing executions. --> <flow:executor id="flowExecutor" registry-ref="flowRegistry" repository-type="simple"/> <!-- Creates the registry of flow definitions for this application --> <flow:registry id="flowRegistry"> <flow:location path="/WEB-INF/flows/**/*-flow.xml" /> </flow:registry>
The flowExecutor and the flowRegistry beans collectively configure the FlowController with one web flow the getRate-flow defined in /WEB-INF/flows/getRate-flow.xml. The flowExecutor uses a "simple" repository, which manages execution state in the user session.
72
Given the above definitions the following URI can be used to initiate the getRate-flow:
/swf-shippingrate/rates.htm?_flowId=getRate-flow
When index.jsp is loaded the following JavaScript invokes the getRate-flow and replaces the content of the getRateWizard div tag with the response returned from the server:
<div id="getRateWizard"> <script type="text/javascript"> window.onload = function() { new SimpleRequest('getRateWizard', 'rates.htm', 'get', '_flowId=getRate-flow'); }; </script> </div>
Functions are first-class citizens and a type in JavaScript. The script above creates an instance of the SimpleRequest function defined in swf_ajax.js. This function invokes Prototype's Ajax.Updater with the specified URL and request parameters. On success the content of the getRateWizard div is replaced with the response returned from the server. On failure such as an HTTP response code other 200 (OK) an error message is displayed. The next few pages are form-based JSP's - selectCustomer.jsp, selectReceiver.jsp, etc. Each of them contains the following JavaScript call at the bottom:
<script type="text/javascript"> formRequest('selectCustomerTypeForm'); </script>
The formRequest function is also defined in swf_ajax.js and it uses Prototype to register a handler for the form submit event:
function formRequest(formElementId) { Event.observe(formElementId, 'submit', handleSubmitEvent, true); }
The handleSubmitEvent function extracts the form parameters, stops the submit event, and posts an AJAX request via XMLHttpRequest. On success the results returned form the server replace the content of the form. On failure such as an HTTP response code other 200 (OK) an error message is displayed.
73
Although not demonstrated in this example a back button can be implemented in parallel with the Next button used to advance from one screen to the next. This would be necessary because the browser back button - a common issue in Ajax applications, contrary to user expectation returns to the page prior to the first Ajax request. As a result of the Ajax requests the entire wizard is able to function within a portion of the page without refresing the remaining information on it.
This is a view state, which will display the initial form using the JSP page /WEB-INF/jsp/selectCustomer.jsp. Notice, the use of a start action executed immediately before the JSP is displayed:
<start-actions> <action bean="formAction" method="setupForm" /> </start-actions>
The "formAction" bean is defined in the Spring servlet context (/WEB-INF/shippingrate-servlet.xml). It specifies a form object and a validator to use for form data binding and validation:
<!-- Performs "form backing object" data binding and validation on input submit --> <bean id="formAction" class="org.springframework.webflow.action.FormAction"> <property name="formObjectName" value="rateCriteria" /> <property name="formObjectClass" value="org.springframework.webflow.samples.shippingrate.domain.RateCriteria <property name="formObjectScope" value="FLOW" /> <property name="validator"> <bean class="org.springframework.webflow.samples.shippingrate.domain.RateCriteriaValidator" /> </property> </bean>
The form object of type RateCriteria will be used to collect data from the user in several steps. The form object will be stored in FLOW scope and will not be re-created with each request as long as the flow hasn't reached its end state. The actual binding of html form fields to the RateCriteria object is based on Spring's data binding mechanism. Html form fields are surrounded with the <spring:bind> tag containing the path nested property field. FormAction's bindAndValidate method will initiate the actual binding on the server side between HTTP request parameters and RateCriteria data fields. When the selectCustomer.jsp submits back to the FlowController via "/swf-shippingrate/rate.htm" it uses a submit button named "_eventId_submit". This indicates to Web Flow a transition to the "selectSender" view state. This view state is defined as follows:
<view-state id="selectSender" view="selectSender"> <render-actions> <bean-action bean="rateService" method="getCountries"> <method-result name="countries" /> </bean-action> </render-actions> <transition on="submit" to="selectReceiver"> <action bean="formAction" method="bindAndValidate">
74
The selectSender view state has a render action: the "rateService" bean that was loaded through the services.xml context referenced in web.xml. The purpose of the render action is to load data required to render the JSP. In this case the rateService bean has a method called getCountries that returns a list of countries to be displayed in a drop-down by the JSP. The "selectSender" view state also defines one transition: on event with id of "submit" a transition to the "selectReceiver" view state occurs. A pre-requisite for the transition to occur is the successful completion of formAction bean's bindAndValidate method. The attribute "validatorMethod" on the bean specifies the name of the method to invoke on the Validator object specifically for the fields of the current screen. If the bindAndValidate method does not succeed the transition does not take place and the flow remains in the "selectSender" view state where the user can review the errors and modify the selection. The next two states in the flow - selectReceiver and selectPackageDetails use similar mechnisms. The rateSevice bean is used to retrieve countries and package types for use in the JSP. The form backing object RateCriteria stored in FLOW scope is used to collect user input with each form submit. The "findRate" action state occurs after all user input has been provided. It is defined as follows:
<action-state id="findRate"> <bean-action bean="rateService" method="getRate"> <method-arguments> <argument expression="flowScope.rateCriteria" /> </method-arguments> <method-result name="rate" /> </bean-action> <transition on="success" to="showRate" /> </action-state>
Logic for the action state is provided by the getRate method of the rateService bean. The RateCriteria object stored in FLOW scope and containing the user input is passed to the rateService bean. The result of the method is exposed in request scope under the name "rate". The next and final state "showRate" is a JSP page, which accesses the calculated rate information and displays it to the user.
7.6.2. Web.xml
The web.xml configuration maps "*.htm" requests to the numberguess servlet - a regular Spring MVC Spring Web Flow Version 2.0-m3 75
DispatcherServlet:
<servlet> <servlet-name>numberguess</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/dispatcher-servlet.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>numberguess</servlet-name> <url-pattern>*.htm</url-pattern> </servlet-mapping>
FlowController is a Web Flow controller. It is the main point of integration between Spring MVC and Spring Web Flow routing requests to one or more managed web flow executions. The FlowController is injected with flowExecutor and flowRegistry beans:
<!-- Launches new flow executions and resumes existing executions. --> <flow:executor id="flowExecutor" registry-ref="flowRegistry" repository-type="singlekey"/> <!-- Creates the registry of flow definitions for this application --> <flow:registry id="flowRegistry"> <flow:location path="/WEB-INF/higherlower.xml" /> <flow:location path="/WEB-INF/mastermind.xml" /> </flow:registry>
The flowExecutor and the flowRegistry beans collectively configure the FlowController with two web flows higherlower and mastermind. This flowExecutor is configured with a simple repository that assigns a single flow execution key per conversation. The key, once assigned, never changes for the duration of the conversation. Given the above definitions the following URI's can be used to initiate each of the two flows:
/swf-numberguess/play.htm?_flowId=higherlower /swf-numberguess/play.htm?_flowId=mastermind
The Spring MVC servlet context also defines a view resolver bean for resolving logical view names. In general Web Flow does not aim to replace the flexibility of Spring MVC for view resolution. It focuses on the C in MVC.
76
The Higherlower flow (/WEB-INF/higherlower.xml) starts with the following flow variable declaration:
<var name="game" class="org.springframework.webflow.samples.numberguess.HigherLowerGame"/>
This variable is automatically created when an execution of the flow begins and will exist in FLOW scope throughout its duration. The start state for the flow is defined as follows:
<view-state id="enterGuess" view="higherlower.enterGuess"> <transition on="submit" to="makeGuess"/> </view-state>
The view resolver bean of Spring MVC will resolve "higherlower.enterGuess" to /WEB-INF/jsp/higherlower.enterGuess.jsp. This JSP has a form with one input field for the guess number. The "game" variable referenced throughout the JSP is the FLOW-scoped variable that was declared at the top of the flow definition. The name of the form submit button "_eventId_submit" indicates the event id to use for deciding where to transition to next. Given an event with id of "submit" the "enterGuess" view state transitions to the "makeGuess" action state defined as follows:
<action-state id="makeGuess"> <evaluate-action expression="flowScope.game.makeGuess(requestParameters.guess)"> <evaluation-result name="guessResult"/> </evaluate-action> <transition on="CORRECT" to="showAnswer"/> <transition on="*" to="enterGuess"/> <transition on-exception="java.lang.NumberFormatException" to="enterGuess"/> </action-state>
The makeGuess action state consists of one evaluate action and three transitions. Evaluate actions are used to invoke logic encapsulated in a FLOW-scoped object - in this case the game bean. The makeGuess method of the game bean returns one of several enum values it defines:
enum GuessResult { TOO_HIGH, TOO_LOW, CORRECT, INVALID }
Web Flow detects the returned result from the makeGuess method is a JDK 1.5 enum type and creates an Event with a String id matching the enum value. If the makeGuess method returns CORRECT a transition to the final showAnswer state occurs. For any other event (defined with the event pattern on="*") Web Flow returns to the enterGuess state. The makeGuess state also defines one on-exception transition demonstrating how specific Exceptions can be incorporated into flow transition logic. The end-state showAnswer resolves to the JSP page /WEB-INF/jsp/higherlower.showAnswer.jsp, which simply shows the correct guess. At this point the flow has ended and the "game" bean is no longer in scope.
77
<var name="game" class="org.springframework.webflow.samples.numberguess.MastermindGame"/> <start-state idref="enterGuess"/> <view-state id="enterGuess" view="mastermind.enterGuess"> <transition on="submit" to="makeGuess"/> </view-state> <action-state id="makeGuess"> <evaluate-action expression="flowScope.game.makeGuess(requestParameters.guess)"> <evaluation-result name="guessResult"/> </evaluate-action> <transition on="CORRECT" to="showAnswer"/> <transition on="*" to="enterGuess"/> </action-state> <end-state id="showAnswer" view="mastermind.showAnswer"/>
The MastermindGame class encapsulates the logic for the game and is stored as a FLOW-scoped bean. It returns one of three possible enum values - WRONG, CORRECT, or INVALID, which Web Flow converts to events with id's matching the enum values. If the guess is INVALID the JSP page /WEB-INF/jsp/mastermind.enterGuess.jsp will print an error message. If the guess is CORRECT the flow will transition to the showAnswer end state and complete the flow.
7.7.2. Web.xml
The web.xml configuration maps "*.htm" requests to the flowlauncher servlet - a regular Spring MVC DispatcherServlet:
<servlet> <servlet-name>flowlauncher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>flowlauncher</servlet-name> <url-pattern>*.htm</url-pattern> </servlet-mapping>
78
Practical Use of Spring Web Flow FlowController is a Web Flow extension of Spring MVC's AbstractController. It contains a FlowExecutor and directs incoming requests for one or more managed flow executions to it. The FlowExecutor bean is configured in the same context:
<!-- Launches new flow executions and resumes existing executions. --> <flow:executor id="flowExecutor" registry-ref="flowRegistry"/> <!-- Creates the registry of flow definitions for this application --> <flow:registry id="flowRegistry"> <flow:location path="/WEB-INF/sampleA.xml" /> <flow:location path="/WEB-INF/sampleB.xml" /> </flow:registry>
A single FlowController may direct all flows for an application serving as a gateway to Web Flow. Based on the above definitions the flows sampleA and sampleB can be invoked as follows:
/swf-flowlauncher/flowController.htm?_flowId=sampleA /swf-flowlauncher/flowController.htm?_flowId=sampleB
The welcome index.html file for the web application invokes the flows and passes additional input using either a URL link or a form submit.
This declaration reads "when a new execution of this flow starts map the input attribute named input into a flowScope attribute also named input". Spring Web Flow will automatically provide the request parameters as input to the flow when launching a new flow execution. Following this declaration the input request parameter will remain available for the duration of the flow. There are 3 states in this flow: the start state, the end state, and a subflow state. The start state is a view state - it will display a JSP page and allow the user to make a choice. The subflow state initiates Sample B as a subflow of the current flow - subflows give the ability to compose independent modules together to compose complex controller workflows. And the end state launches Sample B by redirecting to it. The subflow state launches B with the following input attribute declaration. This declaration reads "pass the value of the flow-scoped attribute named input as an attribute also named input to subflow B.
<attribute-mapper> <input-mapper> <mapping source="flowScope.input" target="input" /> </input-mapper> </attribute-mapper>
The next line is a transition defining how to respond when the subflow ends: advance back to the start state for Sample A.
<transition on="end" to="aPage" />
79
The end state demonstrates how to redirect to Sample B upon completion of the root level flow Sample A:
<end-state id="endAndLaunchB" view="flowRedirect:sampleB?input=${requestParameters.input}" />
This declaration causes A to be terminated and B to start with the given requst input parameter.
The "id" attribute of the end state matches the "on" attribute of the transition in the outer flow's subflow state, which the outer flow uses to resume itself. Also notice how bPage.jsp makes a check to detect if Sample B is running as a subflow of Sample A or if it is running as a top-level flow:
<c:if test="${!flowExecutionContext.activeSession.root}">
The FlowExecutionContext object is exposed to the views (JSPs) to make information like this available during response rendering.
7.8.2. Web.xml
The web.xml configuration maps "/app/*" requests to the itemlist servlet - a regular Spring MVC DispatcherServlet:
<servlet> <servlet-name>itemlist</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>itemlist</servlet-name>
80
<url-pattern>/app/*</url-pattern> </servlet-mapping>
All requests with a servlet path matching "/app/**/**" are mapped to the "flowController" bean. The FlowController is a Web Flow extension of Spring MVC's AbstractController delegating requests to one or more managed web flows. It acts as gateway to Web Flow defined control logic and a single instance can serve the application. The usual way to launch a specific web flow is to pass the _flowId request parameter. However, this example is configured with a RequestPathFlowExecutorArgumentHandler for processing REST-style URL's. Requests for services built around the REST concept are encoded in the URL and not as query string parameters. The way to invoke a web flow with this argument handler is to follow:
http://${host}/${context path}/${dispatcher path}/${flowId}
The FlowController is configured with a flowExecutor and flowRegistry beans containing two web flows itemlist and itemlist-alternate:
<!-- Launches new flow executions and resumes existing executions: Spring 1.2 config version --> <bean id="flowExecutor" class="org.springframework.webflow.config.FlowExecutorFactoryBean"> <property name="definitionLocator" ref="flowRegistry"/> </bean> <!-- Creates the registry of flow definitions for this application: Spring 1.2 config version --> <bean id="flowRegistry" class="org.springframework.webflow.engine.builder.xml.XmlFlowRegistryFactoryBean"> <property name="flowLocations"> <list> <value>/WEB-INF/itemlist.xml</value> <value>/WEB-INF/itemlist-alternate.xml</value> </list> </property> </bean>
The FlowRegistry and FlowExecutor are defined with Spring 1.2 compatible bean definitions. However, starting with Spring 2.0 Web Flow also offers the custom tags flow:registry and flow:executor, which are more readable and less verbose.
81
Based on the above web context definition use the following URL's to invoke the itemlist or the itemlist-alternate web flows:
/swf-itemlist/app/itemlist /swf-itemlist/app/itemlist-alternate
Also defined in itemlist-servlet.xml are three "action" beans - createItemAction, addItemAction, and mapItemAction, which will be referenced from action states in the web flow definitions.
When the form submits an event with the "_eventId_add" button the displayItemList view state transitions to the createItem action state.
<view-state id="displayItemlist" view="itemlist"> <transition on="add" to="createItem" /> </view-state> <action-state id="createItem"> <action bean="createItemAction" /> <transition on="success" to="displayItem" /> </action-state>
The "createItemAction" bean is declared in the Spring MVC context (/WEB-INF/itemlist-servlet.xml). It simply returns "success", which causes a transition to the displayItem view state. The next two states displayItem and addItem allow adding an item to the list variable declared at the top of the flow:
<var name="list" class="java.util.ArrayList" />
The "addItemAction" bean is also declared in the Spring MVC context. It performs the add by accessing the list in flow scope and the item to be added from the request parameters as follows:
Collection list = context.getFlowScope().getRequiredCollection("list"); String data = context.getRequestParameters().get("data"); if (data != null && data.length() > 0) { list.add(data); }
82
Practical Use of Spring Web Flow For any outcome the addItem state transitions back to the initial displayItemList state using an event pattern match:
<action-state id="addItem"> <action bean="addItemAction" /> <transition on="*" to="displayItemlist" /> </action-state>
An output-mapper is used to pass results from a subflow to a parent flow. The above declaration defines an expectation on the subflow to return an output parameter called "item". Accordingly the end state for the inline flow has this output mapping returning a parameter called "item":
<end-state id="finish"> <output-mapper> <mapping source="requestParameters.data" target="item" /> </output-mapper> </end-state>
With the above declarations we see how a subflow can pass output parameters back to its parent flow - in this case the 'data' request parameter is passed back as an output parameter. Once the inner subflow flow has completed the item is passed to the parent flow as an output parameter, which adds it to its flow-scoped list and transitions to the initial "displayItemList" state.
7.9.2. Web.xml
The web.xml configuration maps requests for "*.htm" to the fileupload servlet - a regular Spring MVC
83
DispatcherServlet:
<servlet> <servlet-name>fileupload</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>fileupload</servlet-name> <url-pattern>*.htm</url-pattern> </servlet-mapping>
FlowController is a Web Flow controller. It is the main point of integration between Spring MVC and Spring Web Flow routing requests to one or more managed web flow executions. The FlowController is injected with flowExecutor and flowRegistry beans containing one web flow definition:
<!-- Launches new flow executions and resumes existing executions. --> <flow:executor id="flowExecutor" registry-ref="flowRegistry" repository-type="singlekey"/> <!-- Creates the registry of flow definitions for this application --> <flow:registry id="flowRegistry"> <flow:location path="/WEB-INF/fileupload.xml" /> </flow:registry>
Given the above definitions the following URI can be used to invoke the "fileupload" flow:
/swf-fileupload/admin.htm?_flowId=fileupload
Both flowExecutor and flowRegistry beans are defined with Spring custom tags schema available in Spring 2.0. The custom tags make configuration less verbose and more readable. Regular Spring bean definitions can be used as well with earlier versions of Spring. The Spring MVC context also defines a view resolver bean for resolving logical view names and a multipartResolver bean for the upload component. In general Web Flow does not aim to replace the flexibility of Spring MVC for view resolution. It focuses on the C in MVC.
84
</view-state>
View states allow a user to participate in a flow by presenting a suitable interface. The view attribute "fileForm" is a logical view name, which the Spring MVC view resolver bean will resolve to /WEB-INF/jsp/fileForm.jsp. The fileForm.jsp has an html form that submits back to the same controller (/swf-fileupload/admin.htm) and passes a "_flowExecutionKey" parameter. The value for _flowExecutionKey is provided by the FlowController - it identifies the current instance of the flow and allows Web Flow to resume flow execution, which is paused each time a view is displayed. The name of the form submit button "_eventId_submit" indicates the event id to use for deciding where to transition to next. Given an event with id of "submit" the "selectFile" view transitions to the "uploadFile" state:
<action-state id="uploadFile"> <action bean="uploadAction"/> <transition on="success" to="selectFile"> <set attribute="fileUploaded" scope="flash" value="true"/> </transition> <transition on="error" to="selectFile"/> </action-state>
The "uploadFile" state is an action state. Action states integrate with business application code and respond to the execution of that code by deciding what state of the flow to enter next. The code for the uploadFile state is in the "uploadAction" bean declared in the Spring web context (/WEB-INF/fileupload-servlet.xml):
<bean id="uploadAction" class="org.springframework.webflow.samples.fileupload.FileUploadAction" />
FileUploadAction has simple logic. It picks one of two Web Flow defined events - success or error, depending on whether the uploaded file size is greater than 0 or not. Both success and error transition back to the "selectFile" view state. However, a success event causes an attribute named "fileUploaded" to be set in flash scope A flash-scoped attribute called "file" is also set programmatically in the FileUploadAction bean:
context.getFlashScope().put("file", new String(file.getBytes())); return success();
This illustrates the choice to save attributes in one of several scopes either programatically or declaratively.
7.10.2. Web.xml
The web.xml configuration maps requests for "*.do" to a regular Struts ActionServlet:
<servlet> <servlet-name>action</servlet-name> <servlet-class>org.apache.struts.action.ActionServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>action</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping>
The web.xml also sets up the loading of a Spring context at web application startup:
<context-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/webflow-config.xml </param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
The Spring web context contains beans to set up the Web Flow runtime environment. As will be shown in the next section Struts is configured with a Web Flow action that relies on the presence of a flowExecutor and a flowRegistry beans in this context.
FlowAction is a Struts action acting as a front controller to the Web Flow system routing Struts requests to one or more managed web flow executions. To fully configure the FlowAction a Spring web context is required to define flowExecutor and flowRegistry beans (named exactly so). This is an excerpt from the Spring web context (/WEB-INF/webflow-config.xml) defining these beans:
<!-- Launches new flow executions and resumes existing executions. --> <flow:executor id="flowExecutor" registry-ref="flowRegistry"/> <!-- Creates the registry of flow definitions for this application --> <flow:registry id="flowRegistry"> <flow:location path="/WEB-INF/birthdate.xml"/> <flow:location path="/WEB-INF/birthdate-alternate.xml"/> </flow:registry>
Based on the above, Web Flow is configured with two flows - birthdate and birthdate-alternate, which can be invoked as follows:
86
/swf-birthdate/flowAction.do?_flowId=birthdate /swf-birthdate/flowAction.do?_flowId=birthdate-alternate
The Struts configuration file also defines several global forwards: birthdateForm, cardForm, and yourAge, which will be referenced from Web Flow definitions as logical view names (and left to Struts to resolve to actual JSP pages). In general Web Flow does not aim to replace view resolution capabilities of web frameworks such as Struts or Spring MVC. It focuses on the C in MVC.
The setupForm action is called to perform initializations for the enterBirthdate view state. Its action bean is defined the Spring web context WEB-INF/webflow-config.xml:
<bean id="formAction" class="org.springframework.webflow.samples.birthdate.BirthDateFormAction" />
BirthDateFormAction is a FormAction - it extends Web Flow's FormAction class, which serves a purpose similar to that of Spring MVC's SimpleFormController providing common form functionality for data binding and validation. When the BirthDateFormAction bean is instantiated it sets the name, class and scope of the form object to use for loading form data upon display and collecting form data upon submit:
public BirthDateFormAction() { // tell the superclass about the form object and validator we want to // use you could also do this in the application context XML ofcourse setFormObjectName("birthDate"); setFormObjectClass(BirthDate.class); setFormObjectScope(ScopeType.FLOW); setValidator(new BirthDateValidator()); }
The form object "birthDate" is placed in flow scope, which means it will not be re-created with each request but will be obtained from flow scope instead as long as the request remains within the same flow. Once setupForm is done, the "birthdateForm" view will be rendered. The logical view name "birthdateForm" is a global-forward in struts-config.xml resolving to /WEB-INF/jsp/birthdateForm.jsp. This JSP collects data for the fields "name" and "date" bound to the birthDate form object and posts back to FlowAction with a submit image named "_eventId_submit". An event with the id of "submit" causes a transition to the processBirthdateFormSubmit action state defined as follows:
<action-state id="processBirthdateFormSubmit"> <action bean="formAction" method="bindAndValidate"> <attribute name="validatorMethod" value="validateBirthdateForm" /> </action> <transition on="success" to="enterCardInformation" /> <transition on="error" to="enterBirthdate" />
87
</action-state>
The processBirthDateFormSubmit action state uses the same formAction bean as the one already used to setup the form. This time its bindAndValidate method is used to populate and validate the html form values. Also, note the "validateMethod" attribute used to specify the name of the method to invoke on the Validator object setup in the constructor of the BirthDateFormAction. The use of this attribute allows partial validation of complex objects populated over several consecutive screens. On error the action returns to the view state it came from. On success it transitions to the enterCardInformation view state:
<view-state id="enterCardInformation" view="cardForm"> <transition on="submit" to="processCardFormSubmit" /> </view-state>
The logical view name "cardForm" is a global-forward in struts-config.xml resolving to /WEB-INF/jsp/cardForm.jsp. This JSP collects data for the remaining fields of the birthDate form object "sendCard" and "emailAddress", and posts back to FlowAction with a submit image named "_eventId_submit". An event with the id of "submit" causes a transition to the processCardFormSubmit action state defined as follows:
<action-state id="processCardFormSubmit"> <action bean="formAction" method="bindAndValidate"> <attribute name="validatorMethod" value="validateCardForm" /> </action> <transition on="success" to="calculateAge" /> <transition on="error" to="enterCardInformation" /> </action-state>
For this action state the bindAndValidate method of the formAction bean is used to populate and validate the remaining html form values. The "validateMethod" attribute specifies the name of the method to invoke on the Validator object specific to the fields loaded on the current screen. On error the action returns to the view state it came from. On success it transitions to another action state called calculateAge:
<action-state id="calculateAge"> <action bean="formAction" method="calculateAge" /> <transition on="success" to="displayAge" /> </action-state>
The logic for the calculateAge action state is in the calculateAge method of the same formAction bean used for data binding and validation. This demonstrates the flexibility Web Flow allows in properly structuring control and business logic according to function. The caculateAge method performs business calculations and adds a string in request scope with the calculated age. Upon successful completion the calculateAge action state transitions to the end view state:
<end-state id="displayAge" view="yourAge" />
Once again the logical view name "yourAge" is a global-forward in struts-config.xml resolving to /WEB-INF/jsp/yourAge.jsp. This JSP page retrieves the calculated age from request scope and displays the results for the user. Spring Web Flow Version 2.0-m3 88
The transition to the end state indicates the end of the web flow. The flow execution is cleaned up. If the web flow is entered again a new flow execution will start, creating a new form object named "birthDate" and placing it in flow scope.
Here the setupForm action state is defined as a render-action of the enterBirthdate view state while the transition to the next screen uses a nested action bean invoked before the transition occurs. Notice that success is implicitly required for the transition to occur. Similarly on error the transition does not occur and the same view state is displayed again. The second screen is also defined with a nested transition and action bean:
<view-state id="enterCardInformation" view="cardForm"> <transition on="submit" to="calculateAge"> <action bean="formAction" method="bindAndValidate"> <attribute name="validatorMethod" value="validateCardForm" /> </action> </transition> </view-state>
Note
JSR-168 defines portlets but not how portlets integrate into a portal container. This process is left open to portal vendors who have their own individual mechanisms. The Phonebook-Portlet sample is configured to run with Apache Pluto - a reference implementation of the Java Portlet Specification. However, its dependence on Pluto is limited to configuration in web.xml. Hence it Spring Web Flow Version 2.0-m3 89
Practical Use of Spring Web Flow should be easy to adapt for use in other Portal/Portlet implementations after learning the deployment steps specific for that implementation.
2. 3. 4.
Unzip the binary distribution to any directory. Create the directory [pluto-home]/webapps/swf-phonebook-portlet Copy the content [webflow-release]/spring-webflow-samples/phonebook-portlet/target/artifacts/war-expanded directory created in the previous step Start Pluto with [pluto-home]/bin/startup Go to http://localhost:8080/pluto/portal of the
to
5. 6.
90
7. 8. 9.
Login as tomcat/tomcat (or any other user but see note below) After logging in you will be taken to the Portal Test page. Here you will see a Navigation pull-down menu at the top. Select 'Pluto Admin' from it to go to the Pluto Admin page.
10. On the Pluto Admin page under Portlet Applications you will see a drop-down with available portlet applications 11. Select '/swf-phonebook-portlet' from it, then phonebook from the drop-down next to it, and then press the 'Add Portlet' button 12. Use the Navigation menu at the top to go back to the Test Page. The Phonebook portlet should be present.
Note
The tomcat user must have the 'pluto' role. Open [pluto-home]/conf/tomcat-users.xml and ensure the following lines are there:
<role rolename="pluto"/> <user username="tomcat" password="tomcat" roles="tomcat,pluto"/>
The DispatcherPortlet is Spring's implementation of the Portlet interface dispatching requests for a portlet to registered Portlet MVC handlers. The phonebook portlet is configured with the following Spring contexts containing Portlet MVC handler, controller and view resolver beans:
<init-param> <name>contextConfigLocation</name> <value> /WEB-INF/phonebook-portlet-config.xml /WEB-INF/phonebook-webflow-config.xml </value> </init-param>
The above configuration defines phonebook as a portlet resource. In order to use it in a portal/portlet container additional web.xml configuration is required.
<!-- Generated Portlet Wrapper Servlet for Apache Pluto deployment --> <servlet> <servlet-name>phonebook</servlet-name> <servlet-class>org.apache.pluto.core.PortletServlet</servlet-class> <init-param> <param-name>portlet-name</param-name> <param-value>phonebook</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>phonebook</servlet-name> <url-pattern>/PlutoInvoker/phonebook</url-pattern> </servlet-mapping>
Note
The above configuration was auto generated using ant tasks from Apache Pluto 1.1.0. This configuration is included in web.xml for convenience and also as an example. For the most up-to-date information on required configuration please check Pluto's documentation. The web.xml configuration also contains the following servlet definition:
<servlet> <servlet-name>viewRendererServlet</servlet-name> <servlet-class> org.springframework.web.servlet.ViewRendererServlet </servlet-class> </servlet> <servlet-mapping> <servlet-name>viewRendererServlet</servlet-name> <url-pattern>/WEB-INF/servlet/view</url-pattern> </servlet-mapping>
The main purpose of this servlet is to allow reuse of Spring MVC's flexible view resolution and rendering capabilities in a Portlet application. The DispatcherPortlet converts a PortletRequest/PortletResponse to an HttpServletRequest/HttpServletResponse and then performs an include of this servlet.
and a PortletFlowController:
<bean id="flowController" class="org.springframework.webflow.executor.mvc.PortletFlowController"> <property name="flowExecutor" ref="flowExecutor"/> <property name="defaultFlowId" value="search-flow"/> </bean>
92
Practical Use of Spring Web Flow A PortletModeHandlerMapping allows mapping specific to each portlet mode. The VIEW mode in this case is mapped to the flowController bean, which delegates the request to Web Flow's executor for launching or resuming a flow from a flow definition. For more information on Phonebook flow definitions please refer to the Phonebook sample documentation. One last thing to observe is the following configuration in /WEB-INF/phonebook-webflow-config.xml:
<!-- Launches new flow executions and resumes existing executions. --> <flow:executor id="flowExecutor" registry-ref="flowRegistry"> <flow:execution-attributes> <!-- execution redirects don't apply in a Portlet environment --> <flow:alwaysRedirectOnPause value="false"/> </flow:execution-attributes> </flow:executor>
As the comment indicates the default behavior of redirect after submit must be turned off in a portlet environment where there is no HTTP redirect. For more information on the alwaysRedirectOnPause refer to the following article.
93