Wednesday, March 25, 2009

Netbeans Spring Tutorials

Introduction to the Spring Framework

This document shows you how to construct a simple web MVC application using the Spring Framework. The application enables a user to enter her name in a text field, and upon clicking OK, the name is returned and displayed on a second page with a welcome greeting.

The Spring Framework is a popular open source application framework that can make J2EE development easier. It consists of a container, a framework for managing components, and a set of snap-in services for web user interfaces, transactions, and persistence. A part of the Spring Framework is Spring Web MVC, an extensible MVC framework for creating web applications.

The IDE provides built-in support for Spring Framework 2.5. Framework libraries are packaged with the IDE and are automatically added to the project classpath when the framework is selected. Configuration settings are provided, such as naming and mapping of the Spring Web MVC DispatcherServlet. The JSTL library is automatically registered. Support for Spring XML bean configuration files is also provided, including the following functionality:

  • Code completion. Invoked in Spring XML configuration files for Java classes as well as bean references.
  • Navigation. Hyperlinking of Java classes and properties mentioned in Spring bean definitions, as well as hyperlinking to other Spring bean references.
  • Refactoring. Renaming of references to Java classes in Spring XML configuration files.

For more information on the Spring Framework, visit http://www.springframework.org. For a more fine-grained explanation of how Spring Framework artifacts behave and interact with other objects in an application, visit the official Spring Framework Reference Documentation, or consult the Spring Framework API.

Contents

Content on this page applies to NetBeans IDE 6.1 and 6.5

To complete this tutorial, you need the following software and resources.

Software or Resource Version Required
NetBeans IDE version 6.1 or 6.5 Java
Java Development Kit (JDK) version 5 or 6
GlassFish application server
or
Tomcat servlet container
V2 or V3

version 6.x

Notes:

  • The Web and Java EE installation enables you to optionally install the GlassFish (V2 or V3) application server and the Apache Tomcat servlet container 6.0.18. You must install one of these (or register a different server in the IDE) to work through this tutorial.
  • If you need to compare your project with a working solution, you can download the sample application.
  1. helloView displayed in a browser

Setting up a New Project with Spring Web MVC Support

Creating a Spring Web MVC Skeleton Project

Start by creating a new project for a web application using the Spring Framework:

  1. Choose New Project (Ctrl-Shift-N) from the IDE's File menu. Under Categories select Java Web. (If you are using NetBeans 6.1, select Web.) Under Projects select Web Application. Click Next.
  2. In Project Name, type in HelloSpring. Click Next.
  3. In Step 3, Server and Settings, select the server you plan to work with from the Server drop-down list. Leave all other settings at their defaults and click Next.
  4. In Step 4, the Frameworks panel, select Spring Web MVC 2.5:
    Spring Web MVC 2.5 displayed in the Frameworks panel
    When you select Spring Web MVC 2.5, note that you can configure the name and mapping of the Spring dispatcher servlet under the Configuration tab. If you click the Libraries tab, note that JSTL libraries are by default added to the classpath during project creation.

    Click Finish. The IDE creates a project for the entire application, including all metadata, as well as the project's Ant build script which you can inspect from the Files window (Ctrl-2). You can view the template structure from the Projects window (Ctrl-1). Also note that four files open by default in the IDE's Source Editor: dispatcher-servlet.xml, applicationContext.xml, redirect.jsp, and index.jsp.

Running the Skeleton Project

Before making any changes to project files, try running the new project in the IDE:

  1. In the Projects window, right-click the project node and choose Run Project. The IDE automatically starts your server if it is not already running, builds then deploys the application to it. The application runs using the configuration data contained in dispatcher-servlet.xml. Note any output displayed in the IDE's Output window (Window > Output). The generated output completes with a BUILD SUCCESSFUL message:
    Output window displaying information when running the project
    The IDE's default browser starts up, and you see content from the welcome page view (/WEB-INF/jsp/index.jsp):
    Welcome page output displayed in browser

In order to understand what just took place, start by examining the project's deployment descriptor (web.xml). To open this file in the Source Editor, right-click the WEB-INF > web.xml node in the Projects window and choose Edit. The default entry point for the application is redirect.jsp:


redirect.jsp

Within redirect.jsp, there is a redirect statement that points all requests to index.htm:

<% response.sendRedirect("index.htm"); %>

In the deployment descriptor, note that all requests for URL patterns that match *.htm are mapped to Spring's DispatcherServlet:


dispatcher
org.springframework.web.servlet.DispatcherServlet
2



dispatcher
*.htm

The class of the dispatcher servlet, as shown above, is org.springframework.web.servlet.DispatcherServlet. This class is contained in the Spring libraries, which were added to the project classpath when the project was created. To verify this, in the Projects window drill down from the Libraries node:
DispatcherServlet class displayed in the Projects window

The DispatcherServlet handles incoming requests based on configuration settings found in dispatcher-servlet.xml. Open dispatcher-servlet.xml by clicking on its tab in the Source Editor. Note the following code:




indexController






Three beans are defined in this file: indexController, viewResolver, and urlMapping. When the DispatcherServlet receives a request that matches *.htm such as index.htm, it looks for a controller within urlMapping that can accommodate the request. Above, you see that there is a mappings property that links /index.htm to indexController.

The runtime environment then searches for the definition of a bean named indexController, which is conveniently provided by the skeleton project. Note that indexController extends ParameterizableViewController. This is another class provided by Spring, which simply returns a view. Above, note that p:viewName="index" specifies the logical view name, which is resolved using the viewResolver by prepending /WEB-INF/jsp/ and appending .jsp to it. This allows the runtime to locate the file within the WAR file of the application, and respond with the welcome page view (/WEB-INF/jsp/index.jsp).

Overview of the Application

The application you create is comprised of two JSP pages (which are named views in Spring Web MVC terminology). The first view contains an HTML form with an input field asking for the user's name. The second view is a page that simply displays a hello message containing the user's name.

The views are managed by a controller, which receives requests to the application and decides which views to return. It also passes to the views any information that they need to display (this is called a model). This application's controller is named HelloController.

In a complex web application, the business logic is not contained directly in the controller. Instead, another entity, named a service, is used by the controller whenever it needs to perform business logic. In our application, the business logic is the computation of the hello message, so for this purpose you create a HelloService.

Implementing a Service

Now that you are sure your environment is set up properly, you can begin extending the skeleton project according to your needs. Start by creating the HelloService class.

  1. In the Projects window, right-click the project node and choose New > Java Class.
  2. In the New Java Class wizard that displays, enter HelloService for Class Name, and type in service for Package Name to create a new package for the class. Click Finish. The IDE creates the new class and opens it in the Source Editor. Also note that the Projects window automatically updates to reflect changes.

The HelloService class performs a very simple service. It takes a name as a parameter, and prepares and returns a String that includes the name. In the Source Editor, create the following sayHello() method for the class (changes in bold):

public class HelloService {

public String sayHello(String name) {
return "Hello " + name + "!";
}

}

Implementing the Controller and Model

