Wednesday, May 2, 2007

Spring Web Flow

You must have found the web applications are getting more and more complex and to maintain them is getting even harder. You now have to write or understand and maintain a large and complex page flows (flow of the application). If you are bugged down with creating such a complex application the answer lies in using Spring Web Flow.
Spring Web Flow (SWF) is a component of the Spring Framework's web stack focused on the definition and execution of user interface (UI) flow within a web application. It is a module that allows you to make logical flows of your web application.
Spring Web Flow is so far the best solution for the management of web applications with complex page flow. It provides a powerful controller to control the user navigation in case your application demands
For example:
An example definition of a simple flow to carry out a search process is shown graphically below:





Figure 1 – A Search Flow Example


Why Spring Web Flow


We know that defining and understanding page flow of a complex web application is difficult no matter which framework it is based on. For a struts application you will have to write action to handle and process each request. This approach may be simple for simpler applications but image if you were to write an application with complex paths or process flows involving large number of views, the task would be difficult using the traditional approach. Maintenance of such an application is another tedious job, you will have to dig into the code to understand the process flow of the application. This problem gets considerably worse as a web application gets larger and more complex. So in a way these base Model 2 frameworks hide the higher-level flow which makes it difficult to track the lifecycle of the page flow.

The solution here is provided by the Spring Web Flow. Spring Web Flow allows you to represent the page flows of your application in a clear and simple way and the good part is that you can use it Spring Web Flow with other frameworks like Struts, Spring and JSF.

Advantages of Spring Web Flow:



  • Page flow of the application is visible just by looking at XML or java
    configuration.

  • Web flows are designed to be self contained, and thus are reusable
    multiple of times.

  • The technique to capture the
    page flow remains the same for all the cases and there are no specialized
    approaches for particular situations.


How does Spring Web Flow Work?

Spring Web Flow is composed of a set of states (Displaying a View or executing any Action etc.). Transition of the flow from one state to another is triggered by an event. This continues till the flow completes and enters the end-state. The important Spring Web Flow states are:

The start-state, when a flow is created the initial state of the flow is defined by the start-state attribute in the Webflow.

An action-state executes an action and returns a logical result on its completion. The next to which the flow will be transitioned to depends on the result of this state

A view-state when entered pauses the flow and returns thecontrol to the client/user and the flow is resumed on the user/client event which resumes the flow and triggers the transition to the state depending on the user/client input or decision.

A decision-state is used to determine the next state in the dynamic way or at runtime. If our next state depends on some attributes or properties ( eg. If users are not logged then redirect them to login page ).

A subflow-state is used to represent independent flows which are not dependent on the main flow. A subflow is created as child of main flow (parent flow). When a subflow is called the parent flow is suspended until the child flow completes. This helps to maintain the application as a set of sub-modules (Subflows) which can be used multiple times. The Subflow can be child of another Subflow or of the root flow.

End-state signifies the end of the flow. When a flow enters the end-state the active flow session is terminated. If the end-state of the root flow is entered the resources associated with it are
cleaned up automatically.

Let us have a look at a Sample Web Flow for Movie ticket booking process flow. The UML state diagram for the same is below.




Figure 2 Movie Ticket Booking flow in UML


The following XML demonstrates this web flow.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE webflow PUBLIC "-//SPRING//DTD WEBFLOW//EN"
        "http://www.springframework.org/dtd/spring-webflow.dtd">
 
