doc updates for m2

This commit is contained in:
Keith Donald
2007-10-31 01:07:18 +00:00
parent 0ef1dfaec4
commit 335fe25ea2
5 changed files with 86 additions and 625 deletions

View File

@@ -161,14 +161,6 @@
</entry>
<entry>Empty</entry>
</row>
<row>
<entry>inlineFlows</entry>
<entry>A set of inner flows that will be called as subflows; these flows are locally scoped to the outer flow.</entry>
<entry>
<emphasis>0..*</emphasis>
</entry>
<entry>Empty</entry>
</row>
</tbody>
</tgroup>
</table>
@@ -212,10 +204,6 @@
&lt;exception-handler .../&gt;
&lt;inline-flow&gt;
...
&lt;/inline-flow&gt;
&lt;/flow&gt;
</programlisting>
</sect2>
@@ -233,7 +221,6 @@
flow.setOutputMapper(...);
flow.getGlobalTransitionSet().add(...);
flow.getExceptionHandlerSet().add(...);
flow.addInlineFlow(...);
</programlisting>
<para>
A Flow is typically built by a FlowBuilder rather than assembled
@@ -364,7 +351,7 @@
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"&gt;
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"&gt;
&lt;start-state idref="myStateId"/&gt;
@@ -576,7 +563,7 @@
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"&gt;
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"&gt;
&lt;start-state idref="state1"/&gt;
@@ -635,7 +622,7 @@
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"&gt;
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"&gt;
&lt;start-state idref="state1"/&gt;
@@ -666,7 +653,7 @@
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"&gt;
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"&gt;
&lt;start-state idref="state1"/&gt;
@@ -699,7 +686,7 @@
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"&gt;
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"&gt;
&lt;start-state idref="state1"/&gt;
@@ -740,7 +727,7 @@
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"&gt;
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"&gt;
&lt;start-state idref="state1"/&gt;
@@ -811,20 +798,12 @@
<orderedlist>
<listitem>
<para>
The entered view state makes a <literal>org.springframework.webflow.execution.ViewSelection</literal>
that represents a <emphasis>logical</emphasis> response to issue to the caller.
The entered view state renters a view to issue a response to the caller.
</para>
</listitem>
<listitem>
<para>
The flow execution 'pauses' in this state, and control is returned to the calling
system.
</para>
</listitem>
<listitem>
<para>
The calling system uses the returned <literal>ViewSelection</literal> to present a
suitable interface (or other response) to the user.
The flow execution 'pauses' in this state, and control is returned to the calling system.
</para>
</listitem>
<listitem>
@@ -837,10 +816,7 @@
</para>
<para>
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. Spring Web Flow is currently <emphasis>not</emphasis>
concerned with rendering the actual response; as a controller, a flow makes a <emphasis>logical</emphasis> view selection when user input is required,
where a view selection serves as a response instruction. It is up to the calling system to interpret that instruction to issue a
response suitable for the environment in which the flow is executing.
how a view state responds to a user input event.
</para>
<para>
The properties of a <literal>org.springframework.webflow.engine.ViewState</literal> are summarized below:
@@ -862,7 +838,7 @@
</thead>
<tbody>
<row>
<entry>viewSelector</entry>
<entry>viewFactory</entry>
<entry>The strategy that makes the view selection when this state is entered.</entry>
<entry>
<emphasis>0..1</emphasis>
@@ -884,13 +860,13 @@
</tgroup>
</table>
<para>
The <literal>org.springframework.webflow.execution.ViewSelection</literal> base class is abstract,
acting as a marker indicating a response should be issued to the client interacting
with the flow. Concrete subtypes exist for each of the supported response types.
These response types are summarized below:
The <literal>org.springframework.webflow.execution.ViewFactory</literal> 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:
</para>
<table>
<title>Concrete ViewSelection types</title>
<title>Concrete ViewFactory types</title>
<tgroup cols="2">
<colspec colname="c1" colwidth="2*" />
<colspec colname="c2" colwidth="4*" />
@@ -902,115 +878,23 @@
</thead>
<tbody>
<row>
<entry>ApplicationView</entry>
<entry>Requests the rendering of a local, internal application view resource such as a JSP, Velocity, or Freemarker template.</entry>
<entry>JsfViewFactory</entry>
<entry>Requests the rendering of Java Server Faces (JSF) view.</entry>
</row>
<row>
<entry>FlowExecutionRedirect</entry>
<entry>MvcViewFactory</entry>
<entry>
Requests a redirect back to the ViewState at a unique <emphasis>flow execution URL</emphasis>.
When this URL is accessed on subsequent requests, an ApplicationView will be reconstituted and rendered.
The URL is refreshable while the flow execution remains active.
<note>
<para>
Multiple flow execution URLs may be generated for a single logical user conversation.
In that case, each flow execution URL provides access to the conversation
from a previous point (ViewState). Accessing the URL refreshes the execution
from that point.
</para>
</note>
</entry>
</row>
<row>
<entry>FlowDefinitionRedirect</entry>
<entry>
Requests a redirect that launches an entirely new flow execution. Used to support
<emphasis>redirect to flow</emphasis> (flow chaining) and <emphasis>restart flow</emphasis> use cases.
</entry>
</row>
<row>
<entry>ExternalRedirect</entry>
<entry>
Requests a redirect to an arbitrary external URL, typically used to inteface
with an external system.
</entry>
</row>
<row>
<entry>NullView</entry>
<entry>
Requests that no response be issued; for use in corner cases where the flow itself has already
issued the response.
Requests the rendering of a Spring MVC view.
</entry>
</row>
</tbody>
</tgroup>
</table>
<sect3 id="viewSelector">
<title>ViewSelector</title>
<para>
The creational strategy responsible for making a <literal>ViewSelection</literal> when an <literal>ViewState</literal> is entered
is <literal>org.springframework.webflow.engine.ViewSelector</literal>. This provides a plugin-point for customizing <emphasis>how</emphasis>
a response instruction is constructed.
</para>
<para>
Four <literal>ViewSelector</literal> implementations are provided with Spring Web Flow:
</para>
<table>
<title>ViewSelector implementations</title>
<tgroup cols="2">
<colspec colname="c1" colwidth="2*" />
<colspec colname="c2" colwidth="4*" />
<thead>
<row>
<entry>Implementation</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>ApplicationViewSelector</entry>
<entry>
Returns an ApplicationView referencing a logical <literal>viewName</literal> to render and containing a
<literal>modelMap</literal> with the application data needed by the rendering process
(by default, this map contains the union of the data scopes such flow, flash, and request scope).
Supports setting a <literal>redirect</literal> flag that triggers
a browser redirect to the selected view using a <literal>FlowExecutionRedirect</literal>.
The default implementation.
</entry>
</row>
<row>
<entry>FlowDefinitionRedirectSelector</entry>
<entry>
Returns a FlowDefinitionRedirect with a <literal>flowId</literal> and <literal>executionInput</literal> map requesting
the launch of an entirely new flow execution (an instance of the FlowDefinition identified by the flowId).
Useful for <emphasis>redirect after flow completion</emphasis>, where one flow ending should trigger
the start of another flow independently.
</entry>
</row>
<row>
<entry>ExternalRedirectSelector</entry>
<entry>
Returns an ExternalRedirect that triggers a browser redirect to an abitrary external URL.
Mainly used by end states to redirect to external systems after flow completion,
but can also be used by view states to interface with an external system that may
call back into the flow execution at a later point.
</entry>
</row>
<row>
<entry>NullViewSelector</entry>
<entry>
Returns an NullView indicating that no response should be issued.
</entry>
</row>
</tbody>
</tgroup>
</table>
</sect3>
<sect3>
<title>ViewState class diagram</title>
<para>
The class diagram below shows the ViewState and the associated types used to carry
out the view selection process:
out the view selection process (TODO - needs updating for 2.0):
</para>
<mediaobject>
<imageobject role="fo">
@@ -1037,7 +921,7 @@
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"&gt;
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"&gt;
&lt;start-state idref="displaySearchForm"/&gt;
@@ -1089,7 +973,7 @@
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"&gt;
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"&gt;
&lt;start-state idref="displayList"/&gt;
@@ -1150,7 +1034,7 @@
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"&gt;
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"&gt;
&lt;start-state idref="displayPdf"/&gt;
@@ -1201,7 +1085,7 @@
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"&gt;
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"&gt;
&lt;start-state idref="displayForm"/&gt;
@@ -1467,7 +1351,7 @@
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"&gt;
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"&gt;
&lt;start-state idref="executeSearch"/&gt;
@@ -1553,7 +1437,7 @@
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"&gt;
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"&gt;
&lt;start-state idref="executeSearch"/&gt;
@@ -1659,7 +1543,7 @@
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"&gt;
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"&gt;
&lt;start-state idref="executeSearch"/&gt;
@@ -1735,7 +1619,7 @@
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"&gt;
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"&gt;
...
@@ -1830,7 +1714,7 @@
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"&gt;
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"&gt;
...
@@ -1869,7 +1753,7 @@
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"&gt;
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"&gt;
&lt;action-state id="getNextInterviewQuestion"&gt;
@@ -1904,7 +1788,7 @@
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"&gt;
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"&gt;
&lt;view-state id="selectFile" view="fileUploadForm"&gt;
&lt;transition on="submit" to="uploadFile"/&gt;
@@ -2060,7 +1944,7 @@
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"&gt;
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"&gt;
...
&lt;decision-state id="shippingRequired"&gt;
@@ -2233,7 +2117,7 @@
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"&gt;
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"&gt;
...
@@ -2306,7 +2190,7 @@
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"&gt;
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"&gt;
&lt;input-mapper&gt;
&lt;input-attribute name="shipping"/&gt;
@@ -2338,13 +2222,12 @@
<para>
If the ended flow was acting as a top-level or <emphasis>root flow</emphasis>, the
entire flow execution ends and cannot be resumed. In this case, the end state is responsible
for making a <literal>ViewSelection</literal> that is the basis for the ending response (for example,
a confirmation page, or a redirect request to another flow or an external URL).
for issuing a final response (for example, a confirmation page, or a redirect request to another flow or an external URL).
</para>
<para>
If the ended flow was acting as a subflow, the spawned subflow session ends and
the calling parent flow <emphasis>resumes</emphasis> by responding to the end
result returned. In this case, the responsibility for any <literal>ViewSelection</literal>
result returned. In this case, the responsibility for any final response
falls on the parent flow.
</para>
<para>
@@ -2352,8 +2235,8 @@
and become eligible for garbage collection.
</para>
<para>
As outlined, an end state entered as part of a root flow messages its <literal>ViewSelector</literal>
to make a ending view selection. Typically this is a redirect-based <literal>ViewSelector</literal>,
As outlined, an end state entered as part of a root flow messages its <literal>finalResponseAction</literal>
to make a ending view selection. Typically this is a redirect-based response type,
allowing for <emphasis>redirect after flow completion</emphasis>. An end state entered as part of
a subflow is not responsible for a view selection; this responsibility falls on the calling flow.
</para>
@@ -2399,8 +2282,8 @@
</thead>
<tbody>
<row>
<entry>viewSelector</entry>
<entry>The strategy that makes the ending view selection when this state is entered and the flow is a root flow.</entry>
<entry>finalResponseAction</entry>
<entry>The strategy that makes the ending response when this state is entered and the flow is a root flow.</entry>
<entry>
<emphasis>0..1</emphasis>
</entry>
@@ -2433,7 +2316,7 @@
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"&gt;
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"&gt;
...
@@ -2459,7 +2342,7 @@
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"&gt;
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"&gt;
...
@@ -2490,7 +2373,7 @@
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"&gt;
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"&gt;
...
@@ -2543,7 +2426,7 @@
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"&gt;
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"&gt;
...

