diff --git a/spring-webflow/docs/reference/src/practical.xml b/spring-webflow/docs/reference/src/practical.xml
index 090c9995..24535ef9 100644
--- a/spring-webflow/docs/reference/src/practical.xml
+++ b/spring-webflow/docs/reference/src/practical.xml
@@ -12,22 +12,23 @@
- Phonebook - the original sample demonstrating most features (including subflows).
+ Phonebook - the original sample demonstrating most core features (including subflows).
- Fileupload - demonstrates multipart file upload.
-
+ Sellitem - demonstrates a wizard with conditional transitions, flow scope, flow execution redirects, and continuations.
+
+
+
+
+ Sellitem-JSF - The sellitem sample in a JSF environment
+ (notice how the flow definition is more concise because JSF components care for data binding and validation)
+
- Birthdate - demonstrates Struts integration and the MultiAction.
-
-
-
-
- Shippingrate - demonstrates Spring Web Flow together with Ajax technology.
+ Shippingrate - demonstrates Spring Web Flow together with the Prototype Javascript framework (for Ajax-style flows)
@@ -47,21 +48,18 @@
- Sellitem - demonstrates a wizard with conditional transitions, flow scope, flow execution redirects, and continuations.
-
+ Fileupload - demonstrates multipart file upload.
+
+
+
+
+ Birthdate - demonstrates Struts integration and the MultiAction.
+
- Sellitem-JSF - the
- sellitem sample in a JSF environment (notice how the flow
- definition is more concise because JSF takes care of data
- binding and validation).
-
-
-
-
- Phonebook-Portlet - the
- phonebook sample in a Portlet environment (notice how the flow definitions do not change).
+ Phonebook-Portlet - the phonebook sample in a Portlet environment
+ (notice how the flow definitions do not change).
@@ -153,1189 +151,6 @@ ant dist
-
- Fileupload Example
-
- Overview
-
- Fileupload is a simple one page web application for uploading files to a server. It is based
- on Spring MVC, uses a Web Flow controller and one web flow with two states: a view state for
- displaying the initial JSP page and an action state for processing the submit.
-
-
-
- Web.xml
-
- The web.xml configuration maps requests for "*.htm" to the fileupload servlet - a regular
- Spring MVC DispatcherServlet:
-
-<servlet>
- <servlet-name>fileupload</servlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
-</servlet>
-
-<servlet-mapping>
- <servlet-name>fileupload</servlet-name>
- <url-pattern>*.htm</url-pattern>
-</servlet-mapping>
-
-
-
-
- Spring MVC Context
-
- The Spring MVC servlet context for the fileupload servlet (WEB-INF/fileupload-servlet.xml) defines
- one controller bean:
-
-<bean name="/admin.htm" class="org.springframework.webflow.executor.mvc.FlowController">
- <property name="flowExecutor" ref="flowExecutor" />
-</bean>
-
- FlowController is a Web Flow controller. It is the main point of integration between Spring MVC
- and Spring Web Flow routing requests to one or more managed web flow executions. The
- FlowController is injected with flowExecutor and flowRegistry beans containing one web flow
- definition:
-
-<!-- Launches new flow executions and resumes existing executions. -->
-<flow:executor id="flowExecutor" registry-ref="flowRegistry" repository-type="singlekey"/>
-
-<!-- Creates the registry of flow definitions for this application -->
-<flow:registry id="flowRegistry">
- <flow:location path="/WEB-INF/fileupload.xml" />
-</flow:registry>
-
- Given the above definitions the following URI can be used to invoke the "fileupload" flow:
-
-/swf-fileupload/admin.htm?_flowId=fileupload
-
-
-
- Both flowExecutor and flowRegistry beans are defined with Spring custom tags schema available in
- Spring 2.0. The custom tags make configuration less verbose and more readable. Regular Spring
- bean definitions can be used as well with earlier versions of Spring.
-
-
- The Spring MVC context also defines a view resolver bean for resolving logical view names and a
- multipartResolver bean for the upload component. In general Web Flow does not aim to replace the
- flexibility of Spring MVC for view resolution. It focuses on the C in MVC.
-
-
-
- Fileupload Web Flow
-
- The start state for the fileupload flow (WEB-INF/fileupload.xml) is a view state:
-
-<start-state idref="selectFile"/>
-
-<view-state id="selectFile" view="fileForm">
- <transition on="submit" to="uploadFile"/>
-</view-state>
-
- View states allow a user to participate in a flow by presenting a suitable interface.
- The view attribute "fileForm" is a logical view name, which the Spring MVC view resolver bean
- will resolve to /WEB-INF/jsp/fileForm.jsp.
-
-
- The fileForm.jsp has an html form that submits back to the same controller
- (/swf-fileupload/admin.htm) and passes a "_flowExecutionKey" parameter.
- The value for _flowExecutionKey is provided by the FlowController - it identifies the current
- instance of the flow and allows Web Flow to resume flow execution, which is paused each time a
- view is displayed.
-
-
- The name of the form submit button "_eventId_submit" indicates the event id to use for deciding
- where to transition to next. Given an event with id of "submit" the "selectFile" view transitions
- to the "uploadFile" state:
-
-<action-state id="uploadFile">
- <action bean="uploadAction"/>
- <transition on="success" to="selectFile">
- <set attribute="fileUploaded" scope="flash" value="true"/>
- </transition>
- <transition on="error" to="selectFile"/>
-</action-state>
-
-
-
- The "uploadFile" state is an action state. Action states integrate with business application code and
- respond to the execution of that code by deciding what state of the flow to enter next. The code for the
- uploadFile state is in the "uploadAction" bean declared in the Spring web context (/WEB-INF/fileupload-servlet.xml):
-
-<bean id="uploadAction" class="org.springframework.webflow.samples.fileupload.FileUploadAction" />
-
- FileUploadAction has simple logic. It picks one of two Web Flow defined events - success or error,
- depending on whether the uploaded file size is greater than 0 or not. Both success and error
- transition back to the "selectFile" view state. However, a success event causes an attribute named
- "fileUploaded" to be set in flash scope
-
-
- A flash-scoped attribute called "file" is also set programmatically in the FileUploadAction bean:
-
-context.getFlashScope().put("file", new String(file.getBytes()));
-return success();
-
- This illustrates the choice to save attributes in one of several scopes either programatically or
- declaratively.
-
-
-
-
- Birthdate Example
-
- Overview
-
- Birthdate is a web application with 3 consequitive screens. The first two collect user input
- to populate a form object. The third presents the results of business calculations based on
- input provided in the first two screens.
-
-
- Birthdate demonstrates Spring Web Flow's Struts integration as well as the use of FormAction,
- a multi-action used to do the processing required for all three screens. The sample also uses JSTL
- taglibs in conjunction with flows.
-
-
-
- Web.xml
-
- The web.xml configuration maps requests for "*.do" to a regular Struts ActionServlet:
-
-<servlet>
- <servlet-name>action</servlet-name>
- <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
-</servlet>
-
-<servlet-mapping>
- <servlet-name>action</servlet-name>
- <url-pattern>*.do</url-pattern>
-</servlet-mapping>
-
- The web.xml also sets up the loading of a Spring context at web application startup:
-
-<context-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>
- /WEB-INF/webflow-config.xml
- </param-value>
-</context-param>
-
-<listener>
- <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
-</listener>
-
- The Spring web context contains beans to set up the Web Flow runtime environment. As will be
- shown in the next section Struts is configured with a Web Flow action that relies on the
- presence of a flowExecutor and a flowRegistry beans in this context.
-
-
-
- Struts Configuration
-
- The Struts configuration (WEB-INF/struts-config.xml) defines the following action mapping:
-
-<action-mappings>
- <action path="/flowAction" name="actionForm" scope="request"
- type="org.springframework.webflow.executor.struts.FlowAction"/>
-</action-mappings>
-
- FlowAction is a Struts action acting as a front controller to the Web Flow system routing Struts
- requests to one or more managed web flow executions. To fully configure the FlowAction a Spring
- web context is required to define flowExecutor and flowRegistry beans (named exactly so). This is
- an excerpt from the Spring web context (/WEB-INF/webflow-config.xml) defining these beans:
-
-<!-- Launches new flow executions and resumes existing executions. -->
-<flow:executor id="flowExecutor" registry-ref="flowRegistry"/>
-
-<!-- Creates the registry of flow definitions for this application -->
-<flow:registry id="flowRegistry">
- <flow:location path="/WEB-INF/birthdate.xml"/>
- <flow:location path="/WEB-INF/birthdate-alternate.xml"/>
-</flow:registry>
-
-
-
- Based on the above, Web Flow is configured with two flows - birthdate and birthdate-alternate,
- which can be invoked as follows:
-
-/swf-birthdate/flowAction.do?_flowId=birthdate
-/swf-birthdate/flowAction.do?_flowId=birthdate-alternate
-
- The Struts configuration file also defines several global forwards: birthdateForm, cardForm,
- and yourAge, which will be referenced from Web Flow definitions as logical view names
- (and left to Struts to resolve to actual JSP pages). In general Web Flow does not aim to replace
- view resolution capabilities of web frameworks such as Struts or Spring MVC.
- It focuses on the C in MVC.
-
-
-
- Birthdate Web Flow
-
- The birthdate web flow (WEB-INF/birthdate.xml) defines the following start state:
-
-<view-state id="enterBirthdate" view="birthdateForm">
- <render-actions>
- <action bean="formAction" method="setupForm" />
- </render-actions>
- <transition on="submit" to="processBirthdateFormSubmit" />
-</view-state>
-
- The setupForm action is called to perform initializations for the enterBirthdate view state.
- Its action bean is defined the Spring web context WEB-INF/webflow-config.xml:
-
-<bean id="formAction" class="org.springframework.webflow.samples.birthdate.BirthDateFormAction" />
-
- BirthDateFormAction is a FormAction - it extends Web Flow's FormAction class, which serves a
- purpose similar to that of Spring MVC's SimpleFormController providing common form functionality
- for data binding and validation.
-
-
- When the BirthDateFormAction bean is instantiated it sets the name, class and scope of the form
- object to use for loading form data upon display and collecting form data upon submit:
-
-public BirthDateFormAction() {
- // tell the superclass about the form object and validator we want to
- // use you could also do this in the application context XML ofcourse
- setFormObjectName("birthDate");
- setFormObjectClass(BirthDate.class);
- setFormObjectScope(ScopeType.FLOW);
- setValidator(new BirthDateValidator());
-}
-
- The form object "birthDate" is placed in flow scope, which means it will not be re-created with
- each request but will be obtained from flow scope instead as long as the request remains within
- the same flow.
-
-
- Once setupForm is done, the "birthdateForm" view will be rendered.
- The logical view name "birthdateForm" is a global-forward in struts-config.xml resolving to
- /WEB-INF/jsp/birthdateForm.jsp. This JSP collects data for the fields "name" and "date" bound to
- the birthDate form object and posts back to FlowAction with a submit image named
- "_eventId_submit". An event with the id of "submit" causes a transition to the
- processBirthdateFormSubmit action state defined as follows:
-
-<action-state id="processBirthdateFormSubmit">
- <action bean="formAction" method="bindAndValidate">
- <attribute name="validatorMethod" value="validateBirthdateForm" />
- </action>
- <transition on="success" to="enterCardInformation" />
- <transition on="error" to="enterBirthdate" />
-</action-state>
-
- The processBirthDateFormSubmit action state uses the same formAction bean as the one already used
- to setup the form. This time its bindAndValidate
- method is used to populate and validate the html form values. Also, note the "validateMethod"
- attribute used to specify the name of the method to invoke on the Validator object setup in the
- constructor of the BirthDateFormAction. The use of this attribute allows partial validation of
- complex objects populated over several consecutive screens.
-
-
- On error the action returns to the view state it came from. On success it transitions to the
- enterCardInformation view state:
-
-<view-state id="enterCardInformation" view="cardForm">
- <transition on="submit" to="processCardFormSubmit" />
-</view-state>
-
- The logical view name "cardForm" is a global-forward in struts-config.xml resolving to
- /WEB-INF/jsp/cardForm.jsp. This JSP collects data for the remaining fields of the birthDate form
- object - "sendCard" and "emailAddress", and posts back to FlowAction with a submit image named
- "_eventId_submit". An event with the id of "submit" causes a transition to the
- processCardFormSubmit action state defined as follows:
-
-<action-state id="processCardFormSubmit">
- <action bean="formAction" method="bindAndValidate">
- <attribute name="validatorMethod" value="validateCardForm" />
- </action>
- <transition on="success" to="calculateAge" />
- <transition on="error" to="enterCardInformation" />
-</action-state>
-
- For this action state the bindAndValidate method of the formAction bean is used to populate and
- validate the remaining html form values. The "validateMethod" attribute specifies the name of the
- method to invoke on the Validator object specific to the fields loaded on the current screen.
-
-
- On error the action returns to the view state it came from. On success it transitions to another
- action state called calculateAge:
-
-<action-state id="calculateAge">
- <action bean="formAction" method="calculateAge" />
- <transition on="success" to="displayAge" />
-</action-state>
-
- The logic for the calculateAge action state is in the calculateAge method of the same formAction
- bean used for data binding and validation. This demonstrates the flexibility Web Flow allows in
- properly structuring control and business logic according to function.
-
-
- The caculateAge method performs business calculations and adds a string in request scope with the
- calculated age. Upon successful completion the calculateAge action state transitions to the end
- view state:
-
-<end-state id="displayAge" view="yourAge" />
-
- Once again the logical view name "yourAge" is a global-forward in struts-config.xml resolving to
- /WEB-INF/jsp/yourAge.jsp. This JSP page retrieves the calculated age from request scope and
- displays the results for the user.
-
-
- The transition to the end state indicates the end of the web flow. The flow execution is cleaned up.
- If the web flow is entered again a new flow execution will start, creating a new form
- object named "birthDate" and placing it in flow scope.
-
-
-
- Birthdate-alternate Web Flow
-
- The birthdate-alternate web flow (/WEB-INF/birthdate-alternate.xml) offers an alternative way and
- more compact way of defining the same web flow. For example the birthdate web flow defines two
- independent states for the first screen - a view state (enterBirthdate) and an action state
- (processBirthdateFormSubmit). In birthdate-alternate those are encapsulated in the view state
- enterBirthdate as follows:
-
-<view-state id="enterBirthdate" view="birthdateForm">
- <render-actions>
- <action bean="formAction" method="setupForm" />
- </render-actions>
- <transition on="submit" to="enterCardInformation">
- <action bean="formAction" method="bindAndValidate">
- <attribute name="validatorMethod" value="validateBirthdateForm" />
- </action>
- </transition>
-</view-state>
-
- Here the setupForm action state is defined as a render-action of the enterBirthdate view state
- while the transition to the next screen uses a nested action bean invoked before the transition
- occurs. Notice that success is implicitly required for the transition to occur. Similarly on error
- the transition does not occur and the same view state is displayed again.
-
-
- The second screen is also defined with a nested transition and action bean:
-
-<view-state id="enterCardInformation" view="cardForm">
- <transition on="submit" to="calculateAge">
- <action bean="formAction" method="bindAndValidate">
- <attribute name="validatorMethod" value="validateCardForm" />
- </action>
- </transition>
-</view-state>
-
- The remaining two states - calculateAge and displayAge are identical.
-
-
-
-
- Shippingrate Example
-
- Overview
-
- The Shippingrate sample demonstrates the use of Spring Web Flow in combination with
- Ajaxian techniques. It consists of several wizard-style steps executed
- with Ajax requests and refreshing a portion of the page.
- The input is collected from the user in incremental steps. It is stored
- in a flow-scoped object and is then used to calcualte a shipping rate.
- The example also demonstrates invocation of a service-layer bean
- defined in a Spring context to perform calculations and
- to provide reference data such as countries and package types.
-
-
-
- Web.xml
-
- The web.xml configuration maps requests for "*.htm" to the
- shippingrate servlet - a regular Spring MVC DispatcherServlet:
-
-<servlet>
- <servlet-name>shippingrate</servlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
-</servlet>
-
-<servlet-mapping>
- <servlet-name>shippingrate</servlet-name>
- <url-pattern>*.htm</url-pattern>
-</servlet-mapping>
-
- The web.xml also ensures the following Spring context file is loaded
- at runtime from the web application classpath:
-
-<context-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>
- classpath:org/springframework/webflow/samples/shippingrate/domain/services.xml
- </param-value>
-</context-param>
-
- The services.xml Spring context defines a "rateService" bean providing
- operations for making shipping rate calculations and for retrieving
- reference data required for display in the JSP pages of the application.
-
-
-
- Spring MVC Context
-
- The Spring MVC servlet context for the shippingrate servlet (WEB-INF/shippingrate-servlet.xml)
- defines one controller bean:
-
-<bean name="/rates.htm" class="org.springframework.webflow.executor.mvc.FlowController">
- <property name="flowExecutor" ref="flowExecutor" />
-</bean>
-
- FlowController is a Web Flow controller. It is the main point of integration between Spring MVC
- and Spring Web Flow routing requests to one or more managed web flow executions. The
- FlowController is injected with flowExecutor and flowRegistry beans:
-
-<!-- Launches new flow executions and resumes existing executions. -->
-<flow:executor id="flowExecutor" registry-ref="flowRegistry" repository-type="simple"/>
-
-<!-- Creates the registry of flow definitions for this application -->
-<flow:registry id="flowRegistry">
- <flow:location path="/WEB-INF/flows/**/*-flow.xml" />
-</flow:registry>
-
- The flowExecutor and the flowRegistry beans collectively configure
- the FlowController with one web flow - the getRate-flow defined in
- /WEB-INF/flows/getRate-flow.xml. The flowExecutor uses a "simple"
- repository, which manages execution state in the user session.
-
-
- Given the above definitions the following URI can be used to initiate
- the getRate-flow:
-
-/swf-shippingrate/rates.htm?_flowId=getRate-flow
-
-
-
-
- Ajax Requests
-
- The shippingrate example consists of several wizard-style steps.
- After the initial index.jsp subsequent pages are
- loaded in an Ajax manner without reloading the entire page.
-
-
- The Ajax requests are done with the help of the
- Prototype
- framework and a thin JavaScript layer over it providing
- convenient functions for processing Ajax form and get requests.
- The required Javascript libraries are included in index.jsp as follows:
-
-<script src="prototype.js" type="text/javascript"></script>
-<script src="swf_ajax.js" type="text/javascript"></script>
-
-
-
- When index.jsp is loaded the following JavaScript invokes the getRate-flow
- and replaces the content of the getRateWizard div tag with the response
- returned from the server:
-
-<div id="getRateWizard">
- <script type="text/javascript">
- window.onload = function() {
- new SimpleRequest('getRateWizard', 'rates.htm', 'get', '_flowId=getRate-flow');
- };
- </script>
-</div>
-
- Functions are first-class citizens and a type in JavaScript.
- The script above creates an instance of
- the SimpleRequest function defined in swf_ajax.js. This function invokes
- Prototype's Ajax.Updater with the specified URL and request parameters. On success
- the content of the getRateWizard div is replaced with the response returned
- from the server. On failure such as an HTTP response code other 200 (OK)
- an error message is displayed.
-
-
- The next few pages are form-based JSP's - selectCustomer.jsp, selectReceiver.jsp,
- etc. Each of them contains the following JavaScript call at the bottom:
-
-<script type="text/javascript">
- formRequest('selectCustomerTypeForm');
-</script>
-
- The formRequest function is also defined in swf_ajax.js
- and it uses Prototype to register a handler for the form submit event:
-
-function formRequest(formElementId) {
- Event.observe(formElementId, 'submit', handleSubmitEvent, true);
-}
-
- The handleSubmitEvent function extracts the form parameters, stops the
- submit event, and posts an AJAX request via XMLHttpRequest. On success
- the results returned form the server replace the content of the form.
- On failure such as an HTTP response code other 200 (OK) an error
- message is displayed.
-
-
- Although not demonstrated in this example a back button can be
- implemented in parallel with the Next button used to advance from
- one screen to the next. This would be necessary because the browser
- back button - a common issue in Ajax applications, contrary to user
- expectation returns to the page prior to the first Ajax request.
-
-
- As a result of the Ajax requests the entire wizard is able to
- function within a portion of the page without refresing
- the remaining information on it.
-
-
-
- getRate Web Flow
-
- The getRate-flow (/WEB-INF/jsp/flows/getRate-flow.xml) defines the following start state:
-
-<view-state id="selectCustomerType" view="selectCustomer">
- <transition on="submit" to="selectSender">
- <action bean="formAction" method="bind" />
- </transition>
-</view-state>
-
- This is a view state, which will display the initial form using the
- JSP page /WEB-INF/jsp/selectCustomer.jsp. Notice, the use of a start action
- executed immediately before the JSP is displayed:
-
-<start-actions>
- <action bean="formAction" method="setupForm" />
-</start-actions>
-
- The "formAction" bean is defined in the Spring servlet context
- (/WEB-INF/shippingrate-servlet.xml). It specifies a form object
- and a validator to use for form data binding and validation:
-
-<!-- Performs "form backing object" data binding and validation on input submit -->
-<bean id="formAction" class="org.springframework.webflow.action.FormAction">
- <property name="formObjectName" value="rateCriteria" />
- <property name="formObjectClass" value="org.springframework.webflow.samples.shippingrate.domain.RateCriteria" />
- <property name="formObjectScope" value="FLOW" />
- <property name="validator">
- <bean class="org.springframework.webflow.samples.shippingrate.domain.RateCriteriaValidator" />
- </property>
-</bean>
-
- The form object of type RateCriteria will be used to collect data
- from the user in several steps. The form object will be stored in FLOW scope
- and will not be re-created with each request as long
- as the flow hasn't reached its end state. The actual binding of
- html form fields to the RateCriteria object is based on
- Spring's data binding mechanism. Html form fields are surrounded
- with the <spring:bind> tag containing the path
- nested property field. FormAction's bindAndValidate method
- will initiate the actual binding on the server side
- between HTTP request parameters and RateCriteria data fields.
-
-
- When the selectCustomer.jsp submits back to the FlowController via
- "/swf-shippingrate/rate.htm" it uses a submit button named
- "_eventId_submit". This indicates to Web Flow a transition to
- the "selectSender" view state. This view state is defined as follows:
-
-<view-state id="selectSender" view="selectSender">
- <render-actions>
- <bean-action bean="rateService" method="getCountries">
- <method-result name="countries" />
- </bean-action>
- </render-actions>
- <transition on="submit" to="selectReceiver">
- <action bean="formAction" method="bindAndValidate">
- <attribute name="validatorMethod" value="validateSender" />
- </action>
- </transition>
-</view-state>
-
- The selectSender view state has a render action:
- the "rateService" bean that was loaded through the services.xml context referenced
- in web.xml. The purpose of the render action is to load data required
- to render the JSP. In this case the rateService bean has a method called
- getCountries that returns a list of countries to be displayed in a drop-down
- by the JSP.
-
-
- The "selectSender" view state also defines one transition: on event with
- id of "submit" a transition to the "selectReceiver" view state occurs.
- A pre-requisite for the transition to occur is the successful completion of
- formAction bean's bindAndValidate method. The attribute "validatorMethod" on
- the bean specifies the name of the method to invoke on the Validator object
- specifically for the fields of the current screen.
- If the bindAndValidate method does not succeed the transition does not take
- place and the flow remains in the "selectSender" view
- state where the user can review the errors and modify the selection.
-
-
- The next two states in the flow - selectReceiver and selectPackageDetails use similar
- mechnisms. The rateSevice bean is used to retrieve countries and package types for
- use in the JSP. The form backing object RateCriteria stored in FLOW scope
- is used to collect user input with each form submit.
-
-
- The "findRate" action state occurs after all user input has been provided.
- It is defined as follows:
-
-<action-state id="findRate">
- <bean-action bean="rateService" method="getRate">
- <method-arguments>
- <argument expression="flowScope.rateCriteria" />
- </method-arguments>
- <method-result name="rate" />
- </bean-action>
- <transition on="success" to="showRate" />
-</action-state>
-
- Logic for the action state is provided by the getRate method of
- the rateService bean. The RateCriteria object stored in FLOW scope
- and containing the user input is passed to the rateService bean.
- The result of the method is exposed in request scope under
- the name "rate".
-
-
- The next and final state "showRate" is a JSP page, which accesses the calculated rate
- information and displays it to the user.
-
-
-
-
- Numberguess Example
-
- Overview
-
- Numberguess uses Web Flow to implement two number guessing games.
- For each game the user can enter multiple guesses and depending
- on the answer either transition back to the same screen or
- advance to the final screen. Logic for the guessing games is
- provided through FLOW-scoped beans, which also maintain state
- such as the total number of guesses. The example defines transitions
- using event pattern matching and custom exception handlers.
-
-
-
- Web.xml
-
- The web.xml configuration maps "*.htm" requests to the numberguess servlet -
- a regular Spring MVC DispatcherServlet:
-
-<servlet>
- <servlet-name>numberguess</servlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
- <init-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>/WEB-INF/dispatcher-servlet.xml</param-value>
- </init-param>
-</servlet>
-
-<servlet-mapping>
- <servlet-name>numberguess</servlet-name>
- <url-pattern>*.htm</url-pattern>
-</servlet-mapping>
-
- The Spring web context is loaded from a file called
- /WEB-INF/dispatcher-servlet.xml.
-
-
-
- Spring MVC Context
-
- The Spring MVC web context (WEB-INF/dispatcher-servlet.xml)
- defines one controller bean:
-
-<bean name="/play.htm" class="org.springframework.webflow.executor.mvc.FlowController">
- <property name="flowExecutor" ref="flowExecutor" />
-</bean>
-
- FlowController is a Web Flow controller. It is the main point of
- integration between Spring MVC and Spring Web Flow routing requests
- to one or more managed web flow executions. The FlowController is
- injected with flowExecutor and flowRegistry beans:
-
-<!-- Launches new flow executions and resumes existing executions. -->
-<flow:executor id="flowExecutor" registry-ref="flowRegistry" repository-type="singlekey"/>
-
-<!-- Creates the registry of flow definitions for this application -->
-<flow:registry id="flowRegistry">
- <flow:location path="/WEB-INF/higherlower.xml" />
- <flow:location path="/WEB-INF/mastermind.xml" />
-</flow:registry>
-
- The flowExecutor and the flowRegistry beans collectively configure
- the FlowController with two web flows - higherlower and mastermind.
- This flowExecutor is configured with a simple repository that assigns
- a single flow execution key per conversation. The key, once assigned,
- never changes for the duration of the conversation.
-
-
- Given the above definitions the following URI's can be used to initiate
- each of the two flows:
-
-/swf-numberguess/play.htm?_flowId=higherlower
-/swf-numberguess/play.htm?_flowId=mastermind
-
-
-
- The Spring MVC servlet context also defines a view resolver bean for
- resolving logical view names. In general Web Flow does not aim
- to replace the flexibility of Spring MVC for view resolution.
- It focuses on the C in MVC.
-
-
-
- Higherlower Flow
-
- The Higherlower flow (/WEB-INF/higherlower.xml) starts with the following
- flow variable declaration:
-
-<var name="game" class="org.springframework.webflow.samples.numberguess.HigherLowerGame"/>
-
- This variable is automatically created when an execution of the flow
- begins and will exist in FLOW scope throughout its duration.
-
-
- The start state for the flow is defined as follows:
-
-<view-state id="enterGuess" view="higherlower.enterGuess">
- <transition on="submit" to="makeGuess"/>
-</view-state>
-
- The view resolver bean of Spring MVC will resolve "higherlower.enterGuess"
- to /WEB-INF/jsp/higherlower.enterGuess.jsp.
- This JSP has a form with one input field for the guess number.
- The "game" variable referenced throughout the JSP
- is the FLOW-scoped variable that was declared at the top of
- the flow definition.
-
-
- The name of the form submit button "_eventId_submit" indicates the
- event id to use for deciding where to transition to next. Given an
- event with id of "submit" the "enterGuess" view state transitions
- to the "makeGuess" action state defined as follows:
-
-<action-state id="makeGuess">
- <evaluate-action expression="flowScope.game.makeGuess(requestParameters.guess)">
- <evaluation-result name="guessResult"/>
- </evaluate-action>
- <transition on="CORRECT" to="showAnswer"/>
- <transition on="*" to="enterGuess"/>
- <transition on-exception="java.lang.NumberFormatException" to="enterGuess"/>
-</action-state>
-
-
-
- The makeGuess action state consists of one evaluate action and three
- transitions. Evaluate actions are used to invoke logic encapsulated
- in a FLOW-scoped object - in this case the game bean.
- The makeGuess method of the game bean returns one of several enum
- values it defines:
-
-enum GuessResult {
- TOO_HIGH, TOO_LOW, CORRECT, INVALID
-}
-
- Web Flow detects the returned result from the makeGuess method
- is a JDK 1.5 enum type and
- creates an Event with a String id matching the enum value. If the
- makeGuess method returns CORRECT a transition to the final
- showAnswer state occurs. For any other event (defined with the event
- pattern on="*") Web Flow returns to the enterGuess
- state. The makeGuess state also defines one on-exception transition
- demonstrating how specific Exceptions can be incorporated into
- flow transition logic.
-
-
- The end-state showAnswer resolves to the JSP page
- /WEB-INF/jsp/higherlower.showAnswer.jsp, which simply shows the
- correct guess. At this point the flow has ended and the "game" bean
- is no longer in scope.
-
-
-
- Mastermind Flow
-
- The mastermind flow uses a similar flow definition to implement a 4-digit
- guessing game:
-
-<var name="game" class="org.springframework.webflow.samples.numberguess.MastermindGame"/>
-
-<start-state idref="enterGuess"/>
-
-<view-state id="enterGuess" view="mastermind.enterGuess">
- <transition on="submit" to="makeGuess"/>
-</view-state>
-
-<action-state id="makeGuess">
- <evaluate-action expression="flowScope.game.makeGuess(requestParameters.guess)">
- <evaluation-result name="guessResult"/>
- </evaluate-action>
- <transition on="CORRECT" to="showAnswer"/>
- <transition on="*" to="enterGuess"/>
-</action-state>
-
-<end-state id="showAnswer" view="mastermind.showAnswer"/>
-
- The MastermindGame class encapsulates the logic for the game and
- is stored as a FLOW-scoped bean.
- It returns one of three possible enum values -
- WRONG, CORRECT, or INVALID, which Web Flow converts to events with
- id's matching the enum values. If the guess is INVALID the JSP page
- /WEB-INF/jsp/mastermind.enterGuess.jsp will print an error message.
- If the guess is CORRECT the flow will transition to the showAnswer
- end state and complete the flow.
-
-
-
-
- Flowlauncher Example
-
- Overview
-
- Flowlauncher demonstrates two different ways one web flow can launch
- another - by redirecting to it or by launching it as a subflow.
- Flowlauncher has two flows: Sample A and Sample B. As a root level
- flow Sample A either transitions to B through a subflow state or
- redirects to B in its end state.
-
-
-
- Web.xml
-
- The web.xml configuration maps "*.htm" requests to the flowlauncher servlet -
- a regular Spring MVC DispatcherServlet:
-
-<servlet>
- <servlet-name>flowlauncher</servlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
-</servlet>
-
-<servlet-mapping>
- <servlet-name>flowlauncher</servlet-name>
- <url-pattern>*.htm</url-pattern>
-</servlet-mapping>
-
-
-
-
- Spring MVC Context
-
- The Spring MVC web context (WEB-INF/flowlauncher-servlet.xml) defines one controller bean:
-
-<bean name="/flowController.htm" class="org.springframework.webflow.executor.mvc.FlowController">
- <property name="flowExecutor" ref="flowExecutor" />
-</bean>
-
- FlowController is a Web Flow extension of Spring MVC's AbstractController.
- It contains a FlowExecutor and directs incoming requests for one
- or more managed flow executions to it. The FlowExecutor bean is configured
- in the same context:
-
-<!-- Launches new flow executions and resumes existing executions. -->
-<flow:executor id="flowExecutor" registry-ref="flowRegistry"/>
-
-<!-- Creates the registry of flow definitions for this application -->
-<flow:registry id="flowRegistry">
- <flow:location path="/WEB-INF/sampleA.xml" />
- <flow:location path="/WEB-INF/sampleB.xml" />
-</flow:registry>
-
- A single FlowController may direct all flows for an application serving as
- a gateway to Web Flow. Based on the above definitions the flows
- sampleA and sampleB can be invoked as follows:
-
-/swf-flowlauncher/flowController.htm?_flowId=sampleA
-/swf-flowlauncher/flowController.htm?_flowId=sampleB
-
- The welcome index.html file for the web application invokes
- the flows and passes additional input using either a URL link
- or a form submit.
-
-
-
- Sample A Web Flow
-
- The Sample A web flow (/WEB-INF/sampleA.xml) begins with an input mapping declaration:
-
-<input-mapper>
- <mapping source="input" target="flowScope.input" />
-</input-mapper>
-
- This declaration reads "when a new execution of this flow starts map the
- input attribute named input into a flowScope attribute
- also named input". Spring Web Flow will automatically provide the request
- parameters as input to the flow when launching a new flow execution.
- Following this declaration the input
- request parameter will remain available for the duration of the flow.
-
-
- There are 3 states in this flow: the start state, the end state, and a subflow
- state. The start state is a view state - it will display a JSP page and allow
- the user to make a choice. The subflow state initiates Sample B as a
- subflow of the current flow - subflows give the ability to compose independent
- modules together to compose complex controller workflows. And the end state
- launches Sample B by redirecting to it.
-
-
- The subflow state launches B with the following input attribute declaration.
- This declaration reads "pass the value of the flow-scoped attribute named
- input as an attribute also named input
- to subflow B.
-
-<attribute-mapper>
- <input-mapper>
- <mapping source="flowScope.input" target="input" />
- </input-mapper>
-</attribute-mapper>
-
- The next line is a transition defining how to respond
- when the subflow ends: advance back to the start state for Sample A.
-
-<transition on="end" to="aPage" />
-
-
-
- The end state demonstrates how to redirect to Sample B upon completion of
- the root level flow Sample A:
-
-<end-state id="endAndLaunchB" view="flowRedirect:sampleB?input=${requestParameters.input}" />
-
- This declaration causes A to be terminated and B to start
- with the given requst input parameter.
-
-
-
- Sample B Web Flow
-
- The flow Sample B (/WEB-INF/sampleB.xml) - used as a subflow in Sample A has two
- simple states: a view state and an end state. From the view state "bPage" the
- flow transitions to the end state:
-
-<view-state id="bPage" view="bPage">
- <transition on="end" to="end" />
-</view-state>
-
-<end-state id="end" />
-
- The "id" attribute of the end state matches the "on" attribute of the
- transition in the outer flow's subflow state, which the outer flow
- uses to resume itself.
-
-
- Also notice how bPage.jsp makes a check to detect if Sample B is
- running as a subflow of Sample A or if it is running as a top-level flow:
-
-<c:if test="${!flowExecutionContext.activeSession.root}">
-
-
- The FlowExecutionContext object is exposed to the views (JSPs)
- to make information like this available during response rendering.
-
-
-
- Iitemlist Example
-
- Overview
-
- Itemlist demonstrates how to configure a FlowExecutor with an argument handler
- enabling it to process REST-style requests where the name of the target
- flow is in the URL instead of a _flowId request parameter.
- The example also demonstrates inner flows as well as how an output parameter
- can be passed from a subflow to a parent flow.
- Finally, it serves as an illustration of how to configure Spring Web Flow
- using classic Spring 1.x bean definitions.
-
-
-
- Web.xml
-
- The web.xml configuration maps "/app/*" requests to the itemlist servlet -
- a regular Spring MVC DispatcherServlet:
-
-<servlet>
- <servlet-name>itemlist</servlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
-</servlet>
-
-<servlet-mapping>
- <servlet-name>itemlist</servlet-name>
- <url-pattern>/app/*</url-pattern>
-</servlet-mapping>
-
-
-
-
- Spring MVC Context
-
- The Spring MVC web context (/WEB-INF/itemlist-serlvet.xml) defines one controller
- and one URL handler mapping:
-
-<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
- <property name="alwaysUseFullPath" value="true" />
- <property name="mappings">
- <value>/app/**/**=flowController</value>
- </property>
-</bean>
-
-<bean id="flowController" 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>
-
- All requests with a servlet path matching "/app/**/**" are mapped to the "flowController" bean.
- The FlowController is a Web Flow extension of Spring MVC's AbstractController delegating
- requests to one or more managed web flows. It acts as gateway to Web Flow defined control
- logic and a single instance can serve the application.
-
-
- The usual way to launch a specific web flow is to pass the _flowId request parameter.
- However, this example is configured with a RequestPathFlowExecutorArgumentHandler
- for processing REST-style URL's.
- Requests for services built around the REST concept are encoded in the URL
- and not as query string parameters. The way to invoke a web flow with
- this argument handler is to follow:
-
-http://${host}/${context path}/${dispatcher path}/${flowId}
-
-
-
- The FlowController is configured with a flowExecutor and flowRegistry beans containing
- two web flows - itemlist and itemlist-alternate:
-
-<!-- Launches new flow executions and resumes existing executions: Spring 1.2 config version -->
-<bean id="flowExecutor" class="org.springframework.webflow.config.FlowExecutorFactoryBean">
- <property name="definitionLocator" ref="flowRegistry"/>
-</bean>
-
-<!-- Creates the registry of flow definitions for this application: Spring 1.2 config version -->
-<bean id="flowRegistry" class="org.springframework.webflow.engine.builder.xml.XmlFlowRegistryFactoryBean">
- <property name="flowLocations">
- <list>
- <value>/WEB-INF/itemlist.xml</value>
- <value>/WEB-INF/itemlist-alternate.xml</value>
- </list>
- </property>
-</bean>
-
- The FlowRegistry and FlowExecutor are defined with Spring 1.2 compatible bean definitions.
- However, starting with Spring 2.0 Web Flow also offers the
- custom tags flow:registry and flow:executor, which are more
- readable and less verbose.
-
-
- Based on the above web context definition use the following URL's to invoke
- the itemlist or the itemlist-alternate web flows:
-
-/swf-itemlist/app/itemlist
-/swf-itemlist/app/itemlist-alternate
-
-
-
- Also defined in itemlist-servlet.xml are three "action" beans - createItemAction,
- addItemAction, and mapItemAction, which will be referenced from action states
- in the web flow definitions.
-
-
-
- Itemlist Web Flow
-
- The itemlist flow allows adding items to a list. There are
- two view states - displayItemList and displayItem, and two action states -
- createItem and addItem.
-
-
- The displayItemList view state resolves to /WEB-INF/jsp/itemList.jsp, which
- lists all items on the list and displays an "Add" button with the
- name "_eventId_add". The name of the button indicates the
- event id to use for deciding where to transition to next.
- Also, notice that instead of posting a "_flowId" parameter
- the JSP sets the form action to the value of flowExecutionKey -
- a value automatically made available in the page
- context by Web Flow:
-
-<form action="${flowExecutionKey}" method="post"/>
-
-
-
- When the form submits an event with the "_eventId_add" button
- the displayItemList view state transitions to the
- createItem action state.
-
-<view-state id="displayItemlist" view="itemlist">
- <transition on="add" to="createItem" />
-</view-state>
-
-<action-state id="createItem">
- <action bean="createItemAction" />
- <transition on="success" to="displayItem" />
-</action-state>
-
-
-
- The "createItemAction" bean is declared in the Spring MVC context
- (/WEB-INF/itemlist-servlet.xml). It simply returns "success", which
- causes a transition to the displayItem view state.
-
-
- The next two states displayItem and addItem allow adding an item to the
- list variable declared at the top of the flow:
-
-<var name="list" class="java.util.ArrayList" />
-
- The "addItemAction" bean is also declared in the Spring MVC context.
- It performs the add by accessing the list in flow scope and
- the item to be added from the request parameters as follows:
-
-Collection list = context.getFlowScope().getRequiredCollection("list");
-String data = context.getRequestParameters().get("data");
-if (data != null && data.length() > 0) {
- list.add(data);
-}
-
- For any outcome the addItem state transitions back to the initial
- displayItemList state using an event pattern match:
-
-<action-state id="addItem">
- <action bean="addItemAction" />
- <transition on="*" to="displayItemlist" />
-</action-state>
-
-
-
-
- Itemlist-alternate Web Flow
-
- The Itemlist-alternate web flow (/WEB-INF/itemlist-alternate.xml)
- has functionality equivalent to that of itemlist but instead uses
- a subflow for selecting individual items.
- The "addItem" state is a subflow state
- invoking an inline flow called "item" (also defined in itemlist-alternate.xml)
- accepting an output parameter from the subflow and adding the
- output parameter to a flow-scoped list variable:
-
-<subflow-state id="addItem" flow="item">
- <attribute-mapper>
- <output-mapper>
- <mapping source="item" target-collection="flowScope.list" />
- </output-mapper>
- </attribute-mapper>
- <transition on="finish" to="displayItemlist" />
-</subflow-state>
-
- An output-mapper is used to pass results from a subflow to a parent flow.
- The above declaration defines an expectation on the subflow to return
- an output parameter called "item". Accordingly the end state for the
- inline flow has this output mapping returning a parameter called "item":
-
-<end-state id="finish">
- <output-mapper>
- <mapping source="requestParameters.data" target="item" />
- </output-mapper>
-</end-state>
-
- With the above declarations we see how a subflow can pass output
- parameters back to its parent flow - in this case the 'data' request parameter
- is passed back as an output parameter.
-
-
- Once the inner subflow flow has completed the item is passed to the parent flow
- as an output parameter, which adds it to its flow-scoped list and transitions
- to the initial "displayItemList" state.
-
-
- Sellitem Example
@@ -1936,6 +751,1189 @@ public void registerCustomEditors(PropertyEditorRegistry registry) {
+
+ Shippingrate Example
+
+ Overview
+
+ The Shippingrate sample demonstrates the use of Spring Web Flow in combination with
+ Ajaxian techniques. It consists of several wizard-style steps executed
+ with Ajax requests and refreshing a portion of the page.
+ The input is collected from the user in incremental steps. It is stored
+ in a flow-scoped object and is then used to calcualte a shipping rate.
+ The example also demonstrates invocation of a service-layer bean
+ defined in a Spring context to perform calculations and
+ to provide reference data such as countries and package types.
+
+
+
+ Web.xml
+
+ The web.xml configuration maps requests for "*.htm" to the
+ shippingrate servlet - a regular Spring MVC DispatcherServlet:
+
+<servlet>
+ <servlet-name>shippingrate</servlet-name>
+ <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
+</servlet>
+
+<servlet-mapping>
+ <servlet-name>shippingrate</servlet-name>
+ <url-pattern>*.htm</url-pattern>
+</servlet-mapping>
+
+ The web.xml also ensures the following Spring context file is loaded
+ at runtime from the web application classpath:
+
+<context-param>
+ <param-name>contextConfigLocation</param-name>
+ <param-value>
+ classpath:org/springframework/webflow/samples/shippingrate/domain/services.xml
+ </param-value>
+</context-param>
+
+ The services.xml Spring context defines a "rateService" bean providing
+ operations for making shipping rate calculations and for retrieving
+ reference data required for display in the JSP pages of the application.
+
+
+
+ Spring MVC Context
+
+ The Spring MVC servlet context for the shippingrate servlet (WEB-INF/shippingrate-servlet.xml)
+ defines one controller bean:
+
+<bean name="/rates.htm" class="org.springframework.webflow.executor.mvc.FlowController">
+ <property name="flowExecutor" ref="flowExecutor" />
+</bean>
+
+ FlowController is a Web Flow controller. It is the main point of integration between Spring MVC
+ and Spring Web Flow routing requests to one or more managed web flow executions. The
+ FlowController is injected with flowExecutor and flowRegistry beans:
+
+<!-- Launches new flow executions and resumes existing executions. -->
+<flow:executor id="flowExecutor" registry-ref="flowRegistry" repository-type="simple"/>
+
+<!-- Creates the registry of flow definitions for this application -->
+<flow:registry id="flowRegistry">
+ <flow:location path="/WEB-INF/flows/**/*-flow.xml" />
+</flow:registry>
+
+ The flowExecutor and the flowRegistry beans collectively configure
+ the FlowController with one web flow - the getRate-flow defined in
+ /WEB-INF/flows/getRate-flow.xml. The flowExecutor uses a "simple"
+ repository, which manages execution state in the user session.
+
+
+ Given the above definitions the following URI can be used to initiate
+ the getRate-flow:
+
+/swf-shippingrate/rates.htm?_flowId=getRate-flow
+
+
+
+
+ Ajax Requests
+
+ The shippingrate example consists of several wizard-style steps.
+ After the initial index.jsp subsequent pages are
+ loaded in an Ajax manner without reloading the entire page.
+
+
+ The Ajax requests are done with the help of the
+ Prototype
+ framework and a thin JavaScript layer over it providing
+ convenient functions for processing Ajax form and get requests.
+ The required Javascript libraries are included in index.jsp as follows:
+
+<script src="prototype.js" type="text/javascript"></script>
+<script src="swf_ajax.js" type="text/javascript"></script>
+
+
+
+ When index.jsp is loaded the following JavaScript invokes the getRate-flow
+ and replaces the content of the getRateWizard div tag with the response
+ returned from the server:
+
+<div id="getRateWizard">
+ <script type="text/javascript">
+ window.onload = function() {
+ new SimpleRequest('getRateWizard', 'rates.htm', 'get', '_flowId=getRate-flow');
+ };
+ </script>
+</div>
+
+ Functions are first-class citizens and a type in JavaScript.
+ The script above creates an instance of
+ the SimpleRequest function defined in swf_ajax.js. This function invokes
+ Prototype's Ajax.Updater with the specified URL and request parameters. On success
+ the content of the getRateWizard div is replaced with the response returned
+ from the server. On failure such as an HTTP response code other 200 (OK)
+ an error message is displayed.
+
+
+ The next few pages are form-based JSP's - selectCustomer.jsp, selectReceiver.jsp,
+ etc. Each of them contains the following JavaScript call at the bottom:
+
+<script type="text/javascript">
+ formRequest('selectCustomerTypeForm');
+</script>
+
+ The formRequest function is also defined in swf_ajax.js
+ and it uses Prototype to register a handler for the form submit event:
+
+function formRequest(formElementId) {
+ Event.observe(formElementId, 'submit', handleSubmitEvent, true);
+}
+
+ The handleSubmitEvent function extracts the form parameters, stops the
+ submit event, and posts an AJAX request via XMLHttpRequest. On success
+ the results returned form the server replace the content of the form.
+ On failure such as an HTTP response code other 200 (OK) an error
+ message is displayed.
+
+
+ Although not demonstrated in this example a back button can be
+ implemented in parallel with the Next button used to advance from
+ one screen to the next. This would be necessary because the browser
+ back button - a common issue in Ajax applications, contrary to user
+ expectation returns to the page prior to the first Ajax request.
+
+
+ As a result of the Ajax requests the entire wizard is able to
+ function within a portion of the page without refresing
+ the remaining information on it.
+
+
+
+ getRate Web Flow
+
+ The getRate-flow (/WEB-INF/jsp/flows/getRate-flow.xml) defines the following start state:
+
+<view-state id="selectCustomerType" view="selectCustomer">
+ <transition on="submit" to="selectSender">
+ <action bean="formAction" method="bind" />
+ </transition>
+</view-state>
+
+ This is a view state, which will display the initial form using the
+ JSP page /WEB-INF/jsp/selectCustomer.jsp. Notice, the use of a start action
+ executed immediately before the JSP is displayed:
+
+<start-actions>
+ <action bean="formAction" method="setupForm" />
+</start-actions>
+
+ The "formAction" bean is defined in the Spring servlet context
+ (/WEB-INF/shippingrate-servlet.xml). It specifies a form object
+ and a validator to use for form data binding and validation:
+
+<!-- Performs "form backing object" data binding and validation on input submit -->
+<bean id="formAction" class="org.springframework.webflow.action.FormAction">
+ <property name="formObjectName" value="rateCriteria" />
+ <property name="formObjectClass" value="org.springframework.webflow.samples.shippingrate.domain.RateCriteria" />
+ <property name="formObjectScope" value="FLOW" />
+ <property name="validator">
+ <bean class="org.springframework.webflow.samples.shippingrate.domain.RateCriteriaValidator" />
+ </property>
+</bean>
+
+ The form object of type RateCriteria will be used to collect data
+ from the user in several steps. The form object will be stored in FLOW scope
+ and will not be re-created with each request as long
+ as the flow hasn't reached its end state. The actual binding of
+ html form fields to the RateCriteria object is based on
+ Spring's data binding mechanism. Html form fields are surrounded
+ with the <spring:bind> tag containing the path
+ nested property field. FormAction's bindAndValidate method
+ will initiate the actual binding on the server side
+ between HTTP request parameters and RateCriteria data fields.
+
+
+ When the selectCustomer.jsp submits back to the FlowController via
+ "/swf-shippingrate/rate.htm" it uses a submit button named
+ "_eventId_submit". This indicates to Web Flow a transition to
+ the "selectSender" view state. This view state is defined as follows:
+
+<view-state id="selectSender" view="selectSender">
+ <render-actions>
+ <bean-action bean="rateService" method="getCountries">
+ <method-result name="countries" />
+ </bean-action>
+ </render-actions>
+ <transition on="submit" to="selectReceiver">
+ <action bean="formAction" method="bindAndValidate">
+ <attribute name="validatorMethod" value="validateSender" />
+ </action>
+ </transition>
+</view-state>
+
+ The selectSender view state has a render action:
+ the "rateService" bean that was loaded through the services.xml context referenced
+ in web.xml. The purpose of the render action is to load data required
+ to render the JSP. In this case the rateService bean has a method called
+ getCountries that returns a list of countries to be displayed in a drop-down
+ by the JSP.
+
+
+ The "selectSender" view state also defines one transition: on event with
+ id of "submit" a transition to the "selectReceiver" view state occurs.
+ A pre-requisite for the transition to occur is the successful completion of
+ formAction bean's bindAndValidate method. The attribute "validatorMethod" on
+ the bean specifies the name of the method to invoke on the Validator object
+ specifically for the fields of the current screen.
+ If the bindAndValidate method does not succeed the transition does not take
+ place and the flow remains in the "selectSender" view
+ state where the user can review the errors and modify the selection.
+
+
+ The next two states in the flow - selectReceiver and selectPackageDetails use similar
+ mechnisms. The rateSevice bean is used to retrieve countries and package types for
+ use in the JSP. The form backing object RateCriteria stored in FLOW scope
+ is used to collect user input with each form submit.
+
+
+ The "findRate" action state occurs after all user input has been provided.
+ It is defined as follows:
+
+<action-state id="findRate">
+ <bean-action bean="rateService" method="getRate">
+ <method-arguments>
+ <argument expression="flowScope.rateCriteria" />
+ </method-arguments>
+ <method-result name="rate" />
+ </bean-action>
+ <transition on="success" to="showRate" />
+</action-state>
+
+ Logic for the action state is provided by the getRate method of
+ the rateService bean. The RateCriteria object stored in FLOW scope
+ and containing the user input is passed to the rateService bean.
+ The result of the method is exposed in request scope under
+ the name "rate".
+
+
+ The next and final state "showRate" is a JSP page, which accesses the calculated rate
+ information and displays it to the user.
+
+
+
+
+ Numberguess Example
+
+ Overview
+
+ Numberguess uses Web Flow to implement two number guessing games.
+ For each game the user can enter multiple guesses and depending
+ on the answer either transition back to the same screen or
+ advance to the final screen. Logic for the guessing games is
+ provided through FLOW-scoped beans, which also maintain state
+ such as the total number of guesses. The example defines transitions
+ using event pattern matching and custom exception handlers.
+
+
+
+ Web.xml
+
+ The web.xml configuration maps "*.htm" requests to the numberguess servlet -
+ a regular Spring MVC DispatcherServlet:
+
+<servlet>
+ <servlet-name>numberguess</servlet-name>
+ <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
+ <init-param>
+ <param-name>contextConfigLocation</param-name>
+ <param-value>/WEB-INF/dispatcher-servlet.xml</param-value>
+ </init-param>
+</servlet>
+
+<servlet-mapping>
+ <servlet-name>numberguess</servlet-name>
+ <url-pattern>*.htm</url-pattern>
+</servlet-mapping>
+
+ The Spring web context is loaded from a file called
+ /WEB-INF/dispatcher-servlet.xml.
+
+
+
+ Spring MVC Context
+
+ The Spring MVC web context (WEB-INF/dispatcher-servlet.xml)
+ defines one controller bean:
+
+<bean name="/play.htm" class="org.springframework.webflow.executor.mvc.FlowController">
+ <property name="flowExecutor" ref="flowExecutor" />
+</bean>
+
+ FlowController is a Web Flow controller. It is the main point of
+ integration between Spring MVC and Spring Web Flow routing requests
+ to one or more managed web flow executions. The FlowController is
+ injected with flowExecutor and flowRegistry beans:
+
+<!-- Launches new flow executions and resumes existing executions. -->
+<flow:executor id="flowExecutor" registry-ref="flowRegistry" repository-type="singlekey"/>
+
+<!-- Creates the registry of flow definitions for this application -->
+<flow:registry id="flowRegistry">
+ <flow:location path="/WEB-INF/higherlower.xml" />
+ <flow:location path="/WEB-INF/mastermind.xml" />
+</flow:registry>
+
+ The flowExecutor and the flowRegistry beans collectively configure
+ the FlowController with two web flows - higherlower and mastermind.
+ This flowExecutor is configured with a simple repository that assigns
+ a single flow execution key per conversation. The key, once assigned,
+ never changes for the duration of the conversation.
+
+
+ Given the above definitions the following URI's can be used to initiate
+ each of the two flows:
+
+/swf-numberguess/play.htm?_flowId=higherlower
+/swf-numberguess/play.htm?_flowId=mastermind
+
+
+
+ The Spring MVC servlet context also defines a view resolver bean for
+ resolving logical view names. In general Web Flow does not aim
+ to replace the flexibility of Spring MVC for view resolution.
+ It focuses on the C in MVC.
+
+
+
+ Higherlower Flow
+
+ The Higherlower flow (/WEB-INF/higherlower.xml) starts with the following
+ flow variable declaration:
+
+<var name="game" class="org.springframework.webflow.samples.numberguess.HigherLowerGame"/>
+
+ This variable is automatically created when an execution of the flow
+ begins and will exist in FLOW scope throughout its duration.
+
+
+ The start state for the flow is defined as follows:
+
+<view-state id="enterGuess" view="higherlower.enterGuess">
+ <transition on="submit" to="makeGuess"/>
+</view-state>
+
+ The view resolver bean of Spring MVC will resolve "higherlower.enterGuess"
+ to /WEB-INF/jsp/higherlower.enterGuess.jsp.
+ This JSP has a form with one input field for the guess number.
+ The "game" variable referenced throughout the JSP
+ is the FLOW-scoped variable that was declared at the top of
+ the flow definition.
+
+
+ The name of the form submit button "_eventId_submit" indicates the
+ event id to use for deciding where to transition to next. Given an
+ event with id of "submit" the "enterGuess" view state transitions
+ to the "makeGuess" action state defined as follows:
+
+<action-state id="makeGuess">
+ <evaluate-action expression="flowScope.game.makeGuess(requestParameters.guess)">
+ <evaluation-result name="guessResult"/>
+ </evaluate-action>
+ <transition on="CORRECT" to="showAnswer"/>
+ <transition on="*" to="enterGuess"/>
+ <transition on-exception="java.lang.NumberFormatException" to="enterGuess"/>
+</action-state>
+
+
+
+ The makeGuess action state consists of one evaluate action and three
+ transitions. Evaluate actions are used to invoke logic encapsulated
+ in a FLOW-scoped object - in this case the game bean.
+ The makeGuess method of the game bean returns one of several enum
+ values it defines:
+
+enum GuessResult {
+ TOO_HIGH, TOO_LOW, CORRECT, INVALID
+}
+
+ Web Flow detects the returned result from the makeGuess method
+ is a JDK 1.5 enum type and
+ creates an Event with a String id matching the enum value. If the
+ makeGuess method returns CORRECT a transition to the final
+ showAnswer state occurs. For any other event (defined with the event
+ pattern on="*") Web Flow returns to the enterGuess
+ state. The makeGuess state also defines one on-exception transition
+ demonstrating how specific Exceptions can be incorporated into
+ flow transition logic.
+
+
+ The end-state showAnswer resolves to the JSP page
+ /WEB-INF/jsp/higherlower.showAnswer.jsp, which simply shows the
+ correct guess. At this point the flow has ended and the "game" bean
+ is no longer in scope.
+
+
+
+ Mastermind Flow
+
+ The mastermind flow uses a similar flow definition to implement a 4-digit
+ guessing game:
+
+<var name="game" class="org.springframework.webflow.samples.numberguess.MastermindGame"/>
+
+<start-state idref="enterGuess"/>
+
+<view-state id="enterGuess" view="mastermind.enterGuess">
+ <transition on="submit" to="makeGuess"/>
+</view-state>
+
+<action-state id="makeGuess">
+ <evaluate-action expression="flowScope.game.makeGuess(requestParameters.guess)">
+ <evaluation-result name="guessResult"/>
+ </evaluate-action>
+ <transition on="CORRECT" to="showAnswer"/>
+ <transition on="*" to="enterGuess"/>
+</action-state>
+
+<end-state id="showAnswer" view="mastermind.showAnswer"/>
+
+ The MastermindGame class encapsulates the logic for the game and
+ is stored as a FLOW-scoped bean.
+ It returns one of three possible enum values -
+ WRONG, CORRECT, or INVALID, which Web Flow converts to events with
+ id's matching the enum values. If the guess is INVALID the JSP page
+ /WEB-INF/jsp/mastermind.enterGuess.jsp will print an error message.
+ If the guess is CORRECT the flow will transition to the showAnswer
+ end state and complete the flow.
+
+
+
+
+ Flowlauncher Example
+
+ Overview
+
+ Flowlauncher demonstrates two different ways one web flow can launch
+ another - by redirecting to it or by launching it as a subflow.
+ Flowlauncher has two flows: Sample A and Sample B. As a root level
+ flow Sample A either transitions to B through a subflow state or
+ redirects to B in its end state.
+
+
+
+ Web.xml
+
+ The web.xml configuration maps "*.htm" requests to the flowlauncher servlet -
+ a regular Spring MVC DispatcherServlet:
+
+<servlet>
+ <servlet-name>flowlauncher</servlet-name>
+ <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
+</servlet>
+
+<servlet-mapping>
+ <servlet-name>flowlauncher</servlet-name>
+ <url-pattern>*.htm</url-pattern>
+</servlet-mapping>
+
+
+
+
+ Spring MVC Context
+
+ The Spring MVC web context (WEB-INF/flowlauncher-servlet.xml) defines one controller bean:
+
+<bean name="/flowController.htm" class="org.springframework.webflow.executor.mvc.FlowController">
+ <property name="flowExecutor" ref="flowExecutor" />
+</bean>
+
+ FlowController is a Web Flow extension of Spring MVC's AbstractController.
+ It contains a FlowExecutor and directs incoming requests for one
+ or more managed flow executions to it. The FlowExecutor bean is configured
+ in the same context:
+
+<!-- Launches new flow executions and resumes existing executions. -->
+<flow:executor id="flowExecutor" registry-ref="flowRegistry"/>
+
+<!-- Creates the registry of flow definitions for this application -->
+<flow:registry id="flowRegistry">
+ <flow:location path="/WEB-INF/sampleA.xml" />
+ <flow:location path="/WEB-INF/sampleB.xml" />
+</flow:registry>
+
+ A single FlowController may direct all flows for an application serving as
+ a gateway to Web Flow. Based on the above definitions the flows
+ sampleA and sampleB can be invoked as follows:
+
+/swf-flowlauncher/flowController.htm?_flowId=sampleA
+/swf-flowlauncher/flowController.htm?_flowId=sampleB
+
+ The welcome index.html file for the web application invokes
+ the flows and passes additional input using either a URL link
+ or a form submit.
+
+
+
+ Sample A Web Flow
+
+ The Sample A web flow (/WEB-INF/sampleA.xml) begins with an input mapping declaration:
+
+<input-mapper>
+ <mapping source="input" target="flowScope.input" />
+</input-mapper>
+
+ This declaration reads "when a new execution of this flow starts map the
+ input attribute named input into a flowScope attribute
+ also named input". Spring Web Flow will automatically provide the request
+ parameters as input to the flow when launching a new flow execution.
+ Following this declaration the input
+ request parameter will remain available for the duration of the flow.
+
+
+ There are 3 states in this flow: the start state, the end state, and a subflow
+ state. The start state is a view state - it will display a JSP page and allow
+ the user to make a choice. The subflow state initiates Sample B as a
+ subflow of the current flow - subflows give the ability to compose independent
+ modules together to compose complex controller workflows. And the end state
+ launches Sample B by redirecting to it.
+
+
+ The subflow state launches B with the following input attribute declaration.
+ This declaration reads "pass the value of the flow-scoped attribute named
+ input as an attribute also named input
+ to subflow B.
+
+<attribute-mapper>
+ <input-mapper>
+ <mapping source="flowScope.input" target="input" />
+ </input-mapper>
+</attribute-mapper>
+
+ The next line is a transition defining how to respond
+ when the subflow ends: advance back to the start state for Sample A.
+
+<transition on="end" to="aPage" />
+
+
+
+ The end state demonstrates how to redirect to Sample B upon completion of
+ the root level flow Sample A:
+
+<end-state id="endAndLaunchB" view="flowRedirect:sampleB?input=${requestParameters.input}" />
+
+ This declaration causes A to be terminated and B to start
+ with the given requst input parameter.
+
+
+
+ Sample B Web Flow
+
+ The flow Sample B (/WEB-INF/sampleB.xml) - used as a subflow in Sample A has two
+ simple states: a view state and an end state. From the view state "bPage" the
+ flow transitions to the end state:
+
+<view-state id="bPage" view="bPage">
+ <transition on="end" to="end" />
+</view-state>
+
+<end-state id="end" />
+
+ The "id" attribute of the end state matches the "on" attribute of the
+ transition in the outer flow's subflow state, which the outer flow
+ uses to resume itself.
+
+
+ Also notice how bPage.jsp makes a check to detect if Sample B is
+ running as a subflow of Sample A or if it is running as a top-level flow:
+
+<c:if test="${!flowExecutionContext.activeSession.root}">
+
+
+ The FlowExecutionContext object is exposed to the views (JSPs)
+ to make information like this available during response rendering.
+
+
+
+ Itemlist Example
+
+ Overview
+
+ Itemlist demonstrates how to configure a FlowExecutor with an argument handler
+ enabling it to process REST-style requests where the name of the target
+ flow is in the URL instead of a _flowId request parameter.
+ The example also demonstrates inner flows as well as how an output parameter
+ can be passed from a subflow to a parent flow.
+ Finally, it serves as an illustration of how to configure Spring Web Flow
+ using classic Spring 1.x bean definitions.
+
+
+
+ Web.xml
+
+ The web.xml configuration maps "/app/*" requests to the itemlist servlet -
+ a regular Spring MVC DispatcherServlet:
+
+<servlet>
+ <servlet-name>itemlist</servlet-name>
+ <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
+</servlet>
+
+<servlet-mapping>
+ <servlet-name>itemlist</servlet-name>
+ <url-pattern>/app/*</url-pattern>
+</servlet-mapping>
+
+
+
+
+ Spring MVC Context
+
+ The Spring MVC web context (/WEB-INF/itemlist-serlvet.xml) defines one controller
+ and one URL handler mapping:
+
+<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
+ <property name="alwaysUseFullPath" value="true" />
+ <property name="mappings">
+ <value>/app/**/**=flowController</value>
+ </property>
+</bean>
+
+<bean id="flowController" 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>
+
+ All requests with a servlet path matching "/app/**/**" are mapped to the "flowController" bean.
+ The FlowController is a Web Flow extension of Spring MVC's AbstractController delegating
+ requests to one or more managed web flows. It acts as gateway to Web Flow defined control
+ logic and a single instance can serve the application.
+
+
+ The usual way to launch a specific web flow is to pass the _flowId request parameter.
+ However, this example is configured with a RequestPathFlowExecutorArgumentHandler
+ for processing REST-style URL's.
+ Requests for services built around the REST concept are encoded in the URL
+ and not as query string parameters. The way to invoke a web flow with
+ this argument handler is to follow:
+
+http://${host}/${context path}/${dispatcher path}/${flowId}
+
+
+
+ The FlowController is configured with a flowExecutor and flowRegistry beans containing
+ two web flows - itemlist and itemlist-alternate:
+
+<!-- Launches new flow executions and resumes existing executions: Spring 1.2 config version -->
+<bean id="flowExecutor" class="org.springframework.webflow.config.FlowExecutorFactoryBean">
+ <property name="definitionLocator" ref="flowRegistry"/>
+</bean>
+
+<!-- Creates the registry of flow definitions for this application: Spring 1.2 config version -->
+<bean id="flowRegistry" class="org.springframework.webflow.engine.builder.xml.XmlFlowRegistryFactoryBean">
+ <property name="flowLocations">
+ <list>
+ <value>/WEB-INF/itemlist.xml</value>
+ <value>/WEB-INF/itemlist-alternate.xml</value>
+ </list>
+ </property>
+</bean>
+
+ The FlowRegistry and FlowExecutor are defined with Spring 1.2 compatible bean definitions.
+ However, starting with Spring 2.0 Web Flow also offers the
+ custom tags flow:registry and flow:executor, which are more
+ readable and less verbose.
+
+
+ Based on the above web context definition use the following URL's to invoke
+ the itemlist or the itemlist-alternate web flows:
+
+/swf-itemlist/app/itemlist
+/swf-itemlist/app/itemlist-alternate
+
+
+
+ Also defined in itemlist-servlet.xml are three "action" beans - createItemAction,
+ addItemAction, and mapItemAction, which will be referenced from action states
+ in the web flow definitions.
+
+
+
+ Itemlist Web Flow
+
+ The itemlist flow allows adding items to a list. There are
+ two view states - displayItemList and displayItem, and two action states -
+ createItem and addItem.
+
+
+ The displayItemList view state resolves to /WEB-INF/jsp/itemList.jsp, which
+ lists all items on the list and displays an "Add" button with the
+ name "_eventId_add". The name of the button indicates the
+ event id to use for deciding where to transition to next.
+ Also, notice that instead of posting a "_flowId" parameter
+ the JSP sets the form action to the value of flowExecutionKey -
+ a value automatically made available in the page
+ context by Web Flow:
+
+<form action="${flowExecutionKey}" method="post"/>
+
+
+
+ When the form submits an event with the "_eventId_add" button
+ the displayItemList view state transitions to the
+ createItem action state.
+
+<view-state id="displayItemlist" view="itemlist">
+ <transition on="add" to="createItem" />
+</view-state>
+
+<action-state id="createItem">
+ <action bean="createItemAction" />
+ <transition on="success" to="displayItem" />
+</action-state>
+
+
+
+ The "createItemAction" bean is declared in the Spring MVC context
+ (/WEB-INF/itemlist-servlet.xml). It simply returns "success", which
+ causes a transition to the displayItem view state.
+
+
+ The next two states displayItem and addItem allow adding an item to the
+ list variable declared at the top of the flow:
+
+<var name="list" class="java.util.ArrayList" />
+
+ The "addItemAction" bean is also declared in the Spring MVC context.
+ It performs the add by accessing the list in flow scope and
+ the item to be added from the request parameters as follows:
+
+Collection list = context.getFlowScope().getRequiredCollection("list");
+String data = context.getRequestParameters().get("data");
+if (data != null && data.length() > 0) {
+ list.add(data);
+}
+
+ For any outcome the addItem state transitions back to the initial
+ displayItemList state using an event pattern match:
+
+<action-state id="addItem">
+ <action bean="addItemAction" />
+ <transition on="*" to="displayItemlist" />
+</action-state>
+
+
+
+
+ Itemlist-alternate Web Flow
+
+ The Itemlist-alternate web flow (/WEB-INF/itemlist-alternate.xml)
+ has functionality equivalent to that of itemlist but instead uses
+ a subflow for selecting individual items.
+ The "addItem" state is a subflow state
+ invoking an inline flow called "item" (also defined in itemlist-alternate.xml)
+ accepting an output parameter from the subflow and adding the
+ output parameter to a flow-scoped list variable:
+
+<subflow-state id="addItem" flow="item">
+ <attribute-mapper>
+ <output-mapper>
+ <mapping source="item" target-collection="flowScope.list" />
+ </output-mapper>
+ </attribute-mapper>
+ <transition on="finish" to="displayItemlist" />
+</subflow-state>
+
+ An output-mapper is used to pass results from a subflow to a parent flow.
+ The above declaration defines an expectation on the subflow to return
+ an output parameter called "item". Accordingly the end state for the
+ inline flow has this output mapping returning a parameter called "item":
+
+<end-state id="finish">
+ <output-mapper>
+ <mapping source="requestParameters.data" target="item" />
+ </output-mapper>
+</end-state>
+
+ With the above declarations we see how a subflow can pass output
+ parameters back to its parent flow - in this case the 'data' request parameter
+ is passed back as an output parameter.
+
+
+ Once the inner subflow flow has completed the item is passed to the parent flow
+ as an output parameter, which adds it to its flow-scoped list and transitions
+ to the initial "displayItemList" state.
+
+
+
+
+ Fileupload Example
+
+ Overview
+
+ Fileupload is a simple one page web application for uploading files to a server. It is based
+ on Spring MVC, uses a Web Flow controller and one web flow with two states: a view state for
+ displaying the initial JSP page and an action state for processing the submit.
+
+
+
+ Web.xml
+
+ The web.xml configuration maps requests for "*.htm" to the fileupload servlet - a regular
+ Spring MVC DispatcherServlet:
+
+<servlet>
+ <servlet-name>fileupload</servlet-name>
+ <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
+</servlet>
+
+<servlet-mapping>
+ <servlet-name>fileupload</servlet-name>
+ <url-pattern>*.htm</url-pattern>
+</servlet-mapping>
+
+
+
+
+ Spring MVC Context
+
+ The Spring MVC servlet context for the fileupload servlet (WEB-INF/fileupload-servlet.xml) defines
+ one controller bean:
+
+<bean name="/admin.htm" class="org.springframework.webflow.executor.mvc.FlowController">
+ <property name="flowExecutor" ref="flowExecutor" />
+</bean>
+
+ FlowController is a Web Flow controller. It is the main point of integration between Spring MVC
+ and Spring Web Flow routing requests to one or more managed web flow executions. The
+ FlowController is injected with flowExecutor and flowRegistry beans containing one web flow
+ definition:
+
+<!-- Launches new flow executions and resumes existing executions. -->
+<flow:executor id="flowExecutor" registry-ref="flowRegistry" repository-type="singlekey"/>
+
+<!-- Creates the registry of flow definitions for this application -->
+<flow:registry id="flowRegistry">
+ <flow:location path="/WEB-INF/fileupload.xml" />
+</flow:registry>
+
+ Given the above definitions the following URI can be used to invoke the "fileupload" flow:
+
+/swf-fileupload/admin.htm?_flowId=fileupload
+
+
+
+ Both flowExecutor and flowRegistry beans are defined with Spring custom tags schema available in
+ Spring 2.0. The custom tags make configuration less verbose and more readable. Regular Spring
+ bean definitions can be used as well with earlier versions of Spring.
+
+
+ The Spring MVC context also defines a view resolver bean for resolving logical view names and a
+ multipartResolver bean for the upload component. In general Web Flow does not aim to replace the
+ flexibility of Spring MVC for view resolution. It focuses on the C in MVC.
+
+
+
+ Fileupload Web Flow
+
+ The start state for the fileupload flow (WEB-INF/fileupload.xml) is a view state:
+
+<start-state idref="selectFile"/>
+
+<view-state id="selectFile" view="fileForm">
+ <transition on="submit" to="uploadFile"/>
+</view-state>
+
+ View states allow a user to participate in a flow by presenting a suitable interface.
+ The view attribute "fileForm" is a logical view name, which the Spring MVC view resolver bean
+ will resolve to /WEB-INF/jsp/fileForm.jsp.
+
+
+ The fileForm.jsp has an html form that submits back to the same controller
+ (/swf-fileupload/admin.htm) and passes a "_flowExecutionKey" parameter.
+ The value for _flowExecutionKey is provided by the FlowController - it identifies the current
+ instance of the flow and allows Web Flow to resume flow execution, which is paused each time a
+ view is displayed.
+
+
+ The name of the form submit button "_eventId_submit" indicates the event id to use for deciding
+ where to transition to next. Given an event with id of "submit" the "selectFile" view transitions
+ to the "uploadFile" state:
+
+<action-state id="uploadFile">
+ <action bean="uploadAction"/>
+ <transition on="success" to="selectFile">
+ <set attribute="fileUploaded" scope="flash" value="true"/>
+ </transition>
+ <transition on="error" to="selectFile"/>
+</action-state>
+
+
+
+ The "uploadFile" state is an action state. Action states integrate with business application code and
+ respond to the execution of that code by deciding what state of the flow to enter next. The code for the
+ uploadFile state is in the "uploadAction" bean declared in the Spring web context (/WEB-INF/fileupload-servlet.xml):
+
+<bean id="uploadAction" class="org.springframework.webflow.samples.fileupload.FileUploadAction" />
+
+ FileUploadAction has simple logic. It picks one of two Web Flow defined events - success or error,
+ depending on whether the uploaded file size is greater than 0 or not. Both success and error
+ transition back to the "selectFile" view state. However, a success event causes an attribute named
+ "fileUploaded" to be set in flash scope
+
+
+ A flash-scoped attribute called "file" is also set programmatically in the FileUploadAction bean:
+
+context.getFlashScope().put("file", new String(file.getBytes()));
+return success();
+
+ This illustrates the choice to save attributes in one of several scopes either programatically or
+ declaratively.
+
+
+
+
+ Birthdate Example
+
+ Overview
+
+ Birthdate is a web application with 3 consequitive screens. The first two collect user input
+ to populate a form object. The third presents the results of business calculations based on
+ input provided in the first two screens.
+
+
+ Birthdate demonstrates Spring Web Flow's Struts integration as well as the use of FormAction,
+ a multi-action used to do the processing required for all three screens. The sample also uses JSTL
+ taglibs in conjunction with flows.
+
+
+
+ Web.xml
+
+ The web.xml configuration maps requests for "*.do" to a regular Struts ActionServlet:
+
+<servlet>
+ <servlet-name>action</servlet-name>
+ <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
+</servlet>
+
+<servlet-mapping>
+ <servlet-name>action</servlet-name>
+ <url-pattern>*.do</url-pattern>
+</servlet-mapping>
+
+ The web.xml also sets up the loading of a Spring context at web application startup:
+
+<context-param>
+ <param-name>contextConfigLocation</param-name>
+ <param-value>
+ /WEB-INF/webflow-config.xml
+ </param-value>
+</context-param>
+
+<listener>
+ <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
+</listener>
+
+ The Spring web context contains beans to set up the Web Flow runtime environment. As will be
+ shown in the next section Struts is configured with a Web Flow action that relies on the
+ presence of a flowExecutor and a flowRegistry beans in this context.
+
+
+
+ Struts Configuration
+
+ The Struts configuration (WEB-INF/struts-config.xml) defines the following action mapping:
+
+<action-mappings>
+ <action path="/flowAction" name="actionForm" scope="request"
+ type="org.springframework.webflow.executor.struts.FlowAction"/>
+</action-mappings>
+
+ FlowAction is a Struts action acting as a front controller to the Web Flow system routing Struts
+ requests to one or more managed web flow executions. To fully configure the FlowAction a Spring
+ web context is required to define flowExecutor and flowRegistry beans (named exactly so). This is
+ an excerpt from the Spring web context (/WEB-INF/webflow-config.xml) defining these beans:
+
+<!-- Launches new flow executions and resumes existing executions. -->
+<flow:executor id="flowExecutor" registry-ref="flowRegistry"/>
+
+<!-- Creates the registry of flow definitions for this application -->
+<flow:registry id="flowRegistry">
+ <flow:location path="/WEB-INF/birthdate.xml"/>
+ <flow:location path="/WEB-INF/birthdate-alternate.xml"/>
+</flow:registry>
+
+
+
+ Based on the above, Web Flow is configured with two flows - birthdate and birthdate-alternate,
+ which can be invoked as follows:
+
+/swf-birthdate/flowAction.do?_flowId=birthdate
+/swf-birthdate/flowAction.do?_flowId=birthdate-alternate
+
+ The Struts configuration file also defines several global forwards: birthdateForm, cardForm,
+ and yourAge, which will be referenced from Web Flow definitions as logical view names
+ (and left to Struts to resolve to actual JSP pages). In general Web Flow does not aim to replace
+ view resolution capabilities of web frameworks such as Struts or Spring MVC.
+ It focuses on the C in MVC.
+
+
+
+ Birthdate Web Flow
+
+ The birthdate web flow (WEB-INF/birthdate.xml) defines the following start state:
+
+<view-state id="enterBirthdate" view="birthdateForm">
+ <render-actions>
+ <action bean="formAction" method="setupForm" />
+ </render-actions>
+ <transition on="submit" to="processBirthdateFormSubmit" />
+</view-state>
+
+ The setupForm action is called to perform initializations for the enterBirthdate view state.
+ Its action bean is defined the Spring web context WEB-INF/webflow-config.xml:
+
+<bean id="formAction" class="org.springframework.webflow.samples.birthdate.BirthDateFormAction" />
+
+ BirthDateFormAction is a FormAction - it extends Web Flow's FormAction class, which serves a
+ purpose similar to that of Spring MVC's SimpleFormController providing common form functionality
+ for data binding and validation.
+
+
+ When the BirthDateFormAction bean is instantiated it sets the name, class and scope of the form
+ object to use for loading form data upon display and collecting form data upon submit:
+
+public BirthDateFormAction() {
+ // tell the superclass about the form object and validator we want to
+ // use you could also do this in the application context XML ofcourse
+ setFormObjectName("birthDate");
+ setFormObjectClass(BirthDate.class);
+ setFormObjectScope(ScopeType.FLOW);
+ setValidator(new BirthDateValidator());
+}
+
+ The form object "birthDate" is placed in flow scope, which means it will not be re-created with
+ each request but will be obtained from flow scope instead as long as the request remains within
+ the same flow.
+
+
+ Once setupForm is done, the "birthdateForm" view will be rendered.
+ The logical view name "birthdateForm" is a global-forward in struts-config.xml resolving to
+ /WEB-INF/jsp/birthdateForm.jsp. This JSP collects data for the fields "name" and "date" bound to
+ the birthDate form object and posts back to FlowAction with a submit image named
+ "_eventId_submit". An event with the id of "submit" causes a transition to the
+ processBirthdateFormSubmit action state defined as follows:
+
+<action-state id="processBirthdateFormSubmit">
+ <action bean="formAction" method="bindAndValidate">
+ <attribute name="validatorMethod" value="validateBirthdateForm" />
+ </action>
+ <transition on="success" to="enterCardInformation" />
+ <transition on="error" to="enterBirthdate" />
+</action-state>
+
+ The processBirthDateFormSubmit action state uses the same formAction bean as the one already used
+ to setup the form. This time its bindAndValidate
+ method is used to populate and validate the html form values. Also, note the "validateMethod"
+ attribute used to specify the name of the method to invoke on the Validator object setup in the
+ constructor of the BirthDateFormAction. The use of this attribute allows partial validation of
+ complex objects populated over several consecutive screens.
+
+
+ On error the action returns to the view state it came from. On success it transitions to the
+ enterCardInformation view state:
+
+<view-state id="enterCardInformation" view="cardForm">
+ <transition on="submit" to="processCardFormSubmit" />
+</view-state>
+
+ The logical view name "cardForm" is a global-forward in struts-config.xml resolving to
+ /WEB-INF/jsp/cardForm.jsp. This JSP collects data for the remaining fields of the birthDate form
+ object - "sendCard" and "emailAddress", and posts back to FlowAction with a submit image named
+ "_eventId_submit". An event with the id of "submit" causes a transition to the
+ processCardFormSubmit action state defined as follows:
+
+<action-state id="processCardFormSubmit">
+ <action bean="formAction" method="bindAndValidate">
+ <attribute name="validatorMethod" value="validateCardForm" />
+ </action>
+ <transition on="success" to="calculateAge" />
+ <transition on="error" to="enterCardInformation" />
+</action-state>
+
+ For this action state the bindAndValidate method of the formAction bean is used to populate and
+ validate the remaining html form values. The "validateMethod" attribute specifies the name of the
+ method to invoke on the Validator object specific to the fields loaded on the current screen.
+
+
+ On error the action returns to the view state it came from. On success it transitions to another
+ action state called calculateAge:
+
+<action-state id="calculateAge">
+ <action bean="formAction" method="calculateAge" />
+ <transition on="success" to="displayAge" />
+</action-state>
+
+ The logic for the calculateAge action state is in the calculateAge method of the same formAction
+ bean used for data binding and validation. This demonstrates the flexibility Web Flow allows in
+ properly structuring control and business logic according to function.
+
+
+ The caculateAge method performs business calculations and adds a string in request scope with the
+ calculated age. Upon successful completion the calculateAge action state transitions to the end
+ view state:
+
+<end-state id="displayAge" view="yourAge" />
+
+ Once again the logical view name "yourAge" is a global-forward in struts-config.xml resolving to
+ /WEB-INF/jsp/yourAge.jsp. This JSP page retrieves the calculated age from request scope and
+ displays the results for the user.
+
+
+ The transition to the end state indicates the end of the web flow. The flow execution is cleaned up.
+ If the web flow is entered again a new flow execution will start, creating a new form
+ object named "birthDate" and placing it in flow scope.
+
+
+
+ Birthdate-alternate Web Flow
+
+ The birthdate-alternate web flow (/WEB-INF/birthdate-alternate.xml) offers an alternative way and
+ more compact way of defining the same web flow. For example the birthdate web flow defines two
+ independent states for the first screen - a view state (enterBirthdate) and an action state
+ (processBirthdateFormSubmit). In birthdate-alternate those are encapsulated in the view state
+ enterBirthdate as follows:
+
+<view-state id="enterBirthdate" view="birthdateForm">
+ <render-actions>
+ <action bean="formAction" method="setupForm" />
+ </render-actions>
+ <transition on="submit" to="enterCardInformation">
+ <action bean="formAction" method="bindAndValidate">
+ <attribute name="validatorMethod" value="validateBirthdateForm" />
+ </action>
+ </transition>
+</view-state>
+
+ Here the setupForm action state is defined as a render-action of the enterBirthdate view state
+ while the transition to the next screen uses a nested action bean invoked before the transition
+ occurs. Notice that success is implicitly required for the transition to occur. Similarly on error
+ the transition does not occur and the same view state is displayed again.
+
+
+ The second screen is also defined with a nested transition and action bean:
+
+<view-state id="enterCardInformation" view="cardForm">
+ <transition on="submit" to="calculateAge">
+ <action bean="formAction" method="bindAndValidate">
+ <attribute name="validatorMethod" value="validateCardForm" />
+ </action>
+ </transition>
+</view-state>
+
+ The remaining two states - calculateAge and displayAge are identical.
+
+
+ Phonebook-Portlet Example