diff --git a/spring-webflow/docs/reference/src/index.xml b/spring-webflow/docs/reference/src/index.xml index c5ddc11a..7e68655e 100644 --- a/spring-webflow/docs/reference/src/index.xml +++ b/spring-webflow/docs/reference/src/index.xml @@ -26,6 +26,10 @@ Erwin Vervaet + + Ross + Stoyanchev + diff --git a/spring-webflow/docs/reference/src/practical.xml b/spring-webflow/docs/reference/src/practical.xml index 5744a8c4..dc097d50 100644 --- a/spring-webflow/docs/reference/src/practical.xml +++ b/spring-webflow/docs/reference/src/practical.xml @@ -29,10 +29,14 @@ NumberGuess - demonstrates use of stateful middle-tier components to carry out business logic. - Birthdate - demonstrates Struts integration and the MultiAction. + + Birthdate - demonstrates Struts integration and the MultiAction. + - Fileupload - demonstrates multipart file upload. + + Fileupload - demonstrates multipart file upload. + Phonebook-Portlet - the phonebook sample in a Portlet environment (notice how the flow definitions do not change). @@ -44,4 +48,436 @@ + + Running the Samples + + The samples can be built from the command line and imported as Eclipse projects - all samples come + with Eclipse project settings. It is also possible to start by importing the samples into Eclipse + first and then build with Ant within Eclipse. + + + Building from the Command Line + + Java 1.5 (or greater) and Ant 1.6 (or greater) are prerequisites for running the build. Ensure + those are present in the system path or are passed on the command line. Open a prompt, cd to the + directory where Spring Web Flow was unzipped and run the following: + +cd projects/spring-webflow/build-spring-webflow +ant dist + + This builds all samples preparing "target" areas within each sample project subdirectory + containing webapp structures in both exploded and WAR archive forms. The build provides targets + for deploying to Tomcat. However these webapp structures can be copied to any servlet container. + + + + Deploying to Tomcat + + In order to use the Ant Tomcat tasks cd to the projects/spring-webflow/build-spring-webflow + subdirectory (relative to where Spring Web Flow was unzipped) and add the following property + to the build.properties file (create it if it doesn't exist): + +tomcat.dir=c:/tomcat + + where c:/tomcat is the location of the Tomcat installation to deploy to. When this is done run + "ant tomcat-copy-war" from the same directory to copy all sample applications. + + + + Importing Projects into Eclipse + + Importing the sample projects into Eclipse is easy. With a new or an existing workspace select: + File > Import > Existing Projects into Workspace. In the resulting dialog browse to the project + subdirectory where Spring Web Flow was unzipped and choose it as the root directory to import form. + Press OK. Here Eclipse will list all projects it found including the samples projects. Press Finish and + allow the build to complete. + + + If you previously did a build from the command line Eclipse will compile with no errors. If not + you will need to run the ant build once for these errors to clear and you can do that within + Eclipse. Expand the build-spring-webflow project in Eclipse, right-click build.xml and select: + Run As > Ant Build. This will run the default Ant target and will build all sample projects. + When this is done all errors in the Eclipse problems view should go away. Next you can run the + "tomcat-copy-war" Ant task if you didn't alread do so (see the previous section). + + + + Other IDE's + + Importing samples into other IDE's should be fairly straight-forward. If using another IDE + running the Ant build from the command line first may help as it will populate the lib + subdirectories of each sample project. Follow steps similar as those outlined for Eclipse above. + + + + + 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" method="uploadFile"/> + <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. + + +