This commit is contained in:
Keith Donald
2008-04-13 07:16:44 +00:00
parent 1a8e2678d4
commit ec96e9bb51
8 changed files with 221 additions and 224 deletions

View File

@@ -197,7 +197,7 @@
</para>
<programlisting language="xml"><![CDATA[
<evaluate expression="bookingService.findHotels(searchCriteria)" result="flowScope.hotels"
result-type="dataModel"/>]]>
result-type="dataModel"/>]]>
</programlisting>
</sect3>
</sect2>
@@ -216,7 +216,7 @@
<on-start>
<evaluate expression="bookingService.createBooking(hotelId, currentUser.name)"
result="flowScope.booking" />
result="flowScope.booking" />
</on-start>
<view-state id="enterBookingDetails">
@@ -342,7 +342,7 @@ public interface FlowOutcome {
<on-start>
<evaluate expression="bookingService.createBooking(hotelId, currentUser.name)"
result="flowScope.booking" />
result="flowScope.booking" />
</on-start>
<view-state id="enterBookingDetails">
@@ -453,7 +453,7 @@ public interface FlowOutcome {
<on-start>
<evaluate expression="bookingService.createBooking(hotelId, currentUser.name)"
result="flowScope.booking" />
result="flowScope.booking" />
</on-start>
<view-state id="enterBookingDetails">

View File

@@ -65,8 +65,8 @@
Such expressions are dynamically evaluated by the EL and should not be enclosed in delimiters like <code>${}</code> or <code>#{}</code>.
For example:
</para>
<programlisting type="xml">
&lt;evaluate expression="searchCriteria.nextPage()" /&gt;
<programlisting type="xml"><![CDATA[
<evaluate expression="searchCriteria.nextPage()" />]]>
</programlisting>
<para>
The expression above is a standard expression that invokes the <code>nextPage</code> method on the <code>searchCriteria</code> variable when evaluated.
@@ -84,8 +84,8 @@
Each eval block is explictly delimited with the <code>${}</code> delimiters.
For example:
</para>
<programlisting type="xml">
&lt;view-state id="error" view="error-${externalContext.locale}.xhtml" /&gt;
<programlisting type="xml"><![CDATA[
<view-state id="error" view="error-${externalContext.locale}.xhtml" />]]>
</programlisting>
<para>
The expression above is a template expression.
@@ -110,8 +110,8 @@
Use <code>flowScope</code> to assign a flow variable.
Flow scope gets allocated when a flow starts and destroyed when the flow ends.
</para>
<programlisting type="xml">
&lt;evaluate expression="searchService.findHotel(hotelId)" result="flowScope.hotel" /&gt;
<programlisting type="xml"><![CDATA[
<evaluate expression="searchService.findHotel(hotelId)" result="flowScope.hotel" />]]>
</programlisting>
</sect2>
<sect2 id="el-variable-viewScope">
@@ -121,11 +121,11 @@
View scope gets allocated when a <code>view-state</code> enters and destroyed when the state exits.
View scope is <emphasis>only</emphasis> referenceable from within a <code>view-state</code>.
</para>
<programlisting type="xml">
&lt;on-render&gt;
&lt;evaluate expression="searchService.findHotels(searchCriteria)" result="viewScope.hotels"
type="dataModel" /&gt;
&lt;/on-render&gt;
<programlisting type="xml"><![CDATA[
<on-render>
<evaluate expression="searchService.findHotels(searchCriteria)" result="viewScope.hotels"
result-type="dataModel" />
</on-render>]]>
</programlisting>
</sect2>
<sect2 id="el-variable-requestScope">
@@ -134,8 +134,8 @@
Use <code>requestScope</code> to assign a request variable.
Request scope gets allocated when a flow is called and destroyed when the flow returns.
</para>
<programlisting type="xml">
&lt;set name="requestScope.hotelId" value="requestParameters.id" type="long" /&gt;
<programlisting type="xml"><![CDATA[
<set name="requestScope.hotelId" value="requestParameters.id" type="long" />]]>
</programlisting>
</sect2>
<sect2 id="el-variable-flashScope">
@@ -144,8 +144,8 @@
Use <code>flashScope</code> to assign a flash variable.
Flash scope gets allocated when a flow starts, cleared after every view render, and destroyed when the flow ends.
</para>
<programlisting type="xml">
&lt;set name="flashScope.statusMessage" value="'Booking confirmed'" /&gt;
<programlisting type="xml"><![CDATA[
<set name="flashScope.statusMessage" value="'Booking confirmed'" />]]>
</programlisting>
</sect2>
<sect2 id="el-variable-conversationScope">
@@ -155,8 +155,8 @@
Conversation scope gets allocated when a top-level flow starts and destroyed when the top-level flow ends.
Conversation scope is shared by a top-level flow and all of its subflows.
</para>
<programlisting type="xml">
&lt;evaluate expression="searchService.findHotel(hotelId)" result="conversationScope.hotel" /&gt;
<programlisting type="xml"><![CDATA[
<evaluate expression="searchService.findHotel(hotelId)" result="conversationScope.hotel" />]]>
</programlisting>
</sect2>
<sect2 id="el-variable-requestParameters">
@@ -164,8 +164,8 @@
<para>
Use <code>requestParameters</code> to access a client request parameter:
</para>
<programlisting type="xml">
&lt;set name="requestScope.hotelId" value="requestParameters.id" type="long" /&gt;
<programlisting type="xml"><![CDATA[
<set name="requestScope.hotelId" value="requestParameters.id" type="long" />]]>
</programlisting>
</sect2>
<sect2 id="el-variable-currentEvent">
@@ -173,8 +173,8 @@
<para>
Use <code>currentEvent</code> to access attributes of the current <code>Event</code>:
</para>
<programlisting type="xml">
&lt;evaluate expression="booking.guests.add(currentEvent.guest)" /&gt;
<programlisting type="xml"><![CDATA[
<evaluate expression="booking.guests.add(currentEvent.guest)" />]]>
</programlisting>
</sect2>
<sect2 id="el-variable-currentUser">
@@ -182,9 +182,9 @@
<para>
Use <code>currentUser</code> to access the authenticated <code>Principal</code>:
</para>
<programlisting type="xml">
&lt;evaluate expression="bookingService.createBooking(hotelId, currentUser.name)"
result="flowScope.booking" /&gt;
<programlisting type="xml"><![CDATA[
<evaluate expression="bookingService.createBooking(hotelId, currentUser.name)"
result="flowScope.booking" />]]>
</programlisting>
</sect2>
<sect2 id="el-variable-messageContext">
@@ -193,8 +193,8 @@
Use <code>messageContext</code> to access a context for retrieving and creating flow execution messages, including error and success messages.
See the <code>MessageContext</code> Javadocs for more information.
</para>
<programlisting type="xml">
&lt;evaluate expression="bookingValidator.validate(booking, messageContext)" /&gt;
<programlisting type="xml"><![CDATA[
<evaluate expression="bookingValidator.validate(booking, messageContext)" />]]>
</programlisting>
</sect2>
<sect2 id="el-variable-requestContext">
@@ -223,9 +223,9 @@
Use <code>externalContext</code> to access the client environment, including user session attributes.
See the <code>ExternalContext</code> API JavaDocs for more information.
</para>
<programlisting type="xml">
&lt;evaluate expression="searchService.suggestHotels(externalContext.sessionMap.userProfile)"
result="viewScope.hotels" /&gt;
<programlisting type="xml"><![CDATA[
<evaluate expression="searchService.suggestHotels(externalContext.sessionMap.userProfile)"
result="viewScope.hotels" />]]>
</programlisting>
</sect2>
</sect1>
@@ -235,15 +235,15 @@
When assigning a variable in one of the flow scopes, referencing that scope is required.
For example:
</para>
<programlisting type="xml">
&lt;set name="requestScope.hotelId" value="requestParameters.id" type="long" /&gt;
<programlisting type="xml"><![CDATA[
<set name="requestScope.hotelId" value="requestParameters.id" type="long" />]]>
</programlisting>
<para>
When simply accessing a variable in one of the scopes, referencing the scope is optional.
For example:
</para>
<programlisting type="xml">
&lt;evaluate expression="entityManager.persist(booking)" /&gt;
<programlisting type="xml"><![CDATA[
<evaluate expression="entityManager.persist(booking)" />]]>
</programlisting>
<para>
If no scope is specified, like in the use of <code>booking</code> above, a scope searching algorithm will be employed.

View File

@@ -7,7 +7,7 @@
While most of the changes are semantic, there are a few structural changes.
Please see the upgrade guide for more details about changes between Web Flow 1.0 and 2.0.
</para>
<table>
<table id="field-mappings-table">
<title>Mappings</title>
<tgroup cols="5">
<colspec colname="1.0-element" colwidth="*"/>

View File

@@ -37,8 +37,8 @@
The child flow will inherit from each parent in the order it is listed adding elements and content to the resulting flow.
The resulting flow from the first merge will be considered the child in the second merge, and so on.
</para>
<programlisting language="xml">
&lt;flow parent="common-transitions, common-states"&gt;
<programlisting language="xml"><![CDATA[
<flow parent="common-transitions, common-states">]]>
</programlisting>
</sect2>
<sect2 id="flow-inheritance-level-state">
@@ -55,8 +55,8 @@
The parent and child states must be of the same type.
For instance a view-state cannot inherit from an end-state, only another view-state.
</para>
<programlisting language="xml">
&lt;view-state id="child-state" parent="parent-flow#parent-view-state"&gt;
<programlisting language="xml"><![CDATA[
<view-state id="child-state" parent="parent-flow#parent-view-state">]]>
</programlisting>
</sect2>
</sect1>
@@ -67,8 +67,8 @@
In order to protect these flow from running, they can be marked as <code>abstract</code>.
If an abstract flow attempts to run, a <code>FlowBuilderException</code> will be thrown.
</para>
<programlisting language="xml">
&lt;flow abstract="true"&gt;
<programlisting language="xml"><![CDATA[
<flow abstract="true">]]>
</programlisting>
</sect1>
<sect1 id="flow-inheritance-algorithm">

View File

@@ -42,8 +42,8 @@
Often, these are specific security roles.
The attributes are compared against the user's granted attributes by a Spring Security access decision manager.
</para>
<programlisting language="xml">
&lt;secured attributes="ROLE_USER" /&gt;
<programlisting language="xml"><![CDATA[
<secured attributes="ROLE_USER" />]]>
</programlisting>
<para>
By default, a role based access decision manager is used to determine if the user is allowed access.
@@ -57,8 +57,8 @@
Any, allows access if at least one of the required security attributes is granted to the user.
All, allows access only if each of the required security attributes are granted to the user.
</para>
<programlisting language="xml">
&lt;secured attributes="ROLE_USER, ROLE_ANONYMOUS" match="any" /&gt;
<programlisting language="xml"><![CDATA[
<secured attributes="ROLE_USER, ROLE_ANONYMOUS" match="any" />]]>
</programlisting>
<para>
This attribute is optional.
@@ -121,33 +121,33 @@
The Spring configuration defines <code>http</code> specifics (such as protected URLs and login/logout mechanics) and the <code>authentication-provider</code>.
For the sample applications, a local authentication provider is configured.
</para>
<programlisting language="xml">
&lt;security:http auto-config="true"&gt;
&lt;security:intercept-url pattern="/spring/login*" access="ROLE_ANONYMOUS" /&gt;
&lt;security:intercept-url pattern="/spring/logout-success*" access="ROLE_ANONYMOUS" /&gt;
&lt;security:intercept-url pattern="/spring/logout*" access="ROLE_USER" /&gt;
<programlisting language="xml"><![CDATA[
<security:http auto-config="true">
<security:intercept-url pattern="/spring/login*" access="ROLE_ANONYMOUS" />
<security:intercept-url pattern="/spring/logout-success*" access="ROLE_ANONYMOUS" />
<security:intercept-url pattern="/spring/logout*" access="ROLE_USER" />
&lt;security:form-login login-page="/spring/login"
<security:form-login login-page="/spring/login"
login-url="/spring/login-process"
default-target-url="/spring/main"
authentication-failure-url="/spring/login?login_error=1" /&gt;
authentication-failure-url="/spring/login?login_error=1" />
&lt;security:logout logout-url="/spring/logout" logout-success-url="/spring/logout-success" /&gt;
&lt;/security:http&gt;
<security:logout logout-url="/spring/logout" logout-success-url="/spring/logout-success" />
</security:http>
&lt;security:authentication-provider&gt;
&lt;security:password-encoder hash="md5" /&gt;
&lt;security:user-service&gt;
&lt;security:user name="keith" password="417c7382b16c395bc25b5da1398cf076"
authorities="ROLE_USER,ROLE_SUPERVISOR" /&gt;
&lt;security:user name="erwin" password="12430911a8af075c6f41c6976af22b09"
authorities="ROLE_USER,ROLE_SUPERVISOR" /&gt;
&lt;security:user name="jeremy" password="57c6cbff0d421449be820763f03139eb"
authorities="ROLE_USER" /&gt;
&lt;security:user name="scott" password="942f2339bf50796de535a384f0d1af3e"
authorities="ROLE_USER" /&gt;
&lt;/security:user-service&gt;
&lt;/security:authentication-provider&gt;
<security:authentication-provider>
<security:password-encoder hash="md5" />
<security:user-service>
<security:user name="keith" password="417c7382b16c395bc25b5da1398cf076"
authorities="ROLE_USER,ROLE_SUPERVISOR" />
<security:user name="erwin" password="12430911a8af075c6f41c6976af22b09"
authorities="ROLE_USER,ROLE_SUPERVISOR" />
<security:user name="jeremy" password="57c6cbff0d421449be820763f03139eb"
authorities="ROLE_USER" />
<security:user name="scott" password="942f2339bf50796de535a384f0d1af3e"
authorities="ROLE_USER" />
</security:user-service>
</security:authentication-provider>]]>
</programlisting>
</sect2>
<sect2 id="flow-security-configuration-web">
@@ -157,16 +157,16 @@
This filter will listen for login/logout requests and process them accordingly.
It will also catch <code>AccesDeniedException</code>s and redirect the user to the login page.
</para>
<programlisting language="xml">
&lt;filter&gt;
&lt;filter-name&gt;springSecurityFilterChain&lt;/filter-name&gt;
&lt;filter-class&gt;org.springframework.web.filter.DelegatingFilterProxy&lt;/filter-class&gt;
&lt;/filter&gt;
<programlisting language="xml"><![CDATA[
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
&lt;filter-mapping&gt;
&lt;filter-name&gt;springSecurityFilterChain&lt;/filter-name&gt;
&lt;url-pattern&gt;/*&lt;/url-pattern&gt;
&lt;/filter-mapping&gt;
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>]]>
</programlisting>
</sect2>
</sect1>

View File

@@ -13,19 +13,19 @@
Web Flow provides a Spring schema that allows you to configure the system.
To use this schema, include it in one of your infrastructure-layer beans files:
</para>
<programlisting type="xml">
&lt;beans xmlns="http://www.springframework.org/schema/beans"
<programlisting type="xml"><![CDATA[
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:webflow="http://www.springframework.org/schema/webflow-config"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/webflow-config
http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.0.xsd"&gt;
http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.0.xsd">
&lt;!-- Setup Web Flow here --&gt;
<!-- Setup Web Flow here -->
&lt;/beans&gt;
</beans>]]>
</programlisting>
</sect1>
<sect1 id="system-config-basic">
@@ -38,10 +38,10 @@
<para>
Register your flows in a <code>FlowRegistry</code>:
</para>
<programlisting type="xml">
&lt;webflow:flow-registry id="flowRegistry"&gt;
&lt;webflow:flow-location path="/WEB-INF/flows/booking/booking.xml" /&gt;
&lt;/webflow:flow-registry&gt;
<programlisting type="xml"><![CDATA[
<webflow:flow-registry id="flowRegistry">
<webflow:flow-location path="/WEB-INF/flows/booking/booking.xml" />
</webflow:flow-registry>]]>
</programlisting>
</sect2>
<sect2 id="basic-setup-flow-executor">
@@ -49,8 +49,8 @@
<para>
Deploy a FlowExecutor, the central service for executing flows:
</para>
<programlisting type="xml">
&lt;webflow:flow-executor id="flowExecutor" flow-registry="flowRegistry" /&gt;
<programlisting type="xml"><![CDATA[
<webflow:flow-executor id="flowExecutor" flow-registry="flowRegistry" />]]>
</programlisting>
</sect2>
<para>
@@ -66,28 +66,28 @@
If no flow-builder-services tag is specified, the default service implementations are used.
When the tag is defined, you only need to reference the services you want to customize.
</para>
<programlisting type="xml">
&lt;webflow:flow-registry id="flowRegistry" flow-builder-services="flowBuilderServices"&gt;
&lt;webflow:flow-location path="/WEB-INF/flows/booking/booking.xml" /&gt;
&lt;/webflow:flow-registry&gt;
<programlisting type="xml"><![CDATA[
<webflow:flow-registry id="flowRegistry" flow-builder-services="flowBuilderServices">
<webflow:flow-location path="/WEB-INF/flows/booking/booking.xml" />
</webflow:flow-registry>
&lt;webflow:flow-builder-services id="flowBuilderServices" /&gt;
<webflow:flow-builder-services id="flowBuilderServices" />]]>
</programlisting>
<para>
The configurable services are the <code>conversion-service</code>, <code>formatter-registry</code>, <code>expression-parser</code>, and <code>view-factory-creator</code>.
These services are configured by referencing custom beans you define. For example:
</para>
<programlisting type="xml">
&lt;webflow:flow-builder-services id="flowBuilderServices"
conversion-service="conversionService"
formatter-registry="formatterRegistry"
expression-parser="expressionParser"
view-factory-creator="viewFactoryCreator"/&gt;
<programlisting type="xml"><![CDATA[
<webflow:flow-builder-services id="flowBuilderServices"
conversion-service="conversionService"
formatter-registry="formatterRegistry"
expression-parser="expressionParser"
view-factory-creator="viewFactoryCreator" />
&lt;bean id="conversionService" class="..." /&gt;
&lt;bean id="formatterRegistry" class="..." /&gt;
&lt;bean id="expressionParser" class="..." /&gt;
&lt;bean id="viewFactoryCreator" class="..." /&gt;
<bean id="conversionService" class="..." />
<bean id="formatterRegistry" class="..." />
<bean id="expressionParser" class="..." />
<bean id="viewFactoryCreator" class="..." />]]>
</programlisting>
<sect3 id="builder-service-conversion">
<title>conversion-service</title>
@@ -126,16 +126,16 @@
Use the <code>parent</code> attribute to link two flow registries together in a hierarchy.
When the child registry is queried, if it cannot find the requested flow it will delegate to its parent.
</para>
<programlisting type="xml">
&lt;!-- my-system-config.xml --&gt;
&lt;webflow:flow-registry id="flowRegistry" parent="sharedFlowRegistry"&gt;
&lt;webflow:flow-location path="/WEB-INF/flows/booking/booking.xml" /&gt;
&lt;/webflow:flow-registry&gt;
<programlisting type="xml"><![CDATA[
<!-- my-system-config.xml -->
<webflow:flow-registry id="flowRegistry" parent="sharedFlowRegistry">
<webflow:flow-location path="/WEB-INF/flows/booking/booking.xml" />
</webflow:flow-registry>
&lt;!-- shared-config.xml --&gt;
&lt;webflow:flow-registry id="sharedFlowRegistry"&gt;
&lt;-- Global flows shared by several applications --&lt;
&lt;/webflow:flow-registry&gt;
<!-- shared-config.xml -->
<webflow:flow-registry id="sharedFlowRegistry">
<-- Global flows shared by several applications -->
</webflow:flow-registry>]]>
</programlisting>
</sect2>
<sect2 id="flow-registry-location">
@@ -144,8 +144,8 @@
Use the <code>location</code> element to specify paths to flow definitions to register.
By default, flows will be assigned registry identifiers equal to their filenames minus the file extension.
</para>
<programlisting type="xml">
&lt;webflow:flow-location path="/WEB-INF/flows/booking/booking.xml" /&gt;
<programlisting type="xml"><![CDATA[
<webflow:flow-location path="/WEB-INF/flows/booking/booking.xml" />]]>
</programlisting>
</sect2>
<sect2 id="flow-registry-location-id">
@@ -153,8 +153,8 @@
<para>
Specify an id to assign a custom registry identifier to a flow:
</para>
<programlisting type="xml">
&lt;webflow:flow-location path="/WEB-INF/flows/booking/booking.xml" id="bookHotel" /&gt;
<programlisting type="xml"><![CDATA[
<webflow:flow-location path="/WEB-INF/flows/booking/booking.xml" id="bookHotel" />]]>
</programlisting>
</sect2>
<sect2 id="flow-registry-location-attributes">
@@ -162,13 +162,13 @@
<para>
Use the <code>flow-definition-attributes</code> element to assign custom meta-attributes to a registered flow:
</para>
<programlisting type="xml">
&lt;webflow:flow-location path="/WEB-INF/flows/booking/booking.xml"&gt;
&lt;flow-definition-attributes&gt;
&lt;attribute name="caption" value="Books a hotel" /&gt;
&lt;attribute name="persistence-context" value="true" type="boolean" /&gt;
&lt;/flow-definition-attributes&gt;
&lt;/webflow:flow-location&gt;
<programlisting type="xml"><![CDATA[
<webflow:flow-location path="/WEB-INF/flows/booking/booking.xml">
<flow-definition-attributes>
<attribute name="caption" value="Books a hotel" />
<attribute name="persistence-context" value="true" type="boolean" />
</flow-definition-attributes>
</webflow:flow-location>]]>
</programlisting>
</sect2>
<sect2 id="flow-registry-patterns">
@@ -176,8 +176,8 @@
<para>
Use the <code>flow-location-patterns</code> element to register flows that match a specific resource location pattern:
</para>
<programlisting type="xml">
&lt;webflow:flow-location-pattern value="/WEB-INF/flows/**/*-flow.xml" /&gt;
<programlisting type="xml"><![CDATA[
<webflow:flow-location-pattern value="/WEB-INF/flows/**/*-flow.xml" />]]>
</programlisting>
</sect2>
</sect1>
@@ -188,17 +188,17 @@
<para>
Use the <code>flow-execution-listeners</code> element to register listeners that observe the lifecycle of flow executions:
</para>
<programlisting type="xml">
&lt;flow-execution-listeners&gt;
&lt;listener ref="securityListener"/&gt;
&lt;listener ref="persistenceListener"/&gt;
&lt;/flow-execution-listeners&gt;
<programlisting type="xml"><![CDATA[
<flow-execution-listeners>
<listener ref="securityListener"/>
<listener ref="persistenceListener"/>
</flow-execution-listeners>]]>
</programlisting>
<para>
You may also configure a listener to observe only certain flows:
</para>
<programlisting type="xml">
&lt;listener ref="securityListener" criteria="securedFlow1,securedFlow2"/&gt;
<programlisting type="xml"><![CDATA[
<listener ref="securityListener" criteria="securedFlow1,securedFlow2"/>]]>
</programlisting>
</sect2>
<sect2 id="tuning-flow-execution-repository">
@@ -206,8 +206,8 @@
<para>
Use the <code>flow-execution-repository</code> element to tune flow execution persistence settings:
</para>
<programlisting type="xml">
&lt;flow-execution-repository max-conversations="5" max-snapshots="30" /&gt;
<programlisting type="xml"><![CDATA[
<flow-execution-repository max-conversations="5" max-snapshots="30" />]]>
</programlisting>
<sect3 id="repository-max-conversations">
<title>max-conversations</title>

View File

@@ -12,10 +12,10 @@
<para>
To test the execution of a XML-based flow definition, extend <code>AbstractXmlFlowExecutionTests</code>:
</para>
<programlisting language="java">
<programlisting language="java"><![CDATA[
public class BookingFlowExecutionTests extends AbstractXmlFlowExecutionTests {
}
}]]>
</programlisting>
</sect1>
<sect1 id="override-getResource">
@@ -23,11 +23,11 @@ public class BookingFlowExecutionTests extends AbstractXmlFlowExecutionTests {
<para>
At a minimum, you must override <code>getResource(FlowDefinitionResourceFactory)</code> to return the path to the flow you wish to test:
</para>
<programlisting language="java">
<programlisting language="java"><![CDATA[
@Override
protected FlowDefinitionResource getResource(FlowDefinitionResourceFactory resourceFactory) {
return resourceFactory.createFileResource("src/main/webapp/WEB-INF/flows/booking/booking.xml");
}
}]]>
</programlisting>
</sect1>
<sect1 id="override-configureFlowBuilderContext">
@@ -36,11 +36,11 @@ protected FlowDefinitionResource getResource(FlowDefinitionResourceFactory resou
If your flow has dependencies on externally managed services,
also override <code>configureFlowBuilderContext(MockFlowBuilderContext)</code> to register stubs or mocks of those services:
</para>
<programlisting language="java">
<programlisting language="java"><![CDATA[
@Override
protected void configureFlowBuilderContext(MockFlowBuilderContext builderContext) {
builderContext.registerBean("bookingService", new StubBookingService());
}
}]]>
</programlisting>
</sect1>
<sect1 id="testing-flowstartup">
@@ -48,7 +48,7 @@ protected void configureFlowBuilderContext(MockFlowBuilderContext builderContext
<para>
Have your first test exercise the startup of your flow:
</para>
<programlisting language="java">
<programlisting language="java"><![CDATA[
public void testStartBookingFlow() {
Booking booking = createTestBooking();
@@ -61,7 +61,7 @@ public void testStartBookingFlow() {
assertCurrentStateEquals("enterBookingDetails");
assertTrue(getRequiredFlowAttribute("booking") instanceof Booking);
}
}]]>
</programlisting>
<para>
Assertions generally verify the flow is in the correct state you expect.
@@ -74,7 +74,7 @@ public void testStartBookingFlow() {
You goal should be to exercise all paths through the flow.
You can use the convenient <code>setCurrentState(String)</code> method to jump to the flow state where you wish to begin your test.
</para>
<programlisting>
<programlisting><![CDATA[
public void testEnterBookingDetails_Proceed() {
setCurrentState("enterBookingDetails");
@@ -86,7 +86,7 @@ public void testEnterBookingDetails_Proceed() {
resumeFlow(context);
assertCurrentStateEquals("reviewBooking");
}
}]]>
</programlisting>
</sect1>
<sect1 id="testing-mockingsubflows">
@@ -95,7 +95,7 @@ public void testEnterBookingDetails_Proceed() {
To test calling a subflow, register a mock implementation of the subflow that asserts input was passed in correctly and
returns the correct outcome for your test scenario.
</para>
<programlisting language="java">
<programlisting language="java"><![CDATA[
public void testBookHotel() {
setCurrentState("reviewHotel");
@@ -127,7 +127,7 @@ public Flow createMockBookingSubflow() {
});
// immediately return the bookingConfirmed outcome so the caller can respond
new EndState(mockBookingFlow, "bookingConfirmed");
}
}]]>
</programlisting>
</sect1>
</chapter>

View File

@@ -12,10 +12,10 @@
<para>
Use the <code>view-state</code> element to define a step of the flow that renders a view and waits for a user event to resume:
</para>
<programlisting language="xml">
&lt;view-state id="enterBookingDetails" &gt;
&lt;transition on="submit" to="reviewBooking" /&gt;
&lt;/view-state&gt;
<programlisting language="xml"><![CDATA[
<view-state id="enterBookingDetails">
<transition on="submit" to="reviewBooking" />
</view-state>]]>
</programlisting>
<para>
By convention, a view-state maps its id to a view template in the directory where the flow is located.
@@ -47,8 +47,8 @@
<para>
The view id may be a relative path to view resource in the flow's working directory:
</para>
<programlisting language="xml">
&lt;view-state id="enterBookingDetails" view="bookingDetails.xhtml"&gt;
<programlisting language="xml"><![CDATA[
<view-state id="enterBookingDetails" view="bookingDetails.xhtml">]]>
</programlisting>
</sect2>
<sect2 id="view-explicit-absolute">
@@ -56,8 +56,8 @@
<para>
The view id may be a absolute path to a view resource in the webapp root directory:
</para>
<programlisting language="xml">
&lt;view-state id="enterBookingDetails" view="/WEB-INF/hotels/booking/bookingDetails.xhtml"&gt;
<programlisting language="xml"><![CDATA[
<view-state id="enterBookingDetails" view="/WEB-INF/hotels/booking/bookingDetails.xhtml">]]>
</programlisting>
</sect2>
<sect2 id="view-explicit-logical">
@@ -65,8 +65,8 @@
<para>
With some view frameworks, such as Spring MVC's view framework, the view id may also be a logical identifier resolved by the framework:
</para>
<programlisting language="xml">
&lt;view-state id="enterBookingDetails" view="bookingDetails"&gt;
<programlisting language="xml"><![CDATA[
<view-state id="enterBookingDetails" view="bookingDetails">]]>
</programlisting>
<para>
See the Spring MVC integration section for more information on how to integrate with the MVC <code>ViewResolver</code> infrastructure.
@@ -87,8 +87,8 @@
Use the <code>var</code> tag to declare a view variable.
Like a flow variable, any @Autowired references are automatically restored when the view state resumes.
</para>
<programlisting language="xml">
&lt;var name="searchCriteria" class="com.mycompany.myapp.hotels.SearchCriteria" /&gt;
<programlisting language="xml"><![CDATA[
<var name="searchCriteria" class="com.mycompany.myapp.hotels.SearchCriteria" />]]>
</programlisting>
</sect2>
<sect2 id="view-scope-actions">
@@ -96,11 +96,10 @@
<para>
Use the <code>on-render</code> tag to assign a variable from an action result before the view renders:
</para>
<programlisting language="xml">
&lt;on-render&gt;
&lt;evaluate expression="bookingService.findHotels(searchCriteria)"
result="viewScope.hotels" result-type="dataModel" /&gt;
&lt;/on-render&gt;
<programlisting language="xml"><![CDATA[
<on-render>
<evaluate expression="bookingService.findHotels(searchCriteria)" result="viewScope.hotels" />
</on-render>]]>
</programlisting>
</sect2>
<sect2 id="view-scope-ajax">
@@ -111,21 +110,20 @@
The list is updated in view scope before each render.
Asynchronous event handlers modify the current data page, then request re-rendering of the search results fragment.
</para>
<programlisting language="xml">
&lt;view-state id="searchResults"&gt;
&lt;on-render&gt;
&lt;evaluate expression="bookingService.findHotels(searchCriteria)"
result="viewScope.hotels" result-type="dataModel" /&gt;
&lt;/on-render&gt;
&lt;transition on="next"&gt;
&lt;evaluate expression="searchCriteria.nextPage()" /&gt;
&lt;render fragments="searchResultsTFragment" /&gt;
&lt;/transition&gt;
&lt;transition on="previous"&gt;
&lt;evaluate expression="searchCriteria.previousPage()" /&gt;
&lt;render fragments="searchResultsFragment" /&gt;
&lt;/transition&gt;
&lt;/view-state&gt;
<programlisting language="xml"><![CDATA[
<view-state id="searchResults">
<on-render>
<evaluate expression="bookingService.findHotels(searchCriteria)" result="viewScope.hotels" />
</on-render>
<transition on="next">
<evaluate expression="searchCriteria.nextPage()" />
<render fragments="searchResultsFragment" />
</transition>
<transition on="previous">
<evaluate expression="searchCriteria.previousPage()" />
<render fragments="searchResultsFragment" />
</transition>
</view-state>]]>
</programlisting>
</sect2>
</sect1>
@@ -135,11 +133,10 @@
Use the <code>on-render</code> element to execute one or more actions before view rendering.
Render actions are executed on the initial render as well as any subsequent refreshes, including any partial re-renderings of the view.
</para>
<programlisting language="xml">
&lt;on-render&gt;
&lt;evaluate expression="bookingService.findHotels(searchCriteria)"
result="viewScope.hotels" result-type="dataModel" /&gt;
&lt;/on-render&gt;
<programlisting language="xml"><![CDATA[
<on-render>
<evaluate expression="bookingService.findHotels(searchCriteria)" result="viewScope.hotels" />
</on-render>]]>
</programlisting>
</sect1>
<sect1 id="view-model">
@@ -149,8 +146,8 @@
This attribute is typically used with views that render data controls, such as forms.
The following example declares the <code>enterBookingDetails</code> state manipulates the <code>booking</code> model:
</para>
<programlisting language="xml">
&lt;view-state id="enterBookingDetails" model="booking"&gt;
<programlisting language="xml"><![CDATA[
<view-state id="enterBookingDetails" model="booking">]]>
</programlisting>
<para>
The model may be in any accessible scope, such as <code>flowScope</code> or <code>viewScope</code>.
@@ -175,11 +172,11 @@
<para>
Use the <code>bind</code> attribute to suppress model binding and validation for particular view events.
</para>
<programlisting language="xml">
&lt;view-state id="enterBookingDetails"&gt;
&lt;transition on="proceed" to="reviewBooking"&gt;
&lt;transition on="cancel" to="bookingCancelled" bind="false" /&gt;
&lt;/view-state&gt;
<programlisting language="xml"><![CDATA[
<view-state id="enterBookingDetails">
<transition on="proceed" to="reviewBooking">
<transition on="cancel" to="bookingCancelled" bind="false" />
</view-state>]]>
</programlisting>
</sect1>
<sect1 id="view-validate">
@@ -197,11 +194,11 @@
<title>Implementing a model validate method</title>
<para>
The first way is to define a validate method on the model object class.
To do this, create a public method with the name <code>validate&lt;state&gt;</code>, where <code>state</code> is the id of the view-state.
To do this, create a public method with the name <code>validate</code>, where <code>state</code> is the id of the view-state.
The method must declare a <code>MessageContext</code> parameter for recording validation error messages.
For example:
</para>
<programlisting language="java">
<programlisting language="java"><![CDATA[
public void validateEnterBookingDetails(MessageContext context) {
Calendar calendar = Calendar.getInstance();
if (checkinDate.before(today())) {
@@ -211,18 +208,18 @@ public void validateEnterBookingDetails(MessageContext context) {
context.addMessage(new MessageBuilder().error().source("checkoutDate").defaultText(
"Check out date must be later than check in date").build());
}
}
}]]>
</programlisting>
</sect3>
<sect3 id="view-validation=programmatic-validator">
<title>Implementing a Validator</title>
<para>
The second way is to define a separate object, called a Validator, which validates your model object.
To do this, create a class that defines a public method with the name <code>validate&lt;state&gt;</code>, where <code>state</code> is the id of the view-state.
To do this, create a class that defines a public method with the name <code>validate</code>, where <code>state</code> is the id of the view-state.
The method must declare a <code>Object</code> parameter to accept your model object, and a <code>MessageContext</code> parameter for recording validation error messages.
For example:
</para>
<programlisting language="java">
<programlisting language="java"><![CDATA[
public class BookingValidator {
public void validateEnterBookingDetails(Object object, MessageContext context) {
Booking booking = (Booking) object;
@@ -234,7 +231,7 @@ public class BookingValidator {
"Check out date must be later than check in date").build());
}
}
}
}]]>
</programlisting>
<para>
A Validator can also accept a Spring MVC <code>Errors</code> object, which is required for invoking existing Spring Validators.
@@ -247,10 +244,10 @@ public class BookingValidator {
<para>
From a view-state, transitions without targets can also be defined. Such transitions are called "event handlers":
</para>
<programlisting language="xml">
&lt;transition on="event"&gt;
&lt;-- Handle event --&gt;
&lt;/transition&gt;
<programlisting language="xml"><![CDATA[
<transition on="event">
<-- Handle event -->
</transition>]]>
</programlisting>
<para>
These event handlers do not change the state of the flow.
@@ -261,11 +258,11 @@ public class BookingValidator {
<para>
Use the <code>render</code> element to request partial re-rendering of a view after handling an event:
</para>
<programlisting language="xml">
&lt;transition on="next"&gt;
&lt;evaluate expression="searchCriteria.nextPage()" /&gt;
&lt;render fragments="searchResultsFragment" /&gt;
&lt;/transition&gt;
<programlisting language="xml"><![CDATA[
<transition on="next">
<evaluate expression="searchCriteria.nextPage()" />
<render fragments="searchResultsFragment" />
</transition>]]>
</programlisting>
<para>
The fragments attribute should reference the ID(s) of the view element(s) you wish to re-render.
@@ -281,11 +278,11 @@ public class BookingValidator {
Use the flow's <code>global-transitions</code> element to create event handlers that apply across all views.
Global-transitions are often used to handle global menu links that are part of the layout.
</para>
<programlisting language="xml">
&lt;global-transitions&gt;
&lt;transition on="login"&gt; to="login"&gt;
&lt;transition on="logout"&gt; to="logout"&gt;
&lt;/global-transitions&gt;
<programlisting language="xml"><![CDATA[
<global-transitions>
<transition on="login"> to="login">
<transition on="logout"> to="logout">
</global-transitions>]]>
</programlisting>
</sect2>
</sect1>
@@ -300,7 +297,7 @@ public class BookingValidator {
</para>
<sect2 id="plain-text-message">
<title>Adding plain text messages</title>
<programlisting type="java">
<programlisting type="java"><![CDATA[
MessageContext context = ...
MessageBuilder builder = new MessageBuilder();
context.addMessage(builder.error().source("checkinDate")
@@ -309,19 +306,19 @@ context.addMessage(builder.warn().source("smoking")
.defaultText("Smoking is bad for your health").build());
context.addMessage(builder.info()
.defaultText("We have processed your reservation - thank you and enjoy your stay")
.build());
.build());]]>
</programlisting>
</sect2>
<sect2 id="plain-text-message-intl">
<title>Adding internationalized messages</title>
<programlisting type="java">
<programlisting type="java"><![CDATA[
MessageContext context = ...
MessageBuilder builder = new MessageBuilder();
context.addMessage(builder.error().source("checkinDate").code("checkinDate.notFuture")
.build());
context.addMessage(builder.warn().source("smoking").code("notHealthy")
.resolvableArg("smoking").build());
context.addMessage(builder.info().code("reservationConfirmation").build());
context.addMessage(builder.info().code("reservationConfirmation").build());]]>
</programlisting>
</sect2>
<sect2 id="message-bundles">
@@ -331,11 +328,11 @@ context.addMessage(builder.info().code("reservationConfirmation").build());
To create a flow-specific message bundle, simply define <code>messages.properties</code> file(s) in your flow's directory.
Create a default <code>messages.properties</code> file and a .properties file for each additional <code>Locale</code> you need to support.
</para>
<programlisting type="properties">
<programlisting type="properties"><![CDATA[
#messages.properties
checkinDate=Check in date must be a future date
notHealthy={0} is bad for your health
reservationConfirmation=We have processed your reservation - thank you and enjoy your stay
reservationConfirmation=We have processed your reservation - thank you and enjoy your stay]]>
</programlisting>
</sect2>
</sect1>
@@ -344,8 +341,8 @@ reservationConfirmation=We have processed your reservation - thank you and enjoy
<para>
Use the <code>popup</code> attribute to render a view in a modal popup dialog:
</para>
<programlisting language="xml">
&lt;view-state id="changeSearchCriteria" view="enterSearchCriteria.xhtml" popup="true"&gt;
<programlisting language="xml"><![CDATA[
<view-state id="changeSearchCriteria" view="enterSearchCriteria.xhtml" popup="true">]]>
</programlisting>
<para>
When using Web Flow with the Spring Javascript, no client side code is necessary for the popup to display.
@@ -363,8 +360,8 @@ reservationConfirmation=We have processed your reservation - thank you and enjoy
<para>
Set the history attribute to <code>discard</code> to prevent backtracking to a view:
</para>
<programlisting language="xml">
&lt;view-state id="changeSearchCriteria" history="discard"&gt;
<programlisting language="xml"><![CDATA[
<view-state id="changeSearchCriteria" history="discard">]]>
</programlisting>
</sect2>
<sect2 id="history-invalidate">
@@ -372,8 +369,8 @@ reservationConfirmation=We have processed your reservation - thank you and enjoy
<para>
Set the history attribute to <code>invalidate</code> to prevent backtracking to a view as well all previously displayed views:
</para>
<programlisting language="xml">
&lt;view-state id="changeSearchCriteria" history="invalidate"&gt;
<programlisting language="xml"><![CDATA[
<view-state id="changeSearchCriteria" history="invalidate">]]>
</programlisting>
</sect2>
</sect1>