You can use a SimpleFormController to handle user data and determine which view to return.

  1. Open the New File wizard by pressing Ctrl-N (⌘-N on Mac). Under Categories select Spring Framework; under File Types select Simple Form Controller. Click Next.
  2. Name the class HelloController and create a new package for it by typing controller in the Package text field. Click Finish. The IDE creates the new class and opens it in the Source Editor.
  3. Specify controller properties by uncommenting the setter methods that display by default in the class template. Make changes as follows (changes in bold):
    public HelloController() {
    setCommandClass(Name.class);
    setCommandName("name");
    setSuccessView("helloView");
    setFormView("nameView");
    }
    Setting the FormView enables you to set the name of the view that is used to display the form. This is the page that contains the text field allowing users to enter their name. Setting the SuccessView likewise lets you set the name of the view that should display upon a successful submit. When you set the CommandName you define the name of the command in the model. In this case, the command is the form object with request parameters bound onto it. Setting the CommandClass allows you set the name of the command class. An instance of this class gets populated and validated upon each request.

    Note that an error is flagged for Name in the setCommandClass() method:
    The Source Editor displaying an error for setCommandClass()
    You need to create the Name class as a simple bean to hold information for each request.
  4. In the Projects window, right-click on the project node and choose New > Java Class. The New Java Class wizard displays. Enter Name for the Class Name, and for Package select controller from the drop-down list. Click Finish. The Name class is created and opened in the Source Editor.
  5. For the Name class, create a field named value, then create accessor methods for this field. Type in the following (changes in bold):
    public class Name {

    private String value;
    The IDE can create accessor methods for you. In the Source Editor, right-click on value and choose Insert Code (or press Alt-Insert; Ctrl-I on Mac) to open the Generate Code popup menu. Then choose Getter and Setter.
    The Generate Code popup menu displayed in Source Editor
  6. In the dialog that displays, select the value : String option, then click OK. The getValue() and setValue() methods are added to the Name class:
    public String getValue() {
    return value;
    }

    public void setValue(String value) {
    this.value = value;
    }
  7. Click back to HelloController in the Source Editor and notice the previous error badge has disappeared due to the fact that you created the Name class. Delete the doSubmitAction() method and uncomment the onSubmit() method. The onSubmit() method enables you to create your own ModelAndView, which is what is required here. Make the following changes:
    @Override
    protected ModelAndView onSubmit(Object command) throws Exception {
    Name name = (Name)command;
    ModelAndView mv = new ModelAndView(getSuccessView());
    mv.addObject("helloMessage", helloService.sayHello(name.getValue()));
    return mv;
    }
    As indicated above, the command is recast as a Name object. An instance of ModelAndView is created, and the success view is obtained using a getter in SimpleFormController. Finally, the model is populated with data. The only item in our model is the hello message obtained from the HelloService created earlier. You use the addObject() method to add the hello message to the model under the name helloMessage.
  8. Fix import errors by right-clicking in the Source Editor and choosing Fix Imports (Ctrl-Shift-I; ⌘-Shift-I on Mac). The following import statement is added to the top of the file:
    import org.springframework.web.servlet.ModelAndView;
    Not all errors are fixed however, because the class still cannot identify the HelloService class, nor make use of its sayHello() method.
  9. Within HelloController, declare a private field named HelloService:
    private HelloService helloService;
    Then create a public setter method for the field:
    public void setHelloService(HelloService helloService) {
    this.helloService = helloService;
    }
    Finally, right-click again in the Source Editor and choose Fix Imports. Note that the following statement is added to the top of the file:
    import service.HelloService;
    All errors should now be fixed.
  10. Register HelloService in applicationContext.xml. Open applicationContext.xml in the Source Editor and enter the following bean declaration:
    Spring support in the IDE includes code completion within XML configuration files for Java classes as well as bean references. To invoke code completion, press Ctrl-Space when working in the Source Editor:
    Code completion invoked when pressing Ctrl-Space
  11. Register HelloController in dispatcher-servlet.xml. Open dispatcher-servlet.xml in the Source Editor and enter the following bean declaration:

Implementing the Views

To implement the view for this project, you need to create two JSP classes. The first, which you will call nameView.jsp, serves as the welcome page and allows a user to input a name. The other page, helloView.jsp, displays a greeting message that includes the input name. Begin by creating helloView.jsp.

  1. In the Projects window, right-click the WEB-INF > jsp node and choose New > JSP. The New JSP File wizard opens. Name the file helloView. Click Finish. The new JSP file is created in the jsp folder and opens in the Source Editor.
  2. In the Source Editor, change the file's title to Hello, and change the output message to retrieve the helloMessage of the ModelandView object that is created in HelloController:


    <strong>Hello</strong>


    ${helloMessage}



  3. Create another JSP File in the same manner as above, but name it nameView.
  4. In the Source Editor, add the following tag library declaration to nameView.jsp:
    <%@taglib uri="http://www.springframework.org/tags" prefix="spring" %>
    This imports the Spring tag library, which contains tags useful when implementing views as JSP pages.
  5. Change the contents of the and

    tags to read: Enter Your Name.

  6. Enter the following code beneath the h2 tags:


    Name:






    spring:bind allows you to bind a bean property. The bind tag provides a bind status and value, which you use as the name and value of the input field. This way, when the form is submitted, Spring will know how to extract the submitted value. Here, our command class (controller.Name) has a value property, therefore you set the path to value.

    spring:nestedPath enables you to prepend a specified path to a bean. So, when used with spring:bind as shown above, the path to the bean becomes: name.value. As you recall, the command name of HelloController is name. Therefore, this path refers to the value property of a bean named name in the page scope.
  7. Change the relative entry point for the application. Currently, the project entry point is still index.htm which, as described in Running the Skeleton Project above, redirects to WEB-INF/jsp/index.jsp. You can specify an entry point for the project when it is deployed and run. In the Projects window, right-click the project node and choose Properties. The Project Properties dialog displays. Under Categories select Run. In the Relative URL field, type in /hello.htm, then click OK.

    At this moment you may wonder where the mapping of hello.htm to HelloController is located. You have not added a mapping to the urlMapping bean, as is the case for index.htm, the skeleton project's welcome page. This is possible with a bit of Spring magic provided by the following bean definition in dispatcher-servlet.xml:

    This bean is reponsible for automatically creating an URL mapping for all controllers registered in the file. It takes the fully-qualified class name of the controller (in our case, controller.HelloController) and strips the package name and Controller suffix, then uses the result as a URL mapping. Therefore, for HelloController it creates a hello.htm mapping. This magic however does not work for controllers that are included in the Spring Framework, such as ParameterizableViewController. They require an explicit mapping.
  8. In the Projects window right-click the project node and choose Run. This compiles, deploys and runs the project. Your default browser opens, displaying hello.htm as the project's nameView:
    nameView displayed in a browser
    Enter your name in the text field and click enter. The helloView displays with a greeting message:

LWUIT Tutorial

LWUIT Tutorial

Hello, LWUIT!

To get started right away, follow these steps.

  1. Download the LWUIT JAR file to your computer.
  2. Create a new project. In NetBeans, choose File > New Project... from the menu. Choose the Mobility category and MIDP Application type. Press Next >.

    Now enter a name for the project. Here I've used HelloLWUIT. Choose a location and make sure to uncheck Create Hello MIDlet. Press Next >.

    Finally, choose either Sun Java(TM) Wireless Toolkit 2.5.2 for CLDC or Sprint Wireless Toolkit 3.2 Powered by Sun Java Technology (what a mouthful!) for the Emulator Platform. Choose your favorite device. Press Finish to create the project.

  3. Add LWUIT to the project. In the project you just created is an item called Resources. If you don't see this, make sure you are looking at the Projects view, which is accessible from Window > Projects in the menu.

    Right-click on Resources and choose Add Jar/Zip Navigate to the LWUIT JAR file. When you are finished, you should see the JAR file in the Resources section when you expand it.

  4. Write a simple application. Right-click on your project and choose New > MIDlet from the menu. Enter a name, like HelloLWUITMIDlet, and press Finish.

    Here is the source code for a simple LWUIT MIDlet. Type it in or copy and paste it.


    import javax.microedition.midlet.*;

    import com.sun.lwuit.*;
    import com.sun.lwuit.events.*;

    public class HelloLWUITMidlet extends MIDlet implements ActionListener {
    public void startApp() {
    Display.init(this);

    Form f = new Form("Hello, LWUIT!");
    f.show();

    Command exitCommand = new Command("Exit");
    f.addCommand(exitCommand);
    f.setCommandListener(this);
    }

    public void pauseApp() {}

    public void destroyApp(boolean unconditional) {}

    public void actionPerformed(ActionEvent ae) {
    notifyDestroyed();
    }
    }
  5. Compile and run. In NetBeans, just press F6 to compile and run. A device emulator will pop up with your application. The text "Hello, LWUIT!" appears as the title of the form, and "Exit" is at the bottom of the screen. Congratulations!

    When you're finished admiring your work, press Exit or press F1 (the left soft button) to stop.


Working With Forms

You must initialize LWUIT before using it in your application. To do this, call Display's static init() method, passing in the application object. For example, in a MIDlet's startApp() method, initialize LWUIT like this:

Display.init(this);

Your application will interact with users by showing one or more forms on the screen. A form has a title, perhaps a menu bar, and can contain a wide variety of visual components. The following screen shot shows the three areas of a form.

HelloLWUITMIDlet creates a single, empty Form with a title, like this:

Form f = new Form("Hello, LWUIT!");

To show a Form on the screen, just call show().

f.show();

Filling Forms with Components

LWUIT offers a comprehensive toolbox for showing information on forms and receiving user input, including many familiar components like labels, buttons, and lists.

Simply create the compnents you want to use and pass them to the form's addComponent() method.

The simplest component is a label. A label can display text, an image, or both. Here are a few examples:

//Form f = ... Label label = new Label("Baldy"); f.addComponent(label); Image image = Image.createImage("/res/baldy.png"); Label pictureLabel = new Label(image); f.addComponent(pictureLabel); Label bottomText = new Label(image); bottomText.setText("Baldassare"); bottomText.setTextPosition(Component.BOTTOM); f.addComponent(bottomText);

Buttons are similar to labels but the user can "click" on them. The exact details of clicking depend on the device. On a device with a touch screen, the user can simply touch the button. On devices with four-way navigation, the user can move focus to the button, then press the select key.

Button inherits from Label. Here are a few examples:

//Form f = ... Button button = new Button("Baldy"); f.addComponent(button); Image image = Image.createImage("/res/baldy.png"); Button pictureButton = new Button(image); f.addComponent(pictureButton); Button combined = new Button("Baldassare", image); combined.setTextPosition(Component.BOTTOM); f.addComponent(combined);

Later in this tutorial, you'll learn how to respond to button events. The button takes care of showing visual cues to indicate focus and pressing.

Subclasses of Button represent checkboxes and radio buttons. ButtonGroup is used to manage groups of exclusive radio buttons.

The following example shows one way to add checkboxes and radio buttons to a form. It uses a layout to keep the components neatly lined up. You'll read about layouts later in this tutorial.

Form f = new Form("More Buttons"); f.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); RadioButton rb; ButtonGroup group = new ButtonGroup(); rb = new RadioButton("Grilled chicken"); group.add(rb); f.addComponent(rb); rb = new RadioButton("Filet mignon"); group.add(rb); f.addComponent(rb); rb = new RadioButton("Mahi mahi"); group.add(rb); f.addComponent(rb); rb = new RadioButton("Chili"); group.add(rb); f.addComponent(rb); CheckBox cb; cb = new CheckBox("Guacamole"); f.addComponent(cb); cb = new CheckBox("Tabasco sauce"); f.addComponent(cb); cb = new CheckBox("Mango salsa"); f.addComponent(cb); cb = new CheckBox("Mayonnaise"); f.addComponent(cb); cb = new CheckBox("Whipped cream"); f.addComponent(cb);