View File

@@ -52,15 +52,12 @@
<sect2 id="execution-start">
<title>Flow execution startup</title>
<programlisting>
MutableAttributeMap input = ...
ExternalContext context = ...
ViewSelection startingView = execution.start(input, context);
execution.start(context);
</programlisting>
<para>
When a flow execution reaches a state where input is required to continue, it is said to have <emphasis>paused</emphasis>,
where it waits in that state for user input to be provided. After pausing, the
<literal>ViewSelection</literal> returned is used to issue a response to the user
that provides a vehicle for collecting the required user input.
where it waits in that state for user input to be provided.
</para>
<para>
User input is provided by <emphasis>signaling an event</emphasis> that
@@ -78,7 +75,7 @@
<title>Flow execution resume</title>
<programlisting>
ExternalContext context = ...
ViewSelection nextView = execution.signalEvent("submit", context);
execution.resume(context);
if (execution.isActive()) {
// still active but paused
} else {
@@ -544,7 +541,7 @@
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"&gt;
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd"&gt;
&lt;start-state idref="enterCriteria"/&gt;
@@ -671,138 +668,6 @@
</tbody>
</tgroup>
</table>
<para>
The completed test for this example extending <literal>AbstractXmlFlowExecutionTests</literal> is shown below:
</para>
<programlisting>
public class SearchFlowExecutionTests extends AbstractXmlFlowExecutionTests {
public void testStartFlow() {
ApplicationView view = applicationView(startFlow());
assertCurrentStateEquals("enterCriteria");
assertViewNameEquals("searchCriteria", view);
assertModelAttributeNotNull("searchCriteria", view);
}
public void testCriteriaSubmitSuccess() {
startFlow();
MockParameterMap parameters = new MockParameterMap();
parameters.put("firstName", "Keith");
parameters.put("lastName", "Donald");
ApplicationView view = applicationView(signalEvent("search", parameters));
assertCurrentStateEquals("displayResults");
assertViewNameEquals("searchResults", view);
assertModelAttributeCollectionSize(1, "results", view);
}
public void testCriteriaSubmitError() {
startFlow();
signalEvent("search");
assertCurrentStateEquals("enterCriteria");
}
public void testNewSearch() {
testCriteriaSubmitSuccess();
ApplicationView view = applicationView(signalEvent("newSearch"));
assertCurrentStateEquals("enterCriteria");
assertViewNameEquals("searchCriteria", view);
}
public void testSelectValidResult() {
testCriteriaSubmitSuccess();
MockParameterMap parameters = new MockParameterMap();
parameters.put("id", "1");
ApplicationView view = applicationView(signalEvent("select", parameters));
assertCurrentStateEquals("displayResults");
assertViewNameEquals("searchResults", view);
assertModelAttributeCollectionSize(1, "results", view);
}
@Override
protected FlowDefinitionResource getFlowDefinitionResource() {
return createFlowDefinitionResource("src/main/webapp/WEB-INF/flows/search-flow.xml");
}
@Override
protected void registerMockServices(MockFlowServiceLocator serviceRegistry) {
Flow mockDetailFlow = new Flow("detail-flow");
mockDetailFlow.setInputMapper(new AttributeMapper() {
public void map(Object source, Object target, Map context) {
assertEquals("id of value 1 not provided as input by calling search flow", new Long(1), ((AttributeMap)source).get("id"));
}
});
// test responding to finish result
new EndState(mockDetailFlow, "finish");
serviceRegistry.registerSubflow(mockDetailFlow);
serviceRegistry.registerBean("phonebook", new ArrayListPhoneBook());
}
}
</programlisting>
<para>
With a well-written flow execution test passing that exercises the scenarios
possible for your flow, you have concrete evidence the flow will execute as expected when
deployed into a container.
</para>
<mediaobject>
<imageobject role="fo">
<imagedata fileref="images/junit-greenbar.png" format="PNG" align="center"/>
</imageobject>
<imageobject role="html">
<imagedata fileref="images/junit-greenbar.png" format="PNG" align="center"/>
</imageobject>
<caption>
<para>Go for Green</para>
</caption>
</mediaobject>
</sect2>
<sect2 id="execution-testing-system">
<title>Execution unit testing vs. full-blown system testing</title>
<para>
The previous example shows how to test a flow execution in relative isolation with a mock service
layer and mock subflows. Flow execution testing against a real service-layer
and real subflows is also supported.
</para>
<para>
The next example shows how the <literal>createFlowServiceLocator</literal> method can
be overridden to create the service-layer using a Spring application context:
</para>
<programlisting>
public class SearchFlowExecutionTests extends AbstractXmlFlowExecutionTests {
...
@Override
protected FlowDefinitionResource getFlowDefinitionResource() {
return createFlowDefinitionResource("src/main/webapp/WEB-INF/flows/search-flow.xml");
}
@Override
protected FlowServiceLocator createFlowServiceLocator() {
// create a context to host our middle tier services
ApplicationContext context =
new ClassPathXmlApplicationContext(new String[] {
"classpath:service-layer-config.xml",
"classpath:data-access-layer-config.xml"
});
// create a registry for our flow definitions being tested
FlowDefinitionRegistry registry = new FlowDefinitionRegistryImpl();
// initialize the service locator
DefaultFlowServiceLocator locator = new DefaultFlowServiceLocator(registry, context);
// perform subflow definition registration with the help of a registrar
XmlFlowRegistrar registrar = new XmlFlowRegistrar(locator);
registrar.addResource(createFlowDefinitionResource("/WEB-INF/flows/search-flow.xml"));
registrar.addResource(createFlowDefinitionResource("/WEB-INF/flows/detail-flow.xml"));
registrar.registerFlowDefinitions(registry);
return locator;
}
}
</programlisting>
</sect2>
</sect1>
</chapter>

View File

@@ -26,59 +26,31 @@
</para>
<programlisting>
public interface FlowExecutor {
ResponseInstruction launch(String flowDefinitionId, ExternalContext context);
ResponseInstruction resume(String flowExecutionKey, String eventId, ExternalContext context);
ResponseInstruction refresh(String flowExecutionKey, ExternalContext context);
public void executeFlowRequest(ExternalContext context);
}
</programlisting>
<para>
As you can see there are three central use-cases fulfilled by this interface:
Execution typically consists of either:
<orderedlist>
<listitem>
<para>
Launch (start) a new execution of a flow definition.
Launching (start) a new execution of a flow definition.
</para>
</listitem>
<listitem>
<para>
Resume a paused flow execution by signaling an event against its current state.
</para>
</listitem>
<listitem>
<para>
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 change the state of a flow execution.
Resuming a paused flow execution by signaling an event against its current state.
</para>
</listitem>
</orderedlist>
</para>
<para>
Each operation accepts an <literal>ExternalContext</literal> that provides normalized
access to properties of an external system that has called into Spring Web Flow.
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.
</para>
<para>
Each operation returns a <literal>ResponseInstruction</literal>, which the calling system is
expected to use to issue a suitable response.
</para>
<para>
These relationships are shown graphically below:
</para>
<mediaobject>
<imageobject role="fo">
<imagedata fileref="images/flowexecutorfacade-classdiagram.png" format="PNG" align="center"/>
</imageobject>
<imageobject role="html">
<imagedata fileref="images/flowexecutorfacade-classdiagram.png" format="PNG" align="center"/>
</imageobject>
<caption>
<para>Flow executor</para>
</caption>
</mediaobject>
<para>
As you can see, an <literal>ExternalContext</literal> implementation exists for each of
<literal>ExternalContext</literal> 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.
@@ -139,20 +111,20 @@
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;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"
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-1.0.xsd"&gt;
http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.0.xsd"&gt;
&lt;!-- Launches new flow executions and resumes existing executions. --&gt;
&lt;flow:executor id="flowExecutor" registry-ref="flowRegistry"/>
&lt;web:flow-executor id="flowExecutor" registry-ref="flowRegistry"/>
&lt;!-- Creates the registry of flow definitions for this application --&gt;
&lt;flow:registry id="flowRegistry"&gt;
&lt;flow:location path="/WEB-INF/flows/**/*-flow.xml"/&gt;
&lt;/flow:registry&gt;
&lt;web:flow-registry id="flowRegistry"&gt;
&lt;web:flow-location path="/WEB-INF/flows/**/*-flow.xml"/&gt;
&lt;/web:flow-registry&gt;
&lt;/beans&gt;
</programlisting>
@@ -165,7 +137,7 @@
<sect2 id="executor-custom-repo">
<title>A flow executor using a simple execution repository</title>
<programlisting>
&lt;flow:executor id="flowExecutor" registry-ref="flowRegistry" repository-type="simple"/&gt;
&lt;web:flow-executor id="flowExecutor" registry-ref="flowRegistry" repository-type="simple"/&gt;
</programlisting>
<para>
This executor is configured with a simple repository that manages
@@ -175,7 +147,7 @@
<sect2 id="executor-custom-repo2">
<title>A flow executor using a client-side continuation-based execution repository</title>
<programlisting>
&lt;flow:executor id="flowExecutor" registry-ref="flowRegistry" repository-type="client"/&gt;
&lt;web:flow-executor id="flowExecutor" registry-ref="flowRegistry" repository-type="client"/&gt;
</programlisting>
<para>
This executor is configured with a continuation-based repository that serializes
@@ -185,7 +157,7 @@
<sect2 id="executor-custom-repo3">
<title>A flow executor using a single key execution repository</title>
<programlisting>
&lt;flow:executor id="flowExecutor" registry-ref="flowRegistry" repository-type="singleKey"/>&gt;
&lt;web:flow-executor id="flowExecutor" registry-ref="flowRegistry" repository-type="singleKey"/>&gt;
</programlisting>
<para>
This executor is configured with a simple repository that assigns a single
@@ -196,9 +168,9 @@
<sect2 id="executor-custom-repo4">
<title>A flow executor setting custom conversation management attributes</title>
<programlisting>
&lt;flow:executor id="flowExecutor" registry-ref="flowRegistry">
&lt;flow:repository type="continuation" max-conversations="5" max-continuations="30" conversation-manager-ref="conversationManager"/&gt;
&lt;/flow:executor&gt;
&lt;web:flow-executor id="flowExecutor" registry-ref="flowRegistry">
&lt;web:flow-repository type="continuation" max-conversations="5" max-continuations="30" conversation-manager-ref="conversationManager"/&gt;
&lt;/web:flow-executor&gt;
&lt;bean id="conversationManager" class="example.MyCustomConversationalStateManager"/&gt;
</programlisting>
@@ -216,12 +188,12 @@
<sect2 id="executor-excution-attributes">
<title>A flow executor setting system execution attributes</title>
<programlisting>
&lt;flow:executor id="flowExecutor" registry-ref="flowRegistry" repository-type="continuation"&gt;
&lt;flow:execution-attributes&gt;
&lt;flow:alwaysRedirectOnPause value="false"/&gt;
&lt;flow:attribute name="foo" value="bar"/&gt;
&lt;web:flow-executor id="flowExecutor" registry-ref="flowRegistry" repository-type="continuation"&gt;
&lt;web:flow-execution-attributes&gt;
&lt;web:alwaysRedirectOnPause value="false"/&gt;
&lt;web:attribute name="foo" value="bar"/&gt;
&lt;/flow:execution-attributes&gt;
&lt;/flow-executor&gt;
&lt;/web:flow-executor&gt;
</programlisting>
<para>
This executor is configured to set two flow execution system attributes
@@ -241,11 +213,11 @@
<sect2 id="executor-excution-listeners">
<title>A flow executor setting custom execution listeners</title>
<programlisting>
&lt;flow:executor id="flowExecutor" registry-ref="flowRegistry" repository-type="continuation"&gt;
&lt;flow:execution-listeners&gt;
&lt;flow:listener ref="listener" criteria="order-flow"/&gt;
&lt;/flow:execution-listeners&gt;
&lt;/flow-executor&gt;
&lt;web:flow-executor id="flowExecutor" registry-ref="flowRegistry" repository-type="continuation"&gt;
&lt;web:flow-execution-listeners&gt;
&lt;web:flow-listener ref="listener" criteria="order-flow"/&gt;
&lt;/web:flow-execution-listeners&gt;
&lt;/web:flow-executor&gt;
&lt;!-- A FlowExecutionListener to observe the lifecycle of order-flow executions --&gt;
&lt;bean id="listener" class="example.OrderFlowExecutionListener"/&gt;
@@ -254,264 +226,5 @@
This executor is configured to apply the execution listener to the "order-flow".
</para>
</sect2>
<sect2 id="executor-1.2-compatible">
<title>A Spring 1.2 compatible flow executor configuration</title>
<programlisting>
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd"&gt;
&lt;beans&gt;
&lt;!-- Launches new flow executions and resumes existing executions: Spring 1.2 config version --&gt;
&lt;bean id="flowExecutor" class="org.springframework.webflow.config.FlowExecutorFactoryBean"&gt;
&lt;property name="definitionLocator" ref="flowRegistry"/&gt;
&lt;property name="executionAttributes"&gt;
&lt;map&gt;
&lt;entry key="alwaysRedirectOnPause"&gt;
&lt;value type="java.lang.Boolean"&gt;false&lt;/value&gt;
&lt;/entry&gt;
&lt;/map&gt;
&lt;/property&gt;
&lt;property name="repositoryType" value="CONTINUATION"/&gt;
&lt;/bean&gt;
&lt;!-- Creates the registry of flow definitions for this application: Spring 1.2 config version --&gt;
&lt;bean id="flowRegistry"
class="org.springframework.webflow.engine.builder.xml.XmlFlowRegistryFactoryBean"&gt;
&lt;property name="flowLocations"&gt;
&lt;list&gt;
&lt;value&gt;/WEB-INF/flows/**/*-flow.xml&lt;/value&gt;
&lt;/list&gt;
&lt;/property&gt;
&lt;/bean&gt;
&lt;/beans&gt;
</programlisting>
<para>
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.
</para>
</sect2>
</sect1>
<sect1 id="executor-parameterization">
<title>Flow executor parameterization</title>
<para>
Spring Web Flow allows for full control over how flow executor method arguments such as the
<literal>flowDefinitionId</literal>, <literal>flowExecutionKey</literal>, and <literal>eventId</literal>
are extracted from an incoming controller request with the
<literal>org.springframework.webflow.executor.support.FlowExecutorArgumentExtractor</literal>
strategy.
</para>
<para>
The next several examples illustrate strategies for parameterizing flow controllers
from the browser to launch and resume flow executions:
</para>
<sect2 id="executor-parmeterization-default">
<title>Request parameter-based flow executor argument extraction</title>
<para>
The default executor argument extractor strategy is request-parameter based.
The default request parameters are:
</para>
<table>
<title>Extractor request parameter names</title>
<tgroup cols="2">
<colspec colname="c1" colwidth="2*" />
<colspec colname="c2" colwidth="4*" />
<thead>
<row>
<entry>Parameter name</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry>_flowId</entry>
<entry>The flow definition id, needed to launch a new flow execution.</entry>
</row>
<row>
<entry>_flowExecutionKey</entry>
<entry>The flow execution key, needed to resume and refresh an existing flow execution.</entry>
</row>
<row>
<entry>_eventId</entry>
<entry>The id of an event that occured, needed to resume an existing flow execution.</entry>
</row>
</tbody>
</tgroup>
</table>
<sect3 id="executor-mvc-launch-get">
<title>Launching a flow execution - parameter-style anchor</title>
<programlisting>
&lt;a href="flowController.htm?_flowId=myflow"&gt;Launch My Flow&lt;/a&gt;
</programlisting>
</sect3>
<sect3 id="executor-mvc-launch-post">
<title>Launching a flow execution - form</title>
<programlisting>
&lt;form action="flowController.htm" method="post"&gt;
&lt;input type="submit" value="Launch My Flow"/>
&lt;input type="hidden" name="_flowId" value="myflow"&gt;
&lt;/form&gt;
</programlisting>
</sect3>
<sect3 id="executor-mvc-resume-anchor">
<title>Resuming a flow execution - anchor</title>
<programlisting>
&lt;a href="flowController.htm?_flowExecutionKey=${flowExecutionKey}&amp;_eventId=submit"&gt;
Submit
&lt;/a&gt;
</programlisting>
</sect3>
<sect3 id="executor-mvc-resume-form">
<title>Resuming a flow execution - form</title>
<programlisting>
&lt;form action="flowController.htm" method="post"&gt;
...
&lt;input type="hidden" name="_flowExecutionKey" value="${flowExecutionKey}"&gt;
&lt;input type="hidden" name="_eventId" value="submit"/>
&lt;input type="submit" class="button" value="Submit"&gt;
&lt;/form&gt;
</programlisting>
</sect3>
<sect3 id="executor-mvc-resume-form-buttons">
<title>Resuming a flow execution - multiple form buttons</title>
<programlisting>
&lt;form action="flowController.htm" method="post"&gt;
...
&lt;input type="hidden" name="_flowExecutionKey" value="${flowExecutionKey}"&gt;
&lt;input type="submit" class="button" name="_eventId_submit" value="Submit"&gt;
&lt;input type="submit" class="button" name="_eventId_cancel" value="Cancel"&gt;
&lt;/form&gt;
</programlisting>
<note>
<para>
In this case, the <literal>eventId</literal> is determined by parsing the name of the
button that was pressed.
</para>
</note>
</sect3>
<sect3 id="executor-mvc-refresh">
<title>Refreshing a flow execution</title>
<programlisting>
&lt;a href="flowController.htm?_flowExecutionKey=${flowExecutionKey}"&gt;Refresh&lt;/a&gt;
</programlisting>
</sect3>
</sect2>
<sect2 id="executor-extraction-requestpath">
<title>Request path based flow executor argument extraction</title>
<para>
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 <literal>http://host/app/myflow</literal>, instead of
<literal>http://host/app?_flowId=myflow</literal>.
</para>
<sect3 id="executor-servletmvc-pathextractor">
<title>A flow controller with a request-path based argument extractor</title>
<programlisting>
&lt;bean name="/flowController.htm" class="org.springframework.webflow.executor.mvc.FlowController"&gt;
&lt;property name="flowExecutor" ref="flowExecutor"/&gt;
&lt;property name="argumentHandler"&gt;
&lt;bean class="org.springframework.webflow.executor.support.RequestPathFlowExecutorArgumentHandler"/&gt;
&lt;/property&gt;
&lt;/bean&gt;
</programlisting>
</sect3>
<sect3 id="executor-mvc-launch-get-rest">
<title>Launching a flow execution - REST-style anchor</title>
<programlisting>
&lt;a href="flowController/myflow"/&gt;Launch My Flow&lt;/a&gt;
</programlisting>
</sect3>
<sect3 id="executor-mvc-resume-form-buttons-requestpath">
<title>Resuming a flow execution - multiple form buttons</title>
<programlisting>
&lt;form action="${flowExecutionKey}" method="post"&gt;
...
&lt;input type="submit" class="button" name="_eventId_submit" value="Submit"&gt;
&lt;input type="submit" class="button" name="_eventId_cancel" value="Cancel"&gt;
&lt;/form&gt;
</programlisting>
</sect3>
<sect3 id="executor-mvc-refresh-requestpath">
<title>Refreshing a flow execution</title>
<programlisting>
&lt;a href="flowController/k/${flowExecutionKey}"&gt;Refresh&lt;/a&gt;
</programlisting>
</sect3>
</sect2>
</sect1>
<sect1 id="executor-mvc">
<title>Spring MVC integration</title>
<para>
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.
</para>
<para>
For both Servlet and Portlet MVC, a <literal>FlowController</literal> 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.
</para>
<sect2 id="executor-servletmvc">
<title>A single flow controller executing all flows in a Servlet MVC environment</title>
<programlisting>
&lt;bean name="/flowController.htm" class="org.springframework.webflow.executor.mvc.FlowController"&gt;
&lt;property name="flowExecutor" ref="flowExecutor"/&gt;
&lt;/bean&gt;
</programlisting>
<para>
This controller, exported at the context-relative <literal>/flowController.htm</literal> URL,
delegates to the configured flow executor for driving flow executions in a Spring Servlet
MVC environment.
</para>
</sect2>
<sect2 id="executor-portletmvc">
<title>A single portlet flow controller executing a flow within a Portlet</title>
<programlisting>
&lt;bean id="portletModeControllerMapping"
class="org.springframework.web.portlet.handler.PortletModeHandlerMapping">
&lt;property name="portletModeMap">
&lt;map&gt;
&lt;entry key="view" value-ref="flowController"/&gt;
&lt;/map&gt;
&lt;/property&gt;
&lt;/bean&gt;
&lt;bean id="flowController" class="org.springframework.webflow.executor.mvc.PortletFlowController"&gt;
&lt;property name="flowExecutor" ref="flowExecutor"/&gt;
&lt;property name="defaultFlowId" ref="search-flow"/&gt;
&lt;/bean&gt;
</programlisting>
<para>
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 <literal>search-flow</literal>
will be launched).
</para>
</sect2>
</sect1>
<sect1 id="executor-struts">
<title>Struts integration</title>
<para>
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.
</para>
<sect2 id="executor-struts-simple">
<title>A single flow action executing all flows</title>
<programlisting>
&lt;form-beans&gt;
&lt;form-bean name="actionForm" type="org.springframework.web.struts.SpringBindingActionForm"/&gt;
&lt;/form-beans&gt;
&lt;action-mappings&gt;
&lt;action path="/flowAction" name="actionForm" scope="request"
type="org.springframework.webflow.executor.struts.FlowAction"/&gt;
&lt;/action-mappings&gt;
</programlisting>
</sect2>
</sect1>
</chapter>

