From 5036d9d64e6a3d227213341e19c865dc54f6bcd0 Mon Sep 17 00:00:00 2001 From: Erwin Vervaet Date: Wed, 17 Jan 2007 09:43:58 +0000 Subject: [PATCH] Incorporated documentation material on the shippingrate and numberguess sample applications. --- .../docs/reference/src/practical.xml | 501 +++++++++++++++++- 1 file changed, 484 insertions(+), 17 deletions(-) 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. + + +