Two components for showing lists of items are List and ComboBox. Both use a ListModel to contain the data that is displayed. You can use the default list model, which is sufficient for many applications, or you can create your own. Like Swing, LWUIT uses the Model-View-Controller paradigm for a clean separation of the data, its presentation, and user interactions.

This example shows how to create a simple list. Behind the scenes, a ListModel is created automatically. The combo box uses the same ListModel. When you make a selection in the list or the combo box, the other component is automatically updated to match.

//Form f = ... List list = new List(); list.addItem("uno"); list.addItem("due"); list.addItem("tre"); list.addItem("quattro"); list.addItem("cinque"); list.addItem("sei"); f.addComponent(list); ComboBox comboBox = new ComboBox(list.getModel()); f.addComponent(comboBox);

Another commonly used component is a TextArea, which displays text that users can edit. Text is displayed directly on your form, but entry will be done in a separate screen. LWUIT provides editing on a separate screen to take advantage of whatever predictive text mechanisms are available on the device. This happens transparently as far as your application is concerned. All your application has to do is add a TextArea to a form. LWUIT takes care of handling user interactions for editing.

This example shows how to create a simple text area (more like a field) by specifying the initial text. It also shows creating a larger text area by specifying the number of visible rows and columns.

//Form f = ... TextArea area = new TextArea("Peppino"); f.addComponent(area); TextArea big = new TextArea("On February 24, 1815, the lookout at " + "Notre-Dame de la Garde signalled the arrival of the three-master " + "Pharaon, coming from Smyrna, Trieste and Naples.", 5, 20); f.addComponent(big);

LWUIT includes several other components which are not described in this tutorial. Consult the API documentation for more information.

  • Calendar displays a calendar and allows the user to choose a day.
  • TabbedPane is a special container which represents panes of information with selectable tabs.
  • MediaComponent is used to display rich media content.


Using Layouts

The form takes care of positioning the components. It uses a layout manager to decide where everything goes. For you AWT and Swing jocks, this should sound familar. Layout managers are great for mobile applications because they are good at adjusting to different screen sizes and filling the available space gracefully.

LWUIT includes five layout managers.

  • FlowLayout places components from left to right in rows, just like English text.
  • BorderLayout has a large center area and four smaller areas around the edges.
  • BoxLayout arranges components in a single row or column.
  • GridLayout places components in a grid.
  • More complex layouts can be accomplished using GroupLayout and its helper class Group.

You can build user interfaces that combine layouts by creating Containers that group components with a specific layout and then nesting different Containers together to create a complete user interface.

