diff --git a/spring-webflow/docs/reference/src/flow-definition.xml b/spring-webflow/docs/reference/src/flow-definition.xml index 2cd8ad0c..b466e799 100644 --- a/spring-webflow/docs/reference/src/flow-definition.xml +++ b/spring-webflow/docs/reference/src/flow-definition.xml @@ -1263,9 +1263,11 @@ - The above other points in a flow where actions may be executed do not - allow you to execute a state transition in response to the action result event. - If you need such flow control you must execute the action from within an action state. + + The above other points in a flow where actions may be executed do not + allow you to execute a state transition in response to the action result event. + If you need such flow control you must execute the action from within an action state. + @@ -1967,13 +1969,15 @@ - Caution: flow definitions should not be vehicles for - business logic. In this case the decision made was controller logic, reasoning on a - pre-calculated value to decide what step of the flow to transition to next. That is the kind of logic that - should be in a flow definition. In contrast, having the state itself embed - the business rule defining how shipping status is calculated is a misuse. - Instead, push such a calculation into application code where it belongs and instruct - the flow to invoke that code using an action. + + Caution: flow definitions should not be vehicles for + business logic. In this case the decision made was controller logic, reasoning on a + pre-calculated value to decide what step of the flow to transition to next. That is the kind of logic that + should be in a flow definition. In contrast, having the state itself embed + the business rule defining how shipping status is calculated is a misuse. + Instead, push such a calculation into application code where it belongs and instruct + the flow to invoke that code using an action. + diff --git a/spring-webflow/docs/reference/src/flow-executor.xml b/spring-webflow/docs/reference/src/flow-executor.xml index 804b5eff..f6da2f15 100644 --- a/spring-webflow/docs/reference/src/flow-executor.xml +++ b/spring-webflow/docs/reference/src/flow-executor.xml @@ -229,11 +229,13 @@ - The alwaysRedirectOnPause attribute determines if - a flow execution redirect occurs automatically each time an execution pauses - (automated POST+REDIRECT+GET behavior). - Setting this attribute to false will disable the default 'true' behavior - where a flow execution redirect always occurs on pause. + + The alwaysRedirectOnPause attribute determines if + a flow execution redirect occurs automatically each time an execution pauses + (automated POST+REDIRECT+GET behavior). + Setting this attribute to false will disable the default 'true' behavior + where a flow execution redirect always occurs on pause. + @@ -354,9 +356,11 @@ org.springframework.webflow.executor.support.FlowExecutorArgumentExtractor strategy. - The various flow controllers typically do not use this strategy directly but instead use a - convenient FlowExecutorArgumentHandler implementation that takes care - of argument extraction as well as exposing responsibilities (in callback URLs). + + The various flow controllers typically do not use this strategy directly but instead use a + convenient FlowExecutorArgumentHandler implementation that takes care + of argument extraction as well as exposing responsibilities (in callback URLs). + diff --git a/spring-webflow/docs/reference/src/overview.xml b/spring-webflow/docs/reference/src/overview.xml index f8053cfe..9e8be1d7 100644 --- a/spring-webflow/docs/reference/src/overview.xml +++ b/spring-webflow/docs/reference/src/overview.xml @@ -104,13 +104,15 @@ - Spring Web Flow, like Spring, is a layered framework, - packaged in a manner that allows teams to use the parts they need and nothing else. - For example, one team might use Spring Web Flow in a Servlet environment with Spring MVC - and thus require the Spring MVC integration. Another team might use SWF in a Portlet - environment, and thus require the Portlet MVC integration. Another team might mix and match. - A major benefit of SWF is that it allows you to define reusable, self-contained controller - modules that can execute in any environment. + + Spring Web Flow, like Spring, is a layered framework, + packaged in a manner that allows teams to use the parts they need and nothing else. + For example, one team might use Spring Web Flow in a Servlet environment with Spring MVC + and thus require the Spring MVC integration. Another team might use SWF in a Portlet + environment, and thus require the Portlet MVC integration. Another team might mix and match. + A major benefit of SWF is that it allows you to define reusable, self-contained controller + modules that can execute in any environment. + @@ -613,11 +615,13 @@ - As described above, some subsystem packages are optional depending on your use of the - subsystem. For example, use of Spring Web Flow in a Servlet environment entails use of - the ExternalContext context.servlet package which requires the - Servlet API to be in the classpath. In this case the context.portlet package is not - used and the Portlet API is not required. + + As described above, some subsystem packages are optional depending on your use of the + subsystem. For example, use of Spring Web Flow in a Servlet environment entails use of + the ExternalContext context.servlet package which requires the + Servlet API to be in the classpath. In this case the context.portlet package is not + used and the Portlet API is not required. + diff --git a/spring-webflow/docs/reference/src/practical.xml b/spring-webflow/docs/reference/src/practical.xml index 215b150d..74a197a6 100644 --- a/spring-webflow/docs/reference/src/practical.xml +++ b/spring-webflow/docs/reference/src/practical.xml @@ -1,2192 +1,2196 @@ - - - Practical Use of Spring Web Flow - - Sample applications - - It is recommended that you review the Spring Web Flow sample applications included in the - release distribution for best-practice illustrations of the features of this framework. - A description of each sample is provided below: - - - - - - Phonebook - the original sample demonstrating most core features (including subflows). - - - - - 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) - - - - - Shippingrate - demonstrates Spring Web Flow together with the Prototype Javascript framework (for Ajax-style flows) - - - - - NumberGuess - demonstrates use of stateful middle-tier components to carry out business logic. - - - - - Flowlauncher - demonstrates all the possible ways to launch and resume flows. - - - - - Itemlist - demonstrates REST-style URLs and inline flows. - - - - - Fileupload - demonstrates multipart file upload. - - - - - Birthdate - demonstrates Struts integration and the MultiAction. - - - - - Phonebook-Portlet - the phonebook sample in a Portlet environment - (notice how the flow definitions do not change). - - - - - - - Running the Web Flow sample applications - - 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 building the sample applications. - Ensure those are present in the system path or are passed on the command line. To build Web Flow - samples from 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 also provides basic helper targets - for deploying to Tomcat from Ant; however these webapp structures can be copied to any servlet container, - and each project is also a Eclipse Dynamic Web Project (DWP) for easy deployment inside Eclipse - with the Eclipse Webtools Project (WTP). - - - - 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 from. - Select OK. Here Eclipse will list all projects it found including the sample application projects. - Select the projects you're interested in, and select Finish. - - - If you previously built each project 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. - - - To build all projects inside Eclipse, import and expand the build-spring-webflow project, right-click on - build.xml and select Run As > Ant Build. - Doing this will run the default Ant target and will build all sample projects. - - - To build a single project inside Eclipse, simply select the project, right-click, and - select Run As > Ant Build. You can also use the convenient - shortcut ALT + SHIFT + X (Execute menu), then Q (Run Ant Build). - - - After Ant runs and the libraries needed to compile each project are downloaded, - all errors in the Eclipse problems view should go away. Try refreshing a project (F5) - if you still have errors. In general, from this point on you no longer need Ant: you - can rely on Eclipse's incremental compile and Eclipse's web tools (WTP) built-in JEE support - for deployment. (Ant is only needed in the system for command-line usage or when the list of - jar dependencies for a project changes and new jars need to be downloaded). - - - - Deploying projects inside Eclipse using Eclipse Web Tools (WTP) - - Each Spring Web Flow sample application project is a Eclipse Dynamic Web Project (DWP), - for easy deployment to a server running inside the Eclipse IDE. To take advantage - of this, you must be running Eclipse 3.2 with Web Tools 1.5. - - - To run a sample application as a webapp inside Eclipse, simply select the project, - right-click, and select Run -> Run On Server. A convenient - shortcut for this action is ALT + SHIFT + X (Execute menu), R (Run on Server). - The first time you do this you will be asked to setup a Server, where you are - expected to point Eclipse to a location where you have a Servlet Container - such as Apache Tomcat installed. Once your container has been setup and you finish the - deployment wizard, Eclipse will start the container and automatically publish - your webapp to it. In addition, it will launch a embedded web browser allowing you - to run the webapp fully inside the IDE. - - - - 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. - - - - - Sellitem Example - - Overview - - The Sellitem example demonstrates using Web Flow to build a - shopping cart wizard with - a shipping rate subflow, decision states, service and data access - Spring POJO beans, Spring 2.0 form tags, and a Web Flow - FormAction bean for data binding, validation, and - error reporting. - - - The Sellitem example breaks down its Spring application configuration - into a number of files organized according to purpose. - Although the example itself uses a small number of beans you - may consider organizing a real-world application (with many more - beans) according to similar principles. Before going into the specifics - of each individual context, use the diagram below to - get a brief overview of all configuration files including - location and purpose. - - - - - - - - - - Sellitem Spring & Web Flow Application Wiring - - - - - - Web.xml - - The web.xml configuration maps "*.htm" requests to the sellitem servlet - - a Spring MVC DispatcherServlet: - -<servlet> - <servlet-name>sellitem</servlet-name> - <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> - <init-param> - <param-name>contextConfigLocation</param-name> - <param-value> - /WEB-INF/sellitem-servlet-config.xml - /WEB-INF/sellitem-webflow-config.xml - </param-value> - </init-param> -</servlet> - -<servlet-mapping> - <servlet-name>sellitem</servlet-name> - <url-pattern>*.htm</url-pattern> -</servlet-mapping> - - The contextConifgLocation parameter for the DispatcherServlet indicates the - Spring MVC web context for the sellitem servlet is spread over two xml files: - sellitem-servlet-config.xml and sellitem-webflow-config.xml. - The web.xml also requests an additional Spring context to be loaded - from the classpath through the ContextLoaderListener: - -<context-param> - <param-name>contextConfigLocation</param-name> - <param-value> - classpath:org/springframework/webflow/samples/sellitem/services-config.xml - </param-value> -</context-param> - -<listener> - <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> -</listener> - - This service layer context defines beans to be referenced from - web flow definitions. The next section discusses the - content of this context in more detail. - - - - Services-config.xml - - The services-config.xml loaded from the classpath through Spring MVC's - ContextLoaderListener defines several beans for the service - and data access layers of the application. For example, - the service context defines a DAO bean ("saleProcessor") and injects - it with a data source: - -<bean id="saleProcessor" class="org.springframework.webflow.samples.sellitem.JdbcSaleProcessor"> - <property name="dataSource" ref="dataSource"/> -</bean> - -<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> - <property name="driverClassName" value="org.hsqldb.jdbcDriver"/> - <property name="url" value="jdbc:hsqldb:mem:sellItem"/> - <property name="username" value="sa"/> -</bean> - - The services context also declares a bean of type InMemoryDatabaseCreator - set to autowire by type meaning that its fields will be compared against the types of - beans available in the context and will be automatically set when a match is found. - Hence the dataSource bean is used to set the dataSource property - of InMemoryDatabaseCreator: - -<bean id="databaseCreator" class="org.springframework.webflow.samples.sellitem.InMemoryDatabaseCreator" - autowire="byType"/> - - Looking inside the InMemoryDatabaseCreator, its initDao() method invoked - during context initialization creates a table called T_SALES for use by the sample - application. This table is created in an in-memory hsqldb database called - sellitem (based on the url property of the dataSource bean). - It's also worth noting the bean declarations related to declarative - transaction management: - -<tx:annotation-driven/> - -<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> - <property name="dataSource" ref="dataSource"/> -</bean> - - The "<tx:annotation-driven>"declaration indicates transaction - configuration is governed by Java 5 annotations used in bean classes - such as this annotation in the SaleProcessor interface: - -@Transactional -public interface SaleProcessor { - public void process(Sale sale); -} - - For annotated beans the Spring container automatically creates - proxies according to the transaction semantics in the annotation - metadata. The "<tx:annotation-driven>" tag has a transaction-manager - attribute but this attribute is not required if the transaction - manager bean is named "transactionManager". - - - - Spring MVC Context - - The Spring MVC web context is split over two files - - sellitem-servlet-config.xml and sellitem-webflow-config.xml. - The sellitem-servlet-config.xml defines - a controller and a view resolver. - -<bean name="/pos.htm" class="org.springframework.webflow.executor.mvc.FlowController"> - <property name="flowExecutor" ref="flowExecutor" /> -</bean> - -<!-- Maps flow view-state view names to JSP templates --> -<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> - <property name="prefix" value="/WEB-INF/jsp/" /> - <property name="suffix" value=".jsp" /> -</bean> - - FlowController is a web flow controller extending Spring MVC's AbstractController - delegating requests (in this case for the "/pos.htm" servlet path) to the - flowExecutor bean it is configured with. FlowController acts - as gateway to Web Flow and a single controller instance can serve - the application as most of the actual control logic is encapsulated - in web flow definitions. - - - The sellitem-webflow-config.xml defines web flow specific beans such as - a flow executor, a flow registry and a flow listener beans: - -<!-- Launches new flow executions and resumes existing executions --> -<flow:executor id="flowExecutor" registry-ref="flowRegistry"> - <flow:execution-listeners> - <flow:listener ref="listener" criteria="sellitem-flow" /> - </flow:execution-listeners> -</flow:executor> - -<!-- Creates the registry of flow definitions for this application --> -<flow:registry id="flowRegistry"> - <flow:location path="/WEB-INF/flows/**/*-flow.xml" /> -</flow:registry> - -<!-- Observes the lifecycle of sellitem-flow executions --> -<bean id="listener" - class="org.springframework.webflow.samples.sellitem.SellItemFlowExecutionListener" /> - - The FlowExecutor is the central entry point into the - Spring Web Flow system. It drives the execution of flow definitions - configured through the flowRegistry. The flowRegistry bean is configured - to load definitions from files ending with "-flow.xml" in any - subdirectory of /WEB-INF/flows. This matches to - sellitem-flow.xml, shipping-flow.xml, sellitem-simple-flow.xml, - sellitem-conversation-scope-flow.xml and shipping-conversation-scope-flow.xml. - - - As shown here the flow executor can also be configured with a flow - listener, which is a callback mechanism for flow execution lifecycle events. - The SellItemFlowExecutionListener extends FlowExecutionListenerAdapter - - a default implementation of the FlowExecutionListener interface - sparing the need to implement methods for all lifecycle events. - - - Looking inside SellItemFlowExecutionListener it implements the stateEntering - method executed whenever a new state is about to be entered. - The logic in this method checks if the current web flow state - has an attribute named "role" and if so it ensures the user - has that role: - -String role = nextState.getAttributes().getString("role"); -if (StringUtils.hasText(role)) { - HttpServletRequest request = ((ServletExternalContext)context.getExternalContext()).getRequest(); - if (!request.isUserInRole(role)) { - throw new EnterStateVetoException(context.getActiveFlow().getId(), context.getCurrentState().getId(), - nextState.getId(), "State requires role '" + role - + "', but the authenticated user doesn't have it!"); - } -} - - - - Based on the above definitions - web.xml, Spring MVC controller bean, and - web flow registry, the sellitem-flow can be initiated with the - following URI: - -/swf-sellitem/pos.htm?_flowId=sellitem-flow - - - Note: although it is possible to invoke the shipping-flow directly as well, - it expects an input attribute and is intended to be invoked as a subflow. - - - - - Sellitem-beans.xml - - Before tracing the sequence of states in sellitem-flow.xml notice the - import declaration at the bottom of that file: - -<import resource="sellitem-beans.xml"/> - - The sellitem-beans.xml located in the same directory declares a - web flow FormAction bean for use in the flow definition and configures - it with a SaleValidator and a SellItemPropertyEditorRegistrar: - -<!-- Manages setting up, binding input to, and validating a Sale "backing wizard form object" --> -<bean id="formAction" class="org.springframework.webflow.action.FormAction"> - <property name="formObjectName" value="sale"/> - <property name="validator"> - <bean class="org.springframework.webflow.samples.sellitem.SaleValidator"/> - </property> - <!-- Installs property editors used to format non-String fields like 'shipDate' --> - <property name="propertyEditorRegistrar"> - <bean class="org.springframework.webflow.samples.sellitem.SellItemPropertyEditorRegistrar"/> - </property> -</bean> - - The SellValidator will be used to validate form input data. - The SellItemPropertyEditorRegistrar is responsible for registering - custom property editors. Such editors are used to bind text data from - HTML form fields to server-side Objects. For example - SellItemPropertyEditorRegistrar registers a custom date - editor: - -public void registerCustomEditors(PropertyEditorRegistry registry) { - registry.registerCustomEditor(Date.class, - new CustomDateEditor(new SimpleDateFormat("MM/dd/yyyy"), true)); -} - - This editor will bind the shipDate form field in shippingDetailsForm.jsp - to the shipDate property of the Sale object on the server side. - - - - Sellitem-flow Flow Definition - - The flow begins by declaring a "sale" variable - an object of type Sale: - -<var name="sale" class="org.springframework.webflow.samples.sellitem.Sale"/> - - The formAction bean will use the sale variable for form binding and - validation (see sellitem-beans.xml). - - - The start state for the flow enterPriceAndItemCount is a view state, which resolves - to the JSP page /WEB-INF/jsp/priceAndItemCountForm.jsp: - -<view-state id="enterPriceAndItemCount" view="priceAndItemCountForm"> - <render-actions> - <!-- create the backing form object and initialize a empty errors collection --> - <action bean="formAction" method="setupForm"/> - </render-actions> - <transition on="submit" to="enterCategory"> - <action bean="formAction" method="bindAndValidate"> - <attribute name="validatorMethod" value="validatePriceAndItemCount"/> - </action> - </transition> -</view-state> - - The view state uses a render action to invoke the setupForm method of the - formAction bean. The setupForm method prepares a form object based on - the "sale" variable declared at the top of the flow definition. - - - The priceAndItemCountForm.jsp page collects a price and an itemCount using - Spring 2.0 form input tags binding form fields to properties in the form - backing object "sale". When pressed, the submit button "_eventId_submit" - causes a web flow transition for an event with the id of "submit" to - the view state "enterCategory". Prior to transitioning the formAction's - bindAndValidate method is called to perform binding and (partial) validation - using the validatePriceAndItemCount method of the validator object. - - - The next view state enterCategory (based on categoryForm.jsp) - collects inputs for sale category and whether shipping is required. On - submit it transitions to the requiresShipping state: - -<view-state id="enterCategory" view="categoryForm"> - <transition on="submit" to="requiresShipping"> - <action bean="formAction" method="bind"/> - </transition> -</view-state> - - The requiresShipping state is a decision state making - flow routing decisions. It evaluates a boolean expression against the - executing flow and it decides where to transition to next. - Here the shipping boolean property of the "sale" form backing - object is checked to decide whether to go to the enterShippingDetails - subflow state or proceed directly to processSale. - -<decision-state id="requiresShipping"> - <if test="${flowScope.sale.shipping}" then="enterShippingDetails" else="processSale"/> -</decision-state> - - The enterShippingDetails subflow state is based on shipping-flow.xml - located in the same directory. The form backing object "sale" is - passed to it as an input attribute using an attribute mapper declaration: - -<subflow-state id="enterShippingDetails" flow="shipping-flow"> - <attribute-mapper> - <input-mapper> - <input-attribute name="sale"/> - </input-mapper> - </attribute-mapper> - <transition on="finish" to="processSale"/> -</subflow-state> - - The shipping-flow subflow is a simple flow with one view state. It - collects the shipping details, binds the data and returns to its parent - flow. The id of the subflow end state "finish" is returned to the - parent subflow state causing a transition to the processSale action state. - -<action-state id="processSale"> - <bean-action bean="saleProcessor" method="process"> - <method-arguments> - <argument expression="flowScope.sale"/> - </method-arguments> - </bean-action> - <transition on="success" to="finish"/> -</action-state> - - The saleProcessor bean, a POJO defined in services-config.xml - is invoked using a "bean-action" declaration (as opposed to the "action" - declation used to invoke a web flow Action such as FormAction). - The saleProcessor (an instance of JdbcSaleProcessor) performs a database - update using the values of the Sale object and upon - successful completion transitions to the end view state: - -<end-state id="finish" view="costOverview"> - <entry-actions> - <action bean="formAction" method="setupForm"/> - </entry-actions> -</end-state> - - Then end state calls FormAction's setupForm method again. - This does not re-create the "sale" form object (still in flow scope) - but it does ensure any custom property editors are - registered for use in rendering the JSP. - - - - Sellitem-simple-flow Flow Definition - - A simpler version of the sellitem-flow is available in the sellitem-simple-flow.xml file. - This version uses a view state to gather shipping details instead of using a subflow. You - can launch the sellitem-simple-flow using the following URI: - -/swf-sellitem/pos.htm?_flowId=sellitem-simple-flow - - - - - Sellitem-conversation-scope-flow Flow Definition - - This web flow is equivalent in functionality to the sellitem-flow definition - described above. The main difference is that it uses "conversation" - scope to store the form backing object declared in - /WEB-INF/flows/converstation-scope/sellitem-beans.xml. - -<bean id="formAction" class="org.springframework.webflow.action.FormAction"> - <property name="formObjectName" value="sale"/> - <property name="formObjectScope" value="CONVERSATION"/> - <property name="formErrorsScope" value="CONVERSATION"/> - - Conversation scope retains attributes stored in it for the life - of the flow execution and is shared by all flow sessions. - For example when invoking the shipping details subflow the parent - flow does not need to pass the "sale" form backing object because - it is now stored in conversation scope and is accessible to both flows: - -<subflow-state id="enterShippingDetails" flow="shipping-conversation-scope-flow"> - <transition on="finish" to="processSale"/> -</subflow-state> - - Also, when the "sale" object needs to be accessed it is done by referencing - conversation cope: - -<decision-state id="requiresShipping"> - <if test="${conversationScope.sale.shipping}" then="enterShippingDetails" else="processSale"/> -</decision-state> - - You can launch the sellitem-conversation-scope-flow using the following URI: - -/swf-sellitem/pos.htm?_flowId=sellitem-conversation-scope-flow - - - - - - Sellitem-JSF Example - - Overview - - The Sellitem-JSF example uses Web Flow and JSF to build a shopping - cart wizard. Navigation logic and supporting managed beans - are supplied by Spring Web Flow while UI views and - overall servlet processing is based on JSF technology. - - - - The underlying Web Flow definitions for the Sellitem and the Sellitem-JSF - examples are very similar. To avoid repetition the documentation for - the Sellitem-JSF example focuses primarily on the points of integration between - Web Flow and JSF. For further general information on Web Flow definitions - and supporting Java classes for the Sellitem example please refer to the - Sellitem example documentation. - - - - - Web.xml - - The web.xml contains standard JSF configuration including mappings for - the JSF front servlet: it handles all requests ending with "*.faces": - -<!-- Faces Servlet --> -<servlet> - <servlet-name>Faces Servlet</servlet-name> - <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> - <load-on-startup>1</load-on-startup> -</servlet> - -<servlet-mapping> - <servlet-name>Faces Servlet</servlet-name> - <url-pattern>*.faces</url-pattern> -</servlet-mapping> - - - - In addition the web.xml loads a Spring root web application context containing the services - used by the application: - -<context-param> - <param-name>contextConfigLocation</param-name> - <param-value> - classpath:org/springframework/webflow/samples/sellitem/services-config.xml - /WEB-INF/webflow-config.xml - </param-value> -</context-param> - -<listener> - <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> -</listener> - - The services-config.xml contains POJO beans required for the services and - data access layers of the application. These declarations are very similar - to the Sellitem example (and explained in more detail there). - The webflow-config.xml contains Web Flow related bean - definitions. These definitions will be explained a little bit - further on in the context of how they fit into the JSF phases - lifecycle. - - - - Web Flow JSF Setup in faces-config.xml - - To plug in Web Flow a few things must be added once to faces-config.xml. - This is demonstrated in the faces-config.xml of Sellitem-JSF: - -<application> - <navigation-handler>org.springframework.webflow.executor.jsf.FlowNavigationHandler</navigation-handler> - <variable-resolver>org.springframework.webflow.executor.jsf.DelegatingFlowVariableResolver</variable-resolver> -</application> - -<lifecycle> - <phase-listener>org.springframework.webflow.executor.jsf.FlowPhaseListener</phase-listener> -</lifecycle> - - - - The FlowNavigationHandler delegates view navigation handling to the - the Web Flow system when a flow is initiated or resumed. - - - The DelegatingFlowVariableResolver is suitable for use along side - other variable resolvers to support EL binding expressions like - {#bean.property} where "bean" could be a property in any supported scope. - The resolver search algorithm looks in flash scope first, then flow scope, - then conversation scope. If no variable is found, this resolver - delegates to the next resolver in the chain. - - - The FlowPhaseListener invoked during beforePhase and afterPhase JSF events - is responsible for managing the lifecycle of a FlowExecution and making it available - to other JSF artifacts during the lifecycle of a JSF request. - - - - Web Flow System Setup in webflow-config.xml - - Examining the definitions in faces-config.xml highlighted the ability - to use plug Web Flow in as a navigation handler and as a source - for JSF managed beans. Now we can turn to the question - of how to configure the web flow system itself in a JSF environment. - - - The Spring web context fragment /WEB-INF/webflow-config.xml contains the following configuration: - -<!-- 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/flows/sellitem-flow.xml" /> -</flow:registry> - - Here the flow executor is configured to support execution of a single - flow definition - sellitem-flow.xml. The executor bean has been assigned the id - "flowExecutor". This id is significant and is required for the JSF artifacts to detect - the executor and its services. - - - - Launching the sellitem-flow - - The intro.jsp page shows how the configured web flow sellitem-flow.xml can be launched using - a JSF command link component. - -<h:form> - <h:commandLink value="Sell Item" action="flowId:sellitem-flow"/> -</h:form> - - This causes the sellitem-flow to be initiated. Once a flow is initiated - each subsequent JSP page can participate in the flow (the flow execution key - is tracked for you). - - - A few notable differences between Sellitem and Sellitem-JSF to keep in mind: - - - - The JSF version of the sellitem flow definition is simpler because JSF components care for data binding and validation. - - - - - In its web flow definition Sellitem-JSF uses actual JSP names (instead of the logical view names used in Sellitem) - to be rendered by JSF. This is consistent with normal JSF-isms. - - - - - The JSP pages in Sellitem-JSF use unified EL to access the converastion scoped Sale object - e.g. #{sale.price}. - - - - Sellitem-JSF uses JSF component tags for UI and Sellitem uses Spring form tags. - - - - There is no need to manually track the flow execution key because it is tracked for you in the JSF view root. - - - - The combination of delegating flow variable resolution plus automatic flow execution key management - means JSF views selected a flow look like standard JSF views to JSF developers. Also, JSF components - help simplify flow definition logic as the flow no longer has to worry about data binding and validation. - - - For more information and understanding on the Sellitem flow definition logic itself - please refer to the documentation for the original Sellitem example. - - - - - 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 - - Overview - - The Phonebook-Portlet demonstrates how to run the - Phonebook - sample as a JSR-168 portlet. The functionality for Phonebook and Phonebook-Portlet - including web flow definitions, JSP pages, and Java classes is the same and - already well documented. - The focus in Phonebook-Portlet is specifically on how to configure - and run Phonebook in a Portal container. - - - - JSR-168 defines portlets but not how portlets integrate into a - portal container. This process is left open to portal vendors who - have their own individual mechanisms. - The Phonebook-Portlet sample is configured to run with - Apache Pluto - - a reference implementation of the Java Portlet Specification. - However, its dependence on Pluto is limited to configuration in web.xml. - Hence it should be easy to adapt for use - in other Portal/Portlet implementations after learning the - deployment steps specific for that implementation. - - - - - Portal/Portlet Related Software Used in the Sample - - This section provides a very brief introduction to the portal related - supporting software used in the sample - namely Apache Pluto - and the Portlet MVC framework. If this is not new for you - feel free to skip to the next section. - - - Apache Pluto - - For those familiar with servlet applications the process - of deploying and running a portlet application can be - confusing and requires some explanation. - Typically an application with JSR-168 portlets runs in - one webapp while a portal/portlet container runs - in a separate webapp making cross-context calls to - portlets. How exactly this is configured - depends on each portal vendor. - - - Pluto is an open-source reference implementation of the - Java Portlet specification. The following general steps - are required to run portlets with it. First the - the portlet application's web.xml is "injected" with - configuration required for Pluto. Secondly Pluto's - Portal web application, usually set to run at - http://localhost:8080/pluto/portal - is used to add or remove portlets to one or more - portal container pages. - - - The web.xml for the Phonebook-Portlet sample has - already been "injected" with the configuration required - for Pluto 1.1.0. Although this enables it for use with Pluto - you must still use the - admin pages of Pluto's Portal web application to add - the Phonebook-Portlet to a test portal page. For more - information on how to do this please follow instructions from - Apache Pluto. - - - - Portlet MVC Framework - - The Portlet MVC framework represents Spring's support for JSR-168. - It has many parallels with the Spring MVC framework such - as the DispatcherPortlet, the Controller interface, - handler mappings, view resolvers, and exception handlers. - The main differences between Portlet MVC and Spring MVC - have to do with the lifecycles of a portlet and its - distinct phases as defined in the Porlet Specification: - the action and the render phases. - For more information see - - Chapter 16 (Portlet MVC Framework) from the Spring reference - documentation. - - - - Getting Phonebook Portlet up and Runnign with Apache Pluto - - Since the phonebook portlet was tested with Apache Pluto we've - decided to documents the steps taken to deploy and run it - - Download the Pluto 1.1 binary distribution named pluto-current-bundle from http://portals.apache.org/pluto - Unzip the binary distribution to any directory. - Create the directory [pluto-home]/webapps/swf-phonebook-portlet - Copy the content of [webflow-release]/spring-webflow-samples/phonebook-portlet/target/artifacts/war-expanded to the directory created in the previous step - Start Pluto with [pluto-home]/bin/startup - Go to http://localhost:8080/pluto/portal - Login as tomcat/tomcat (or any other user but see note below) - After logging in you will be taken to the Portal Test page. - Here you will see a Navigation pull-down menu at the top. Select 'Pluto Admin' from it to go to the Pluto Admin page. - On the Pluto Admin page under Portlet Applications you will see a drop-down with available portlet applications - Select '/swf-phonebook-portlet' from it, then phonebook from the drop-down next to it, and then press the 'Add Portlet' button - Use the Navigation menu at the top to go back to the Test Page. The Phonebook portlet should be present. - - - - The tomcat user must have the 'pluto' role. Open - [pluto-home]/conf/tomcat-users.xml and ensure the - following lines are there: - -<role rolename="pluto"/> -<user username="tomcat" password="tomcat" roles="tomcat,pluto"/> - - - - - - Portlet.xml Configuration - - Portlet.xml is a standard deployment descriptor where - portlet resources are defined. The Phonebook-Portlet - is based the Portlet MVC DispatcherPorlet: - -<portlet-class> - org.springframework.web.portlet.DispatcherPortlet -</portlet-class> - - The DispatcherPortlet is Spring's implementation of the Portlet interface - dispatching requests for a portlet to registered Portlet MVC handlers. - The phonebook portlet is configured with the following Spring - contexts containing Portlet MVC handler, controller and - view resolver beans: - -<init-param> - <name>contextConfigLocation</name> - <value> - /WEB-INF/phonebook-portlet-config.xml /WEB-INF/phonebook-webflow-config.xml - </value> -</init-param> - - The above configuration defines phonebook as a portlet resource. In order - to use it in a portal/portlet container - additional web.xml configuration is required. - - - - Web.xml Configuration - - The Java Portlet Specification is defined as a layer over existing Servlet - infrastructure. Therefore some sort of a servlet is required to accept servlet - requests and expose portlet resources. Portal vendors - provide such servlets and specific configuration varies by vendor. - The Phonebook-Portlet has the following Apache Pluto servlet definition - and servlet mapping: - -<!-- Generated Portlet Wrapper Servlet for Apache Pluto deployment --> -<servlet> - <servlet-name>phonebook</servlet-name> - <servlet-class>org.apache.pluto.core.PortletServlet</servlet-class> - <init-param> - <param-name>portlet-name</param-name> - <param-value>phonebook</param-value> - </init-param> - <load-on-startup>1</load-on-startup> -</servlet> -<servlet-mapping> - <servlet-name>phonebook</servlet-name> - <url-pattern>/PlutoInvoker/phonebook</url-pattern> -</servlet-mapping> - - - - - The above configuration was auto generated using ant tasks from - Apache Pluto 1.1.0. This configuration is included in web.xml - for convenience and also as an example. - For the most up-to-date information on required configuration please - check Pluto's documentation. - - - - The web.xml configuration also contains the following servlet definition: - -<servlet> - <servlet-name>viewRendererServlet</servlet-name> - <servlet-class> - org.springframework.web.servlet.ViewRendererServlet - </servlet-class> -</servlet> -<servlet-mapping> - <servlet-name>viewRendererServlet</servlet-name> - <url-pattern>/WEB-INF/servlet/view</url-pattern> -</servlet-mapping> - - - - The main purpose of this servlet is to allow reuse of Spring MVC's flexible - view resolution and rendering capabilities in a Portlet application. - The DispatcherPortlet converts a PortletRequest/PortletResponse to an - HttpServletRequest/HttpServletResponse and then performs an include of - this servlet. - - - - Portlet MVC Configuration - - The phonebook-portlet-config.xml is very similar to the Spring MVC - equivalent phonebook-servlet.xml from the Phonebook sample. The main - difference is in the use of a PortletModeHandlerMapping: - -<bean id="portletModeControllerMapping" - class="org.springframework.web.portlet.handler.PortletModeHandlerMapping"> - <property name="portletModeMap"> - <map> - <entry key="view" value-ref="flowController"/> - </map> - </property> -</bean> - - and a PortletFlowController: - -<bean id="flowController" class="org.springframework.webflow.executor.mvc.PortletFlowController"> - <property name="flowExecutor" ref="flowExecutor"/> - <property name="defaultFlowId" value="search-flow"/> -</bean> - - A PortletModeHandlerMapping allows mapping specific to each - portlet mode. The VIEW mode in this case is mapped to the - flowController bean, which delegates the request to Web Flow's - executor for launching or resuming a flow from a flow definition. - For more information on Phonebook flow definitions please - refer to the - Phonebook - sample documentation. - - - One last thing to observe is the following configuration in - /WEB-INF/phonebook-webflow-config.xml: - -<!-- Launches new flow executions and resumes existing executions. --> -<flow:executor id="flowExecutor" registry-ref="flowRegistry"> - <flow:execution-attributes> - <!-- execution redirects don't apply in a Portlet environment --> - <flow:alwaysRedirectOnPause value="false"/> - </flow:execution-attributes> -</flow:executor> - - As the comment indicates the default behavior of redirect after submit - must be turned off in a portlet environment where there is no HTTP redirect. - For more information on the alwaysRedirectOnPause refer to the following - article. - - - - + + + Practical Use of Spring Web Flow + + Sample applications + + It is recommended that you review the Spring Web Flow sample applications included in the + release distribution for best-practice illustrations of the features of this framework. + A description of each sample is provided below: + + + + + + Phonebook - the original sample demonstrating most core features (including subflows). + + + + + 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). + + + + + Shippingrate - demonstrates Spring Web Flow together with the Prototype Javascript framework (for Ajax-style flows). + + + + + NumberGuess - demonstrates use of stateful middle-tier components to carry out business logic. + + + + + Flowlauncher - demonstrates all the possible ways to launch and resume flows. + + + + + Itemlist - demonstrates REST-style URLs and inline flows. + + + + + Fileupload - demonstrates multipart file upload. + + + + + Birthdate - demonstrates Struts integration and the MultiAction. + + + + + Phonebook-Portlet - the phonebook sample in a Portlet environment + (notice how the flow definitions do not change). + + + + + + + Running the Web Flow sample applications + + 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 building the sample applications. + Ensure those are present in the system path or are passed on the command line. To build Web Flow + samples from 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 also provides basic helper targets + for deploying to Tomcat from Ant; however these webapp structures can be copied to any servlet container, + and each project is also a Eclipse Dynamic Web Project (DWP) for easy deployment inside Eclipse + with the Eclipse Webtools Project (WTP). + + + + 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 from. + Select OK. Here Eclipse will list all projects it found including the sample application projects. + Select the projects you're interested in, and select Finish. + + + If you previously built each project 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. + + + To build all projects inside Eclipse, import and expand the build-spring-webflow project, right-click on + build.xml and select Run As > Ant Build. + Doing this will run the default Ant target and will build all sample projects. + + + To build a single project inside Eclipse, simply select the project, right-click, and + select Run As > Ant Build. You can also use the convenient + shortcut ALT + SHIFT + X (Execute menu), then Q (Run Ant Build). + + + After Ant runs and the libraries needed to compile each project are downloaded, + all errors in the Eclipse problems view should go away. Try refreshing a project (F5) + if you still have errors. In general, from this point on you no longer need Ant: you + can rely on Eclipse's incremental compile and Eclipse's web tools (WTP) built-in JEE support + for deployment. (Ant is only needed in the system for command-line usage or when the list of + jar dependencies for a project changes and new jars need to be downloaded). + + + + Deploying projects inside Eclipse using Eclipse Web Tools (WTP) + + Each Spring Web Flow sample application project is a Eclipse Dynamic Web Project (DWP), + for easy deployment to a server running inside the Eclipse IDE. To take advantage + of this, you must be running Eclipse 3.2 with Web Tools 1.5. + + + To run a sample application as a webapp inside Eclipse, simply select the project, + right-click, and select Run -> Run On Server. A convenient + shortcut for this action is ALT + SHIFT + X (Execute menu), R (Run on Server). + The first time you do this you will be asked to setup a Server, where you are + expected to point Eclipse to a location where you have a Servlet Container + such as Apache Tomcat installed. Once your container has been setup and you finish the + deployment wizard, Eclipse will start the container and automatically publish + your webapp to it. In addition, it will launch a embedded web browser allowing you + to run the webapp fully inside the IDE. + + + + 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. + + + + + Sellitem Example + + Overview + + The Sellitem example demonstrates using Web Flow to build a + shopping cart wizard with + a shipping rate subflow, decision states, service and data access + Spring POJO beans, Spring 2.0 form tags, and a Web Flow + FormAction bean for data binding, validation, and + error reporting. + + + The Sellitem example breaks down its Spring application configuration + into a number of files organized according to purpose. + Although the example itself uses a small number of beans you + may consider organizing a real-world application (with many more + beans) according to similar principles. Before going into the specifics + of each individual context, use the diagram below to + get a brief overview of all configuration files including + location and purpose. + + + + + + + + + + Sellitem Spring & Web Flow Application Wiring + + + + + + Web.xml + + The web.xml configuration maps "*.htm" requests to the sellitem servlet - + a Spring MVC DispatcherServlet: + +<servlet> + <servlet-name>sellitem</servlet-name> + <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> + <init-param> + <param-name>contextConfigLocation</param-name> + <param-value> + /WEB-INF/sellitem-servlet-config.xml + /WEB-INF/sellitem-webflow-config.xml + </param-value> + </init-param> +</servlet> + +<servlet-mapping> + <servlet-name>sellitem</servlet-name> + <url-pattern>*.htm</url-pattern> +</servlet-mapping> + + The contextConifgLocation parameter for the DispatcherServlet indicates the + Spring MVC web context for the sellitem servlet is spread over two xml files: + sellitem-servlet-config.xml and sellitem-webflow-config.xml. + The web.xml also requests an additional Spring context to be loaded + from the classpath through the ContextLoaderListener: + +<context-param> + <param-name>contextConfigLocation</param-name> + <param-value> + classpath:org/springframework/webflow/samples/sellitem/services-config.xml + </param-value> +</context-param> + +<listener> + <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> +</listener> + + This service layer context defines beans to be referenced from + web flow definitions. The next section discusses the + content of this context in more detail. + + + + Services-config.xml + + The services-config.xml loaded from the classpath through Spring MVC's + ContextLoaderListener defines several beans for the service + and data access layers of the application. For example, + the service context defines a DAO bean ("saleProcessor") and injects + it with a data source: + +<bean id="saleProcessor" class="org.springframework.webflow.samples.sellitem.JdbcSaleProcessor"> + <property name="dataSource" ref="dataSource"/> +</bean> + +<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> + <property name="driverClassName" value="org.hsqldb.jdbcDriver"/> + <property name="url" value="jdbc:hsqldb:mem:sellItem"/> + <property name="username" value="sa"/> +</bean> + + The services context also declares a bean of type InMemoryDatabaseCreator + set to autowire by type meaning that its fields will be compared against the types of + beans available in the context and will be automatically set when a match is found. + Hence the dataSource bean is used to set the dataSource property + of InMemoryDatabaseCreator: + +<bean id="databaseCreator" class="org.springframework.webflow.samples.sellitem.InMemoryDatabaseCreator" + autowire="byType"/> + + Looking inside the InMemoryDatabaseCreator, its initDao() method invoked + during context initialization creates a table called T_SALES for use by the sample + application. This table is created in an in-memory hsqldb database called + sellitem (based on the url property of the dataSource bean). + It's also worth noting the bean declarations related to declarative + transaction management: + +<tx:annotation-driven/> + +<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> + <property name="dataSource" ref="dataSource"/> +</bean> + + The "<tx:annotation-driven>"declaration indicates transaction + configuration is governed by Java 5 annotations used in bean classes + such as this annotation in the SaleProcessor interface: + +@Transactional +public interface SaleProcessor { + public void process(Sale sale); +} + + For annotated beans the Spring container automatically creates + proxies according to the transaction semantics in the annotation + metadata. The "<tx:annotation-driven>" tag has a transaction-manager + attribute but this attribute is not required if the transaction + manager bean is named "transactionManager". + + + + Spring MVC Context + + The Spring MVC web context is split over two files - + sellitem-servlet-config.xml and sellitem-webflow-config.xml. + The sellitem-servlet-config.xml defines + a controller and a view resolver. + +<bean name="/pos.htm" class="org.springframework.webflow.executor.mvc.FlowController"> + <property name="flowExecutor" ref="flowExecutor" /> +</bean> + +<!-- Maps flow view-state view names to JSP templates --> +<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> + <property name="prefix" value="/WEB-INF/jsp/" /> + <property name="suffix" value=".jsp" /> +</bean> + + FlowController is a web flow controller extending Spring MVC's AbstractController + delegating requests (in this case for the "/pos.htm" servlet path) to the + flowExecutor bean it is configured with. FlowController acts + as gateway to Web Flow and a single controller instance can serve + the application as most of the actual control logic is encapsulated + in web flow definitions. + + + The sellitem-webflow-config.xml defines web flow specific beans such as + a flow executor, a flow registry and a flow listener beans: + +<!-- Launches new flow executions and resumes existing executions --> +<flow:executor id="flowExecutor" registry-ref="flowRegistry"> + <flow:execution-listeners> + <flow:listener ref="listener" criteria="sellitem-flow" /> + </flow:execution-listeners> +</flow:executor> + +<!-- Creates the registry of flow definitions for this application --> +<flow:registry id="flowRegistry"> + <flow:location path="/WEB-INF/flows/**/*-flow.xml" /> +</flow:registry> + +<!-- Observes the lifecycle of sellitem-flow executions --> +<bean id="listener" + class="org.springframework.webflow.samples.sellitem.SellItemFlowExecutionListener" /> + + The FlowExecutor is the central entry point into the + Spring Web Flow system. It drives the execution of flow definitions + configured through the flowRegistry. The flowRegistry bean is configured + to load definitions from files ending with "-flow.xml" in any + subdirectory of /WEB-INF/flows. This matches to + sellitem-flow.xml, shipping-flow.xml, sellitem-simple-flow.xml, + sellitem-conversation-scope-flow.xml and shipping-conversation-scope-flow.xml. + + + As shown here the flow executor can also be configured with a flow + listener, which is a callback mechanism for flow execution lifecycle events. + The SellItemFlowExecutionListener extends FlowExecutionListenerAdapter - + a default implementation of the FlowExecutionListener interface + sparing the need to implement methods for all lifecycle events. + + + Looking inside SellItemFlowExecutionListener it implements the stateEntering + method executed whenever a new state is about to be entered. + The logic in this method checks if the current web flow state + has an attribute named "role" and if so it ensures the user + has that role: + +String role = nextState.getAttributes().getString("role"); +if (StringUtils.hasText(role)) { + HttpServletRequest request = ((ServletExternalContext)context.getExternalContext()).getRequest(); + if (!request.isUserInRole(role)) { + throw new EnterStateVetoException(context.getActiveFlow().getId(), context.getCurrentState().getId(), + nextState.getId(), "State requires role '" + role + + "', but the authenticated user doesn't have it!"); + } +} + + + + Based on the above definitions - web.xml, Spring MVC controller bean, and + web flow registry, the sellitem-flow can be initiated with the + following URI: + +/swf-sellitem/pos.htm?_flowId=sellitem-flow + + + Note: although it is possible to invoke the shipping-flow directly as well, + it expects an input attribute and is intended to be invoked as a subflow. + + + + + Sellitem-beans.xml + + Before tracing the sequence of states in sellitem-flow.xml notice the + import declaration at the bottom of that file: + +<import resource="sellitem-beans.xml"/> + + The sellitem-beans.xml located in the same directory declares a + web flow FormAction bean for use in the flow definition and configures + it with a SaleValidator and a SellItemPropertyEditorRegistrar: + +<!-- Manages setting up, binding input to, and validating a Sale "backing wizard form object" --> +<bean id="formAction" class="org.springframework.webflow.action.FormAction"> + <property name="formObjectName" value="sale"/> + <property name="validator"> + <bean class="org.springframework.webflow.samples.sellitem.SaleValidator"/> + </property> + <!-- Installs property editors used to format non-String fields like 'shipDate' --> + <property name="propertyEditorRegistrar"> + <bean class="org.springframework.webflow.samples.sellitem.SellItemPropertyEditorRegistrar"/> + </property> +</bean> + + The SellValidator will be used to validate form input data. + The SellItemPropertyEditorRegistrar is responsible for registering + custom property editors. Such editors are used to bind text data from + HTML form fields to server-side Objects. For example + SellItemPropertyEditorRegistrar registers a custom date + editor: + +public void registerCustomEditors(PropertyEditorRegistry registry) { + registry.registerCustomEditor(Date.class, + new CustomDateEditor(new SimpleDateFormat("MM/dd/yyyy"), true)); +} + + This editor will bind the shipDate form field in shippingDetailsForm.jsp + to the shipDate property of the Sale object on the server side. + + + + Sellitem-flow Flow Definition + + The flow begins by declaring a "sale" variable - an object of type Sale: + +<var name="sale" class="org.springframework.webflow.samples.sellitem.Sale"/> + + The formAction bean will use the sale variable for form binding and + validation (see sellitem-beans.xml). + + + The start state for the flow enterPriceAndItemCount is a view state, which resolves + to the JSP page /WEB-INF/jsp/priceAndItemCountForm.jsp: + +<view-state id="enterPriceAndItemCount" view="priceAndItemCountForm"> + <render-actions> + <!-- create the backing form object and initialize a empty errors collection --> + <action bean="formAction" method="setupForm"/> + </render-actions> + <transition on="submit" to="enterCategory"> + <action bean="formAction" method="bindAndValidate"> + <attribute name="validatorMethod" value="validatePriceAndItemCount"/> + </action> + </transition> +</view-state> + + The view state uses a render action to invoke the setupForm method of the + formAction bean. The setupForm method prepares a form object based on + the "sale" variable declared at the top of the flow definition. + + + The priceAndItemCountForm.jsp page collects a price and an itemCount using + Spring 2.0 form input tags binding form fields to properties in the form + backing object "sale". When pressed, the submit button "_eventId_submit" + causes a web flow transition for an event with the id of "submit" to + the view state "enterCategory". Prior to transitioning the formAction's + bindAndValidate method is called to perform binding and (partial) validation + using the validatePriceAndItemCount method of the validator object. + + + The next view state enterCategory (based on categoryForm.jsp) + collects inputs for sale category and whether shipping is required. On + submit it transitions to the requiresShipping state: + +<view-state id="enterCategory" view="categoryForm"> + <transition on="submit" to="requiresShipping"> + <action bean="formAction" method="bind"/> + </transition> +</view-state> + + The requiresShipping state is a decision state making + flow routing decisions. It evaluates a boolean expression against the + executing flow and it decides where to transition to next. + Here the shipping boolean property of the "sale" form backing + object is checked to decide whether to go to the enterShippingDetails + subflow state or proceed directly to processSale. + +<decision-state id="requiresShipping"> + <if test="${flowScope.sale.shipping}" then="enterShippingDetails" else="processSale"/> +</decision-state> + + The enterShippingDetails subflow state is based on shipping-flow.xml + located in the same directory. The form backing object "sale" is + passed to it as an input attribute using an attribute mapper declaration: + +<subflow-state id="enterShippingDetails" flow="shipping-flow"> + <attribute-mapper> + <input-mapper> + <input-attribute name="sale"/> + </input-mapper> + </attribute-mapper> + <transition on="finish" to="processSale"/> +</subflow-state> + + The shipping-flow subflow is a simple flow with one view state. It + collects the shipping details, binds the data and returns to its parent + flow. The id of the subflow end state "finish" is returned to the + parent subflow state causing a transition to the processSale action state. + +<action-state id="processSale"> + <bean-action bean="saleProcessor" method="process"> + <method-arguments> + <argument expression="flowScope.sale"/> + </method-arguments> + </bean-action> + <transition on="success" to="finish"/> +</action-state> + + The saleProcessor bean, a POJO defined in services-config.xml + is invoked using a "bean-action" declaration (as opposed to the "action" + declation used to invoke a web flow Action such as FormAction). + The saleProcessor (an instance of JdbcSaleProcessor) performs a database + update using the values of the Sale object and upon + successful completion transitions to the end view state: + +<end-state id="finish" view="costOverview"> + <entry-actions> + <action bean="formAction" method="setupForm"/> + </entry-actions> +</end-state> + + Then end state calls FormAction's setupForm method again. + This does not re-create the "sale" form object (still in flow scope) + but it does ensure any custom property editors are + registered for use in rendering the JSP. + + + + Sellitem-simple-flow Flow Definition + + A simpler version of the sellitem-flow is available in the sellitem-simple-flow.xml file. + This version uses a view state to gather shipping details instead of using a subflow. You + can launch the sellitem-simple-flow using the following URI: + +/swf-sellitem/pos.htm?_flowId=sellitem-simple-flow + + + + + Sellitem-conversation-scope-flow Flow Definition + + This web flow is equivalent in functionality to the sellitem-flow definition + described above. The main difference is that it uses "conversation" + scope to store the form backing object declared in + /WEB-INF/flows/converstation-scope/sellitem-beans.xml. + +<bean id="formAction" class="org.springframework.webflow.action.FormAction"> + <property name="formObjectName" value="sale"/> + <property name="formObjectScope" value="CONVERSATION"/> + <property name="formErrorsScope" value="CONVERSATION"/> + + Conversation scope retains attributes stored in it for the life + of the flow execution and is shared by all flow sessions. + For example when invoking the shipping details subflow the parent + flow does not need to pass the "sale" form backing object because + it is now stored in conversation scope and is accessible to both flows: + +<subflow-state id="enterShippingDetails" flow="shipping-conversation-scope-flow"> + <transition on="finish" to="processSale"/> +</subflow-state> + + Also, when the "sale" object needs to be accessed it is done by referencing + conversation cope: + +<decision-state id="requiresShipping"> + <if test="${conversationScope.sale.shipping}" then="enterShippingDetails" else="processSale"/> +</decision-state> + + You can launch the sellitem-conversation-scope-flow using the following URI: + +/swf-sellitem/pos.htm?_flowId=sellitem-conversation-scope-flow + + + + + + Sellitem-JSF Example + + Overview + + The Sellitem-JSF example uses Web Flow and JSF to build a shopping + cart wizard. Navigation logic and supporting managed beans + are supplied by Spring Web Flow while UI views and + overall servlet processing is based on JSF technology. + + + + The underlying Web Flow definitions for the Sellitem and the Sellitem-JSF + examples are very similar. To avoid repetition the documentation for + the Sellitem-JSF example focuses primarily on the points of integration between + Web Flow and JSF. For further general information on Web Flow definitions + and supporting Java classes for the Sellitem example please refer to the + Sellitem example documentation. + + + + + Web.xml + + The web.xml contains standard JSF configuration including mappings for + the JSF front servlet: it handles all requests ending with "*.faces": + +<!-- Faces Servlet --> +<servlet> + <servlet-name>Faces Servlet</servlet-name> + <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> + <load-on-startup>1</load-on-startup> +</servlet> + +<servlet-mapping> + <servlet-name>Faces Servlet</servlet-name> + <url-pattern>*.faces</url-pattern> +</servlet-mapping> + + + + In addition the web.xml loads a Spring root web application context containing the services + used by the application: + +<context-param> + <param-name>contextConfigLocation</param-name> + <param-value> + classpath:org/springframework/webflow/samples/sellitem/services-config.xml + /WEB-INF/webflow-config.xml + </param-value> +</context-param> + +<listener> + <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> +</listener> + + The services-config.xml contains POJO beans required for the services and + data access layers of the application. These declarations are very similar + to the Sellitem example (and explained in more detail there). + The webflow-config.xml contains Web Flow related bean + definitions. These definitions will be explained a little bit + further on in the context of how they fit into the JSF phases + lifecycle. + + + + Web Flow JSF Setup in faces-config.xml + + To plug in Web Flow a few things must be added once to faces-config.xml. + This is demonstrated in the faces-config.xml of Sellitem-JSF: + +<application> + <navigation-handler>org.springframework.webflow.executor.jsf.FlowNavigationHandler</navigation-handler> + <variable-resolver>org.springframework.webflow.executor.jsf.DelegatingFlowVariableResolver</variable-resolver> +</application> + +<lifecycle> + <phase-listener>org.springframework.webflow.executor.jsf.FlowPhaseListener</phase-listener> +</lifecycle> + + + + The FlowNavigationHandler delegates view navigation handling to the + the Web Flow system when a flow is initiated or resumed. + + + The DelegatingFlowVariableResolver is suitable for use along side + other variable resolvers to support EL binding expressions like + {#bean.property} where "bean" could be a property in any supported scope. + The resolver search algorithm looks in flash scope first, then flow scope, + then conversation scope. If no variable is found, this resolver + delegates to the next resolver in the chain. + + + The FlowPhaseListener invoked during beforePhase and afterPhase JSF events + is responsible for managing the lifecycle of a FlowExecution and making it available + to other JSF artifacts during the lifecycle of a JSF request. + + + + Web Flow System Setup in webflow-config.xml + + Examining the definitions in faces-config.xml highlighted the ability + to use plug Web Flow in as a navigation handler and as a source + for JSF managed beans. Now we can turn to the question + of how to configure the web flow system itself in a JSF environment. + + + The Spring web context fragment /WEB-INF/webflow-config.xml contains the following configuration: + +<!-- 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/flows/sellitem-flow.xml" /> +</flow:registry> + + Here the flow executor is configured to support execution of a single + flow definition - sellitem-flow.xml. The executor bean has been assigned the id + "flowExecutor". This id is significant and is required for the JSF artifacts to detect + the executor and its services. + + + + Launching the sellitem-flow + + The intro.jsp page shows how the configured web flow sellitem-flow.xml can be launched using + a JSF command link component. + +<h:form> + <h:commandLink value="Sell Item" action="flowId:sellitem-flow"/> +</h:form> + + This causes the sellitem-flow to be initiated. Once a flow is initiated + each subsequent JSP page can participate in the flow (the flow execution key + is tracked for you). + + + A few notable differences between Sellitem and Sellitem-JSF to keep in mind: + + + + The JSF version of the sellitem flow definition is simpler because JSF components care for data binding and validation. + + + + + In its web flow definition Sellitem-JSF uses actual JSP names (instead of the logical view names used in Sellitem) + to be rendered by JSF. This is consistent with normal JSF-isms. + + + + + The JSP pages in Sellitem-JSF use unified EL to access the converastion scoped Sale object - e.g. #{sale.price}. + + + + Sellitem-JSF uses JSF component tags for UI and Sellitem uses Spring form tags. + + + + There is no need to manually track the flow execution key because it is tracked for you in the JSF view root. + + + + The combination of delegating flow variable resolution plus automatic flow execution key management + means JSF views selected a flow look like standard JSF views to JSF developers. Also, JSF components + help simplify flow definition logic as the flow no longer has to worry about data binding and validation. + + + For more information and understanding on the Sellitem flow definition logic itself + please refer to the documentation for the original Sellitem example. + + + + + 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 + + Overview + + The Phonebook-Portlet demonstrates how to run the + Phonebook + sample as a JSR-168 portlet. The functionality for Phonebook and Phonebook-Portlet + including web flow definitions, JSP pages, and Java classes is the same and + already well documented. + The focus in Phonebook-Portlet is specifically on how to configure + and run Phonebook in a Portal container. + + + + JSR-168 defines portlets but not how portlets integrate into a + portal container. This process is left open to portal vendors who + have their own individual mechanisms. + The Phonebook-Portlet sample is configured to run with + Apache Pluto - + a reference implementation of the Java Portlet Specification. + However, its dependence on Pluto is limited to configuration in web.xml. + Hence it should be easy to adapt for use + in other Portal/Portlet implementations after learning the + deployment steps specific for that implementation. + + + + + Portal/Portlet Related Software Used in the Sample + + This section provides a very brief introduction to the portal related + supporting software used in the sample - namely Apache Pluto + and the Portlet MVC framework. If this is not new for you + feel free to skip to the next section. + + + Apache Pluto + + For those familiar with servlet applications the process + of deploying and running a portlet application can be + confusing and requires some explanation. + Typically an application with JSR-168 portlets runs in + one webapp while a portal/portlet container runs + in a separate webapp making cross-context calls to + portlets. How exactly this is configured + depends on each portal vendor. + + + Pluto is an open-source reference implementation of the + Java Portlet specification. The following general steps + are required to run portlets with it. First the + the portlet application's web.xml is "injected" with + configuration required for Pluto. Secondly Pluto's + Portal web application, usually set to run at + http://localhost:8080/pluto/portal + is used to add or remove portlets to one or more + portal container pages. + + + The web.xml for the Phonebook-Portlet sample has + already been "injected" with the configuration required + for Pluto 1.1.0. Although this enables it for use with Pluto + you must still use the + admin pages of Pluto's Portal web application to add + the Phonebook-Portlet to a test portal page. For more + information on how to do this please follow instructions from + Apache Pluto. + + + + Portlet MVC Framework + + The Portlet MVC framework represents Spring's support for JSR-168. + It has many parallels with the Spring MVC framework such + as the DispatcherPortlet, the Controller interface, + handler mappings, view resolvers, and exception handlers. + The main differences between Portlet MVC and Spring MVC + have to do with the lifecycles of a portlet and its + distinct phases as defined in the Porlet Specification: + the action and the render phases. + For more information see + + Chapter 16 (Portlet MVC Framework) from the Spring reference + documentation. + + + + Getting Phonebook Portlet up and Runnign with Apache Pluto + + Since the phonebook portlet was tested with Apache Pluto we've + decided to documents the steps taken to deploy and run it + + Download the Pluto 1.1 binary distribution named pluto-current-bundle from http://portals.apache.org/pluto + Unzip the binary distribution to any directory. + Create the directory [pluto-home]/webapps/swf-phonebook-portlet + Copy the content of [webflow-release]/spring-webflow-samples/phonebook-portlet/target/artifacts/war-expanded to the directory created in the previous step + Start Pluto with [pluto-home]/bin/startup + Go to http://localhost:8080/pluto/portal + Login as tomcat/tomcat (or any other user but see note below) + After logging in you will be taken to the Portal Test page. + Here you will see a Navigation pull-down menu at the top. Select 'Pluto Admin' from it to go to the Pluto Admin page. + On the Pluto Admin page under Portlet Applications you will see a drop-down with available portlet applications + Select '/swf-phonebook-portlet' from it, then phonebook from the drop-down next to it, and then press the 'Add Portlet' button + Use the Navigation menu at the top to go back to the Test Page. The Phonebook portlet should be present. + + + + + The tomcat user must have the 'pluto' role. Open + [pluto-home]/conf/tomcat-users.xml and ensure the + following lines are there: + +<role rolename="pluto"/> +<user username="tomcat" password="tomcat" roles="tomcat,pluto"/> + + + + + + + Portlet.xml Configuration + + Portlet.xml is a standard deployment descriptor where + portlet resources are defined. The Phonebook-Portlet + is based the Portlet MVC DispatcherPorlet: + +<portlet-class> + org.springframework.web.portlet.DispatcherPortlet +</portlet-class> + + The DispatcherPortlet is Spring's implementation of the Portlet interface + dispatching requests for a portlet to registered Portlet MVC handlers. + The phonebook portlet is configured with the following Spring + contexts containing Portlet MVC handler, controller and + view resolver beans: + +<init-param> + <name>contextConfigLocation</name> + <value> + /WEB-INF/phonebook-portlet-config.xml /WEB-INF/phonebook-webflow-config.xml + </value> +</init-param> + + The above configuration defines phonebook as a portlet resource. In order + to use it in a portal/portlet container + additional web.xml configuration is required. + + + + Web.xml Configuration + + The Java Portlet Specification is defined as a layer over existing Servlet + infrastructure. Therefore some sort of a servlet is required to accept servlet + requests and expose portlet resources. Portal vendors + provide such servlets and specific configuration varies by vendor. + The Phonebook-Portlet has the following Apache Pluto servlet definition + and servlet mapping: + +<!-- Generated Portlet Wrapper Servlet for Apache Pluto deployment --> +<servlet> + <servlet-name>phonebook</servlet-name> + <servlet-class>org.apache.pluto.core.PortletServlet</servlet-class> + <init-param> + <param-name>portlet-name</param-name> + <param-value>phonebook</param-value> + </init-param> + <load-on-startup>1</load-on-startup> +</servlet> +<servlet-mapping> + <servlet-name>phonebook</servlet-name> + <url-pattern>/PlutoInvoker/phonebook</url-pattern> +</servlet-mapping> + + + + + The above configuration was auto generated using ant tasks from + Apache Pluto 1.1.0. This configuration is included in web.xml + for convenience and also as an example. + For the most up-to-date information on required configuration please + check Pluto's documentation. + + + + The web.xml configuration also contains the following servlet definition: + +<servlet> + <servlet-name>viewRendererServlet</servlet-name> + <servlet-class> + org.springframework.web.servlet.ViewRendererServlet + </servlet-class> +</servlet> +<servlet-mapping> + <servlet-name>viewRendererServlet</servlet-name> + <url-pattern>/WEB-INF/servlet/view</url-pattern> +</servlet-mapping> + + + + The main purpose of this servlet is to allow reuse of Spring MVC's flexible + view resolution and rendering capabilities in a Portlet application. + The DispatcherPortlet converts a PortletRequest/PortletResponse to an + HttpServletRequest/HttpServletResponse and then performs an include of + this servlet. + + + + Portlet MVC Configuration + + The phonebook-portlet-config.xml is very similar to the Spring MVC + equivalent phonebook-servlet.xml from the Phonebook sample. The main + difference is in the use of a PortletModeHandlerMapping: + +<bean id="portletModeControllerMapping" + class="org.springframework.web.portlet.handler.PortletModeHandlerMapping"> + <property name="portletModeMap"> + <map> + <entry key="view" value-ref="flowController"/> + </map> + </property> +</bean> + + and a PortletFlowController: + +<bean id="flowController" class="org.springframework.webflow.executor.mvc.PortletFlowController"> + <property name="flowExecutor" ref="flowExecutor"/> + <property name="defaultFlowId" value="search-flow"/> +</bean> + + A PortletModeHandlerMapping allows mapping specific to each + portlet mode. The VIEW mode in this case is mapped to the + flowController bean, which delegates the request to Web Flow's + executor for launching or resuming a flow from a flow definition. + For more information on Phonebook flow definitions please + refer to the + Phonebook + sample documentation. + + + One last thing to observe is the following configuration in + /WEB-INF/phonebook-webflow-config.xml: + +<!-- Launches new flow executions and resumes existing executions. --> +<flow:executor id="flowExecutor" registry-ref="flowRegistry"> + <flow:execution-attributes> + <!-- execution redirects don't apply in a Portlet environment --> + <flow:alwaysRedirectOnPause value="false"/> + </flow:execution-attributes> +</flow:executor> + + As the comment indicates the default behavior of redirect after submit + must be turned off in a portlet environment where there is no HTTP redirect. + For more information on the alwaysRedirectOnPause refer to the following + article. + + + +