Photo by tanakawho - http://www.flickr.com/photos/28481088@N00/1026515526/in/set-72157594223472430/
del.icio.us Digg DZone Reddit StumbleUpon
1 | 2 | 3 | 4 | 5 | 6 | 7 | Next »
Software Development

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.

Social bookmarks: del.icio.us Digg DZone Reddit StumbleUpon
1 | 2 | 3 | 4 | 5 | 6 | 7 | Next »

Comments (27)

this works only if You are using Tomcat 6.

Tomcat 5.5 and before don't support Unified Expression Language (UEL)
By Florian Rissner on May 16, 2008 at 9:26 AM PDT
Hi Willie,

Nice article, and very helpful.

I have a question,
I have been using spring-webflow-1.0.5.jar and on upgrading it to spring-webflow-2.0-m3.jar i get:

WARNING: Ignored XML validation warning
org.xml.sax.SAXParseException: schema_reference.4: Failed to read schema document 'http://www.springframework.org/schema/webflow-config/spring-webflow-config-1.0.xsd', because 1) could not find the document; 2) the document could not be read; 3) the root element of the document is not <xsd:schema>.

Why am i gettin this error on upgrading th jar?
Any clues?
By Deep Edward on May 19, 2008 at 9:19 PM PDT
Hi Edward. Nice to see you over here. (I recognize you from the Spring forums.) I'm just taking a guess here but it looks like you need to update the schema location in your app context file to point to the new SWF 2.0 schema instead of the old 1.0 schema. The schema location is defined on your <beans> element, at the top of the file. Also I would recommend using 2.0 GA instead of 2.0 M3 since there are definitely differences between 2.0 M3 and 2.0 GA. Good luck.
By Willie Wheeler on May 19, 2008 at 10:25 PM PDT
FYI, the schema location is

http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.0.xsd

which is exactly the same as the 1.0, but with a 2.0 instead.
By Willie Wheeler on May 19, 2008 at 10:28 PM PDT
Yes,

I tried posting it in your site first, but 'twas throwing some exceptions.

Will ask you when I come across any doubts here itself :)

