15. Using Spring Web Flow – Spring in Action, Second Edition

Chapter 15. Using Spring Web Flow

This chapter covers

  • Creating conversational web applications

  • Defining flow states and actions

  • Integrating Spring Web Flow with Struts and JSF

Have you ever given much thought to what might be the most important force that drives successful software?

There are many opinions out there on this topic. Some say proper methodology leads to successful software projects. Others say that a cleverly schemed architecture sets the foundation for software to flourish. Some might tell you it’s the people on the project who determine its outcome. Depending on who you ask, you might be told that certain technology choices lend themselves to triumphant software.

As a seasoned software developer, I have given much thought to this question. And I think I have the answer. Methodology, architecture, people, and technology are certainly important factors that play into a project’s success. But I submit that something else is far more critical to whether or not a project is a blockbuster or a dud. And that something is...

Pizza.

That’s right... it’s pizza. (Go ahead. Get your highlighter out. You’ll want to mark this for future reference.)

Every successful project team I’ve been on has, at one time or another (sometimes frequently), enjoyed a meal together with pizza. Not only is pizza a universal favorite food choice among programmers, but I believe that there’s something about a warm slice of melted cheese over crust, adorned with meats and veggies, that inspires a project to greatness. Nothing brings out design ideas and camaraderie like breaking bread over a cardboard box full of pizza.

For a moment, just for fun, imagine that you and your team have just released the latest version of your product and want to treat your team to some pizza to unwind. What would the phone call to the local pizzeria sound like?

Ring... Ring... Ring...

“Hello, this is Spring Pizza, home of the hottest slice in town. May I have your telephone number, please?”

“Sure, it’s 972-555-1312.”

“Is this for 1414 Oak Drive?”

“Yeah.”

“Okay, will this be for carryout or delivery?”

“Delivery.”

“Great. What can we get for you?”

“I need two large carnivores”

“Okay, anything else?”

“Yeah, how about two large veggie pizzas...”

“Okay...”

“...and a medium Canadian bacon with pineapple.”

“Anything else?”

“No, that’ll be it.”

“Okay, that’s two large veggies, two large carnivores, and a medium Canadian bacon with pineapple. Is that correct?”

“That’s right.”

“Okay, your total comes to $44.57. Will that be cash, check, or charge?”

“I’ll pay cash.”

“All right. Your order should be delivered in about 30 minutes. Have a good day and thank you for calling Spring Pizza.”

Okay, I admit it. That was a bit corny. Nevertheless, this conversation (or one similar to it) takes place millions of times every day between pizza lovers and those who bake the cherished Italian pie.

This exchange isn’t much different from how a user might interact with a web application. Some applications seem to follow a script, guiding the user along in a conversation to achieve a certain goal. In the case of the phone call, the goal was to have some pizzas delivered. Similarly, you’ve probably used the shopping cart of an e-commerce site where the goal was to order and pay for a product. A conversation takes place in both scenarios: one between a customer and a pizzeria employee and one between a customer and a web application.

Spring Web Flow is an exciting new web framework based on the Spring Framework that facilitates development of conversational web applications. In this chapter, we’re going to explore Spring Web Flow and see how it fits into the Spring web framework landscape. Along the way, we’ll build a conversational web application that simulates the pizza order phone call.

Before we get too carried away with taking pizza orders, however, there’s a bit of groundwork to be laid. Let’s start by seeing what itch is scratched by Spring Web Flow.

Getting started with Spring Web Flow

There are two kinds of interaction in web applications. Many websites are based on free-flow navigation. That is, the user is given an array of links and buttons to choose from and they’re in full control of the application’s flow. The conversation between the user and the web application is very one-sided—the user tells the application where to and the application goes there.

But occasionally you come across web applications where the application guides the user from one page to the next. There seems to be a conversation where the application asks some questions to which the user responds, triggering some functionality in the application. Although the user may have several choices to make, the application follows a predefined flow. The shopping cart of most online shopping sites is a small, but typical, example of conversational web interaction.

Spring Web Flow is an extension to Spring MVC (actually, it’s just another controller) that provides for the development of conversation-style navigation in a web application. The key features provided for by Spring Web Flow are:

  • The ability to define an application’s flow external to the application’s logic

  • The ability to create reusable flows that can be used across multiple applications

In chapter 13, we saw how to build web applications using Spring MVC. Spring MVC’s controllers are great for developing free-flow applications but are ill-suited for conversational web applications.

To understand Spring MVC’s shortcomings with regard to conversational applications, let’s suppose that the phone call between the pizzeria employee and the customer were to take place in an online pizza order entry application. If we were to build the pizza order entry application using Spring MVC, we’d likely end up coding the application’s flow into each controller and JSP.

For example, we might end up with a JSP with a link that looks like this:

<a href="addPizza.htm">Add Pizza</a>

As for the controller behind that link, it may be a form controller like this one:

public class AddPizzaController extends SimpleFormController {
  public AddPizzaController() {}

    protected ModelAndView onSubmit(Object command,
      BindException bindException) throws Exception {

    Pizza pizza = (Pizza) command;

    addPizzaToOrder(pizza);

    return new ModelAndView("orderDetail");
  }
}

While there’s nothing inherently wrong with that link or the controller, it isn’t ideal when taken in the context of the conversational pizza application. That’s because both the link and the controller know too much. The link’s URL and the controller’s ModelAndView each hold a piece of information about the application’s flow. But there’s no place to go to see the complete picture; instead, the application’s flow is embedded and scattered across the application’s JSPs and controllers.

Because the flow definition is sprinkled throughout the application’s code, it’s not easy to understand the overall flow of the application. To do so would require viewing the code for several controllers and JSPs. Moreover, changing the flow would be difficult because it would require changing multiple source files.

As you’ll soon see, Spring Web Flow loosens the coupling between an application’s code and its page flow by enabling you to define the flow in a separate, selfcontained flow definition. But before we can define an application flow, there’s a little bit of groundwork that has to be dealt with. Let’s see how to set up the infrastructural pieces of a Spring Web Flow application.

Installing Spring Web Flow

Spring Web Flow, although a subproject of the Spring Framework, isn’t part of the Spring Framework proper. Therefore, before we can get started building flow-based applications, we’ll need to add Spring Web Flow to our project’s classpath.

