One of the things I consistently find difficult to do in Struts is restricting a user's navigation to a well defined path, especially when users start tampering with URLs. In order to ensure that we are in a valid state we:
- Use subclassing of actions, where the parent action defines an abstract execute method for the child to implement and checks state for us before delegating to the child to handle the request. This usually leads to very tight coupling, and a headache when something needs to be changed.
- We duplicate code in each action to make sure we are in the correct state. Maybe we use helpers, but the code still smells.
As I try to bring Struts to bear in portal development I have found it even harder to keep things coherent (IMHO, Struts has more than a bit of an impedance mismatch with portal development). A consultant that I worked with introduced me to the portlet state pattern, which is a simple solution that works well for portlet applications. The general idea is that navigation is controlled by the state that the application is currently in, the current state is stored in the session, and actions modify the current state. The controller can ensure that only actions which the current state allows are executed. One shortcoming is that the transitions are encoded in separate Java classes, meaning that the documentation value that a Struts config file would have provided is gone. Given the time I might have come up with an externalized configuration solution but I never quite got around to it.
A better way?
In building the next generation of a login/registration/self-help front end for an existing portal I quickly realized that I didn't want to write bunches of code to make sure a user had not changed a url and navigated past part of the login or registration flow (see Problem 1). What I wanted was the portlet state pattern for a Struts web application. I looked at solutions for Struts and found a few:
Spring Web Flow.
In less than an hour I had the existing main flow for the application defined with stub pages, and was adding the new states. I ended up breaking some of it down later into sub-flows, as well as playing around with the different types of states more than I probably should have, but it was fun! What I found after a day of work was a well defined, mature, testable, self-documenting framework for defining and enforcing flow in web applications. There are certainly non-flow based web applications out there, but I do know that the vast majority of the web applications that I have written in my life need some way of enforcing a known set of transitions between states.
One of the things that surprised me was how easy it was to "get something working" with mocked up pages and "fake" actions but the navigation that I would expect between pages, and then drop in real actions/data/views one at a time, having a "working" application at each step of the way.
SWF is that it isn't coupled to a web application framework (to be fair Commons Chain isn't either). This means that it should work great when I am wearing my portlet development hat, and that it is testable without needing to mock up a servlet environment. To accomplish this SWF implements an abstraction layer over the Servlet API. This means that it can be tested outside of a servlet container, and that it can work with other APIs (like the portlet API). In reality it is an engine that other tools and frameworks call into. For more information look at the documentation for ExternalContext. It works with Struts, Spring MVC and JSF out of the box.
For those "afraid" of XML configuration, there is a fully functional programatic API for building your flow. I found the XML configuration more practical, and it has the benefit of being able to be manipulated using XSLT to generate documentation for the flow (that my boss, and the customer, could read). I even hacked together an XSLT template to generate a dot file that can be processed by graphviz to generate a visual representation of the flow (This won't be necessary when the Spring IDE WebFlow Editor is complete, hopefully someday soon).
A couple of stumbling blocks I hit along the way:
- Many of the tutorials that are out in the wild are for a pre-1.0 release of the framework and therefore might not be a 1:1 match to the current version. The project has been around for quite a while (some history). The API, and configuration syntax has changed for the 1.0 release and I did hit a couple of snags trying to follow the examples that I found out on the net. There is a whole set of examples bundled with the framework that were a good introduction. The manual for the framework is a good technical reference, but is far to low level. One thing I think that the project needs is a good quick start tutorial that is up to date with the 1.0 API.
- The configuration uses OGNL to define expressions which are used to retrieve values from the contexts and pass to underlying objects. The error messages that are generated when the OGNL expression can't be parsed or bound are sometimes "less than helpful". I don't know if more work on error handling from the parser would help in this case, or if there is not enough contextual information being passed back by the parser.
I will be using Spring Web Flow more in the near future, and can't wait to get to try it on some of the more complicated portlets that I have been working on. I know it would have made the last search/display/edit/create portlet that I did a lot easier. I wonder if I should rewrite it using SWF...
Note: One interesting project that looks similar to SWF is Commons SCXML, which is based on a W3C specification. The format of the configuration files is similar to the SWF configuration file, and it will be interesting to see how the two technologies grow with/against one another.