<webflow id="bookticket" start-state="obtainMovieInfo">
        <action-state id="obtainMovieInfo">
               <action bean="bookingActions" method="bindAndValidate"/>
               <transition on="success" to="availableShows"/>
               <transition on="error" to="tryAgain"/>
        </action-state>
 
        <action-state id="availableShows">
               <action bean="bookingActions"/>
               <transition on="success" to="displayShowTimings"/>
        </action-state>
 
        <view-state id="displayShowTimings" view="showTimings">
               <transition on="startOver" to="cancel"/>
               <transition on="select" to="selectShow"/>
        </view-state>
 
        <action-state id="selectShow">
               <action bean="bookingActions"/>
               <transition on="success" to="isPersonalInfoRequired"/>
        </action-state>
 
        <decision-state id="isPersonalInfoRequired">
               <if test="${requestScope.person == null}" then="enterPersonalInformation"/>
               <if test="${requestScope.person.preferences.alwaysConfirmPersonalInfo}"
                       then="enterPersonalInformation" else="displayReservationVerification"/>
        </decision-state>
 
        <subflow-state id="enterPersonalInformation" flow="person">
               <attribute-mapper>
                       <input value="${requestScope.person.id}" as="personId"/>
               </attribute-mapper>
               <transition on="finish" to="displayReservationVerification"/>
        </subflow-state>
 
        <view-state id="displayReservationVerification" view="reservationVerification">
               <transition on="startOver" to="cancel"/>
               <transition on="assignSeats" to="chooseSeatAssignments"/>
               <transition on="book" to="book"/>
        </view-state>
 
        <subflow-state id="chooseSeatAssignments" flow="seatAssignments">
               <attribute-mapper>
                       <input value="${requestScope.person.id}" as="personId"/>
                       <input name="show"/>
               </attribute-mapper>
               <transition on="finish" to="displayReservationVerification"/>
        </subflow-state>
 
        <action-state id="book">
               <action bean="bookingActions"/>
               <transition on="success" to="displayConfirmation"/>
        </action-state>
 
        <end-state id="displayConfirmation" view="reservationConfirmation"/>
 
        <end-state id="tryAgain" view="tryAgain"/>
 
        <end-state id="cancel" view="home"/>
 
</webflow>

Just from the look of this XML you can understand the logical flow (process flow) of the application even without looking at any other part of the code.

In the above XML definition you can see the webflow contains subflows, these subflows can act as modules of your application and are reusable. This is the most advantageous property of Spring Web Flow

XML Definition Explained:

Let us have a look at each component of the above XML definition in SpringWeb Flow.

The Flow Definition

Starting with line 1 of the XML-based flow definition:
<webflow id="bookticket" start-state="obtainMovieInfo">
        ...
</webflow>


The webflow element defines the flow, specifying its id and start-state>. The id is a unique identifier. When a new flow session is activated the start state is the first state to which the flow
transitions on success.

So here when a new session is activated for bookticket the first state is obtainMovieInfo.

Now in obtainMovieInfo state definition.

<action-state id="obtainMovieInfo">
        <action bean="bookingActions" method="bindAndValidate"/>
        <transition on="success" to="availableShows"/>
        <transition on="error" to="tryAgain"/>
</action-state>
This is a action state and it will return a logical result on execution. So when the flow enters this business case (obtainMovieInfo) bindAndValidate method is called with bookingActions identifier. If this process is successful, availableShows state is entered otherwise tryAgain state is entered.

The Booking Action

When using Spring Web Flow with Spring the bookingActions bean definition in the web-context.xml will be as:
web-context.xml
 
<bean id="bookingActions"
class="net.javabeat.examples.bookticket.BookingActions">
    <property name="bookingAgent" ref="myBookingAgent"/>
</bean>

This way our action is manages and configured via dependency injection via Spring.


The Available Shows Action State

Now let us move on to the next action state:
<action-state id="availableShows">
        <action bean="bookingActions"/>
        <transition on="success" to="displayShowTimings"/>
</action-state>
Here we pass the validated Movie object as input and it will return a collection of show timings.

The actual code will look like:

public class BookingActions extends FormAction {
    ...
    public Event availableShows(RequestContext context) {
        Movie movie = (Movie)context.getRequestScope().getAttribute("movie");
        Collection<ShowTimings> showTimings = bookingAgent.availableShows(movie);
        context.getRequestScope().setAttribute("showTimings", showTimings);
        return success();
    }
}


The method availableShows will be called when the flow enters the availableShows state; this is generic for all the states. When a state is entered a method is invoked on the target action bean. And on the successful execution of this method the showTimings collection is returned and the state is transitioned to displayShowTimings View.


Display Show Timings View State

