Added practical reference doc material contributed by Ross.
This commit is contained in:
@@ -26,6 +26,10 @@
|
||||
<firstname>Erwin</firstname>
|
||||
<surname>Vervaet</surname>
|
||||
</author>
|
||||
<author>
|
||||
<firstname>Ross</firstname>
|
||||
<surname>Stoyanchev</surname>
|
||||
</author>
|
||||
</authorgroup>
|
||||
<legalnotice>
|
||||
<para>
|
||||
|
||||
@@ -29,10 +29,14 @@
|
||||
<para>NumberGuess - demonstrates use of stateful middle-tier components to carry out business logic.</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>Birthdate - demonstrates Struts integration and the MultiAction.</para>
|
||||
<para>
|
||||
<link linkend="birthdate-sample">Birthdate</link> - demonstrates Struts integration and the MultiAction.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>Fileupload - demonstrates multipart file upload.</para>
|
||||
<para>
|
||||
<link linkend="fileupload-sample">Fileupload</link> - demonstrates multipart file upload.
|
||||
</para>
|
||||
</listitem>
|
||||
<listitem>
|
||||
<para>Phonebook-Portlet - the phonebook sample in a Portlet environment (notice how the flow definitions do not change).</para>
|
||||
@@ -44,4 +48,436 @@
|
||||
</orderedlist>
|
||||
</para>
|
||||
</sect1>
|
||||
<sect1 id="running-samples">
|
||||
<title>Running the Samples</title>
|
||||
<para>
|
||||
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.
|
||||
</para>
|
||||
<sect2>
|
||||
<title>Building from the Command Line</title>
|
||||
<para>
|
||||
Java 1.5 (or greater) and Ant 1.6 (or greater) are prerequisites for running the build. Ensure
|
||||
those are present in the system path or are passed on the command line. Open a prompt, cd to the
|
||||
directory where Spring Web Flow was unzipped and run the following:
|
||||
<programlisting>
|
||||
cd projects/spring-webflow/build-spring-webflow
|
||||
ant dist
|
||||
</programlisting>
|
||||
This builds all samples preparing "target" areas within each sample project subdirectory
|
||||
containing webapp structures in both exploded and WAR archive forms. The build provides targets
|
||||
for deploying to Tomcat. However these webapp structures can be copied to any servlet container.
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Deploying to Tomcat</title>
|
||||
<para>
|
||||
In order to use the Ant Tomcat tasks cd to the projects/spring-webflow/build-spring-webflow
|
||||
subdirectory (relative to where Spring Web Flow was unzipped) and add the following property
|
||||
to the build.properties file (create it if it doesn't exist):
|
||||
<programlisting>
|
||||
tomcat.dir=c:/tomcat
|
||||
</programlisting>
|
||||
where c:/tomcat is the location of the Tomcat installation to deploy to. When this is done run
|
||||
"ant tomcat-copy-war" from the same directory to copy all sample applications.
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Importing Projects into Eclipse</title>
|
||||
<para>
|
||||
Importing the sample projects into Eclipse is easy. With a new or an existing workspace select:
|
||||
File > Import > Existing Projects into Workspace. In the resulting dialog browse to the project
|
||||
subdirectory where Spring Web Flow was unzipped and choose it as the root directory to import form.
|
||||
Press OK. Here Eclipse will list all projects it found including the samples projects. Press Finish and
|
||||
allow the build to complete.
|
||||
</para>
|
||||
<para>
|
||||
If you previously did a build from the command line Eclipse will compile with no errors. If not
|
||||
you will need to run the ant build once for these errors to clear and you can do that within
|
||||
Eclipse. Expand the build-spring-webflow project in Eclipse, right-click build.xml and select:
|
||||
Run As > Ant Build. This will run the default Ant target and will build all sample projects.
|
||||
When this is done all errors in the Eclipse problems view should go away. Next you can run the
|
||||
"tomcat-copy-war" Ant task if you didn't alread do so (see the previous section).
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Other IDE's</title>
|
||||
<para>
|
||||
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.
|
||||
</para>
|
||||
</sect2>
|
||||
</sect1>
|
||||
<sect1 id="fileupload-sample">
|
||||
<title>Fileupload Example</title>
|
||||
<sect2>
|
||||
<title>Overview</title>
|
||||
<para>
|
||||
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.
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Web.xml</title>
|
||||
<para>
|
||||
The web.xml configuration maps requests for "*.htm" to the fileupload servlet - a regular
|
||||
Spring MVC DispatcherServlet:
|
||||
<programlisting>
|
||||
<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>
|
||||
</programlisting>
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Spring MVC Context</title>
|
||||
<para>
|
||||
The Spring MVC servlet context for the fileupload servlet (WEB-INF/fileupload-servlet.xml) defines
|
||||
one controller bean:
|
||||
<programlisting>
|
||||
<bean name="/admin.htm" class="org.springframework.webflow.executor.mvc.FlowController">
|
||||
<property name="flowExecutor" ref="flowExecutor" />
|
||||
</bean>
|
||||
</programlisting>
|
||||
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:
|
||||
<programlisting>
|
||||
<!-- 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>
|
||||
</programlisting>
|
||||
Given the above definitions the following URI can be used to invoke the "fileupload" flow:
|
||||
<programlisting>
|
||||
/swf-fileupload/admin.htm?_flowId=fileupload
|
||||
</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
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.
|
||||
</para>
|
||||
<para>
|
||||
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.
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Fileupload Web Flow</title>
|
||||
<para>
|
||||
The start state for the fileupload flow (WEB-INF/fileupload.xml) is a view state:
|
||||
<programlisting>
|
||||
<start-state idref="selectFile"/>
|
||||
|
||||
<view-state id="selectFile" view="fileForm">
|
||||
<transition on="submit" to="uploadFile"/>
|
||||
</view-state>
|
||||
</programlisting>
|
||||
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.
|
||||
</para>
|
||||
<para>
|
||||
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.
|
||||
</para>
|
||||
<para>
|
||||
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:
|
||||
<programlisting>
|
||||
<action-state id="uploadFile">
|
||||
<action bean="uploadAction" method="uploadFile"/>
|
||||
<transition on="success" to="selectFile">
|
||||
<set attribute="fileUploaded" scope="flash" value="true"/>
|
||||
</transition>
|
||||
<transition on="error" to="selectFile"/>
|
||||
</action-state>
|
||||
</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
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):
|
||||
<programlisting>
|
||||
<bean id="uploadAction" class="org.springframework.webflow.samples.fileupload.FileUploadAction" />
|
||||
</programlisting>
|
||||
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
|
||||
</para>
|
||||
<para>
|
||||
A flash-scoped attribute called "file" is also set programmatically in the FileUploadAction bean:
|
||||
<programlisting>
|
||||
context.getFlashScope().put("file", new String(file.getBytes()));
|
||||
return success();
|
||||
</programlisting>
|
||||
This illustrates the choice to save attributes in one of several scopes either programatically or
|
||||
declaratively.
|
||||
</para>
|
||||
</sect2>
|
||||
</sect1>
|
||||
<sect1 id="birthdate-sample">
|
||||
<title>Birthdate Example</title>
|
||||
<sect2>
|
||||
<title>Overview</title>
|
||||
<para>
|
||||
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.
|
||||
</para>
|
||||
<para>
|
||||
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.
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Web.xml</title>
|
||||
<para>
|
||||
The web.xml configuration maps requests for "*.do" to a regular Struts ActionServlet:
|
||||
<programlisting>
|
||||
<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>
|
||||
</programlisting>
|
||||
The web.xml also sets up the loading of a Spring context at web application startup:
|
||||
<programlisting>
|
||||
<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>
|
||||
</programlisting>
|
||||
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.
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Struts Configuration</title>
|
||||
<para>
|
||||
The Struts configuration (WEB-INF/struts-config.xml) defines the following action mapping:
|
||||
<programlisting>
|
||||
<action-mappings>
|
||||
<action path="/flowAction" name="actionForm" scope="request"
|
||||
type="org.springframework.webflow.executor.struts.FlowAction"/>
|
||||
</action-mappings>
|
||||
</programlisting>
|
||||
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:
|
||||
<programlisting>
|
||||
<!-- 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>
|
||||
</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
Based on the above, Web Flow is configured with two flows - birthdate and birthdate-alternate,
|
||||
which can be invoked as follows:
|
||||
<programlisting>
|
||||
/swf-birthdate/flowAction.do?_flowId=birthdate
|
||||
/swf-birthdate/flowAction.do?_flowId=birthdate-alternate
|
||||
</programlisting>
|
||||
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.
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Birthdate Web Flow</title>
|
||||
<para>
|
||||
The birthdate web flow (WEB-INF/birthdate.xml) defines the following start state:
|
||||
<programlisting>
|
||||
<view-state id="enterBirthdate" view="birthdateForm">
|
||||
<render-actions>
|
||||
<action bean="formAction" method="setupForm" />
|
||||
</render-actions>
|
||||
<transition on="submit" to="processBirthdateFormSubmit" />
|
||||
</view-state>
|
||||
</programlisting>
|
||||
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:
|
||||
<programlisting>
|
||||
<bean id="formAction" class="org.springframework.webflow.samples.birthdate.BirthDateFormAction" />
|
||||
</programlisting>
|
||||
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.
|
||||
</para>
|
||||
<para>
|
||||
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:
|
||||
<programlisting>
|
||||
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());
|
||||
}
|
||||
</programlisting>
|
||||
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.
|
||||
</para>
|
||||
<para>
|
||||
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:
|
||||
<programlisting>
|
||||
<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>
|
||||
</programlisting>
|
||||
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.
|
||||
</para>
|
||||
<para>
|
||||
On error the action returns to the view state it came from. On success it transitions to the
|
||||
enterCardInformation view state:
|
||||
<programlisting>
|
||||
<view-state id="enterCardInformation" view="cardForm">
|
||||
<transition on="submit" to="processCardFormSubmit" />
|
||||
</view-state>
|
||||
</programlisting>
|
||||
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:
|
||||
<programlisting>
|
||||
<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>
|
||||
</programlisting>
|
||||
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.
|
||||
</para>
|
||||
<para>
|
||||
On error the action returns to the view state it came from. On success it transitions to another
|
||||
action state called calculateAge:
|
||||
<programlisting>
|
||||
<action-state id="calculateAge">
|
||||
<action bean="formAction" method="calculateAge" />
|
||||
<transition on="success" to="displayAge" />
|
||||
</action-state>
|
||||
</programlisting>
|
||||
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.
|
||||
</para>
|
||||
<para>
|
||||
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:
|
||||
<programlisting>
|
||||
<end-state id="displayAge" view="yourAge" />
|
||||
</programlisting>
|
||||
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.
|
||||
</para>
|
||||
<para>
|
||||
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.
|
||||
</para>
|
||||
</sect2>
|
||||
<sect2>
|
||||
<title>Birthdate-alternate Web Flow</title>
|
||||
<para>
|
||||
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:
|
||||
<programlisting>
|
||||
<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>
|
||||
</programlisting>
|
||||
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.
|
||||
</para>
|
||||
<para>
|
||||
The second screen is also defined with a nested transition and action bean:
|
||||
<programlisting>
|
||||
<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>
|
||||
</programlisting>
|
||||
The remaining two states - calculateAge and displayAge are identical.
|
||||
</para>
|
||||
</sect2>
|
||||
</sect1>
|
||||
</chapter>
|
||||
|
||||
Reference in New Issue
Block a user