A variant of Container's addComponent() method allows you to provide a hint, or constraint, that is used when determining the position of each component. For example, the five regions of a BorderLayout are represented by the static variables NORTH, SOUTH, EAST, WEST, and CENTER. To add a component to the WEST (left) region, you would do this:

// Form f = ... // Componeont c = ... f.setLayout(new BorderLayout()); f.addComponent(BorderLayout.WEST, c);

This example shows how to combine a BorderLayout and a BoxLayout. The CENTER region of the BorderLayout contains the main content (an image) while the SOUTH region contains a button bar in an X_AXIS BoxLayout. Any components or containers placed in the center region stretch to fill the available space. In this case, the label is center-aligned so that the image appears in the center.

// Form f = ... f.setLayout(new BorderLayout()); Image image = Image.createImage("/res/baldy.png"); Label bottomText = new Label(image); bottomText.setAlignment(Component.CENTER); bottomText.setText("Baldassare"); bottomText.setTextPosition(Component.BOTTOM); Container buttonBar = new Container(new BoxLayout(BoxLayout.X_AXIS)); buttonBar.addComponent(new Button("Add")); buttonBar.addComponent(new Button("Remove")); buttonBar.addComponent(new Button("Edit")); buttonBar.addComponent(new Button("Send")); buttonBar.addComponent(new Button("Exit")); f.addComponent(BorderLayout.CENTER, bottomText); f.addComponent(BorderLayout.SOUTH, buttonBar);


Using Transitions






Many applications use more than one form. For example, a messaging application might use one screen to show incoming messages, one screen for composing messages, and one screen to display an address book.

To show more than one form in LWUIT, simply create Forms and call each show() method when you want to display the corresponding form.

A transition provides a helpful visual cue to the user about the relationship between different forms. LWUIT can slide forms horizontally or vertically. LWUIT also includes a gradual fade from one form to the next. On devices with the JSR 184 Mobile 3D Graphics API, LWUIT can also perform some three dimensional transitions.

If LWUIT's transitions don't meet your needs, you can write your own transition.

CommonTransitions contains static factory methods for creating slide and fade transitions.

  • createSlide() creates a transition that slides a new form onto the screen while sliding the current form off. Sliding can be up, down, left, or right.
  • createFade() makes a transition that smoothly fades from the current from to a new one.

For devices that support the JSR 184 Mobile 3D Graphics API, LWUIT offers static methods in Transition3D for three additional types of transitions.

  • createFlyIn() zooms the new form in from the center to fill the entire screen.
  • createCube() gives the illusion that the current form is the front face of a cube. The cube rotates to show another face which is the new form.
  • createRotation() flips the current form like a playing card. The new form is revealed on the other side.

You can set an incoming and outgoing transition for each form. The incoming transition is used when the form will be displayed. The outgoing transition is used when the form will be replaced by another.

The following example sets up two forms with a single button on each. Press the button to switch between the forms using slide transitions.

The slides transitions are created by specifying whether the slide is horizontal or vertical, a direction (true for forward, false for reverse), and the time in milliseconds.

In the example, the first form's outgoing transition is horizontal, backward (meaning that the form will slide off to the left), and 200 milliseconds long. The second form's outgoing transition is the same, but forward, which means it slides off to the right.

import javax.microedition.midlet.*;

import com.sun.lwuit.*;
import com.sun.lwuit.animations.*;
import com.sun.lwuit.events.*;

public class TransitionMIDlet extends MIDlet implements ActionListener {
private Form mFirstForm, mSecondForm;

public void startApp() {
if (mFirstForm == null) {
Display.init(this);

mFirstForm = new Form("First Form");

Button button = new Button("Switch");
mFirstForm.addComponent(button);
button.addActionListener(this);

mFirstForm.setTransitionOutAnimator(
CommonTransitions.createSlide(
CommonTransitions.SLIDE_HORIZONTAL, false, 200));

mSecondForm = new Form("Second Form");

Button button2 = new Button("Switch");
mSecondForm.addComponent(button2);
button2.addActionListener(this);

mSecondForm.setTransitionOutAnimator(
CommonTransitions.createSlide(
CommonTransitions.SLIDE_HORIZONTAL, true, 200));

mFirstForm.show();
}
}

public void pauseApp() {}

public void destroyApp(boolean unconditional) {}

public void actionPerformed(ActionEvent ae) {
Form current = Display.getInstance().getCurrent();
if (current == mFirstForm) {
mSecondForm.show();
} else if (current == mSecondForm) {
mFirstForm.show();
}
}
}

Event Handling

Responding to Button Clicks

LWUIT notifies your application about events via listener interfaces. Your application provides implementations of the appropriate interface, registers them with LWUIT, and takes action when an event occurs.

To respond to a button press, for example, your application needs to do two things:

  1. Define an implementation of ActionListener.
  2. Pass an instance of the ActionListener to the Button's addActionListener().

The following example uses an inner class to implement ActionListener. Every time you press the button, the label changes.

final Form f = new Form("Events");

Button button = new Button("Push me!");
final Label countLabel = new Label(" ");
button.addActionListener(new ActionListener() {
private int c = 0;
public void actionPerformed(ActionEvent ae) {
c++;
countLabel.setText(Integer.toString(c));
f.layoutContainer();
}
});

The call to layoutContainer() ensures that the label's size is adjusted correctly when the number gets bigger. For example, notice how the size adjusts when the count goes from 9 to 10.

If you use a single listener for more than one button, you can find out which button was pressed by calling the ActionEvent's getSource() method.

Using Commands

LWUIT includes the concept of a command. A command is something that your user can do in your application. LWUIT decides how to show the command on the screen and how the user makes it happen. Usually, commands are assigned to the soft buttons on a mobile phone. LWUIT will normally show commands as labels at the bottom corners of the screen; the user can invoke them by pressing the corresponding soft button. On touch devices, the user can touch directly on a command to invoke it.

Conceptually, commands in LWUIT closely resemble commands in MIDP. Some of the details are slightly different, but if you already understand MIDP commands, you'll have no trouble with LWUIT commands.

In LWUIT, create commands and add them to forms. Then register a listener object with the form to be notified when the user invokes a command.

This simple example creates a form and registers a listener. The listener shuts down the application when it is invoked.

import javax.microedition.midlet.*;

import com.sun.lwuit.*;
import com.sun.lwuit.events.*;

public class HelloLWUITMidlet extends MIDlet implements ActionListener {

private Form mForm;

public void startApp() {
if (mForm == null) {
Display.init(this);

Form f = new Form("Hello, LWUIT!");

Command exitCommand = new Command("Exit");
f.addCommand(exitCommand);
f.setCommandListener(this);

f.show();
}
}

public void pauseApp() {
}

public void destroyApp(boolean unconditional) {
}

public void actionPerformed(ActionEvent ae) {
destroyApp(true);
notifyDestroyed();
}


}

If you add more than two commands to a form, they won't fit on the two available soft buttons. In this case, one of the soft buttons will become a menu. When the user invokes the menu, it shows a list of the rest of the commands.

When your listener's actionPerformed() method must respond to more than one command, you can find out which one was invoked by calling the ActionEvent's getCommand() method.

Other Event Types