This view displays all the available shows for that movie (from show Timings collection).
<view-state id="displayShowTimings" view="showTimings">
        <transition on="startOver" to="cancel"/>
        <transition on="select" to="selectShow"/>
</view-state>

This is a view state and the flow will be paused for the user response. Here the user can either select or startOver. On select event the flow will enter the selectShow state and on
startOver the flow will enter the cancel state depending on the user input.


Client Side State

On the client side the id of the executing flow is tracked and the input values to the next event are also provided using this id.

For example, in a jsp:

<input type="hidden" value="<c:out value="${flowExecution.id}"/>">

The “Is Personal Info Required?” Decision State

After the user selects a show the flow has to make a dynamic decision where to go next depending on the state of the user. If the user is logged in you may want to redirect him to the booking page and if he is not logged in you may want him to enter his details – like name and credit card number.

For this dynamic decision we use the decision state. The definition for the decision state is below:

<decision-state id="isPersonalInfoRequired">
        <if test="${requestScope.person == null}" then="enterPersonalInformation"/>
        <if test="${requestScope.person.preferences.alwaysConfirmPersonalInfo}"
               then="enterPersonalInformation" else="displayReservationVerification"/>
</decision-state>

Personal Information SubFlow State

Maintaining personal is independent of the application logical flow a personal can update his personal information without booking any tickets. For such an independent flow process we can use Subflow state.

enterPersonalInformation subflow state:

<subflow-state id="enterPersonalInformation" flow="person">
        <attribute-mapper>
               <input value="${requestScope.person.id}" as="personId"/>
        </attribute-mapper>
        <transition on="finish" to="displayReservationVerification"/>
</subflow-state>

The attribute-mapper element maps attributes to and from the subflow using the attribute id. And when the subflow completes the control is returned back to parent flow. In our business flow when the enterPersonalInformation state is entered the personal flow is created as the child of the current flow ( Webflow in this case). The personId attribute is passed to the child flow as input and the processing of the subflow is independent of the parent flow from here on till it completes. On the completion of the subflow, the parent flow resumes and depending on the result of the child flow it determines which state to transition to.

The Display Confirmation End State

After a show is successfully booked our flow ends and we want to display a confirmation. See below:
<end-state id="displayConfirmation" view="reservationConfirmation"/>
This is the end-state and when the bookticket flow enters this state the flow is terminated and the resources are cleaned up for reuse (this is an automatic process in Spring Web Flow).

Note: had the ending flow been acting as a subflow, the entered end state is treated as a subflow result the resuming parent flow can respond to. More specifically, the entered end state ID is used as grounds for a state transition in the resuming parent flow's subflow state. You can see this in action by taking a look at the "enterPassengerInformation" subflow state definition. Note how it responds to the "finish" result of the subflow, which corresponds to a
"finish" end state within the passenger flow.


Flow Deployment

We have written the flow definition, we would like to use it now with our Spring application.

All we will have to do is.

<bean name="/book.htm" class="org.springframework.web.flow.mvc.FlowController">
    <property name="flow">
        <ref bean="bookFlow"/>
    </property>
</bean>
 
<bean id="bookFlow" class="org.springframework.web.flow.config.XmlFlowFactoryBean">
   <property name="location" value="classpath:bookticket-flow.xml"/>
</bean>



When to use Spring Web Flow

Spring Web Flow should be used in application where you want user to have controlled navigation and the navigation is complex and the process flow is large. Because Spring Web Flow is stateful it is good for complex business flows. It should not be used in simple applications with asmall flow or which need a stateless solution.

Some examples where Spring Web Flow would be most appropriate:

· Book Tickets
· Online shopping

Writing Your First Application Using Spring Web Flow:

