[TSE] Designing Stateful Web Application Control Flow with Erwin Vervaet
Spring Web Flow (SWF) does not fit into an application or a feature where free-flow navigation is required. It works best where you need to lock down and control navigation. SWF is not designed to be a web framework, but rather to solve the specific problem of navigation and state management between many pages.
Erwin is a Senior consultant at Ervacon and has extensive experience using Java SE and Java EE. He is the inventor and co-lead of the Spring Web Flow project.
Core Functionality
- Navigation is controlled by a flow (complete navigational control)
- Automatic state management (scoping, cleanup)
- A flow is a reusable application module (fully encapsulated: input/output contract, black box)
Spring Web Flow provides Navigational Control. A flow definition controls the entire conversation. The flow is the controller and drives all navigation. When user input is required, the flow pauses and a view is rendered. Clients signal events and are unaware of overall navigation rules. The flow reacts to user events and decides what to do next. A flow is a simple Finite State Machine (a set of states with transitions b/w them). SWF is not a general purpose workflow engine. It's simplified for use with web conversations and lacks features like split and join.
SWF provides state management with web continuations. What is a web continuation you ask?
A continuation is a saved snapshot of the executable state of a program at any given point in time. It is possible to restore this state and restart the execution of the program from that point onward.
The flow execution repository takes a snapshot of flow execution for each request. In a sense, the flow execution becomes a continuation.
Spring Web Flow is a very advanced state management mechanism. It has fully automatic state management with scopes (converstation, flow, flash, request), cleanup, compatible with browser buttons (back, forward, refresh, new window). Finally, flows are observable using FlowExecutionListener
.
To link to services (or call service-level beans), you can simply use your normal bean ids. You can subclass SWF's Action and inject your services into it. However, in most cases, you can simply use SWF's FormAction class. SWF can bind to any POJO method. For example:
<bean-action bean="phonebook" method="search"> <method-arguments> <argument expression="flowScope.searchCriteria"/> </method-arguments> <method-result name="results"/> </bean-action>
This might bind to the following interface, which is wired up as the "phonebook" bean.
public interface Phonebook {
|
To deploy a flow definition, you first have to define your flows in a flow registry.
<flow:registry id="flowRegistry"> <flow:location path="/WEB-INF/flows/**-flow.xml"/> </flow:registry>
A flow id is assigned by convention. For example, /WEB-INF/flows/search-flow.xml
becomes "search-flow".
To execute a flow, you need to define a flow executor that manages flow on behalf of the client. It loads flow definitions (from flowRegistry), drives flow executions (launch, resume, refresh) and saves/loads flow execution b/w requests (execution repository and state management).
<flow:executor id="flowExecutor" registry-ref="flowRegistry" repository-type="continuation"/>
Finally, you'll need to configure a "Flow Controller". This is specific to the web framework you're using. Below is an example for Spring MVC:
<bean name="/phonebook.htm" class="org.springframework.webflow.executor.mvc.FlowController"> <property name="flowExecutor" ref="flowExecutor"/> </bean>
SWF exposes some variables to the view model, namely "flowExecutionContext" and "flowExecutionKey". To participate in a flow execution, you have to submit _flowExecutionKey
and _eventId
parameters in every request. For a form, these can be hidden fields:
<form:form action="phonebook.htm" commandName="searchCriteria" method="post"> <input type="hidden" name="_flowExecutionKey" value="${flowExecutionKey}"> <input type="submit" class="button" name="_eventId_search" value="Search"> ... </form:form>
For links, you'll need to include these parameters as well:
<a href="phonebook.htm?_flowExecutionKey=${flowExecutionKey}&_eventId=select&id=${colleague.id}"> ${colleague.firstName} ${colleague.lastName}</a>
NOTE: This morning, I asked Rob Harrop if the _flowExecutionKey hidden field will ever be auto-generated by the <form:form> tag. His response was that they are doing something to make this more automatic, but it may not happen in the context of the <form:form> tag.
Best Practices
By default, SWF does redirect after POST. To change it, you can configure the flowExecutor bean a bit differently.
<flow:executor id="flowExecutor" registry-ref="flowRegistry" repository-type="continuation"> <flow:execution-attributes> <flow:alwaysRedirectOnPause value="false"/> </flow:execution-attributes> </flow:executor>
You can also explicitly use the "redirect:" prefix in the view name. In other words, the best practice of "Redirect after POST" has been built-in for you.
SWF comes with 4 repository (storage) implementations:
- simple: single flow execution per converstation (flowExecutionKey changes on every request), low resource usage, no browser button support. Use when you can lock down the browser and resources (memory) are scarce. By default, displays a nice stack trace (PermissionDeniedFlowExecutionAccessException) when you click back. Use case: your company has removed the back and forward buttons from end-user's customized browsers.
- singleKey: same as simple repository, but keeps same flowExecutionKey over the lifecycle of the flow. Tricks the browser into thinking it keeps refreshing the same exact page - there is no browser history. They do this by using the exact same URL for every page. The nice thing about using this repository is it really does disable the back and forward buttons. Use case: low memory available and want to block back/forward button usage.
- continuation: the default implementation if you don't specify the "repository-type" attribute on your flowExecutor. Allows free browsing in a controlled way. The downside is high memory usage because many snapshots are taken. Snapshots contain all the data associated with the conversation; more than just request parameters. Use when you have enough resources available and browser buttons need to be supported. The flowExecutionKey is different for each and every request in order to record the unique id of the snapshot.
- client: flow execution stored in the flowExecutionKey and on the client. It's nice because there's low resource usage on the server. However, it only supports POST (b/c of GET URL length limits). It also requires more bandwidth, has security concerns and has no support for double submit out-of-the-box.
Best practice? Use the correct repository for your situation! Somebody asked if SWF provides the information to build a "breadcrumbs" type of menu. Erwin says "yes", but there's no way to easily access that information (i.e. with JSP tags). Another audience member asks if it's possible to invalid snapshots? Erwin says "no", but it is possible to configure the maximum number of snapshots to store. They are considering adding support for invalidating certain snapshots after you pass a certain state in the flow.
Even though I knew a lot about SWF going into this talk, I was very impressed with Erwin's presentation. He did an excellent job at doing live demos that showed the different features of SWF. Even better, he did them with SWF's "phonebook" application, so it should be possible to reproduce many of his demos. At the end of his presentation, Erwin listed a number of resources for Spring Web Flow:
- Spring Web Flow Homepage
- Community Forum
- Ervacon Spring Web Flow Portal
- Test the sample applications on-line
- Books: Expert Spring MVC and Web Flow
I recently updated Spring Live's Chapter 13 for SWF 1.0, but Erwin and Keith's Expert Spring MVC and Web Flow (by Seth Ladd, Darren Davison, Steven Devijver and Colin Yates) is much better than my 10-page example. My updated chapter is currently in tech edit, so you may have to wait another couple weeks before it's in a released version.
Posted by Younghoe.Info on December 09, 2006 at 02:39 AM MST #
When you mention Erwin and Keith's book, which book is that? If you are refering to Expert Spring MVC and Web Flow, you should give credit to Colin Yates for those chapters. If they are writing their own book, that's awesome because Web Flow deserves a dedicated book.
Posted by Seth Ladd on December 09, 2006 at 03:23 AM MST #
Posted by Matt Raible on December 09, 2006 at 03:55 AM MST #
Posted by Daniela on June 02, 2007 at 11:47 PM MDT #
Posted by Steve M. on September 22, 2007 at 07:46 AM MDT #