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