Assuming that you have the basic knowledge of Spring, HTML and JSP, because we will be using Spring in our application and of course you need to know HTML and JSP. First you have to download Spring Web Flow and Spring Framework (you can download them from http://www.springframework.org/).

Let us begin.

1. Setting environment variables: You should have spring-webflow-1.0.jar, spring-binding-1.0.jar and spring.jar files in your classpath.

2. Now that the environment is ready we are all set to design our first flow. We will design a login page. On startup a form view will be displayed in the browser. On submit validate the form input data and login the user.
3. We can now create the flow definition for our login process.

myFlow.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE flow PUBLIC "-//SPRING//DTD WEBFLOW 1.0//EN"
       "http://www.springframework.org/dtd/spring-webflow-1.0.dtd">
 
<flow start-state="displayLoginForm">
 
    <view-state id="displayLoginForm" view="form">
        <entry-actions>
            <action bean="loginForm" method="setupForm"/>
        </entry-actions>
        <transition on="submit" to="processLogin">
            <action bean="loginForm" method="bindAndValidate"/>
        </transition>
    </view-state>
 
    <action-state id="processLogin">
        <action bean="loginForm" method="processLogin"/>
        <transition on="success" to="finish"/>
    </action-state>
 
    <end-state id="finish" view="success"/>
 
</flow>
 

4. Creating flow action: Define the formAction bean and validate and submit logic.

MyLoginAction.java

public class MyLoginAction extends FormAction {
    public MyLoginAction() {
        setFormObjectClass(FormObject.class);
    }
 
    public static class FormObject implements Serializable {
        private String user;
         private String password;
 
        public String getUser() {
            return user;
        }
 
        public void setUser(String user) {
            this.user = user;
        }
         public String getPassword(){
            return password;
        }
 
        public void setPassword(String password) {
            this.password = password;
        }
 
    }
 
    public Event processLogin(RequestContext context) throws Exception {
        FormObject formObject = (FormObject)getFormObject(context);
        // todo login logic
        return success();
    }
 
}

5. Now we will deploy our flow using Spring Application Context. We will use XmlFlowRegistryFactoryBean.

dispatcher-servlet.xml

<bean id="flowRegistry" class="org.springframework.webflow.registry.XmlFlowRegistryFactoryBean">
   <property name="flowLocations" value="/WEB-INF/flows/myflow.xml"/>
</bean>
<bean id="formAction" class="net.javabeat.example.MyLoginAction"/>

6. Define a FlowController and ViewResolver:

dispatcher-servlet.xml

<bean name="/myApp.htm" class="org.springframework.webflow.executor.mvc.FlowController">
    <property name="flowLocator" ref="flowRegistry"/>
</bean>
 
<!-- Maps flow view-state view names to JSP templates with JSTL support -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
    <property name="prefix" value="/WEB-INF/jsp/"/>
    <property name="suffix" value=".jsp"/>
</bean>

  1. Next,
    create the views of the flow.
/WEB-INF/jsp/form.jsp
<html>
<head>
   <title>My Login Page</title>
</head>
<body>
<p>
   <table>
   <form action="myApp.htm">
       <tr>
           <td>User</td>
           <td><input type="text" name="user" size="25"/></td>
       </tr>
  <tr>
           <td>Password</td>
           <td><input type="text" name="password" size="25"/></td>
       </tr>
 
       <tr>
           <td colspan="2" align="right">
               <input name="_eventId_submit" type="submit">
               <input type="hidden" name="_flowExecutionKey" value="${flowExecutionKey}">
           </td>
       </tr>
   </form>
   </table>
</p>
</body>
</html>

_eventId and _flowExecutionKey parameters are required for the view to submit to the server. _flowExecutionKey is used to maintain the state of the flow. This way the server identifies the current flow of the client. _eventId is used to signal which event occurred on the client, in our login application it will submit “submit” if the submit button is pressed. And depending on its value the flow transition occurs to the next step.Now we are ready to deploy and test our application. To test our application

the URL will be /myApp.htm?_flowId=myFlow URL. When we access this URL a new ‘myFlowFlowExecution will be created and transitioned to its start-state (displayLoginForm) and the next steps will follow according to the myFlow flow definition.


Conclusion

Spring Web Flow is the best solution for managing a complex business process flow. And to add to that it can be integrated with your existing environment like Struts and JSF. So what are you waiting for, Go get it.

No comments: