Launch a Web Flow with parameters

The way you can launch a certain Spring Web Flow (SWF) in you web application by passing it a set of initial parameters is following the next steps:

1. Specify the parameters you must pass to the flow in the url (In this case param1 and param2)

/home.htm?_flowId=home-flow&param1=value1&param2=value2

2. The flow must define a section called <start-actions>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?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-actions>
	   <set attribute="param1Name" scope="flow" value="${requestParameters.param1}"/>              
	   <set attribute="param2Name" scope="flow" value="${requestParameters.param2}"/>       
	</start-actions>
 
	<start-state idref="validateSecurity"/>
 
	<action-state id="validateSecurity">
	   <action bean="security.action" method="validateActiveUser"/>
	   <transition on="success" to="displayHome"/>
	   <transition on="error" to="showError"/>
	</action-state>
 
	<view-state id="displayHome" view="home/homeMenu"/>
 
	<end-state id="showError" view="home/securityMessage"/>	
</flow>

If you see the flow parameters called param1Name and param2Name are used to stored the references of request passed parameters param1 and param2 respectively.

3. Accessing them from your Action class
You can in your Action class get values of param1Name and param2Name by accessing the flow scope of the RequestContext (context.getFlowScope())

1
2
3
4
public Event validateSecurity(RequestContext context) throws Exception{
	Object param1=context.getFlowScope().get("param1Name");
	Object param2=context.getFlowScope().get("param2Name");
}
Email Email Print Print
1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...

The little price of a name

Awesome, there is not other word to describe the numbers some people have been paid for a name in the world wide web. Today “America.com” domain is for auction by an anonymous owner. So the expectations behind it are simply huge because there are hoping to brake the record establishing by “Sex.com” domain name time ago which by the way, it was about 12.5 millions dollars. Just take a look to this numbers below:

Sex.com (12,500,000 USD)
Games.com (11,000,000 USD)
Porn.com (9,500,000 USD)
Beer.com (7,000,000 USD)
Vodka.com (3,000,000 USD)

So far, sex and liquor domain names related has been the more expensive, but it seems this situation is going down because according last years buyers trends, it seems investors are most interested today in find names related to “geographical domains” which are names related to countries or cities, as the last year example “Chinese.com” for about 1.1 millions dollars.

Conclusion, there is not more than start registering each place you know, may be in future you can be another dotcom rich guy.

Email Email Print Print
1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...

Centralizing contacts in one place

I must accept it, this is a feature needed by everyone. The capability to have the contacts info updated as easy as possible is a dream. In this case, I was trying UNIK for some time. This service it’s great to keep in a central and classified way the contact list moreover if all your contacts are also registered. Otherwise you probably will use it for backing up that sensible data or like a contacts reference.

It’s possible to import contacts from other services.

Registering to UNIK

So, you can control all in one place.

The Dashboard

At the end, if this kind of service as well as can import from other sites as GMail, Yahoo could synchronize them would be simply terrific, because I think this is exactly the real value added, keeping in mind the current offered service, which by the way can helping us without any doubt. Excellent service.

Email Email Print Print
1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...

Spring Web Flow, quick notes to start coding

Spring Web Flow also called SWF, is a MVC framework adopted by Spring Source as a part of the Spring Framework projects family. Firstly created by Keith Donald and Erwin Vervaet, as a result to the limited page flow functionality offered by most used MVC frameworks.

Spring Web Flow allows represent the web application flow according a group of states, giving to you a clear and simple way to develop and to other people a simple manner to understand it. So in few words SWF is a MVC framework, substitution for Struts, JSF or Spring MVC even, but Action classes built on SWF can be integrated with them which is great portability advantage.

The framework as been defined as “the next generation Java web application controller framework that allows developers to model user actions as high-level modules called flows that are runnable in any environment. The framework delivers improved productivity and testability while providing a strong solution to enforcing navigation rules and managing application state

I. The basis in a nutshell

Your page web flow will be represented as states
If you have the following application flow:

Web application

In a normal MVC architecture based web application, you gonna have views (web pages) invoking logic store in Actions (classes):

MVC Web application

In the SWF view point, you will represent the web flow above as a sequence of states (like the next image shown). If you see flow logic are represented here even (validate, loadData, and clearForm methods for instance):

Web application flow

SWF provides two possible ways to define a flow; the first one is through a XML document or by using the SWF API. The first one in most cases is preferred as a result XML portability and it’s clear to understand advantage.

The important concepts; from here we will talk about:

  • Flow: Sequence of states that defines a certain flow of page or pages in your application. A web application can be composed by one or more flows
  • State: A specific point in the flow where some logic is executed such as showing a page or executing an action.
  • Transition: Each state has one or more transitions that are used to move to another state.
  • Event: A transition is triggered by an event.

Types of states
SWF specifies a set of states you can use to define your web application behavior which are:

  • start-state: Each flow will have a single start state. In this place you specify the id of another state in the flow where flow execution will start.
  • action-state: A flow can have multiple action states, defining places in the Web Flow where actions like web page fields validation, service invocations, set session data, etc.
  • view-state: View states define points in the flow where a web page will be shown.
  • decision-state - This kind of state takes decisions related to dynamic routing inside a flow. According a given parameters it can redirect the flow to one or more subsequent states.
  • subflow-state - This state type launches another Web Flow as a subflow. It allows you to reuse a flow from inside another flow. Attribute mappers are used to exchange data between a flow and its subflows.
  • end-state - A state signaling the end of flow execution. Normally is a web page but not the rule.

Scope types
The web application lives in a HTTP Session, you probably already know that, but SWF provides also a set of scopes that are inherent to any application built on it. It uses it to store settings according it purpose. It’s a place also where you can store yours.

  • Request scope: This scope is related to a single request into the application.
  • Flash scope: This scoped attributes are preserved until the next execution of an user event. So each time an event is triggered the flash scope is cleared.
  • Flow scope: Persists for the life of a flow session. The states in a flow can share information using flow scope.
  • Conversation scope: Available to all flows in a conversation.
  • Scopes

The flow structure
You can create a flow by declaring it in a XML. The flow by default needs one start-state where it will start executing, zero or more states that defines the behavior (in the middle), and one or more end-states to finish the flow.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<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">
 
    <!-- Define the state to start-->
    <start-state idref="stateToStart"/>
 
    <!-- This is the initial state-->
    <view-state id="stateToStart" view="loginPage">
        <transition on="success" to="showMenu"/>
    </view-state>	
 
    <!-- This is where the flow finish-->
    <end-state id="showMenu" view="menuPage"/>
</flow>

Registering the flow
The page flow that contains the sequence of states (the example above) needs to be registered to a controller, in order you can launch the flow wherever you want inside your web application (from a link may be).

If you need to call the previous flow in your web application, it’s necessary to save it in a XML file. In this case, I named it as myPageFlow.xml after that you must map the flow with a certain controller name that SWF will use to reference it. (This is just a code to highlight the point, a complete example is in the third part).

1
2
3
4
5
6
7
8
<flow:executor id="my.flow.executor" registry-ref="product.flow.registry"/>
    <flow:registry id="my.flow.registry">
        <flow:location path="classpath:config/flows/myPageFlow.xml"/>		
    </flow:registry>    
 
<bean name="/myFlow.htm" class="org.springframework.webflow.executor.mvc.FlowController">
        <property name="flowExecutor" ref="my.flow.executor"/>
</bean>

In the line 3 is registered the flow file and in line 6 that flow will be associated with the controller name, in this case myFlow.htm, therefore I will use a link as follows to launch the flow:

<a href="myFlow.htm?_flowId=myPageFlow">Enter to my flow</a>

The controller is who launch flows, this is the reason you need to pass it the parameter_flowId. This parameter is used to look up the flow with the given id, so normally you will pass the name of the file that contains the flow (without the extension).

For instance, if you use the same controller to start other flow like myPageFlow2.xml:

<a href="myFlow.htm?_flowId=myPageFlow2">Enter to my flow2</a>

It’s necessary to mapping it also (line 4):

1
2
3
4
5
6
7
8
9
<flow:executor id="my.flow.executor" registry-ref="product.flow.registry"/>
    <flow:registry id="my.flow.registry">
        <flow:location path="classpath:config/flows/myPageFlow.xml"/>		
        <flow:location path="classpath:config/flows/myPageFlow2.xml"/>		
    </flow:registry>    
 
<bean name="/myFlow.htm" class="org.springframework.webflow.executor.mvc.FlowController">
        <property name="flowExecutor" ref="my.flow.executor"/>
</bean>

II. Flow states in detail

Returning back to the states explanation, you can see SWF has several types of states. The way you can define them is the following:

Action state
It’s used to call a certain method in your Action class. The Action class must be defined as a Spring bean in some Spring XML file with given name. This name is which we’ll use to call the action in the flow.

	<action-state id="name">
		<action bean="actionName" method="actionMethodNameToCall"/>
		<transition on="success" to="successState"/>
		<transition on="error" to="errorState"/>
	</action-state>

Your Action class you want to call from here must have some rules:

  • Must extends one of the parent Actions classes provided by SWF such as AbstractAction, FormAction, MultiAction, etc. normally the most used is FormAction but it depends on the specific purpose you have to build. These actions are on the org.springframework.webflow.action package.
    public class ProductAction extends FormAction {…}
  • The method you need to call must receive by parameter a RequestContext object and return an Event object.
    public class ProductAction extends FormAction {
        public Event actionMethodNameToCall(RequestContext context) {
    	    return super.success();
        }
    }

SWF parent actions structure have already implemented events to return like success, error, yes, no from your Action class. Remember these events are which you use in transitions, so if you have a transition like:

<transition on="yes" to="ahotherStateName"/>

Your Action’s method in some place, must return that event:

public Event actionMethodNameToCall(RequestContext context) {
	return super.yes();
}

You can also have your own events just instantiating an org.springframework.webflow.execution.Event object and specifying the source which is the owner Action class and the event id which is the name you will refer it into the transition.

For instance, if you return an event with the id myEvent:

public Event actionMethodNameToCall(RequestContext context) {
	return new Event(this, “myEvent”, null);
}

In your flow you will have a transition that will response to your new event as follows:

<transition on="myEvent" to="ahotherStateName"/>

View State
It’s used to call certain JSP page or view. The name you define in the view attribute is the real name of the page. For instance for mainMenu.jsp you define as:

	<view-state id="showMenu" view="mainMenu">
		<transition on="accept" to="acceptState"/>
		<transition on="cancel" to="cancelState"/>
	</view-state>

If the web page is stored in some folder structure is necessary define it also:

	<view-state id="showMenu" view="jsps/menus/mainMenu">

It’s possible also call certain logic before showing the web page. Load a data catalog, for instance, or set some configuration in the model. So whatever you need to load before show the view you can do by using the <render-actions> section. Here you can invoke an Action method or any other bean method an store the result in any scope you need:

	<view-state id="showMenu" view="productList">
		<render-actions>
            		<action bean="productAction" method="loadAllProduct"/>
		</render-actions>
		<transition on="success" to="callSuccessState"/>
	</view-state>

In a case of a view (web page), how do I manage events to trigger a transition ? Simply, web pages normally will produce events to the flow by clicking some link, image, submit button, whatever implies a change in the page, so if we have:

	<view-state id="displayOrders" view="orderList">
		<transition on="returnToMenu" to="showMainMenu"/>
		<transition on="clickLogout" to="doLogout"/>
	</view-state>

It’s possible to propagate from a web page the events in your state’s transitions with HTML links or HTML forms.

  • Using links
    The way we will propagate the returnToMenu event is:

           <a href="myFlow.htm?_flowExecutionKey=${flowExecutionKey}&_eventId=returnToMenu">
    		Return to Menu
           </a>

    The link is composed by:

    • myFlow.htm: The controller name where flow is registered.
    • _flowExecutionKey: This is a SWF generated key that identifies the current executing flow.
    • _eventId: The event that launch a transition
  • Using forms
            <form action="myFlow.htm" method="post">
                <input type="hidden" name="_flowExecutionKey" value="${flowExecutionKey}"/>
                <input type="image" name="_eventId_returnToMenu" value="Submit" src="menu.gif"/>
                <input type="image" name="_eventId_clickLogout" value="Delete" src="logout.gif"/>
            </form>

    The form has the flow controller in the action attribute, the conversation id in the _flowExecutionKey hidden field and the buttons or in this case images that propagates the events following the syntax _eventId_[eventName] in the name attribute.

Decision State
It’s used to evaluate data passing from the page request in order to decide which state is the next in the flow. Remember SWF scopes, so in this case the attributes we are evaluating come in the request scope.

	<decision-state id="selectReportToDisplay">
		<if test="${requestScope.productReportCommand.reportName=='detailed'}" then="showDetailedReportState"/>
		<if test="${requestScope.productReportCommand.reportName=='productByCompany'}"
			then="showProductByCompanyReportState" else="defaultReportState"/>
	</decision-state>

SubFlow State
Subflows are used to split your web flow in reusable sections that you can use in another. They are optional as most and it uses something called “attribute mappers” to exchange data between a flow and its subflows.

	<subflow-state id="enterProductDetail" flow="product-details">
		<attribute-mapper>
			<input value="${requestScope.productCommand.id}" as="productId"/>
		</attribute-mapper>
		<transition on="finish" to="displayProducts"/>
	</subflow-state>

III. Putting all together: A Step by Step example

The example is a product maintenance. It provides C.R.U.D. (Create, Retrieve, Update, and Delete) operations over products. The architecture basically is composed by JSP views that are managed by an Action class which by the way call a service to retrieve or update data.

The following is the application goal:

Example State Diagram

So, the page flow will be the following:

Example page flow

1. The Main menu
It’s just the entry point of the example application

Main Menu

index.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<%@page contentType="text/html"%>
<%@page pageEncoding="UTF-8"%>
 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
   "http://www.w3.org/TR/html4/loose.dtd">
 
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>JSP Page</title>
    </head>
    <body>   
        <h3>Main Menu</h3>
        <p>Welcome to our inventory application.</p>
        <p><a href="products.htm?_flowId=product-flow">Product List</a></p>       
    </body>
</html>

Please notice the line 15 of the code above, which is the link that launches the web flow.

2. The JSP for list products

This view is a page where you can query all the products stored in the datasource. It provides a button to call the new/edit form to include a new product or edit an existing and a button to go to help also.

Product list

product-list.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<%@page contentType="text/html"%>
<%@page pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> 
 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
   "http://www.w3.org/TR/html4/loose.dtd">
 
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>JSP Page</title>
    </head>
    <body>    
        <h2>Product catalog</h2>
        <a href="products.htm?_flowExecutionKey=${flowExecutionKey}&_eventId=pressNewButton">
            <img border="0" src="imgs/new.gif">
        </a>                                		
        <a href="products.htm?_flowExecutionKey=${flowExecutionKey}&_eventId=pressHelpButton&helpMessage=PRODUCT_CATALOG_HELP">
            <img border="0" src="imgs/help.gif">
        </a>                                				
        <table border="1" width="35%" align="left">
          <tr> 
            <td width="27%" align="center"><strong>Code</strong></td>
            <td width="73%" align="center"><strong>Description</strong></td>
          </tr>
          <c:forEach items="${productList}" var="productCommand" varStatus="status"> 
              <tr> 
                <td align="left"> 
                    <a href="products.htm?_flowExecutionKey=${flowExecutionKey}&_eventId=pressEditLink&productCode=${productCommand.code}"> ${productCommand.code} </a> 
                </td>
                <td align="left"> ${productCommand.description} </td>
              </tr>
          </c:forEach> 
        </table>
   </body>
</html>

If you see from the line 15 to 20 there are two links that show you one way to invoke transitions pressNewButton and pressHelpButton from the current executing state which is showProductList. The second one pressNewButton also pass the parameter helpMessage to the state this transition will execute.

Also notice _flowExecutionKey which is a required parameter as well as _eventId to define the name of the transition to go.

3. The JSP for include a new product or edit
This view is a page where you can insert or edit a given product. It is called by the New button in the Products list page, or by clicking the specific link of a existing product to edit it.

Product edition

product-info.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<%@page contentType="text/html"%>
<%@page pageEncoding="UTF-8"%>
 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
   "http://www.w3.org/TR/html4/loose.dtd">
 
<html>
    <head>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
            <title>JSP Page</title>
    </head>
    <body>
        <h3>Product Information</h3>
        <form action="products.htm" method="post" name="myForm">
            <input type="hidden" name="_flowExecutionKey" value="${flowExecutionKey}"/>
            <table width="34%" border="0" cellspacing="0" cellpadding="0">
                <tr> 
                  <td width="27%"><strong>Code</strong></td>
                  <td width="73%"><input type="text" name="code" value="${productCommand.code}"></td>
                </tr>
                <tr> 
                  <td><strong>Description</strong></td>
                  <td><input type="text" name="description" value="${productCommand.description}"></td>
                </tr>
                <tr>
                  <td colspan="2"> </td>
                </tr>
                <tr> 
                  <td colspan="2">
                      <input type="image" name="_eventId_pressSubmit" value="Submit" src="imgs/accept.gif"/>
                      <input type="image" name="_eventId_pressDelete" value="Delete" src="imgs/delete.gif"/>
                      <input type="image" name="_eventId_pressCancel" value="Cancel" src="imgs/cancel.gif"/>
                    </td>
                </tr>
            </table>
        </form>
    </body>
