diff --git a/spring-webflow/docs/reference/src/practical.xml b/spring-webflow/docs/reference/src/practical.xml
index 1ccd5eb6..98b4f5c0 100644
--- a/spring-webflow/docs/reference/src/practical.xml
+++ b/spring-webflow/docs/reference/src/practical.xml
@@ -11,7 +11,29 @@
- Phonebook - the original sample demonstrating most features (including subflows).
+
+ Phonebook - the original sample demonstrating most features (including subflows).
+
+
+
+
+ Fileupload - demonstrates multipart file upload.
+
+
+
+
+ Birthdate - demonstrates Struts integration and the MultiAction.
+
+
+
+
+ Shippingrate - demonstrates Spring Web Flow together with Ajax technology.
+
+
+
+
+ NumberGuess - demonstrates use of stateful middle-tier components to carry out business logic.
+ Sellitem - demonstrates a wizard with conditional transitions, flow scope, flow execution redirects, and continuations.
@@ -22,22 +44,6 @@
Itemlist - demonstrates REST-style URLs and inline flows.
-
- Shippingrate - demonstrates Spring Web Flow together with Ajax technology.
-
-
- NumberGuess - demonstrates use of stateful middle-tier components to carry out business logic.
-
-
-
- Birthdate - demonstrates Struts integration and the MultiAction.
-
-
-
-
- Fileupload - demonstrates multipart file upload.
-
- Phonebook-Portlet - the phonebook sample in a Portlet environment (notice how the flow definitions do not change).
@@ -504,4 +510,465 @@ public BirthDateFormAction() {
+
+ 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.
+
+
+
+ This sample has been tested successfully on Internet Explorer 6 and 7,
+ Firefox 2.0, and Safari 2. There are known Javascript issues
+ with use on Firefox 1.5.
+
+
+
+ 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.
+
+
+