Chapter 5. Flow executors

5.1. Introduction


Flow executors are the highest-level entry points into the Spring Web Flow system, responsible for driving the execution of flows across a variety of environments.

In this chapter you'll learn how to execute flows within Spring MVC, Struts, and Java Server Faces (JSF) based applications.

5.2. FlowExecutor

org.springframework.webflow.executor.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.

The FlowExecutor interface is shown below:

    public interface FlowExecutor {
        ResponseInstruction launch(String flowDefinitionId, ExternalContext context);
        ResponseInstruction resume(String flowExecutionKey, String eventId, ExternalContext context);
        ResponseInstruction refresh(String flowExecutionKey, ExternalContext context);
    }    	
    	

As you can see there are three central use-cases fulfilled by this interface:

  1. Launch (start) a new execution of a flow definition.

  2. Resume a paused flow execution by signaling an event against its current state.

  3. Request that the last response issued by a flow execution be re-issued. Unlike start and signalEvent, the refresh operation is an idempotent operation that does not affect the state of a flow execution.

Each operation accepts an ExternalContext that provides normalized access to properties of an external system that has called into Spring Web Flow, allowing access to environment-specific request parameters as well as request, session, and application-level attributes.

Each operation returns a ResponseInstruction which the calling system is expected to use to issue a suitable response.

These relationships are shown graphically below:

Flow executor

As you can see, an ExternalContext implementation exists 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 special case.

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

Property nameDescriptionCardinality
definitionLocatorThe service for loading flow definitions to be executed, typically a FlowDefinitionRegistry1
executionFactoryThe factory for creating new flow executions.1
executionRepositoryThe repository for saving and loading persistent (paused) flow executions1

5.2.2. A typical flow executor configuration with Spring 2.0

    <?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:flow="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-1.0.xsd">
           	    	
        <!-- 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/flows/**/*-flow.xml"/>
        </flow:registry>
        
    </beans>
	        

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.

5.2.3. A flow executor using a simple execution repository

    <flow:executor id="flowExecutor" registry-ref="flowRegistry" repository-type="simple"/>
	    	

This executor is configured with a simple repository that manages execution state in the user session.

5.2.4. A flow executor using a client-side continuation-based execution repository

    <flow:executor id="flowExecutor" registry-ref="flowRegistry" repository-type="client"/>
	    	

This executor is configured with a continuation-based repository that serializes continuation state to the client using no server-side state.

5.2.5. A flow executor using a single key execution repository

    <flow:executor id="flowExecutor" registry-ref="flowRegistry" repository-type="singleKey"/>
	    	

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.

5.2.6. A flow executor setting system execution attributes

    <flow:executor id="flowExecutor" registry-ref="flowRegistry" repository-type="continuation">
        <flow:execution-attributes>
            <flow:alwaysRedirectOnPause value="false"/>
            <flow:attribute name="foo" value="bar"/>
        </flow:execution-attributes>
    </flow-executor>
			

This executor is configured to set two flow execution system attributes alwaysRedirectOnPause=false and foo=bar.

[Note]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 where a flow execution redirect always occurs on pause.

5.2.7. A flow executor setting custom execution listeners

    <flow:executor id="flowExecutor" registry-ref="flowRegistry" repository-type="continuation">
        <flow:execution-listeners>
            <flow:listener ref="listener" criteria="order-flow"/>
        </flow:execution-listeners>
    </flow-executor>

    <!-- A FlowExecutionListener to observe the lifecycle of order-flow executions -->
    <bean id="listener" class="org.springframework.webflow.samples.sellitem.SellItemFlowExecutionListener"/>
			

This executor is configured to apply the execution listener to the "order-flow".

5.2.8. A Spring 1.2 compatible flow executor configuration

    <?xml version="1.0" encoding="UTF-8"?>	    	
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" 
	    "http://www.springframework.org/dtd/spring-beans.dtd">
    <beans>
    
        <!-- 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"/>
            <property name="executionAttributes">
                <map>
                    <entry key="alwaysRedirectOnPause">
                        <value type="java.lang.Boolean">false</value>
                    </entry>
                </map>
            </property>
            <property name="repositoryType" value="CONTINUATION"/>
        </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/flows/**/*-flow.xml</value>
                </list>
            </property>
        </bean>
	
    </beans>
	        

This achieves similar semantics as the Spring 2.0 version above. The 2.0 version is more concise, provides stronger validation, and encapsulates internal details such as FactoryBean class names. The 1.2 version is Spring 1.2 or > compatible and digestable by Spring IDE 1.3.

5.3. Spring MVC integration

Spring Web Flow integrates with both Servlet and Portlet MVC which ship with the core Spring Framework. Use of Portlet MVC requires Spring 2.0.

For both Servlet and Portlet MVC a FlowController acts as an adapter between Spring MVC and Spring Web Flow. As an adapter, this controller has knowledge of both systems and delegates to a flow executor for driving the execution of flows. One controller typically executes all flows of an application, relying on parameterization to determine what flow to launch or what flow execution to resume.

5.3.1. A single flow controller executing all flows in a Servlet MVC environment

    <bean name="/flowController.htm" class="org.springframework.webflow.executor.mvc.FlowController">
        <property name="flowExecutor" ref="flowExecutor"/>
    </bean>
	    	

This controller, exported at the context-relative /flowController.htm URL, delegates to the configured flow executor for driving flow executions in a Spring Servlet MVC environment.

5.3.2. A single portlet flow controller executing a flow within a Portlet

    <bean id="portletModeControllerMapping"
             class="org.springframework.web.portlet.handler.PortletModeHandlerMapping">
        <property name="portletModeMap">
            <map>
                <entry key="view" value-ref="flowController"/>
            </map>
        </property>
    </bean>

    <bean id="flowController" class="org.springframework.webflow.executor.mvc.PortletFlowController">
        <property name="flowExecutor" ref="flowExecutor"/>
        <property name="defaultFlowId" ref="search-flow"/>
    </bean>
	    	

This controller, exported for access with the configured portlet mode, delegates to the configured flow executor for driving flow executions in a Spring Portlet MVC environment (by default, an execution of the search-flow will be launched).

5.4. Flow executor parameterization

Spring Web Flow allows for full control over how flow executor method arguments such as the flowDefinitionId, flowExecutionKey, and eventId are extracted from an incoming controller request with the org.springframework.webflow.executor.support.FlowExecutorArgumentExtractor strategy.

[Note]Note
The various flow controllers typically do not use this strategy directly but instead use a convenient FlowExecutorArgumentHandler implementation that takes care of argument extraction as well as exposing responsibilities (in callback URLs).

The next several examples illustrate strategies for parameterizing flow controllers from the browser to launch and resume flow executions:

5.4.1. Request parameter-based flow executor argument extraction

The default executor argument extractor strategy is request-parameter based. The default request parameters are:

Table 5.2. Extractor request parameter names

Parameter nameDescription
_flowIdThe flow definition id, needed to launch a new flow execution.
_flowExecutionKeyThe flow execution key, needed to resume and refresh an existing flow execution.
_eventIdThe id of an event that occured, needed to resume an existing flow execution.

5.4.1.1. Launching a flow execution - parameter-style anchor

	<a href="flowController.htm?_flowId=myflow">Launch My Flow</a>
		    	

5.4.1.2. Launching a flow execution - form

    <form action="flowController.htm" method="post">
        <input type="submit" value="Launch My Flow"/>
        <input type="hidden" name="_flowId" value="myflow">
    </form>
	    		

5.4.1.3. Resuming a flow execution - anchor

    <a href="flowController.htm?_flowExecutionKey=${flowExecutionKey}&_eventId=submit">
        Submit
    </a>
	    		

5.4.1.4. Resuming a flow execution - form

    <form action="flowController.htm" method="post">
        ...
        <input type="hidden" name="_flowExecutionKey" value="${flowExecutionKey}">
        <input type="hidden" name="_eventId" value="submit"/>
        <input type="submit" class="button" value="Submit">
    </form>
		    	