</html>

To highlight in this view is the controller name in the action attribute of the form as well as the _flowExecutionKey parameter passed as an hidden field and the names of the buttons images that represent the way you invoke transitions of the current state using HTML forms.

3. The Command object
This is the form backing object (command) used to pass data mapped in HTML forms fields to the respective Action class (ProductAction in this case).

ProductCommand.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class ProductCommand implements Serializable{
 
    private String code;
    private String description;
 
    public ProductCommand() {        
    }
 
    public String getCode() {return code;}
 
    public void setCode(String code) {this.code = code;}
 
    public String getDescription() {return description;}
 
    public void setDescription(String description) {this.description = description;}
 
    …    
}

4. The Action
This is the class responsible to handle the data passed by the views and for call the service (ProductService) to retrieve or update data. Remember the rules I mentioned above related to the Action class.

ProductAction.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
package org.darcia.swf.web.action;import org.springframework.webflow.action.FormAction;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.RequestContext;
 
public class ProductAction extends FormAction{
 
    private ProductService service;
 
    public ProductAction() {
    }
 
    public Event newProduct(RequestContext context) throws java.lang.Exception{
        //Get the form backing object
        ProductCommand command = getCommandFromContext(context);        
        try{
            service.store(command.getCode(),command.getDescription());
        }catch(Exception ex){
            return super.error();
        }        
        return super.success();
    }
 
    public Event updateProduct(RequestContext context) throws java.lang.Exception{
        //Get the form backing object
        ProductCommand command = getCommandFromContext(context);        
        try{
            service.update(command.getCode(),command.getDescription());
        }catch(Exception ex){
            return super.error();
        }        
        return super.success();
    }
 
    public Event deleteProduct(RequestContext context) throws java.lang.Exception{
        //Get the form backing object
        ProductCommand command = getCommandFromContext(context);        
        try{
            service.delete(command.getCode());
        }catch(Exception ex){
            return super.error();
        }        
        return super.success();
    }
 
    public Event getProduct(RequestContext context) throws java.lang.Exception{        
        //Get the code passed by parameter
        String productCode=context.getFlowScope().getString("productCode");        
        //Get the service data
        Product rawProduct=service.get(productCode);        
        //Store the business data in the view layer command
        ProductCommand command = new ProductCommand(rawProduct.getCode(),rawProduct.getDescription());
        //Store the command object in the view session        
        context.getRequestScope().put("productCommand",command);        
        return super.success();
    }
 
    public Event getAllProducts(RequestContext context) throws java.lang.Exception{        
        //Get the service data
        List rawProductList=service.getAll();        
        List viewData = new ArrayList();
        //Store the business data in the view layer command
        for (int i = 0; i < rawProductList.size(); i++) {
            Product rawProduct = (Product) rawProductList.get(i);
            viewData.add(new ProductCommand(rawProduct.getCode(),rawProduct.getDescription()));
        }
        //Store the command object in the view session        
        context.getRequestScope().put("productList",viewData);        
        return super.success();
    }
 
    public String storeHelpMessage(String message) throws java.lang.Exception{
        return message;
    }
 
    private ProductCommand getCommandFromContext(RequestContext context) throws Exception{
        Object formObject = (Object)super.getFormObject(context);		
        if(formObject instanceof ProductCommand){			
            return (ProductCommand)formObject;
        }        
        return null;
    }
 
    …
}

Here are some quick tips that you need to keep in mind when you code the Action:

  • The way for get the reference of the current command object (view form backing bean) is with method super.getFormObject(...) you have to pass by param the RequestContext:

    ProductCommand formObject = (ProductCommand)super.getFormObject(context);
  • The way you put something in the request scope in order it can be looked up from the view is through the RequestContext all methods receive by param:

    context.getRequestScope().put("productCommand",myCommand);
  • The way you get a reference to the raw HttpServletRequest or HttpServletResponse if is needed is:

    HttpServletRequest request = ((ServletExternalContext)context.getExternalContext()).getRequest();
    HttpServletResponse response = ((ServletExternalContext)context.getExternalContext()).getResponse();