Most of your event handling will involve ActionListener for commands and buttons, but LWUIT includes other useful listeners:

  • Use DataChangedListener to find out when a ListModel changes. Remember, ListModel represents the data behind a list or combo box.
  • A SelectionListener is notified when the current selection of a ListModel changes.
  • A FocusListener is notified when a component gains or loses focus.
  • Finally, StyleListener receives notifications about changes in a component's style. The next section of this tutorial describes styles and themes.

Styles and Themes

Changing Components One-by-one

The visual appearance of every LWUIT component is determined, in part, by an associated style object. To make changes to an individual component's appearance, you can retrieve the style object and call methods to make changes.

Here is a simple example which modifies the style of some labels.

Label first = new Label("No style changes");
f.addComponent(first);
Label second = new Label("Transparent");
second.getStyle().setBgTransparency(0);
f.addComponent(second);
Label third = new Label("Purple");
Style style = third.getStyle();
style.setBgColor(0xaa00ff);
style.setFgColor(0xffffff);
f.addComponent(third);

The second label has had its background set to be fully transparent. The third label is set to have a purple background. Other types of changes are possible, like fonts and padding. See the Style documentation for the full details.

Changing All Components with Themes

Using style objects is fine for changing components individually, but if you'd like to change the appearance of all components in your application, you will need to use a theme.

A theme is simply a collection of properties, things like colors, fonts, and images. You can load a theme from a resource file and apply to update all the components in your application. Typically you would do this when you are intializing with code like this:

try {
Resources r = Resources.open("/res/javaTheme.res");
UIManager.getInstance().setThemeProps(r.getTheme("javaTheme"));
} catch (IOException ioe) {
System.out.println("Couldn't load theme.");
}

>

LWUIT includes a desktop tool for editing and creating themes called ResourceEditor. You can use this tool to edit themes and view their general appearance in real time.

For ultimate control of how components are drawn, you can create a custom look and feel that affects every aspect of component drawing. For more information, consult the Developer's Guide and the API documentation for LookAndFeel.





Tuesday, March 24, 2009

Spring Webflow Tutorial

Tutorial: Build a Shopping Cart with Spring Web Flow 2.0

This step-by-step tutorial shows you how to build a shopping cart using the new Spring Web Flow 2.0 framework.

This article shows you how to get started with Spring Web Flow (SWF) 2.0 by building a simple shopping cart application. We'll do this as a series of steps:

  1. First we'll just get Spring MVC working.
  2. We'll use Spring Web Flow to create a minimal flow with a single state.
  3. Finally we'll build the app out to contain multiple flows and subflows that make calls against backend logic.

SWF 2.0 is at the time of this writing very new, and there are several differences as compared to SWF 1.0.x. We will not however spend much time discussing those differences; instead we'll just focus on SWF 2.0.

To get the most out of this article, you should already be familiar with Spring in general and Spring MVC in particular.


An Overview of Spring Web Flow

Spring Web Flow builds upon Spring MVC to support user-level, application-directed control flows. For example, an e-commerce application may need to guide the end user through a checkout process that spans several web pages. The flow is not simply a multipage form (Spring MVC itself already supports that); rather it is a multistep process involving standard control flow constructions such as decisions ("please confirm or cancel your order"), loops ("we recommend products x, y, z... add as many as you like to your order") and subflows ("are you a new customer? please create an account"). In general, implementing such flows is not trivial. SWF is an elegant solution to just this sort of problem.

To understand better, contrast application-directed interactions with the user-directed interactions that typically constitute the main part of an application's functionality. In user-directed interactions, the end user picks some function from a set of available functions, provides some parameters around that function, and then asks the application to carry it out. For instance, the end user tells Photoshop to fill a selected region with "evil green" (RGB 00FF00). The app dutifully does just that, and control returns to the end user.