DeepEdward
By Deep Edward on May 24, 2008 at 6:26 AM PDT
Beautiful tutorial -- many "Ah Ha!" moments. Everything you show works like a charm (except the SiteMesh filter didn't work for me, but I just took it out like you mentioned).

I would really like to see a working example with Ajax in SWF 2.0. The reference guide material is a bit sketchy...

Thanks!
By cmontgomery on May 24, 2008 at 6:27 AM PDT
Hi Willie,

Am back, and I have a question.

It is regarding <form:checkboxes> / <form:checkbox> where I have an issue.

In my page i have multiple checkboxes. I retrieve the list and display the list of checkboxes(using <form:checkboxes>). Now as this is list of objects to which I need to bind the value, how do I bind a particular checkbox to an object, in which I have the property?
I tried binding the property but it does not bind.
Any solution?
By deepEdward on May 30, 2008 at 1:16 AM PDT
HI I Get this error when I try to run it ...
java.lang.NoClassDefFoundError: org/springframework/core/NamedThreadLocal

I'm using spring 2.5.1 and tomcat 5.5. Also in tomcat 6 it's not working
Can u please explain why I get this error
By Dilan on May 30, 2008 at 3:45 AM PDT
Use the Sprin jar 2.5.4.
This file is has the required file.
By DeepEdward on May 30, 2008 at 5:06 AM PDT
Hi Deep. I don't have an answer for you (sounds like it works in Spring MVC but not SWF 2.0.1?) but I'm posting the URL to your thread on the Spring forums.

http://forum.springframework.org/showthread.php?p=183555#post183555
By Willie Wheeler on May 30, 2008 at 7:16 AM PDT
Hi Willie,

How are you doin?

Got one doubt here..
I am using webflow version 1.0.5.
I have a situation where I need to make use of edit pages.

In this case, when the user clicks on a button(say, edit) which fetches the data from the db and then navigates to the edit pages.
On fetching the whole object from the db, how do I set it to my commandObject so that the fields in the pages are binded to the respective values contained in the object fetched from the database.

Please give suggestions,
DeepEdward
By deepEdward on Jun 12, 2008 at 6:19 AM PDT
I haven't used SWF 1.x so I can't speak to that specifically, but in SWF 2 you would use the model attribute on the <view-state> element to specify a model object that you want bound to the form. Then you could use an <on-start> element in your flow to load up the appropriate model object. Here's an example:

<flow ...>
<input name="accountId" required="true"/>
<on-start>
<evaluate expression="accountService.loadAccount(accountId)" result="flowScope.account" />
</on-start>
<view-state id="editAccount" model="account">
...
</view-state>
</flow>

The hotel application that comes with SWF 2 has an example of this. See booking-mvc\src\main\webapp\WEB-INF\hotels\booking
By Willie Wheeler on Jun 22, 2008 at 10:49 PM PDT
Just saw something in the SWF 2.0.2 reference manual that helped me understand better the first comment by Florian Rissner.

@Florian: You are right that Tomcat 6 comes with el-api.jar. If you are running Tomcat 5.5 you'll just need to provide an EL dependency (like OGNL) yourself.
By Willie Wheeler on Jun 24, 2008 at 2:02 AM PDT
I cannot get the example to work. I'm using Tomcat 5.5 with OGNL 2.6.9. WebFlow v. 2.0.2-RELEASE and Java 1.5 currently.

The /WEB-INF/jsp/account/registerForm.jsp gets rendered, but it still contains: ${flowExecutionUrl} instead of the real URL.
By Patrick Gebhardt on Jun 24, 2008 at 9:14 AM PDT
OK, I'll take a look at that this evening and let you know what happens.
By Willie Wheeler on Jun 24, 2008 at 9:22 AM PDT
Small update:
<%= request.getAttribute("flowExecutionUrl") %>
works.

You can contact me with my email too, its not displayed, but i guess you may have access to it, if not, its firstname.lastname@gmail.com (replace first/lastname accordingly)
By Patrick Gebhardt on Jun 24, 2008 at 10:08 AM PDT
Hi guys. So I didn't dive too deeply into this, but I tried deploying to Tomcat 5.5.26 and saw what you're talking about. Basically the problem is that Tomcat isn't evaluating the JSTL EL. That can be fixed by adding the following declaration to the beginning of the JSPs:

<%@ page isELIgnored="false" %>

There's probably a way to make Tomcat do that without having to modify all the JSPs but like I said I didn't look too deeply into it.

I'll fix mycart3.zip right now. Thanks guys for pointing this out.
By Willie Wheeler on Jun 24, 2008 at 11:18 PM PDT
It works for all JSP files, if you put the following in your web.xml:
<jsp-config>
<jsp-property-group>
<url-pattern>*.jsp</url-pattern>
<el-ignored>false</el-ignored>
</jsp-property-group>
</jsp-config>
By Patrick Gebhardt on Jun 25, 2008 at 5:10 AM PDT
The requested resource (/SpringShoopingCart/account/&_eventId=submit) is not available.
This error is appear when I press od the link submit i the page registrationForm.jsp
Where to change some of the conf. files to me it possible to run.
By Josifov Gorgi on Jun 27, 2008 at 5:43 AM PDT
Hi,

I am trying to upload a file using spring webflow2.0.2.

I get the following exception(see the post on spring forum, http://forum.springframework.org/showthread.php?t=54466)

When i do the upload in spring mvc it works fine, but the same does not work in webflow.It seems that the apache commons-fileupload jar used for file upload tries to fetch the same file and throws a FileNotFoundException.

Any ideas Willie how to solve this.
By deepEdward on Jul 2, 2008 at 8:45 PM PDT
Hey there,

I'm having buid problems at the second step of the tutorial. When I run Build in Ant it shows the following error error: error reading C:\myCart\WebContent\WEB-INF\lib\spring-binding-2.0.2.jar; error in opening zip file
[javac] 1 error
I tried also with different spring-binding -2.0.0 but still no joy.
Please advise...
By Luke on Jul 20, 2008 at 3:35 AM PDT
To me it looks like a corrupted JAR, but I don't understand why you would get it for both JARs. Did you try opening the JARs (e.g. with the jar tool from the JDK, or with WinZip, etc.) to see if you could actually open them?
By Willie Wheeler on Jul 20, 2008 at 9:52 AM PDT
Hi Willy,

Thanks for reply. The files seem to be corrupted and cannot be opened in any way. Moreover I was trying to get new ones but unfortunately I could not find them on the web. Could You please send me those 2 files spring-binding and spring-js on my email luke_g@poczta.fm. Could I possibly ask You for Your email? I am pretty new to that stuff and I would appreciate some more help. Thanks one more time.
By Luke on Jul 21, 2008 at 10:38 AM PDT
@Luke: You will need to download them from the Spring website:

http://www.springframework.org/download

You'll see that there are some downloads for Spring Web Flow. The JARs you need are included with that.
By Willie Wheeler on Jul 21, 2008 at 5:20 PM PDT
Hi Willie,
Thanks for the article. I am very new to Spring and WebFlow. I am having a problem getting the mycart3 working. Unfortunately, I cannot download it to my machine so I have to re-type it. When I try to bring up the home.do page I end up getting a null pointer exception in CartController. It looks like the cartService is null. Looking through the files, I cannot find out where this should be set up. What am I missing?
thanks
By Tom on Aug 19, 2008 at 9:36 AM PDT
Hi Tom. Sounds like you aren't injecting the service bean into the controller. Check your application context file. I don't remember for sure but I believe I'm doing component scanning (using <context:component-scan>) in the Spring config. Make sure that you are component scanning the service bean, and also that the service bean has the @Service("cartService") annotation so that the component scan can pick it up.
By Willie Wheeler on Aug 19, 2008 at 1:37 PM PDT
Hi Willie,

Good tutorial! A few comments:

1) You should make mention in the article that there are some additional dependencies for mycart3.zip, like you did for mycart1.zip and mycart2.zip. Specifically, the user needs to copy 4 additional JAR files into their WEB-INF/lib directory:

1. cglib-nodep-2.1_3.jar
2. jstl.jar
3. standard.jar
4. sitemesh-2.3.jar (from http://www.opensymphony.com/sitemesh/download.action)

This is mentioned in the mycart3.zip readme.txt file, located in the mycart3\web\WEB-INF\lib directory.

2) In the "Subflows" section of the article, you mention that login flow is both a top-level flow and a subflow. However, in the checkout.xml file, you use a view-state tag instead of a subflow-state tag (like you do for the add to cart and register subflows). Why?

3) The EL used in the JSPs is the JSP-EL (which you refer to as the JSTL EL and earlier as the Unified EL), whereas the EL used in the flow definition files (e.g., checkout.xml) is the OGNL. This is not clear in the article, where it sounds like you're using OGNL in both cases.

Thanks.

- Matt
By Matt on Sep 4, 2008 at 4:40 PM PDT
Post a comment
Home | Consulting | Articles | Blog | About | Contact
Copyright © 2008 Wheeler Software, LLC.