5.4.1.5. Resuming a flow execution - multiple form buttons

    <form action="flowController.htm" method="post">
        ...
        <input type="hidden" name="_flowExecutionKey" value="${flowExecutionKey}">
        <input type="submit" class="button" name="_eventId_submit" value="Submit">
        <input type="submit" class="button" name="_eventId_cancel" value="Cancel">
    </form>
		    	
[Note]Note

In this case the eventId is determined by parsing the name of the button that was pressed.

5.4.1.6. Refreshing a flow execution

    <a href="flowController.htm?_flowExecutionKey=${flowExecutionKey}">Refresh</a>
	    		

5.4.2. Request path based flow executor argument extraction

The request-path based argument extractor strategy relies on executor arguments being path elements as much as possible. This results in friendlier REST-style URLs such as http://host/app/myflow instead of http://host/app?_flowId=myflow.

5.4.2.1. A flow controller with a request-path based argument extractor

    <bean name="/flowController.htm" class="org.springframework.webflow.executor.mvc.FlowController">
        <property name="flowExecutor" ref="flowExecutor"/>
        <property name="argumentHandler">
            <bean class="org.springframework.webflow.executor.support.RequestPathFlowExecutorArgumentHandler"/>
        </property>        
    </bean>
		    	

5.4.2.2. Launching a flow execution - REST-style anchor

	<a href="flowController/myflow"/>Launch My Flow</a>
		    	

5.4.2.3. Resuming a flow execution - multiple form buttons

    <form action="${flowExecutionKey}" method="post">
        ...
        <input type="submit" class="button" name="_eventId_submit" value="Submit">
        <input type="submit" class="button" name="_eventId_cancel" value="Cancel">
    </form>
		    	

5.4.2.4. Refreshing a flow execution

    <a href="flowController/k/${flowExecutionKey}">Refresh</a>
	    		

5.5. Struts integration

Spring Web Flow integrates with Struts 1.x or >. The integration is very similiar to Spring MVC where a single front controller (FlowAction) drives the execution of all flows for the application by delegating to a configured flow executor.

5.5.1. A single flow action executing all flows

    <form-beans>
        <form-bean name="actionForm" type="org.springframework.web.struts.SpringBindingActionForm"/>
    </form-beans>
	    	
    <action-mappings>
        <action path="/flowAction" name="actionForm" scope="request"
                type="org.springframework.webflow.executor.struts.FlowAction"/>
    </action-mappings>
	    	

5.6. Java Server Faces (JSF) integration

Spring Web Flow integrates with JSF. The JSF integration relies on custom implementations of core JSF artifacts such as navigation handler and phase listener to drive the execution of flows.

5.6.1. A typical faces-config.xml file

<faces-config>
    <application>
        <navigation-handler>
            org.springframework.webflow.executor.jsf.FlowNavigationHandler
        </navigation-handler>
        <property-resolver>
            org.springframework.webflow.executor.jsf.FlowPropertyResolver
        </property-resolver>
        <variable-resolver>
            org.springframework.webflow.executor.jsf.FlowVariableResolver
        </variable-resolver>
        <variable-resolver>
            org.springframework.web.jsf.DelegatingVariableResolver
        </variable-resolver>
        <variable-resolver>
            org.springframework.web.jsf.WebApplicationContextVariableResolver
        </variable-resolver>
    </application>

    <lifecycle>
        <phase-listener>org.springframework.webflow.executor.jsf.FlowPhaseListener</phase-listener>
    </lifecycle>
</faces-config>
	    	

5.6.2. Launching a flow execution - command link

    <h:commandLink value="Go" action="flowId:myflow"/>
	    	

5.6.3. Resuming a flow execution - form

    <h:form id="form">
        ...
        <h:inputText id="propertyName" value="#{flowScope.managedBeanName.propertyName}"/>
        ...
        <input type="hidden" name="_flowExecutionKey" value="${flowExecutionKey}">
        <h:commandButton type="submit" value="Next" action="submit"/>
    </h:form>