There are many cases, however, in which we want the application to drive a complex interaction between itself and the end user. Perhaps the most obvious example is the one I mentioned above: the e-commerce checkout process. See Figure 1 for one possible flow (and in fact this is the flow we're going to implement):

Figure 1. A sample checkout flow.
Figure 1. A sample checkout flow.

In the checkout flow above, we have a starting state, an end state, and several intermediate states. In the first state, we show the customer the contents of a shopping cart, along with some recommended products that the customer may want to buy. The customer can add any of those products to the shopping cart, or he might decide to move forward by indicating whether he is a new or returning customer. If he's a new customer, he'll need to create an account; otherwise he can just log in with his existing account. In either case, he'll need to select a payment method, provide shipping information, and so forth. At any step along the way he can cancel out of the checkout process.

Benefits of using Spring Web Flow

While you can certainly implement that flow without SWF, there are some challenges involved if you're doing it from scratch. Let's take a look at some of those.

Understanding flow logic. For one, because the logic behind the flow itself is reasonably complex, it would be nice to be able to isolate the flow and its logic from the various pieces (like JSP pages, business logic, etc.) that make it up. But the most straightforward implementation of flow logic would most likely involve distributing the flow logic (such as state transitions) across lots of different files. So one challenge is that you either have to do a lot of extra work to externalize the flow logic, or else you have to live with that logic being distributed, which makes it much harder to understand.

Reusing flow logic. Another challenge is related to the fact that a flow has a logical structure that stands on its own, and in many cases you'd like to be able to reuse that—either across multiple apps, or else in multiple places within a single app. This is hard to accomplish without an easy way to isolate that flow.

Getting the implementation right. A third challenge is just that it's easy to get the technical details wrong. For instance, what happens if the end user hits the browser's back button in the middle of the flow? If the previous page had a form, we don't usually want the browser to confuse the user with a warning about resubmitting data or whatever. Coding up flows from scratch means that you have to handle this sort of thing explicitly. It would be nice not to have to mess around with this kind of thing—to have it handled automatically for you.

That's enough of an overview for us to get started. Let's now look at setting up our sample project.


Spring MVC Setup

Since Spring Web Flow is build on Spring MVC, let's start by getting Spring MVC working. We'll create a simple home page for our shopping cart, and we'll serve this up using a plain old Spring controller (well, it will be annotated) rather than serving it using Spring Web Flow. That's because the home page itself isn't part of any particular flow; it simply provides entry points into various flows, such as creating an account, logging in, adding an item to a shopping cart, and checking out. Besides allowing us to make sure we have Spring MVC working before moving forward, this approach will also allow us to see how to integrate Spring MVC and Spring Web Flow.

For your convenience, here's a download of the minimalistic (i.e. only Spring MVC, no SWF) shopping cart we're about to take a look at:

The download above does not include its dependencies. You will need to grab those separately. I've provided the links below.

Dependencies for mycart1.zip

These are all part of the Spring 2.5.4 distribution. Spring Web Flow 2.0 requires Spring 2.5.4 or higher. [download]

  • spring.jar (located in /spring-framework-2.5.4/dist)
  • spring-webmvc.jar (located in /spring-framework-2.5.4/dist/modules)
  • commons-logging.jar (located in /spring-framework-2.5.4/jakarta-commons)

We will be adding dependencies as we progress; for the moment we're just getting Spring MVC set up.

Create a Spring MVC Controller

Here's a very simple Spring MVC controller. We'll be updating this over the course of the article.

Code listing: mycart.CartController
  1. package mycart;

  2. import org.springframework.stereotype.Controller;
  3. import org.springframework.web.bind.annotation.RequestMapping;

  4. @Controller
  5. public class CartController {

  6. @RequestMapping("/home.do")
  7. public void doHome() {
  8. }
  9. }

This controller doesn't do much at all. Basically we're using annotations to map /home.do requests to a JSP. For more details on how that works, please see my article Annotation-Based MVC in Spring 2.5.

Create a JSP

Here's the home page JSP I just mentioned. Like CartController, we'll be updating this.

Code listing: /WEB-INF/jsp/home.jsp

  1. "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  2. <html>
  3. <head>
  4. <title>Products for Geeks - GeekWareztitle>
  5. head>
  6. <body>
  7. <h1>Welcome to GeekWarezh1>
  8. body>
  9. html>

Create web.xml

Here's our web.xml file:

Code listing: /WEB-INF/web.xml
  1. xml version="1.0" encoding="UTF-8"?>
  2. <web-app xmlns="http://java.sun.com/xml/ns/javaee"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
  5. http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
  6. version="2.5">

  7. mycart org.springframework.web.servlet.DispatcherServlet mycart *.do

All we're doing here is creating the Spring MVC DispatcherServlet front controller. Because we've named it mycart, the default behavior for DispatcherServlet is to look for a Spring application context configuration file at /WEB-INF/mycart-servlet.xml, which we are about to see.

Eventually this front controller will handle not only our "normal" non-SWF requests, but also our SWF requests. However I'm getting ahead of myself.

Create the Spring application context file

Here's mycart-servlet.xml, which DispatcherServlet loads as just explained:

Code listing: /WEB-INF/mycart-servlet.xml
  1. xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:context="http://www.springframework.org/schema/context"
  4. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5. xsi:schemaLocation="http://www.springframework.org/schema/beans
  6. http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
  7. http://www.springframework.org/schema/context
  8. http://www.springframework.org/schema/context/spring-context-2.5.xsd">



  9. <context:annotation-config/>

  10. <context:component-scan base-package="mycart"/>


  11. <bean class=
  12. "org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>


  13. <bean class=
  14. "org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>


  15. <bean id="viewResolver" class=
  16. "org.springframework.web.servlet.view.InternalResourceViewResolver">
  17. <property name="prefix" value="/WEB-INF/jsp/"/>
  18. <property name="suffix" value=".jsp"/>
  19. bean>
  20. beans>

Nothing special here assuming you already know Spring MVC.

Milestone 1: Spring MVC is Working

At this point you should be able to deploy the application. Point your browser at

http://localhost:8080/mycart1/home.do

and you should get a very simple home page. If so, congratulations, Spring MVC is working.

Now it's time to create our first flow using Spring Web Flow.


Creating a Simple Flow: Spring Application Context Configuration

Now we have Spring MVC working, so it's time to expand on that and get Spring Web Flow working too. We'll start by creating a very simple flow—one that has only a single view state. First we'll look at the updated Spring application context configuration. After that we'll look at the flow definition itself.

To make it easier for you to follow along, here's the updated, one-state version of our shopping cart:

Dependencies for mycart2.zip

Because we're now using SWF, we have additional dependencies. Here's the full set so far:

These come from the Spring 2.5.4 distribution. [download]

  • spring.jar (located in /spring-framework-2.5.4/dist)
  • spring-webmvc.jar (located in /spring-framework-2.5.4/dist/modules)
  • commons-logging.jar (located in /spring-framework-2.5.4/jakarta-commons)

You will need the following JARs from the Spring Web Flow 2.0 distribution. [download]

  • spring-webflow-2.0.0.jar
  • spring-binding-2.0.0.jar
  • spring-js-2.0.0.jar

You will need the following JAR from the OGNL web site:

  • ognl-2.6.9.jar

Spring Web Flow uses either OGNL or Unified EL for parsing expressions. For this article I arbitrarily picked OGNL though you can use jboss-el.jar (currently the only Unified EL implementation that will work with SWF) too.

web.xml and mycart.CartController

For now we aren't making any changes to either of these at all.

Figure 2. Overview of SWF components.
Figure 2. Overview of SWF components.

Spring application context overview

Before jumping into the code, let's do a quick overview of the various components involved. Please see Figure 2.

We already saw DispatcherServlet in web.xml. (So it's not part of mycart-servlet.xml; we're discussing it here just to show the relationship between the servlet and the FlowController.) DispatcherServlet is the Spring MVC front controller and it receives any Spring MVC requests—including SWF requests, as SWF is based on Spring MVC—according to your web.xml configuration.

We're using SimpleUrlHandlerMapping to map flow requests from DispatcherServlet to FlowController.

So now FlowController has the request. FlowController is just a Spring MVC controller that receives flow requests and passes them to FlowExecutor for actual processing.

FlowExecutor contains the actual logic for processing Spring Web Flow requests. Among other things it determines for any given request which flow is involved and figures out what state transition to enact, based on the request.

When FlowExecutor needs a flow, it grabs the flow from the FlowRegistry, which is responsible for loading the flow from a flow configuration file and maintaining it on behalf of the FlowExecutor.

FlowBuilderServices is just a container for various services that FlowRegistry needs when constructing flows. This includes, for example, a service to provide for the creation of view factories. In our example, we will see that we're specifically using a MvcViewFactoryCreator, which creates view factories for Spring MVC views.

Finally, we define a ViewResolver, which is a Spring MVC interface that carries logical view names to physical resources (such as JSPs). For example, a ViewResolver might carry the logical name checkout/viewcart to the physical resource /WEB-INF/jsp/checkout/viewcart.jsp.

Spring application context configuration file

And finally here's the configuration file itself:

Code listing: /WEB-INF/mycart-servlet.xml
  1. xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:context="http://www.springframework.org/schema/context"
  4. xmlns:flow="http://www.springframework.org/schema/webflow-config"
  5. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  6. xsi:schemaLocation="http://www.springframework.org/schema/beans
  7. http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
  8. http://www.springframework.org/schema/context
  9. http://www.springframework.org/schema/context/spring-context-2.5.xsd
  10. http://www.springframework.org/schema/webflow-config
  11. http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.0.xsd">






  12. <context:annotation-config/>

  13. <context:component-scan base-package="mycart"/>


  14. <bean class=
  15. "org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>


  16. <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
  17. <property name="mappings">
  18. <value>
  19. /account/register.do=flowController
  20. value>
  21. property>
  22. <property name="alwaysUseFullPath" value="true"/>
  23. bean>


  24. <bean class=
  25. "org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>


  26. <bean class=
  27. "org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>


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




  33. <bean id="flowController" class=
  34. "org.springframework.webflow.mvc.servlet.FlowController">
  35. <property name="flowExecutor" ref="flowExecutor"/>
  36. bean>

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


  38. <flow:flow-registry id="flowRegistry"
  39. flow-builder-services="flowBuilderServices">
  40. <flow:flow-location path="/WEB-INF/flows/register.xml"/>
  41. flow:flow-registry>

  42. <flow:flow-builder-services id="flowBuilderServices"
  43. view-factory-creator="viewFactoryCreator"/>

  44. <bean id="viewFactoryCreator" class=
  45. "org.springframework.webflow.mvc.builder.MvcViewFactoryCreator">
  46. <property name="viewResolvers">
  47. <list>
  48. <ref bean="viewResolver"/>
  49. list>
  50. property>
  51. bean>
  52. beans>

Now let's look at our basic flow definition, which will initially at least be much simpler than the application context file.


Creating a Simple Flow: Flow Definition File and JSPs

We'll now add a SWF flow definition file, update our original home page JSP, and add a new JSP for registering as a new customer.

The flow definition file

Here's a definition for our first flow, which will be a bare-bones registration process. We'll add more flows to the application but this is our starting point just to get SWF working. You'll need to create a directory /WEB-INF/flows, and then add the following flow definition file to that directory, naming the file register.xml.

Code listing: /WEB-INF/flows/register.xml
  1. xml version="1.0" encoding="UTF-8"?>
  2. <flow xmlns="http://www.springframework.org/schema/webflow"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://www.springframework.org/schema/webflow
  5. http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">


  6. <view-state id="register" view="account/registerForm">
  7. <transition on="submitRegistration" to="accountAdded"/>
  8. <transition on="cancelRegistration" to="cancelRegistration"/>
  9. view-state>

  10. <end-state id="accountAdded"
  11. view="externalRedirect:contextRelative:/home.do"/>
  12. <end-state id="cancelRegistration"
  13. view="externalRedirect:contextRelative:/home.do"/>
  14. flow>

Here we have a single state, called a view state, and all it does is show us the hardcoded JSP that we created earlier. We're identifying the state itself as register, and the logical view name associated with this view state is account/registerForm. This is the name that the view resolver we defined in the Spring app context file will map to a physical location; in this case the physical location will be /WEB-INF/jsp/account/registerForm.jsp.

By default, SWF interprets the first state in the file as being the start state, or entry point into the flow. There must be exactly one start state. The flow itself is called register (this is because we've named the flow definition file register.xml). As it happens we've also named the first state register though there's no requirement for the start state to have the same name as the flow.

I've defined a couple of transitions out of the register state. One transition, submitRegistration, responds to registration submission events on the user interface, such as the user clicking a submit button on a registration form. The other transition, cancelRegistration, responds to cancelation events on the UI, such as the user clicking a cancel link. Each of these transitions leads to another state in the flow. In this case, both of the transitions lead to end states, which are exits out of the flow, but transitions can lead to other view states (or even other sorts of state) as well. As shown there can be multiple end states for a flow. Here my end states happen to be doing the same thing; they're redirecting the user to a context-relative (as in relative to the servlet context) path /home.do, which you will recall is just the home page. There are some other relativizations you can do as well:

Relativization Example
Servlet mapping-relative externalRedirect:/hotels/index
Servlet-relative path externalRedirect:servletRelative:/hotels/index
Context-relative path externalRedirect:contextRelative:/dispatcher/hotels/index
Server-relative path externalRedirect:serverRelative:/springtravel/dispatcher/hotels/index
Absolute URL externalRedirect:http://www.paypal.com/
Table 1. Relativizations for externalRedirect.

Anyway we're doing the context-relative redirection in this case. This is how we jump out of SWF and return control to the application. (Or how we return control to a calling flow, but we'll get to that.)

The JSPs

Now let's revisit our home page JSP. It's still pretty similar but I'm adding a registration link.

Code listing: /WEB-INF/jsp/home.jsp

  1. "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  2. <html>
  3. <head>
  4. <title>Products for Geeks - GeekWareztitle>
  5. head>
  6. <body>
  7. <h1>Welcome to GeekWarezh1>

  8. <div><a href="account/register.do">Registera>div>
  9. body>
  10. html>

The registration link takes us into the register flow, since the last path segment is register.do. In our Spring app context we told the flow registry about register.xml, so SWF will know to map requests for /account/register.do to the register flow.

Now we need a registration form. The following page is our current version of a registration "form", but as you can see it isn't really a form at all (yet). It is just a couple of links:

Code listing: /WEB-INF/jsp/account/registerForm.jsp

  1. "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  2. <html>
  3. <head>
  4. <title>Register - GeekWareztitle>
  5. head>
  6. <body>
  7. <h1>Registerh1>

  8. <div>
  9. <a href="${flowExecutionUrl}&_eventId=submitRegistration">Submita> |
  10. <a href="${flowExecutionUrl}&_eventId=cancelRegistration">Cancela>
  11. div>
  12. body>
  13. html>

The point of the two links is to give the user a way to generate submitRegistration and cancelRegistration events. We saw in the flow definition that these two events trigger state transitions. Of special importance is the URL for each of these links. Notice that by using ${flowExecutionUrl}, we're still pointing the user at the same /account/register.do servlet path. That's because we're still working within the register flow. The ${flowExecutionUrl} URL includes an execution HTTP parameter whose value is a key that SWF uses for flow-tracking purposes. In addition we add an _eventId parameter that we use to drive state transitions. The event IDs are what we're referencing when we define transitions in the flow definition file.

So really so far all we can do at this point is move back and forth between the home page and the registration page, but we're doing it with Spring Web Flow.

Milestone 2: Spring Web Flow is Working

Point your web browser at

http://localhost:8080/mycart2/home.do

making any adjustments you need to make for the port or application context. Also note that the context is now mycart2 instead of mycart1 like it was in the first version of the code. If you're able to click back and forth between the home page and the registration page, then congrats, Spring Web Flow 2.0 is working! Celebrate!


Building Out the Application

Now that we've gotten Spring MVC and SWF working, it's time to build out the shopping cart application. Please download the following file before continuing:

We won't be looking at every aspect of this application, but I'll highlight the most interesting pieces from a SWF point of view.

Dependencies for mycart3.zip

Besides the dependencies we had for mycart2.zip, there are four others you'll need:

After you download mycart3.zip (and its dependencies), you can deploy it and then point your browser at

http://localhost:8080/mycart/home.do

Here are some notes to keep in mind:

  • My registration and login flows don't actually "do" anything. They're not hooked up to Spring Security and they're not hooked up to a database. So when you register you can just hit the submit button, and when you log in you can just hit the login button. I just wanted to focus on the flows themselves.
  • I'm using Sitemesh to ensure a unified layout across the pages. Even if you are not familiar with Sitemesh, don't worry: it is very straightforward. Basically you just put a servlet filter in front of the pages and the filter decorates the pages with a template (/WEB-INF/jsp/pagetemplate.jsp in this case) according to a simple configuration file (/WEB-INF/decorators.xml). If however you don't want to use Sitemesh, simply remove the filter from web.xml and Sitemesh is gone.

Multiple flows

Any given app may contain multiple flows, and mycart3 is such an app. We have four different flows:

  • addToCart: add an item to a shopping cart
  • checkout: shopping cart checkout process
  • login: log into the app
  • register: register for a new user account

In some of the cases we have what we would intuitively consider a flow in that there are multiple states involved; in other cases there is only one state to speak of and so it may seem strange to regard the flow as being a flow. However we'll see how that can make sense in the following section.

To create multiple flows, you will need to do the following. First, create the flow definition files and put them in your /WEB-INF/flows directory (or wherever you decided to put them). Then add the flow locations to the flow registry, and the flow URL mappings to your SimpleUrlHandlerMapping (if that's what you're using), in your Spring application context, like so:

Code listing: /WEB-INF/mycart-servlet.xml
  1. ...

  2. <bean id="flowUrlMappings" class=
  3. "org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
  4. <property name="mappings">
  5. <value>
  6. /addToCart.do=flowController
  7. /checkout.do=flowController
  8. /account/login.do=flowController
  9. /account/register.do=flowController
  10. value>
  11. property>
  12. <property name="alwaysUseFullPath" value="true"/>
  13. bean>

  14. <flow:flow-registry id="flowRegistry" flow-builder-services="flowBuilderServices">
  15. <flow:flow-location path="/WEB-INF/flows/addToCart.xml"/>
  16. <flow:flow-location path="/WEB-INF/flows/checkout.xml"/>
  17. <flow:flow-location path="/WEB-INF/flows/login.xml"/>
  18. <flow:flow-location path="/WEB-INF/flows/register.xml"/>
  19. flow:flow-registry>

  20. ...

Once you have those in place you should be set up for multiple flows. Point your links at them and try them out!


Subflows

Going hand-in-hand with the idea of multiple flows is the idea that some flows might be subflows of other flows. In mycart3, all four flows can be independently accessed, but in addition to that we have the addToCart, login and register flows being subflows to the checkout flow. See Figure 3.

Figure 3. Subflows.
Figure 3. Subflows.

Here's the idea. The three flows we've identified as subflows are defined as separate flows because clearly there are use cases where it makes sense for them to be accessed outside of a checkout process. For example, we want to be able to add products to a shopping cart while we're browsing the product catalog. And of course we want people to be able to register and login even if they're not in a checkout process.

But those are also flows that we might want to include as part of a checkout process too. During checkout, we might want to recommend products to the customer. Or if the user hasn't yet logged in or registered, we'd want them to do that as part of the checkout process rather than forcing them to do that before they could enter the checkout process.

By defining the login, registration and add-to-cart as flows, we make them available both independently (as top-level flows) and also as part of a larger flow (like the checkout flow). That's why we have these defined as flows even though they are currently implemented as single-state flows. (It is however easy to imagine these being multi-state flows. For example, the login flow may ask you to answer a challenge question if it detects that you're coming in from an unusual IP address, or the add-to-cart flow may ask you to enter a quantity before proceeding.)