View File

@@ -31,6 +31,10 @@
<firstname>Ross</firstname>
<surname>Stoyanchev</surname>
</author>
<author>
<firstname>Jeremy</firstname>
<surname>Grelle</surname>
</author>
</authorgroup>
<legalnotice>
<para>

View File

@@ -271,8 +271,8 @@
</entry>
<entry>
Core,
context.servlet requires Servlet API 2.3,
context.portlet requires Portlet API 1.0 in addition to Servlet API 2.3
context.servlet requires Servlet API 2.4,
context.portlet requires Portlet API 1.0 in addition to Servlet API 2.4
</entry>
</row>
<row>
@@ -419,8 +419,8 @@
</entry>
<entry>
Engine Implementation,
Spring Beans 1.2.7,
Spring Context 1.2.7,
Spring Beans 2.0,
Spring Context 2.0,
<literal>builder.xml</literal> requires JDK 1.5 or Xerces for XSD support
</entry>
</row>
@@ -475,7 +475,7 @@
</entry>
<entry>None</entry>
<entry>
Spring Beans 1.2.7,
Spring Beans 2.0,
JUnit 3.8.1
</entry>
</row>
@@ -533,8 +533,7 @@
<entry>None</entry>
<entry>
Core,
Spring Web MVC 1.2.7,
Portlet MVC requires Spring 2.0
Spring Web MVC 2.0
</entry>
</row>
<row>
@@ -625,9 +624,6 @@
<para>
XML-based flow building requires Xerces 2 or JDK 5.0 (for XSD support).
</para>
<para>
The Spring Web Flow Portlet integration requires Spring Portlet MVC 2.0.
</para>
<para>
Our active community support forum is located at http://forum.springframework.org.
</para>