We'll now add a SWF flow definition file, update our original home page JSP, and add a new JSP for registering as a new customer.
Here's a definition for our first flow, which will be a bare-bones
registration process. We'll add more flows to the application but
this is our starting point just to get SWF working. You'll need to
create a directory /WEB-INF/flows, and then add the
following flow definition file to that directory, naming the file
register.xml.
/WEB-INF/flows/register.xml
<?xml version="1.0" encoding="UTF-8"?>
<flow xmlns="http://www.springframework.org/schema/webflow"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/webflow
http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">
<!-- By default, the first state is the start state. -->
<view-state id="register" view="account/registerForm">
<transition on="submitRegistration" to="accountAdded"/>
<transition on="cancelRegistration" to="cancelRegistration"/>
</view-state>
<end-state id="accountAdded"
view="externalRedirect:contextRelative:/home.do"/>
<end-state id="cancelRegistration"
view="externalRedirect:contextRelative:/home.do"/>
</flow>
Here we have a single state, called a view state, and all
it does is show us the hardcoded JSP that we created earlier. We're
identifying the state itself as register, and the logical
view name associated with this view state is
account/registerForm. This is the name that the view
resolver we defined in the Spring app context file will map to a
physical location; in this case the physical location will be
/WEB-INF/jsp/account/registerForm.jsp.
By default, SWF interprets the first state in the file as being the
start state, or entry point into the flow. There must be
exactly one start state. The flow itself is called
register (this is because we've named the flow definition
file register.xml). As it happens we've also named the
first state register though there's no requirement for
the start state to have the same name as the flow.
I've defined a couple of transitions out of the
register state. One transition,
submitRegistration, responds to registration submission
events on the user interface, such as the user clicking a submit
button on a registration form. The other transition,
cancelRegistration, responds to cancelation events on the
UI, such as the user clicking a cancel link. Each of these
transitions leads to another state in the flow. In this case, both of
the transitions lead to end states, which are exits out of
the flow, but transitions can lead to other view states (or even other
sorts of state) as well. As shown there can be multiple end states
for a flow. Here my end states happen to be doing the same thing;
they're redirecting the user to a context-relative (as in relative to
the servlet context) path /home.do, which you will recall
is just the home page. There are some other relativizations you can do as well:
| Relativization | Example |
|---|---|
| Servlet mapping-relative | externalRedirect:/hotels/index |
| Servlet-relative path | externalRedirect:servletRelative:/hotels/index |
| Context-relative path | externalRedirect:contextRelative:/dispatcher/hotels/index |
| Server-relative path | externalRedirect:serverRelative:/springtravel/dispatcher/hotels/index |
| Absolute URL | externalRedirect:http://www.paypal.com/ |
Anyway we're doing the context-relative redirection in this case. This is how we jump out of SWF and return control to the application. (Or how we return control to a calling flow, but we'll get to that.)
Now let's revisit our home page JSP. It's still pretty similar but I'm adding a registration link.
/WEB-INF/jsp/home.jsp
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<title>Products for Geeks - GeekWarez</title>
</head>
<body>
<h1>Welcome to GeekWarez</h1>
<div><a href="account/register.do">Register</a></div>
</body>
</html>
The registration link takes us into the register flow,
since the last path segment is register.do. In our
Spring app context we told the flow registry about
register.xml, so SWF will know to map requests for
/account/register.do to the register
flow.
Now we need a registration form. The following page is our current version of a registration "form", but as you can see it isn't really a form at all (yet). It is just a couple of links:
/WEB-INF/jsp/account/registerForm.jsp
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<title>Register - GeekWarez</title>
</head>
<body>
<h1>Register</h1>
<div>
<a href="${flowExecutionUrl}&_eventId=submitRegistration">Submit</a> |
<a href="${flowExecutionUrl}&_eventId=cancelRegistration">Cancel</a>
</div>
</body>
</html>
The point of the two links is to give the user a way to generate
submitRegistration and cancelRegistration
events. We saw in the flow definition that these two events trigger
state transitions. Of special importance is the URL for each of these
links. Notice that by using ${flowExecutionUrl}, we're still pointing
the user at the same /account/register.do servlet path.
That's because we're still working within the register
flow. The ${flowExecutionUrl} URL includes an execution
HTTP parameter whose value is a key that SWF uses for flow-tracking
purposes. In addition we add an _eventId parameter that
we use to drive state transitions. The event IDs are what we're
referencing when we define transitions in the flow definition
file.
So really so far all we can do at this point is move back and forth between the home page and the registration page, but we're doing it with Spring Web Flow.
Point your web browser at
http://localhost:8080/mycart2/home.domaking any adjustments you need to make for the port or application
context. Also note that the context is now mycart2
instead of mycart1 like it was in the first version of
the code. If you're able to click back and forth between the home
page and the registration page, then congrats, Spring Web Flow 2.0 is
working! Celebrate!