So here is the checkout flow:

Code listing: /WEB-INF/flows/checkout.xml
  1. xml version="1.0" encoding="UTF-8"?>
  2. <flow xmlns="http://www.springframework.org/schema/webflow"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://www.springframework.org/schema/webflow
  5. http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">


  6. <on-start>
  7. <set name="flowScope.products" value="cartService.products"/>
  8. <set name="flowScope.shippingOptions" value="cartService.shippingOptions"/>
  9. on-start>


  10. <view-state id="viewCart" view="viewcart">

  11. <set name="requestScope.shoppingCart" value="shoppingCart"/>
  12. <set name="requestScope.recommendations" value="cartService.recommendations"/>
  13. on-render>
  14. <transition on="addToCart" to="addProductToCart"/>
  15. <transition on="register" to="register"/>
  16. <transition on="login" to="login"/>
  17. view-state>

  18. <subflow-state id="addProductToCart" subflow="addToCart">

  19. <subflow-state id="register" subflow="register">
  20. <transition on="accountAdded" to="paymentAndShipmentOptions"/>
  21. <transition on="cancelRegistration" to="viewCart"/>
  22. subflow-state>


  23. <subflow-state id="login" subflow="login">

  24. <view-state id="paymentAndShipmentOptions" view="options">
  25. <transition on="submit" to="confirmOrder"/>
  26. <transition on="back" to="viewCart"/>
  27. view-state>


  28. <view-state id="confirmOrder" view="confirmorder">
  29. <on-render>
  30. <set name="requestScope.shoppingCart" value="shoppingCart"/>
  31. on-render>
  32. <transition on="continue" to="thankYou">
  33. <evaluate expression="cartService.submitOrderForPayment()"/>
  34. transition>
  35. view-state>


  36. <view-state id="thankYou" view="thanks">
  37. <transition on="continue" to="shop"/>
  38. view-state>


  39. <end-state id="shop" view="externalRedirect:contextRelative:/home.do"/>

  40. <global-transitions>
  41. <transition on="cancelCheckout" to="shop"/>
  42. global-transitions>
  43. flow>