5. The Service
This is a plain service that provides data to the Action and store it in the datasource. Due is just an example the data is stored in a java.util.List which is handled the service.

ProductService.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package org.darcia.swf.business.service;public class ProductService {
 
    private List datasource = new ArrayList();
 
    public ProductService() {
        datasource.add(new Product("P001","Rice"));
        datasource.add(new Product("P002","Beans"));
        datasource.add(new Product("P003","Orange"));
        datasource.add(new Product("P004","Carrot"));
        datasource.add(new Product("P005","Beef"));        
    }
 
    public void store(String code, String description) throws java.lang.Exception{
        //Verifies if already exists someone with that code
        Product product = lookUpForProduct(code);
        if(product != null) throw new Exception("The code is already taken");
        datasource.add(new Product(code,description));
    }
 
    public void update(String code, String description) throws java.lang.Exception{
        //Search the product
        Product product = lookUpForProduct(code);
        if(product == null) throw new Exception("The code is not valid");        
        //Store the new description
        product.setDescription(description);        
    }
 
    public void delete(String code) throws java.lang.Exception{
        //Search the product
        Product product = lookUpForProduct(code);
        if(product == null) throw new Exception("The code is not valid");                
        datasource.remove(product);
    }
 
    public Product get(String code){        
        //Search the product
        Product product = lookUpForProduct(code);        
        return product;
    }
 
    public List getAll(){        
        return datasource;
    }    
 
…    
}

The Product object you see in the service is just used to store the product information and it’s passed to the Action as DTO also.

Product.java

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Product implements Serializable{
 
    private String code;
    private String description;
 
    public String getCode() {return code;}
 
    public void setCode(String code) {this.code = code;}
 
    public String getDescription() {return description;}
 
    public void setDescription(String description) {this.description = description;}  
}

6. Defining the Web Flow
The following is the web flow of the product’s maintenance. As you can see everything defined in the UML states diagram shown above are defined here also. One of the SWF advantages is that only viewing the page flow you can understand the application behavior.

product-flow.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
<?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="showProductList"/>
 
    <view-state id="showProductList" view="product-list">
        <render-actions>
                <action bean="productAction" method="getAllProducts"/>
        </render-actions>
        <transition on="pressNewButton" to="showNewPage"/>
        <transition on="pressEditLink" to="showEditPage">
             <set attribute="productCode" scope="flow" value="${requestParameters.productCode}" /> 
        </transition>
        <transition on="pressHelpButton" to="showHelp">
             <set attribute="helpMessage" scope="flow" value="${requestParameters.helpMessage}" /> 
        </transition>        
        <transition on="pressCancelButton" to="showMainMenuState"/>        
    </view-state>
 
    <!-- States related to the inclusion of a new product -->
    <view-state id="showNewPage" view="product-info">
        <render-actions>
                <action bean="productAction" method="cleanProductBackingBean"/>
        </render-actions>
        <transition on="pressSubmit" to="addNewProduct">
            <action bean="productAction" method="bindAndValidate"/>
        </transition>
        <transition on="pressCancel" to="showProductList"/>
    </view-state>
 
    <action-state id="addNewProduct">
        <action bean="productAction" method="newProduct"/>
        <transition on="success" to="showProductList"/>
        <transition on="error" to="showError"/>
    </action-state>        
 
    <!-- States related to the edition of an existing product -->
    <view-state id="showEditPage" view="product-info">
        <render-actions>
            <action bean="productAction" method="getProduct"/>
        </render-actions>
        <transition on="pressSubmit" to="updateProduct">
            <action bean="productAction" method="bindAndValidate"/>
        </transition>
        <transition on="pressDelete" to="deleteProduct">
            <action bean="productAction" method="bind"/>
        </transition>
        <transition on="pressCancel" to="showProductList"/>
    </view-state>
 
    <action-state id="updateProduct">
        <action bean="productAction" method="updateProduct"/>
        <transition on="success" to="showProductList"/>
        <transition on="error" to="showError"/>