Keith Donald on Reuseable UI Flows with Spring Web Flow 1.0
- Define all controller logic for an application task, such as a search process, in one place, instead of scattering that logic across many places.
- Compose flows together to create rich controller modules from simple parts.
- Enforce strict user navigation rules with a natural, object-oriented linear programming model and without coding verbose if/else blocks.
- Have memory you allocate during flow execution automatically clean itself up when the flow ends or expires.
- Deploy a flow for execution in a Servlet environment using your base web framework of choice.
- Change base web frameworks without changing your flow definitions.
- Change environments all together, going from JUnit test to Portlet for example, without changing your flow definitions.
- Change your flow definition logic on-the-fly during integration testing at any time without a container restart.
- Receive automatic POST+REDIRECT+GET behavior by default with no custom coding.
- Receive automatic browser button support (back, forward, refresh) with no custom coding.
- Store task data in any of four managed scopes: request, flash, flow, and conversation; each with their own distinct semantics.
- Test flows in isolation without the container. Ensure your application control logic works before you deploy.
- Phonebook - the original sample demonstrating most features (including subflows)
- Sellitem - demonstrates a wizard with conditional transitions, flow execution redirects, custom text field formatting, and continuations
- Flowlauncher - demonstrates all the possible ways to launch and resume flows
- Itemlist - demonstrates REST-style URLs and inline flows
- Shippingrate - demonstrates Spring Web Flow together with Ajax technology
- NumberGuess - demonstrates stateful beans, evaluate actions, and "single key" flow execution redirects.
- Birthdate - demonstrates Struts integration
- Fileupload - demonstrates multipart file upload, set actions, and flash scope
- Phonebook-Portlet - the phonebook sample in a Portlet environment (notice how the flow definitions do not change)
- Sellitem-JSF - the sellitem sample in a JSF environment
Because we are ready now. Web Flow delivers an innovative approach to developing web application controllers -- there is nothing quite like it out there -- and it took us time and a good deal of trial-and-error to get the model right and the APIs stable. We are there now and it feels great.
The history around Spring Web Flow is an interesting one. We "officially" started in February 2005. This was after I discovered Erwin from his project "Spring Web Flows" which he started in 2004, where he pioneered the general approach and the initial cut at the flow definition language. Erwin's contributions came from his experience building applications for the financial industry.
There he was influenced by an IBM product called WebSphere Branch Transformation Toolkit, and a module called "presentation flows" within that. That's where the original ideas for SWF came from. Erwin's thinking was, "hey, this has real potential if implemented right -- let's get something out there for a well thought out environment -- Spring -- and see if we can get somewhere".
In the winter of 2004 Interface21 was working on a large project at VOCA, the largest payment clearing house in Europe, to develop a control interface to their payment processing engine. It was Rod, Colin, myself, and Ben Alex on that project. The domain called for a complex UI with many multi-step work-flows, and notably flows that operated on versioned, temporal data. When we started looking at web technologies that fit our requirements, well, we didn't find much. We were required to use Struts for political reasons, but vanilla Struts simply provided no first-class concept of a flow. We looked at Beehive but it didn't give us the runtime flow execution concept, either. Then we found Spring Web Flows. Erwin's simple model gave us what was needed - the ability to model flows as self-contained modules, then manage and observe distinct executions of those modules at runtime.
From there I officially approached Erwin about bringing Spring Web Flows in as an official Spring project. He accepted, and we and our community have spent the last year and a half getting the product to where it is today--1.0 feature complete and ready for production use. In the time leading up to this release, we have built an solid public API, a rich flow definition language, a robust and extensible flow engine implementation, and added all sorts of nifty features based on user feedback such as continuations, conversation scope, flash scope, always redirect on pause, etc. We feel we really have something special and a solid base to build on in the future.
Next Donald was asked about how Web Flow can be integrated in different web frameworks.
Sure. We like to think of Spring Web Flow as a "next generation web application controller framework". What Web Flow allows you to do is define your controller logic in modules called a "flow definitions". Once defined, you can quickly create an execution of a flow definition in any plain Java SE environment -- all you need is Spring Web Flow and a few supporting jars on the classpath to do that. So, bottom line, I can quickly use JUnit to test my controller logic without needing the Servlet API, Struts, Spring MVC, or anything else.
So the core of Web Flow as well as the flow definitions themselves are decoupled from a specific environment like Servlet, Portlet, or any base web framework. When you are ready to execute your flows inside a Servlet engine like Tomcat, that is when you integrate with a framework that cares for HTTP request-dispatch and response rendering. We ship integration with Struts Classic, Spring MVC, and JSF to make it easy to execute flows in these environments. In all these cases, the Web Flow system and your flow definitions remain self-contained. This has a lot of benefits, of course--testability and encapsulation are two most important ones in my mind. This also means people can use Web Flow in a lot of different environments, which has always been important to our users.
JBoss's Seam and Spring Web Flow both have the concept of scopes. Web Flow for instance has request, flash, flow, and conversation. Donald was asked to compare the two.
Next the conversation turned to the innovative features in Web Flow 1.0:
Yes, to my knowledge Seam also has the concept of distinct scopes, so some of these concepts are similar. Basically, objects placed in request scope live for the duration of a call into a flow execution, similar to Seam's event scope. When the call completes, the data goes out of scope.
Objects placed in flash scope live after what we call a "pause", which occurs when a flow execution renders a view to the user, goes to sleep, and waits for the next user event to resume. Flash scoped data hangs around until the next user event is signaled in the next POST request, even after multiple browser refreshes. This is somewhat similar to Seam's idea of page scope, though it's important to note in Spring Web Flow a view rendered by a flow execution doesn't necessarily correspond to a page - it could be a zone within a page. We borrowed the name 'flash' from Rails, as this scope serves a similar purpose.
Objects placed in flow scope live for the duration of an executing flow session. Such objects are like local flow variables. So, for example, if I kickoff a "Master Search Flow" and allocate some stuff to flow scope, that stuff will hang around until the flow ends. It is important to note that the objects in flow scope are *local* to the flow and not accessible to other flows. So if during search execution a "Detail Result" flow was spawned as a subflow, that detail flow would not be able to access "Master" state unless the state had been explicitly passed in as input.
Finally, objects placed in conversation scope live for the life of an overall flow execution, which represents a single logical conversation with a user. These objects are shared by all flows that may be launched during the course of a single execution. So you can look at "flow scope" as a bucket for local flow variables, and "conversation scope" as as an external container for global flow execution variables. Conversation scope similar to the Seam's concept with the same name.
Well, everyone loves the elegance of the linear programming model. Got a task that executes over multiple steps, and the order of the steps is important? Spring Web Flow is ideal for that. In one file you can quickly define the task navigation rules and them test them in JUnit. You don't have to worry about the flow resuming in the right spot during the course of task execution -- Web Flow handles that complexity for you, and enforces the linear progression you require.
Also, Spring Web Flow can invoke Spring-managed business services directly from instructions in your flow definition, so you typically don't end up writing more than one file per logical application task. That is a lot less code to maintain. The productivity benefits there are real there.
Flash scope is really neat, but the truth is it was simple to implement - what was much harder to get right were the flow and conversational data structures and the general flow execution model. Users really like not having to worry about state being cleaned up - when a flow ends, having the data clean up automatically is a big win.
Continuations allow for seamless browser back button support out-of-the-box. Say I get to step 6 of a 10 step task. I can go back to step 3 and continue from there. When I resume from step 3, the state of the flow matches that previous point in time. This is ideal when the outcome of future steps depend on what was completed in previous steps. It just works.
People can change their flow definitions and see those changes take affect without a container restart. With that said, you still should be unit testing your flow definitions.
"Always redirect on pause" -- a default behavior -- gives you the POST+REDIRECT+GET pattern automatically with no special coding. Each time a top-level flow execution pauses, a redirect is performed automatically. You can refresh, go back and forward freely and without browser warnings. You essentially get that for free.
With Ajax being an industry changing movement over the last year Donald was asked about how it interacts with UI flows:
There are essentially two Ajaxian-techniques we have seen our users apply with Spring Web Flow:
Besides that, SWF and Ajax are somewhat orthogonal - you can make asynchronous calls back to the server outside the control of the flow execution, for example, by calling your business services directly. In this case SWF never gets involved.
Certainly, we want to show more ways of integrating SWF, particularly in rich web application environments, for 1.1.
The conversation concluded with discussing what is next for Spring Webflow 1.1. Donald noted that there are many interesting ideas on the roadmap. He is most excited about increasing integration with such items as official support for flow-scoped persistence contexts (with Hibernate and JPA), and enhanced integration with JSF. There is also work being done to promote SWF as a more general purpose web development technology in the future. Today, developers typically mix-and-match Web Flow usage along-side "traditional" controllers like Struts Actions or Spring MVC Controllers. He explained that Spring Web Flow really has the potential to be *the* controller technology for a large class of rich web applications. Finally Donald mentioned that the Spring Experience in December will include sessions showcasing the latest advances in the technology.
This JIRA may be of interest to you: opensource.atlassian.com/projects/spring/browse....
We certainly welcome and will review Struts 2 integration contributions now, and will will consider shipping convenient integration once we can be assured of Struts 2's general availability and stability. Alternatively, and possibly even better, Struts 2 could ship its own official SWF integration. That may make even more sense, as SWF is designed as an embeddable flow execution engine.
Complete example of componentized flow