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.
+
+
+
+