You can download Spring Web Flow from the Spring Web Flow website (http://www.springframework.org/webflow). Be sure to get the latest version (as I write this, Spring Web Flow 1.0.3 has just been released). Once you’ve downloaded and unzipped the distribution zip file, you’ll find the Spring Web Flow JAR files in the root directory. Add these to your application’s classpath and you’re ready to go.

It’s even easier to add Spring Web Flow to your application if you’re using Maven 2. In the pom.xml file, add the following dependencies:

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webflow</artifactId>
  <version>1.0.3</version>
  <scope>compile</scope>
</dependency>

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-binding</artifactId>
  <version>1.0.3</version>
  <scope>compile</scope>
</dependency>

With Spring Web Flow set up in the application’s classpath, we’re ready to start configuring it in the Spring application context. We’ll start with FlowController, the gateway controller to Spring Web Flow.

Setting up the FlowController

All interactions with Spring Web Flow go through Spring Web Flow’s FlowController. FlowController is a Spring MVC controller that acts as a front controller for Spring Web Flow applications. However, instead of performing a specific function like most Spring MVC controllers, FlowController is responsible for handling all requests pertaining to a flow.

As with any other Spring MVC controller, FlowController must be declared in a Spring application context. The following XML shows a typical FlowController <bean> declaration:

<bean id="flowController"
    class="org.springframework.webflow.executor.mvc.
                  FlowController">
  <property name="flowExecutor" ref="flowExecutor" />
</bean>

The flowExecutor property is the only mandatory property. It must be wired with a flow executor, which ultimately carries out the steps described in a flow. We’ll declare a flow executor bean in a moment, but first we have some Spring MVC plumbing to take care of with regard to FlowController.

So that we’ll have a way of interacting with Spring Web Flow through a URL, we’ll also need to configure a mapping to the FlowController. This mapping can be created using any of the handler mappings described in chapter 13, but we tend to favor SimpleUrlHandlerMapping:

<bean id="urlMapping"
    class="org.springframework.web.servlet.
                handler.SimpleUrlHandlerMapping">
  <property name="mappings">
    <props>
      <prop key="flow.htm">flowController</prop>
    </props>
  </property>
</bean>

The reason why we chose SimpleUrlHandlerMapping here is because we may end up mapping other controllers in the same application. We think that SimpleUrlHandlerMapping is the most flexible of Spring MVC’s handler mappings, allowing us to map virtually any URL pattern to any controller.

As mapped here, FlowController will answer to the URL pattern flow.htm. It’s worth noting that the same mapping could also be created in a more convention-over-configuration approach using ControllerClassNameHandlerMapping:

<bean id="urlMapping"
    class="org.springframework.web.servlet.mvc.
             support.ControllerClassNameHandlerMapping" />

ControllerClassNameHandlerMapping may be an appealing choice if your application will be completely flow-based or if your application’s other controllers are named appropriately with respect to the URL patterns they’ll be mapped to.

Because FlowController is a Spring MVC controller, you’ll also need to be sure to configure DispatcherServlet in your application’s web.xml file as you would do for any Spring MVC application. See section 13.1.2 for a refresher on how DispatcherServlet should be configured.

Configuring a flow executor

While FlowController handles web requests destined for Spring Web Flow, it doesn’t execute the flow. It is merely a courier between the user and a request executor. A flow executor’s job is like that of an air traffic controller. It keeps track of all of the flows that are currently being executed and directs each flow to the state they should go to next.

As you’ve seen already, the FlowController must be wired with a reference to a flow executor. This means that we’ll need to configure a flow executor in Spring.

If you’re using Spring 2.0, the easiest way to configure a flow executor is to use Spring Web Flow’s custom configuration schema. The first thing you must do is add Spring Web Flow’s schema to your Spring application context’s XML file:

<beans xmlns=
   "http://www.springframework.org/schema/beans"
  xmlns:xsi=
    "http://www.w3.org/2001/XMLSchema-instance"
  xmlns:flow=
    "http://www.springframework.org/schema/
         webflow-config"
  xsi:schemaLocation=
     "http://www.springframework.org/schema/beans
      http://www.springframework.org/schema/beans/
           spring-beans-2.0.xsd
      http://www.springframework.org/schema/webflow-config
      http://www.springframework.org/schema/webflow-config/
           spring-webflow-config-1.0.xsd">
...
</beans>

Once you’ve added the schema declaration, declaring a flow executor is as simple as using the <flow:executor> element like this:

<flow:executor id="flowExecutor"
      registry-ref="flowRegistry" />

Under the covers, the <flow:executor> element will be used to declare a FlowExecutorFactoryBean in the Spring application context. If you’re using a pre-2.0 version of Spring (or if typing large amounts of XML gives you a warm feeling of satisfaction) then you can manually declare a FlowExecutorFactoryBean as follows:

<bean id="flowExecutor"
    class="org.springframework.webflow.config.
               FlowExecutorFactoryBean">
  <property name="definitionLocator"
      ref="flowRegistry"/>
  <property name="executionAttributes">
    <map>
       <entry key="alwaysRedirectOnPause">
        <value type="java.lang.Boolean">true
         </value>
      </entry>
    </map>
  </property>
  <property name="repositoryType"
      value="CONTINUATION"/>
</bean>

In Spring Web Flow, flow definitions are defined in separate XML files. FlowExecutorFactoryBean uses a flow registry to keep track of all of the flow definitions that it may need. The flow registry is given to the flow executor through <flow:executor>’s registry-ref attribute or through FlowExecutorFactoryBean’s definitionLocator property.

Registering flow definitions

The flow registry is effectively a librarian that curates a collection of flow definitions. When the flow executor needs a flow, it will ask the flow registry for it.

A flow registry can be configured in Spring 2.0 using the <flow:registry> element, as follows:

<flow:registry id="flowRegistry">
  <flow:location
      path="/WEB-INF/flows/**/*-flow.xml" />
</flow:registry>

The <flow:registry> element must contain one or more <flow:location> elements. Each <flow:location> element identifies the path to one or more flow definitions that the flow registry should manage. Notice that in the example above, the path is defined using Ant-style wildcards. This indicates that all files ending with -flow.xml that are in the /WEB-INF/flows/ directory (and subdirectoreis) should be loaded by the flow registry.

When a flow definition is loaded into the flow registry, it is registered with a name that is equal to the filename of the flow definition after chopping off the file extension. For example, if a flow definition file is named Pizza-flow.xml, it will be registered in the flow registry with the name Pizza-flow. This name will be used to refer to the flow when constructing URLs.

If you’re not using Spring 2.0 or would just prefer to configure Spring Web Flow using the traditional <bean> element, you can also configure a flow registry as a <bean> using the following XML:

<bean id="flowRegistry"
    class="org.springframework.webflow.engine.
            builder.xml.XmlFlowRegistryFactoryBean">
  <property name="flowLocations">
    <list>
      <value>/WEB-INF/flows/**/*-flow.xml </value>
    </list>
  </property>
</bean>

As you can see, XmlFlowRegistryFactoryBean is the class that hides behind the curtain of the <flow:registry> element. And its flowLocations property is the pre-2.0 means of itemizing the flow definitions to be loaded into the registry.

With the core Spring Web Flow configuration in place, we’re almost ready to build our pizza order flow. But first, let’s establish the core concepts of a flow.

Spring Web Flow essentials

In Spring Web Flow, three main elements make up an application flow: states, events, and transitions.

States are points in a flow where some activity takes place. This activity could be the application performing some logic (perhaps saving customer information to a database), or it could be where the user is presented with a page and asked to take some action.

Spring Web Flow defines six different kinds of state, as shown in table 15.1.

Table 15.1. Spring Web Flow’s selection of states.

State type

XML element

What it’s for

Action

<action-state>

Action states are where the logic of the flow takes place. Action states typically store, retrieve, derive, or otherwise process information to be displayed or processed by subsequent states.

Decision

<decision-state>

Decision states branch the flow in two or more directions. They examine information within flow scope to make flow routing decisions.

End

<end-state>

The end state is the last stop for a flow. Once a flow has reached end state, the flow is terminated and the user is sent to a final view.

Start

<start-state>

The start state is the entry point for a flow. A start state does not do anything itself and effectively serves as a reference point to bootstrap a flow.

Subflow

<subflow-state>

A subflow state starts a new flow within the context of a flow that is already underway. This makes it possible to create flow components that can be composed together to make more complex flows.

View

<view-state>

A view state pauses the flow and invites the user to participate in the flow. View states are used to communicate information to the user or to prompt them to enter data.

The selection of states provided by Spring Web Flow makes it possible to construct virtually any arrangement of functionality into a conversational web application. While not all flows will require all of the states described in table 15.1, you’ll probably end up using most of them at one time or another.

Of all the states in table 15.1, you’ll probably find that your flow is mostly composed of view states and action states. These two states represent each side of a conversation between the user and the application. View states are the user’s side of the conversation, presenting information to the user and awaiting their response. Action states are the application’s side of the conversation, where the application performs some application logic in response to a user’s input or the results of another action state.

Once a state has completed, it fires an event. The event is simply a String value that indicates the outcome of the state. By itself, the event fired by a state has no meaning. For it to serve any purpose, it must be mapped to a transition. Whereas a state defines an activity within a flow, transitions define the flow itself. A transition indicates which state the flow should go to next.

To illustrate these concepts, consider the simple flow diagram in figure 15.1.

Figure 15.1. A flow is made up of states, events, and transitions. This flow has three states, two transitions, and two events.

The flow in figure 15.1 is intended to show a typical “hello world” flow. The flow starts by transitioning to a view state named “hello” that displays the familiar “hello world” greeting. Once the user indicates that they are finished, a “continue” event is fired, triggering a transition to the end state.

This simple flow can be expressed in Spring Web Flow with the following 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-1.0.xsd">

  <start-state idref="hello" />

  <view-state id="hello"
      view="hello">
    <transition on="continue" to="finish"/>
  </view-state>

  <end-state id="finish"
      view="flowRedirect:Hello-flow" />
</flow>

Observe that all three states are clearly defined in this XML document. Furthermore, the transitions from one state to the next are also easy to spot.

The “hello” flow is a good introduction to Spring Web Flow, but it’s only a small taste of what’s in store. Rather than dwell on this flow too long, let’s see how to put Spring Web Flow to work in a more sizable example. Let’s build a pizza order entry application using Spring Web Flow.

Creating a flow

In the next section, we’re going to build an application for pizza order entry using Spring Web Flow. In doing so, you’ll see that we’ll be able to define the application’s flow completely external to the application code and views. This will make it possible to rearrange the flow without requiring any changes to the application itself. It will also aid in understanding the overall flow of the application because the flow’s definition is contained in a single location.

Laying the flow groundwork

To define the pizza order flow, we’ll start by creating a skeleton flow definition file. It’ll start out rather empty, but it will be full of state and transition definitions before this chapter is done.

Flows in Spring Web Flow are defined in XML. Regardless of the specifics of the flow, all flow definition files are rooted with the <flow> element:

<?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-1.0.xsd">
...
</flow>

The first thing you should understand about Spring Web Flow definition files is that they are based on a completely different XML schema than the Spring container configuration file. Where the Spring configuration file is used to declare <bean>s and how they are related to each other, a Spring Web Flow definition declares flow states and transitions.

Flow variables

The whole purpose of the pizza order flow is to build a pizza order. Therefore, we’ll need a place to store the order information. Listing 15.1 shows the Order class, a simple domain bean for carrying pizza order information.

Example 15.1. The Order class represents an order in the pizza flow

package com.springinaction.pizza.domain;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

public class Order implements Serializable {
  private Customer customer;
  private List<Pizza> pizzas;
  private Payment payment;

  public Order() {
    pizzas = new ArrayList<Pizza>();
    customer = new Customer();
  }

  public Customer getCustomer() {
    return customer;
  }

  public void setCustomer(Customer customer) {
    this.customer = customer;
    }

  public List<Pizza> getPizzas() {
    return pizzas;
  }

  public void setPizzas(List<Pizza> pizzas) {
    this.pizzas = pizzas;
  }

  public void addPizza(Pizza pizza) {
    pizzas.add(pizza);
  }

  public Payment getPayment() {
    return payment;
  }

  public void setPayment(Payment payment) {
    this.payment = payment;
  }
}

So that the Order object is available to all of the states in the flow, we’ll need to declare it in the flow definition file with a <var> element:

<var name="order"
      class="com.springinaction.pizza.domain.Order"
      scope="flow"/>

Notice that the scope attribute is set to flow. Flow variables can be declared to live in one of four different scopes, as listed in table 15.2.

Table 15.2. Scopes that data can live in within a flow.

Scope

Visibility

Request

If an object is created in Request scope, it is only visible within the context of the current request. Request-scoped variables do not survive redirects.

Flash

An object created in Flash scope is visible within the context of the current request and until the next user event is triggered. Flash-scoped variables live beyond redirects.

Flow

If an object is created in Flow scope, it will be visible within the context of the current flow execution, but will not be visible to subflows.

Conversation

Objects created in Conversation scope are visible within the context of the current flow execution as well as in the context of subflow executions.

Since we’ll need the Order object throughout the entire life of the flow, we’ve set the scope attribute to flow.

Now let’s add the first states to our flow.

Start and end states

All flows begin with a start state. A start state is to a flow what a main() method is to a Java program. That is to say, it exists only as a marker of where the flow should begin. The only thing that occurs within a start state is that a transition is performed to the next state.

In Spring Web Flow, a start state is defined in XML with a <start-state> element. All flows must have exactly one <start-state> to indicate where the flow should begin. Here’s how we’ve defined the start state for the pizza order flow:

<start-state idref="askForPhoneNumber" />

This <start-state> definition is very typical of any <start-state> in any flow definition. In fact, the idref attribute, which indicates the beginning state of the flow, is the only attribute available to <start-state>. In this case, we’re indicating that the flow should begin with a state named askForPhoneNumber. (We’ll define askForPhoneNumber state in a moment.)

Just as all flows must start somewhere, they all eventually must come to an end. Therefore, we must also define an end state in the flow:

<end-state id="finish"
    view="orderComplete" />

The <end-state> element defines the hopping-off point for a flow. When a flow transitions to this state, there’s no turning back. An end state terminates the flow and then displays a view specified by the view attribute. In this case, we’ve asked the flow to send the user to the view whose name is orderComplete. This logical view name is ultimately mapped to an actual view implementation using a Spring MVC view resolver.

For example, suppose that we have configured an InternalResourceViewResolver in Spring like this:

<bean id="viewResolver"
    class="org.springframework.web.servlet.view.
                    InternalResourceViewResolver">
  <property name="prefix" value="/WEB-INF/jsp/" />
  <property name="suffix" value=".jsp" />
</bean>

When the flow ends, the orderComplete view name will be resolved by Internal-ResourceViewResolver to the actual view implementation in /WEB-INF/jsp/ orderComplete.jsp. (For a review of InternalResourceViewResolver, see section 14.1.1.)

As an alternative to jumping out of the flow completely, we could define the end state to start over with a brand new flow. For example, consider the following <end-state> definition:

<end-state id="finish"
    view="flowRedirect:PizzaOrder-flow" />

Now, instead of setting the view attribute to the logical name of a view, we’re using the flowRedirect: directive to tell Spring Web Flow to redirect the user to the beginning of a flow. In this case, we’re redirecting to the same flow we just finished, so that the user can enter another pizza order.

Although all flows must have exactly one <start-state>, a flow can have as many <end-state>s as you want to define alternate endings for a flow. Each flow can take the user to a different view after the flow ends. In the case of the pizza order flow, we only need one <end-state>. Nevertheless, we thought you might like to know that a flow can have more than one.

The basic shell of the pizza order flow is in place. The state diagram in figure 15.2 illustrates what we’ve built thus far.

Figure 15.2. The start and end states bookend all Spring Web Flow definitions.

We’re now ready to start adding basic functionality in the form of states and transitions. Since the flow’s <start-state> indicates that the flow should start with a state named askForPhoneNumber, that’s where we’ll also start in defining the part of the flow that collects customer information.

Gathering customer information

A key step in taking a pizza order is to find out who the customer is and where they live. After all, if you don’t know where they live, how can you possibly deliver their pizza to them? In figure 15.3, we’ve fleshed out the flow diagram from figure 15.2 a bit to describe the portion of the flow that collects the customer information.

Figure 15.3. Find out where to deliver the pizza by gathering customer information in the flow.

As is customary in the pizza delivery trade, we’ll start by asking the customer for their phone number. If the customer has placed an order with us before, we’ll already have the rest of their information and the phone number is all we’ll need to look up their address. If the phone number doesn’t turn up any customer data, we’ll need to ask the user for their address.

Asking the customer for their phone number

The first step in acquiring customer information is to ask the user for their phone number. This is a simple matter of presenting the user with a web page with a form for entering the phone number.

View states are used to involve a user in a flow by displaying information and possibly asking the user for input. This makes a view state the perfect choice for presenting the phone number form to the user. The following <view-state> element should do the trick:

<view-state id="askForPhoneNumber"
    view="phoneNumberForm">
  <transition on="submit" to="lookupCustomer" />
</view-state>

The view attribute of <view-state> specifies the logical name of a view to be displayed to the user. In this case phoneNumberForm is resolved to /WEB-INF/jsp/ phoneNumberForm.jsp by the InternalResourceViewResolver we declared earlier. A simplified form of phoneNumberForm.jsp is shown in listing 15.2.

Example 15.2. A form that asks for a customer’s phone number

Listing 15.2 contains a basic HTML form with a text field to prompt for the phone number and a submit button. There are, however, a few details of this form that are specific to Spring Web Flow.

Recall that all links within a Spring Web Flow application should go through FlowController’s URL mapping. Likewise, forms should also be submitted to the same URL. Therefore, the action parameter of the <form> element is set to flow.htm, which as we know from section 15.1.1 is the URL pattern mapped to the FlowController.

So that FlowController will know which flow the request is for, we must also identify the flow by setting the _flowExecutionKey parameter. For that reason, we’ve added a hidden field named _flowExecutionKey that holds the flow execution key that will be submitted along with the form data.

The final thing to note is the odd name given to the submit button. Clicking this button triggers an event to Spring Web Flow from a form submission. When the form is submitted, the name of this parameter is split into two parts. The first part, _eventId, signals that we’re identifying the event. The second part, submit, is the name of the event to be triggered when the form is submitted.

Looking back at the askForPhoneNumber <view-state> declaration, we see that the submit event triggers a transition to a state named lookupCustomer, where the form will be processed to look up the customer information.

Looking up customer data

When using <view-state> to prompt the user for a phone number, we allowed the user to take part in the flow. Now it’s the application’s turn to perform some work as it attempts to look up the customer data in an action state.

The lookupCustomer state is defined as an <action-state> with the following excerpt of XML:

<action-state id="lookupCustomer">
  <action bean="lookupCustomerAction" />

  <transition on="success"
      to="showOrder" />

  <transition on-exception=
      "com.springinaction.pizza.service.
            CustomerNotFoundException"
      to="addNewCustomer" />
</action-state>

The action implementation is referenced by the bean attribute of the <action> subelement. Here, we’ve specified that the functionality behind the lookupCustomer action state is defined in a bean named lookupCustomerAction. The lookupCustomerAction bean is configured in Spring as follows:

<bean id="lookupCustomerAction"
    class="com.springinaction.pizza.flow.
               LookupCustomerAction">
  <property name="customerService"
      ref="customerService" />
</bean>

The bean referenced by the bean attribute of <action> must implement Spring Web Flow’s Action interface. As you can see in LookupCustomerAction (listing 15.3), the only compulsory method of the Action interface is the execute() method.

Example 15.3. A flow action for looking up customer information

LookupCustomerAction assumes that it will be processing the submission of the askForPhoneNumber view state and retrieves the phone number from the request parameters. It then passes that phone number to the lookupCustomer() method of the injected CustomerService object to retrieve a Customer object. If a Customer is found, the pizza order is retrieved from flow scope and its customer property is set accordingly.

At this point, we have all of the customer information we need, so we’re ready to start adding pizzas to the order. So the execute() method concludes by returning a success event. Looking back at the definition of the lookupCustomer <action-state>, we see that a success event results in a transition to the show-Order event.

If, however, lookupCustomer() can’t find a Customer based on the given phone number, it will throw a CustomerNotFoundException. In that case, we need the user to add a new customer. Therefore, the lookupCustomer <action-state> is also declared with an exception transition that sends the flow to the addNewCustomer state if a CustomerNotFoundException is thrown from LookupCustomerAction’s execute() method.

Adding a new customer

As for the addNewCustomer state itself, it’s a view state that prompts the user for customer information. The following <view-state> definition specifies that the newCustomerForm view should be displayed by this state:

<view-state id="addNewCustomer" view="newCustomerForm:>
...
</view-state>

As configured here, the addNewCustomer view state will display the view defined in /WEB-INF/jsp/newCustomerForm.jsp (listing 15.4).

Example 15.4. A form for creating a new customer

Although the addNewCustomer state will display the new customer form, it’s not able to process the form data. Eventually the user will submit the form and we’ll need a way to bind the form data to a back-end Customer object. Fortunately, Spring Web Flow provides FlowAction, a special Spring Web Flow Action implementation that knows how to deal with common form-binding logic. To use FlowAction, the first thing we’ll need to do is configure it as a <bean> in the Spring application context:

<bean id="customerFormAction"
    class="org.springframework.webflow.action.FormAction">
  <property name="formObjectName" value="customer" />
  <property name="formObjectScope" value="REQUEST" />
  <property name="formObjectClass"
      value="com.springinaction.pizza.domain.Customer" />
</bean>

FormAction has three important properties that describe the object that will be bound to the form. The formObjectName property specifies the name of the object, formObjectScope specifies the scope, and formObjectClass specifies the type. In this case we’re asking FormAction to work with a Customer object in request scope as customer.

When the form is first displayed, we’ll need FormAction to produce a blank Customer object so that we’ll have an object to bind the form data to. To make that happen, we’ll add FormAction’s setupForm() method as a render action in the addNewCustomer state:

<view-state id="addNewCustomer" view="newCustomerForm">
  <render-actions>
    <action bean="customerFormAction"
        method="setupForm"/>
  </render-actions>
...
</view-state>

Render actions are a way of associating an action with the rendering of a view. In this case, the FormAction’s setupForm() method will be called just before the customer form is displayed and will place a fresh Customer object in request scope. In a sense, FormAction’s setupForm() is a lot like a Spring MVC form controller’s formBackingObject() (see section 13.3.3) in that it prepares an object to be bound to a form.

When the new customer form is submitted, we’ll need a transition to handle the submit event. The following <transition> addition to addNewCustomer describes what should happen:

<view-state id="addNewCustomer" view="newCustomerForm">
  <render-actions>
    <action bean="customerFormAction"
        method="setupForm"/>
  </render-actions>
  <transition on="submit" to="showOrder">
    <action bean="customerFormAction" method="bind" />
    <evaluate-action expression=
        "flowScope.order.setCustomer(requestScope.customer)" />
  </transition>
</view-state>

The <transition> element itself is mostly straightforward. It simply says that a submit event should trigger a transition to the state named showOrder. But before we go to the showOrder state, we must first bind the form data to the form-backing Customer object and set the customer property of the flow-scoped Order. This is a two-step process:

  1. First, we use an <action> element to ask FormAction’s bind() method to bind the submitted form data to the form-backing Customer object that is in request scope.

  2. Next, with a fully populated Customer object in request scope, we use <evaluate-action> to copy the request-scoped Customer object to the Order object’s customer property.

Our pizza order flow now has all of the customer information gathering states it needs. At this point, the overall flow looks little like what you see in figure 15.4.

Figure 15.4. Where the customer information states fit into the pizza order flow.

Unfortunately, there’s still that fuzzy cloud of yet-to-be-defined “flow states” that needs to be addressed. Let’s add a few more states to the flow that enable us to add pizzas to the order.

Building a pizza order

When it comes to defining a flow for creating a pizza order, the most critical part is where the pizzas get added to the order. Without that, we’re not really delivering anything to the customer (which in most businesses results in there being no customer).

So, the next couple of states we introduce to the flow will serve to build the pizza order. Figure 15.5 shows the new states and how they’ll relate to one another.

Figure 15.5. Adding states to the flow to build the pizza order.

In this section, we’ll add two states to our flow: one that displays the current order and one to let the user add a pizza to the order.

Displaying the order

The showOrder state is a simple <view-state> that renders a view to display the current order information. It is defined as follows:

<view-state id="showOrder" view="orderDetails">
  <transition on="addPizza" to="addPizza" />
  <transition on="continue" to="takePayment" />
</view-state>

There’s nothing particularly special about this <view-state> definition. Compared to some <view-state>s we’ve seen already, this one is plain vanilla. It simply renders the view whose name is orderDetails. InternalResourceViewResolver will resolve this view name to /WEB-INF/jsp/orderDetails.jsp, which is the JSP file shown in listing 15.5.

Example 15.5. orderDetails.jsp, which displays the current pizza order to the user

The main things I’d like to draw your attention to in listing 15.5 are the three links that are being created to fire Spring Web Flow events. One fires a continue event to proceed with checkout. Another fires a cancel event to cancel the order and start over. Another fires an addPizza event so that we can add a new pizza to the order.

Notice that all three of these links are very similar. Consider the addPizza link, for example:

<a href="flow.htm?_flowExecutionKey=${flowExecutionKey}
     &_eventId=addPizza">Add Pizza</a>

There are three very important elements of the link’s href attribute that guide Spring Web Flow:

  • As we’ve discussed before, all links within a flow must go through the Flow-Controller. Therefore the root of the link is flow.htm, the URL pattern mapped to the FlowController.

  • To identify the flow execution to Spring Web Flow, we’ve set the _flowExecutionKey parameter to the page-scoped ${flowExecutionKey} variable. This way FlowController will be able to distinguish one user’s flow execution from another.

  • Finally, the _eventId parameter identifies the event to fire when this link is clicked on. In this case, we’re firing the addPizza event, which, as defined in the showOrder state, should trigger a transition to the addPizza state.

Speaking of the addPizza state, let’s go ahead and add it to the flow.

Adding a pizza to the order

Since we’ll be prompting the user to choose a pizza, it makes sense for the addPizza state to be a view state. Here’s the <view-state> definition we’ll use:

<view-state id="addPizza" view="newPizzaForm">
  <render-actions>
    <action bean="pizzaFormAction" method="setupForm"/>
  </render-actions>
  <transition on="submit" to="showOrder">
    <action bean="pizzaFormAction" method="bind" />
    <evaluate-action expression=
        "flowScope.order.addPizza(requestScope.pizza)" />
  </transition>
</view-state>

If this <view-state> definition looks familiar, it’s because we used a similar pattern when adding a new customer. Just as with the customer form, we’re using a FormAction to set up and bind the form data. As you can see from the definition of the pizzaFormAction bean, this time the form-backing object is a Pizza object:

<bean id="pizzaFormAction"
    class="org.springframework.webflow.action.FormAction">
  <property name="formObjectName" value="pizza" />
  <property name="formObjectClass"
      value="com.springinaction.pizza.domain.Pizza" />
  <property name="formObjectScope" value="REQUEST" />
</bean>

When the new pizza form is submitted, the <evaluate-action> copies the request-scoped Pizza into the order by calling the flow-scoped Order object’s addPizza() method. Once the pizza has been added to the order, the flow transitions back to the showOrder view state to display the order’s status.

Our flow is really starting to take shape. Figure 15.6 shows how the overall flow looks at this point.

Figure 15.6. The pizza order flow after adding the showOrder and addPizza states.

We’re almost finished with our pizza order flow definition. But there are still a few more states left to wrap up the order process. Let’s complete the flow definition by defining the states that complete the pizza order.

Completing the order

In the last section I stated that building the pizza order is the most critical part of the flow. After further contemplation, however, I think that is the most critical part only from the customer’s point of view. From the pizzeria’s point of view, the most critical part of the flow is the part where we get paid for making and delivering the pizzas. (You didn’t think we were giving pizzas away, did you?)

To complete the pizza order flow (and to take the money out of our customer’s pockets), we’ll add two more states to the flow, as illustrated in figure 15.7.

Figure 15.7. The final two states in the flow take payment and submit the order.

Let’s start by adding that all-important flow state—the one that sucks money right out of the customer’s credit card.

Taking payment

In order to take payment, we must prompt the user for credit card information. Since we’re asking the user to get involved again, this means that a view state is in order. The following <view-state> displays the payment form to the user and processes the credit card payment:

<view-state id="takePayment" view="paymentForm">
  <transition on="submit" to="submitOrder">
    <bean-action bean="paymentProcessor"
        method="approveCreditCard">
      <method-arguments>
         <argument expression=
            "${requestParameters.creditCardNumber}"/>
         <argument expression=
            "${requestParameters.expirationMonth}"/>
         <argument expression=
            "${requestParameters.expirationYear}"/>
         <argument expression=
             "${flowScope.order.total}" />
      </method-arguments>
    </bean-action>
  </transition>
  <transition on-exception=
          "com.springinaction.pizza.PaymentException"
       to="takePayment" />
</view-state>

Once again, we’ve defined a less-than-simple <view-state>. What makes this <view-state> interesting is that in addition to displaying a form, it processes the form submission upon a submit event. What makes it really interesting is how we process the form data.

In some previously defined <view-state>s, we used Spring Web Flow’s Form-Action to bind form data to an object, but we never really performed any processing on those objects. This time, we’re doing more than just collecting data—we need to approve the credit card transaction by passing the form data to a payment processor.

The payment processor, as referenced by the bean attribute of <bean-action>, is just a bean in the Spring application context. It may’ve been configured in Spring like this:

<bean id="paymentProcessor" class=
    "com.springinaction.pizza.service.PaymentProcessor" />

The specifics of the PaymentProcessor class and its implementation aren’t pertinent to this discussion other than to say that PaymentProcessor must expose an approveCreditCard() method whose signature looks like this:

public void approveCreditCard(String creditCardNumber,
    String expMonth, String expYear,
    float amount) throws PaymentException {
...
}

The reason why this approveCreditCard() method is needed is because the method attribute of <bean-action> points to an approveCreditCard() method that takes four arguments, as enumerated in the <argument> elements contained within <method-arguments>. The arguments are:

  • The first argument is the credit card number. The value passed in to approveCreditCard() comes directly from the request parameters, as indicated by the expression ${requestParameters.creditCardNumber}. This assumes that the paymentForm view contains a form with a field named creditCardNumber.

  • Likewise, the next two arguments are the credit card’s expiration month and year and are also pulled from the request parameters. Again, it is assumed that the paymentForm view contains fields named expiration-Month and expirationYear.

  • Finally, we must know the amount of the transaction for which we’re approving payment. This information is readily available from the flow-scoped Order object’s total property and is specified using the ${flow-Scope.order.total} expression.

When the form in the paymentForm view is submitted, the credit card number and expiration date fields, along with the order total, will be passed along in a call to PaymentProcessor’s approveCreditCard() method before transitioning to the submitOrder state.

In the event that the payment can’t be approved, approveCreditCard() will throw a PaymentException. For this occasion, we’ve also included an on-exception transition that will take the user back to the paymentForm to remedy the problem (perhaps by trying a different credit card number).

It should be noted that we could have also used an <action> definition here instead of a <bean-action>. The downside of using <action>, however, is that the action class must be an implementation of Spring Web Flow’s Action interface. It doesn’t allow for pure POJO action implementations. <bean-action>, on the other hand, provides a nonintrusive alternative, enabling us to invoke any method on any class configured in the Spring application context, without implementing a Spring Web Flow–specific interface.

Submitting the order

Finally, we’re ready to submit the order. The user doesn’t need to be involved for this part of the flow, so an <action-state> is a suitable choice. The following <action-state> saves the order and finishes the flow:

<action-state id="submitOrder">
  <bean-action bean="orderService" method="saveOrder">
    <method-arguments>
      <argument expression="${flowScope.order}" />
    </method-arguments>
  </bean-action>

  <transition on="success" to="finish" />
</action-state>

Notice that we’re using a <bean-action> to define the logic behind the <actionstate>. Here, the saveOrder() method will be called on the bean whose name is orderService. The flow-scoped Order object will be passed in as a parameter. This means that the class behind the orderService bean must expose a save-Order() method whose signature looks like this:

public void saveOrder(Order order) {
...
}

As before, the actual implementation of this method isn’t relevant to the discussion of Spring Web Flow, so we’ve purposefully omitted it to avoid confusion.

The flow now appears to be complete. We have all of the states in place and we should be able to start taking pizza orders. But we’re missing one small transition that is used throughout the entire flow.

A few finishing touches

On more than one occasion, we’ve seen links that fire cancel events within the flow, but we’ve never told you how those cancel events are handled. Up to now, we’ve been avoiding the issue, but now it’s time to meet it head on.

At any view state within the flow, the customer may choose to cancel the order and start over. When that happens, we need the flow to transition to the finish state to close down the flow execution. A naive way of handling cancel events is to place appropriate <transition>s all throughout the flow. For example, consider the cancel transition that could have been added to the showOrder state:

<view-state id="showOrder" view="orderDetails">
  <transition on="addPizza" to="addPizza" />
  <transition on="continue" to="takePayment" />
  <transition on="cancel" to="finish" />
</view-state>

The problem with doing it this way is that we must copy the exact same <transition> element to all of our flow’s <view-state>s. Our flow is simple enough that duplicating the <transition> wouldn’t be too painful. Nonetheless, it still results in an undesired duplication of code and should be avoided.

Instead of defining the same <transition> multiple times throughout an entire flow, Spring Web Flow offers the ability to define global transitions. Global transitions are transition definitions that are applicable from any flow state. To add global transitions to a flow, simply add a <global-transitions> element as a child of the <flow> element and place <transition> elements within it. For example:

<global-transitions>
  <transition on="cancel" to="finish" />
</global-transitions>

Here we’ve defined a global transition to the finish state whenever a cancel event is fired. This makes it possible to handle the cancel event from any state within the flow.

Our pizza order flow is almost complete. We have flow states to gather customer information, to add pizzas to and order, to take payment, and to save the order. The final flow is illustrated in figure 15.8.

Figure 15.8. The pizza order flow is now complete... or is it?

After putting all of the pieces together, we get the complete pizza order flow definition file, PizzaOrder-flow.xml, as shown in listing 15.6.

Example 15.6. The complete pizza order flow definition

<?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-1.0.xsd">

  <var name="order"
      class="com.springinaction.pizza.domain.Order"
      scope="flow"/>

  <start-state idref="askForPhoneNumber"  />

  <view-state id="askForPhoneNumber"
      view="phoneNumberForm">
    <transition on="submit" to="lookupCustomer" />
  </view-state>

  <action-state id="lookupCustomer">
    <action bean="lookupCustomerAction" />

     <transition on="success"
        to="checkDeliveryArea" />

     <transition on-exception=
        "com.springinaction.pizza.service.CustomerNotFoundException"
         to="addNewCustomer" />
  </action-state>

  <view-state id="addNewCustomer" view="newCustomerForm">
     <render-actions>
       <action bean="customerFormAction"
           method="setupForm"/>
     </render-actions>
    <transition on="submit" to="checkDeliveryArea">
      <action bean="customerFormAction" method="bind" />
       <evaluate-action expression=
          "flowScope.order.setCustomer(requestScope.customer)" />
    </transition>
  </view-state>


  <decision-state id="checkDeliveryArea">
    <if test="${flowScope.order.customer.inDeliveryArea}"
        then="showOrder"
         else="warnNoDeliveryAvailable"/>
  </decision-state>

  <view-state id="warnNoDeliveryAvailable"
       view="deliveryWarning">
    <transition on="continue" to="showOrder" />
  </view-state>


  <view-state id="showOrder" view="orderDetails">
    <transition on="addPizza" to="addPizza" />
    <transition on="continue" to="takePayment" />
  </view-state>

  <view-state id="addPizza" view="newPizzaForm">
     <render-actions>
      <action bean="pizzaFormAction" method="setupForm"/>
     </render-actions>
    <transition on="submit" to="showOrder">
      <action bean="pizzaFormAction" method="bind" />
       <evaluate-action expression=
          "flowScope.order.addPizza(requestScope.pizza)" />
    </transition>
  </view-state>
  <view-state id="takePayment" view="paymentForm">
    <transition on="submit" to="submitOrder">
       <bean-action bean="paymentProcessor"
          method="approveCreditCard">
        <method-arguments>
           <argument expression=
              "${requestParameters.creditCardNumber}"/>
           <argument expression=
              "${requestParameters.expirationMonth}"/>
           <argument expression=
              "${requestParameters.expirationYear}"/>
           <argument expression=
               "${flowScope.order.total}" />
        </method-arguments>
      </bean-action>
    </transition>

    <transition
      on-exception="com.springinaction.pizza.PaymentException"
      to="takePayment" />
  </view-state>

  <action-state id="submitOrder">
    <bean-action bean="orderService" method="saveOrder">
      <method-arguments>
        <argument expression="${flowScope.order}" />
      </method-arguments>
    </bean-action>

     <transition on="success" to="finish" />
  </action-state>

  <end-state id="finish"
      view="flowRedirect:PizzaOrder-flow" />

  <global-transitions>
    <transition on="cancel" to="finish" />
  </global-transitions>
</flow>

We could stop here and move on to another topic. But never content to leave well enough alone, let’s look at a few ways that we can improve on the pizza order flow by using some advanced Spring Web Flow techniques.

Advanced web flow techniques

Although the pizza order flow is now complete and ready to take orders for the delicious Italian pies, there is still some room for improvement. Specifically, we have two improvements in mind:

  • Once we have a customer’s information on hand, we should determine whether or not they live within our delivery area.

  • The customer information portion of the flow may come in handy for other flows. So, it would be nice to extract that portion of the flow into a subflow that can be reused in another flow (perhaps for a flower delivery service).

Coincidentally, these improvements involve the two flow states from table 15.1 that we haven’t talked about yet: decision states and subflow states. That makes this an opportune time to give those two flow states a try. Let’s start by looking at how to fork the direction of a flow using decision states.

Using decision states

After the lookupCustomer and addNewCustomer states and just before the show-Order state, we have a decision to make: can we deliver the pizza to the customer’s given address?

More accurately, our flow has a decision to make. If the customer lives within the delivery area then there’s no problem. The flow should proceed normally. But if the customer lives outside of the delivery area, we should transition to a warning page to indicate that we can’t deliver pizza to the customer’s address but that they are welcome to place the order for carryout and pick it up themselves.

When flow-diverging decisions must be made, a decision state is in order. A decision state is the Spring Web Flow equivalent of an if/else statement in Java. It evaluates some Boolean expression and based on the results will send the flow in one of two directions.

Decision states are defined with the <decision-state> element. The following <decision-state> is what we’ll use to decide whether or not to warn the user that they are out of the delivery area:

<decision-state id="checkDeliveryArea">
  <if test="${flowScope.order.customer.inDeliveryArea}"
      then="showOrder"
      else="warnNoDeliveryAvailable"/>
</decision-state>

At the heart of <decision-state> is the <if> element. The test attribute specifies some expression to be evaluated. If the expression evaluates to true, the flow will transition to the state specified by the then attribute. Otherwise, the flow will transition to the state given in the else attribute.

Here the inDeliveryArea property of the Customer object is evaluated. If it’s true then the flow will continue at the showOrder state. Otherwise, we’ll warn the user that delivery is not available for them and that they’ll have to pick up their pizza.

As for the delivery warning, it’s a simple view state, as defined here:

<view-state id="warnNoDeliveryAvailable"
    view="deliveryWarning">
  <transition on="continue" to="showOrder" />
</view-state>

The user is presented with the view whose name is deliveryWarning and is prompted to continue or cancel. If they choose to continue, they will transition to the showOrder state. If they decide to cancel the order, they will be transitioned to the finish state (per the global cancel transition).

Aside from adding these two new states, the only other thing we’ll need to do is to rewire the lookupCustomer and addNewCustomer states to transition to check-DeliveryArea instead of showOrder:

<action-state id="lookupCustomer">
  <action bean="lookupCustomerAction" />

  <transition on="success"
      to="checkDeliveryArea" />

  <transition on-exception=
      "com.springinaction.pizza.service.CustomerNotFoundException"
       to="addNewCustomer" />
</action-state>

<view-state id="addNewCustomer" view="newCustomerForm">
  <render-actions>
    <action bean="customerFormAction"
         method="setupForm"/>
  </render-actions>
  <transition on="submit" to="checkDeliveryArea">
    <action bean="customerFormAction" method="bind" />
    <evaluate-action expression=
        "flowScope.order.setCustomer(requestScope.customer)" />
  </transition>
</view-state>

Adding the delivery area <decision-state> not only changes the structure of the flow, it also changes how the flow is presented to the user. Before, anyone could place an order for pizza delivery, regardless of where they lived. Now, however, the flow is changed to indicate that delivery isn’t available for those outside of the delivery area. This is reflected in the updated flow diagram in figure 15.9.

Figure 15.9. The new flow diagram after adding the delivery area check.

The next flow improvement we’ll make will alter the structure of the flow, but shouldn’t have any impact on how the flow is presented to the user. Let’s see how to extract a portion of the pizza order flow into a subflow.

Extracting subflows and using substates

You’ve probably noticed by now that the flow definition file is getting a bit lengthy. Counting the two flow states that we added in the previous section and the start and end states, our pizza order flow’s state count is up to 11. Although this is far from being the most complex flow ever defined, it is starting to get unwieldy.

In Java, when a method gets too big, it is often beneficial to pull out related lines of code and place them into their own method. In his Refactoring: Improving the Design of Existing Code (Addison-Wesley, 1999), Martin Fowler refers to this technique as an “extract method.” By performing an extract method refactoring, lengthy methods become shorter and easier to follow.

Fowler’s book doesn’t cover flow definitions per se. Nevertheless, the idea behind extract methods applies equally well to flow definitions. Flow definitions can get quite lengthy and difficult to follow. It may be advantageous to extract a related set of states into their own flow and to reference that flow from the main flow. As with methods, the reward is smaller and easier-to-understand flow definitions.

Examining the pizza order flow, we find that 5 of the 11 states pertain to gathering customer information. Since these states make up almost half of the whole flow, they make a good candidate to be extracted into their own flow.

Creating a customer data flow

The first step in performing an “extract subflow” refactoring is to copy the five customer-centric flow states out of PizzaOrder-flow.xml into their own flow definition file. Listing 15.7 shows CustomerInfo-flow.xml, the new flow that will contain the customer information states.

Example 15.7. CustomerInfo-flow.xml—the customer information states extracted into their own flow

Most of listing 15.7 should look familiar. That’s because it was extracted from the original order flow. However, upon closer inspection, you’ll find that it’s not a direct copy from the original flow. We had to make a few minor changes to accommodate the new flow structure.

Because this new flow knows nothing about the pizza order, we no longer have a place to put the customer data. Therefore, the first change we made was to declare a flow-scoped Customer object using the <var> element. We’ll use this Customer object throughout the flow.

For example, the <decision-state> element has changed to test the flow-scoped Customer object instead of the customer property of a flow-scoped Order object.

In the original pizza order flow, the checkDeliveryArea and warnNoDelivery-Available states transitioned to the showOrder state once finished. But the show-Order state isn’t in this flow, so we’ve changed those transitions to go to the finish state. As we’ll see in a moment, the pizza order flow will transition to the showOrder state once the customer info flow completes.

Speaking of the finish state, it looks a bit different in the customer info flow than in the pizza order flow. Since this flow will end by transitioning to a state in the order flow, there’s no need to specify a view attribute.

Also (and even more interesting), notice the <output-mapper> element. This element maps the flow-scoped Customer to an output variable named customer, effectively returning the Customer object to the calling flow.

Using a subflow

Back in PizzaOrder-flow.xml, we’ll need to completely remove all of the customer info states that we extracted into CustomerInfo-flow.xml. They’ll be replaced with a <subflow-state> that references the new flow:

<subflow-state id="getCustomerInfo" flow="CustomerInfo-flow" >
  <attribute-mapper>
    <output-mapper>
      <mapping source="customer" target="flowScope.order.customer"/>
    </output-mapper>
  </attribute-mapper>
  <transition on="finish" to="showOrder" />
</subflow-state>

According to the flow attribute, the getCustomerInfo state is a subflow state that references the flow named CustomerInfo-flow. A subflow state is a state that references another flow. When a flow enters a subflow state, the current flow is suspended and the referenced flow is executed. This makes subflow states analogous to method calls in Java and the subflows themselves analogous to methods. In the case of the getCustomerInfo state, we are calling the CustomerInfo-flow flow.

As you’ll recall, the last thing that happens in CustomerInfo-flow is that the flow-scoped Customer object is mapped to an output variable named customer. In the <subflow-state>, we now map that output variable to the customer property of the order flow’s flow-scoped Order object. This makes sure that the Order is properly populated with the result of the customer info flow.

There’s still one more minor change that will need to be made in the order flow. Previously, the <start-state> referred to the askForPhoneNumber state. Now, however, the askForPhoneNumber state is in a separate flow. Therefore, we’ll need to change the <start-state> to reference the getCustomerInfo state:

<start-state idref="getCustomerInfo" />

And that’s it! Our “extract subflow” refactor is now complete. The new flow (or flows, as the case is now) are depicted in figure 15.10.

Figure 15.10. The flows after performing an extract subflow on the customer information states.

Before we move on, we should point out that there’s another benefit of the extract subflow refactoring. A close examination of the customer info flow reveals that the customer info flow is focused on one thing and one thing only: gathering customer information. There’s no hint of a pizza order to be found. This means that the customer info flow could be reused in any flow where we may need to collect customer data. If, for example, we were to open up a flower delivery service, the customer info flow would fit in nicely.

By now you should see the value of building flow-based applications using Spring Web Flow. But what if you want to plug a flow into an existing JSF or Struts application? No problem! Let’s look at how Spring Web Flow integrates into other web frameworks.

Integrating Spring Web Flow with other frameworks

In this chapter, we’ve focused on building Spring Web Flow applications within Spring MVC. Nevertheless, we thought you might find it interesting to know that Spring Web Flow doesn’t have to be used with Spring MVC. In fact, Spring Web Flow comes with out of the box support for use in

  • Jakarta Struts

  • JavaServer Faces

  • Spring Portlet MVC

There’s also an open issue in the Spring Web Flow issue list describing how to integrate Spring Web Flow into WebWork 2 (which will presumably work in Struts 2 as well). You can read about the WebWork integration and follow the status of the issue at http://opensource.atlassian.com/projects/spring/browse/SWF-76.

Regardless of which web framework you choose to build flows within, you’ll find that your flow definitions are portable across all supported frameworks. The only difference between each framework is at the integration points (e.g., FlowController for a Spring MVC application versus FlowAction in a Struts application).

Before we end our discussion on Spring Web Flow, let’s see how to use Spring Web Flow’s out-of-the-box support for integration with Jakarta Struts and JSF.

Jakarta Struts

Up to now, the main entry point into a Spring Web Flow application has been through a Spring controller (FlowController for Spring). Struts, however, doesn’t support Spring controllers. Therefore, we’ll need a Struts-specific approach for using Spring Web Flow within a Struts application.

Instead of controllers, Struts is based on Actions. Consequently, Spring Web Flow integrates into Struts through FlowAction, a Struts Action implementation that performs the same job as FlowController does for Spring MVC. That is, FlowAction is a Struts-specific front controller for Spring Web Flow.

To use FlowAction, declare it in the <action-mappings> section of strutsconfig.xml. The following <action> configures Spring Web Flow to respond to requests whose URL ends with /flow.do:

<action path="/flow"
    name="actionForm"
    scope="request"
    type="org.springframework.webflow.executor.
               struts.FlowAction"/>

As with the Spring MVC version of Spring Web Flow, the Struts FlowAction uses a handful of parameters to guide the flow (the same as those described in table 15.1). In Struts, these parameters must be bound to an ActionForm implementation. More specifically, Spring Web Flow parameters must be bound to SpringBindingActionForm. Therefore, we’ll also need to configure a Struts <form-bean> entry to tell Struts about SpringBindingActionForm:

<form-bean name="actionForm" type=
    "org.springframework.web.struts.
              SpringBindingActionForm"/>

Notice that the name of the <form-bean> is the same as the name of the <action>. This is how Struts knows that SpringBindingActionForm is to be used to bind parameters for FlowAction.

That’s all you must do to use Spring Web Flow with Struts. Flows for Struts are defined exactly the same way as flows running under Spring MVC. The only slight difference is that in a Struts-based flow a <view-state>’s view refers to a Struts <forward>.

If Struts isn’t your cup of tea, Spring Web Flow integrates with one more web framework. Let’s see how to use Spring Web Flow with JavaServer Faces (JSF).

JavaServer Faces

To use Spring Web Flow with a JSF application, we’ll need to configure several custom Spring Web Flow elements in the application’s faces-config.xml file. The following excerpt from faces-config.xml shows the relevant customizations:

<application>
  <navigation-handler>
    org.springframework.webflow.executor.jsf.FlowNavigationHandler
  </navigation-handler>
  <property-resolver>
     org.springframework.webflow.executor.jsf.FlowPropertyResolver
  </property-resolver>
  <variable-resolver>
     org.springframework.webflow.executor.jsf.FlowVariableResolver
  </variable-resolver>
  <variable-resolver>
    org.springframework.web.jsf.DelegatingVariableResolver
  </variable-resolver>
  <variable-resolver>
    org.springframework.web.jsf.WebApplicationContextVariableResolver
  </variable-resolver>
</application>

In JSF, navigation is usually handled by consulting <navigation-rule> entries in faces-config.xml. In Spring Web Flow, however, navigation is determined by the flow’s <transition> definitions. Therefore, for Spring Web Flow to work with JSF, we need to configure FlowNavigationHandler, a customer navigation handler that uses a flow definition to guide navigation when running within a flow (falling back to the default NavigationHandler when not in a flow).

We’re also going to need a way for JSF to resolve variables and their properties from flow scope. Unfortunately, JSF’s default variable and property resolution knows nothing about flow-scoped variables. Spring Web Flow’s FlowVariableResolver and FlowPropertyResolver enable JSF to find flow-scoped data when binding expressions that are prefixed with flowScope (e.g., flowScope.order.total).

We also must configure two other variable resolvers: DelegatingVariable-Resolver and WebApplicationContextVariableResolver. These variable resolvers are useful in resolving JSF variables as beans from the Spring application context. We’ll talk more about these two variable resolvers in sections 16.4.2 and 16.4.4 when we discuss the integration of JSF and Spring.

Finally, one more tweak must be made to faces-config.xml for Spring Web Flow to work with JSF. A flow’s execution must be saved between requests and restored at the beginning of each request. Spring Web Flow’s FlowPhaseListener is a JSF PhaseListener implementation that manages the flow execution on behalf of JSF. It is configured in faces-config.xml as follows:

<lifecycle>
  <phase-listener>
    org.springframework.webflow.executor.jsf.FlowPhaseListener
  </phase-listener>
</lifecycle>

FlowPhaseListener has three specific responsibilities, each associated with a phase in the JSF lifecycle:

  • At BEFORE_RESTORE_VIEW, FlowPhaseListener restores a flow execution, based on the flow execution identifier in the request arguments (if any).

  • At BEFORE_RENDER_RESPONSE, FlowPhaseListener generates a new identifier for the updated flow execution to be stored as.

  • At AFTER_RENDER_RESPONSE, FlowPhaseListener saves the updated flow execution, using the identifier generated in BEFORE_RENDER_RESPONSE.

Once all of the necessary entries have been placed in faces-config.xml, we’re ready to launch a flow. The following snippet of JSF creates a link to kick off the flow named Order-flow:

<h:commandLink
    value="Order Pizza"
    action="flowId:Order-flow"/>

Summary

Not all web applications are freely navigable. Sometimes, a user must be guided along, asked appropriate questions and led to specific pages based on their responses. In these situations, an application feels less like a menu of options and more like a conversation between the application and the user.

In this chapter, we’ve explored Spring Web Flow, a web framework that enables development of conversational applications. Along the way, we built a flow-based application to take pizza orders. We started by defining the overall path that the application should take, starting with gathering customer information and concluding with the order being saved in the system.

A flow is made up of several states and transitions that define how the conversation will traverse from state to state. As for the states themselves, they come in one of several varieties: action states that perform some business logic, view states that involve the user in the flow, decision states that dynamically direct the flow, and start and end states that signify the beginning and end of a flow. In addition, there are subflow states that are, themselves, defined by a flow.

Although much of our discussion of Spring Web Flow assumed that our flow would run as part of a Spring MVC application, we also learned that Spring Web Flow can be used with Jakarta Struts and JavaServer Faces. In fact, the flow definitions themselves are completely transferable from one web framework to another.

Speaking of integration, Spring Web Flow isn’t the only part of Spring that plays well with other web frameworks. Coming up in the next chapter, we’ll see how to use the rest of Spring with several popular web frameworks, including Tapestry, WebWork, Struts, and JavaServer Faces. We’ll also have a look at how to build Ajax functionality into an application by exposing Spring beans as Ajaxremoted objects.