In the code above, we are using the element to call a subflow from a parent flow. The subflow attribute specifies one of the flows you registered with the flow registry in the Spring configuration. The flow starts at the subflow's start state, and continues until the subflow hits an end state. The end state IDs provide keys that you can reference from the calling flow to effect state transitions. For example, in the flow above, accountAdded is one of the end states for the register flows, and so one of the elements references that end state.

Recall from register.xml (see page 4) that the end states specified a view attribute. If the register flow is called directly, then SWF will use the view attribute to decide which view to show the user when the flow reaches a given end state. If, however, the flow is called as part of a subflow (instead of being called directly), SWF will ignore the view attribute and instead follow whatever transition is defined in the calling .


Using EL to Call Services

SWF allows you to make calls against service beans using an expression language (EL). You can use either Unified EL or else OGNL for that, as mentioned earlier in the article. We happen to be using OGNL though I understand that Unified EL and OGNL are mostly the same, at least where their core syntax is concerned.

There are various places in your flow where you might want to invoke service beans, and SWF provides various mechanisms for doing that. Here's a table showing how to call services from different locations in your flow definition file:

Location How to call the service bean
On flow start /flow/on-start element
On state entry /flow/view-state/on-entry element
Immediately before rendering a view /flow/view-state/on-render element
On state exit /flow/view-state/on-exit element
On state transition /flow/view-state/transition/evaluate element
On flow end /flow/on-end element
Table 2. How to call service beans from different locations.

Note that the above does not represent a 100% complete list of places where you can use EL, but it gives you most of the major cases and also the basic idea.

In checkout.xml we've used a number of the methods above. For example, we use to grab the product catalog and shipping options from the cart service and place them on the flow scope, which allows them to be used for the duration of the flow. The value attribute on the set element is specified using EL. (It looks a lot like JSP EL, if you are familiar with that.) The cartService bean is available because we defined a Spring bean with that ID.

We also use to prepare the viewCart state for rendering. In this case, we place the user's shopping cart on the request scope so the JSP can easily reference it, and we also pull some recommendations off of the cart service and place those on the request scope as well. Once again the value attribute is specified using EL.

Global Transitions

At the end of checkout.xml, you will see that I've defined a element. This allows me to define a transition that applies to all states in the flow. In this case, any time the user raises the cancelCheckout event, the global transition I've defined will kick in and carry the user to the shop state. Pretty handy for transitions that occur in multiple places throughout the flow.

That's Our Whirlwind Tour

That concludes this tutorial on Spring Web Flow 2.0. We've really only scratched the surface—for instance, we haven't even touched the new AJAX support that SWF 2.0 introduces—but this should give you an overall feel for how things work. The SWF 2.0 distribution comes with a hotel booking sample application that shows you how to get SWF working with form validation and persistence as well (areas I've suppressed in this article so that I could focus on flow definition).

If you have suggestions and especially corrections to the article or the code, please leave them below. SWF 2.0 is new so I have no doubt that you will be able to show me places where I could be doing something better. Thanks!