Spring WebFlow and MyFaces Orchestra
Both of these libraries provide conversation scopes for variables, lifecycle events for views, and conversation-scoped persistence support. There is definitely a lot of overlap; they effectively aim to solve the same problem via somewhat different approaches.
This page looks at the similarities and differences between these libraries. This is definitely a work-in-progress. If you have further information about this topic, please contribute to this page.
This page currently addresses WebFlow 1.0.4 and Orchestra-core 1.0.
WebFlow aims to be completely portable across multiple UI frameworks. It defines its own "flow definition" format that can be used unchanged on multiple frameworks.
Orchestra also aims to be usable with multiple UI frameworks. However as it aims to "integrate" with each framework rather than add layers to them; there is no portable "flow definition". Flows are instead defined using the native mechanisms of each framework as much as possible.
WebFlow has been developed explicitly with the aims of supporting JSF, SpringMVC and Struts. This therefore includes both "component-oriented" frameworks and "form-oriented" frameworks.
Orchestra is a younger project, and currently only has support for JSF. It is expected that adding support for any component-oriented framework should be simple. It is not yet known whether Orchestra will be usable with form-oriented frameworks such as Struts.
WebFlow requires Spring.
Orchestra requires a decent dependency-injection (DI) framework, but is not bound to any specific one. The 1.0 release includes only support for Spring core 2.x as the DI framework, but others (Guice, PicoContainer, etc) are not difficult to add. And in particular, it is hoped that future releases of the JEE spec will extend its very primitive DI features sufficiently for Orchestra to run directly on it without an external DI framework.
It's fair to say that WebFlow is more complex to learn than Orchestra. It introduces a lot of new terminology (states, transitions).
WebFlow defines its own fairly complex configuration format for "workflows"; this is normally represented as an xml file (though alternatives are possible). The configuration defines a sequence of views, together with rules for navigating between them, and other associated data. In fact, WebFlow describes itself as a "flow engine".
Orchestra aims to be simpler: it provides simple support for conversations and workflows while being as unintrusive as possible.
WebFlow includes the rules for navigating between pages into its "flow definition" files.
The advantage is that the configuration gives a clear overall view of a workflow as the beans, views, and navigation flows are all defined in a single webflow configuration file. It also means that the code can be ported to a different UI framework (eg Struts to JSF) without changing any workflow definitions.
However the disadvantage is that all this is alien to the framework that WebFlow is being used on. In particular, JSF already has its own inter-page navigation approach (navigation-rules config file, handled by a NavigationHandler class). Using WebFlow means using a completely non-JSF-style approach to page navigation.
Using WebFlow with something like Spring-MVC does work well, as Spring-MVC does not natively have an equivalent to the JSF NavigationHandler.
Note that navigation rules can be viewed and edited graphically with the Spring IDE Eclipse plugin. However they cannot be viewed/edited with tools that understand a particular UI framework's navigation features (eg a JSF-specific navigation tool).
One interesting feature of this is that the navigation (ie state transitions) can be easily unit-tested. It may not be so easy to unit-test the navigation configuration of a UI framework.
Orchestra does not impose any new navigation facilities; an app should use whatever facility the UI framework provides.
For example, when using JSF, no new configuration format/navigation techniques need to be learnt: the standard JSF NavigationHandler should be used.
However the information about how navigation occurs between pages in a workflow is separated from other information about the workflow.
When using Orchestra with a UI framework that does not have any native navigation framework, then choosing how to navigate between pages is up to the developer; orchestra does not provide a solution.
Because the navigation rules are native, they can be viewed/edited with tools that understand a particular UI framework's navigation features (eg a JSF-specific navigation tool). However of course workflow-related data will not be available.
In WebFlow, only one "top-level" flow is active at a time, although it can have "subflows". There is a single "conversation scope" that is visible to the top-level flow and all the subflows. In addition there is a "flow scope" that is private to each flow/subflow; these are "stacked" so as a subflow is created a new clean "flow scope" is pushed onto the stack. A mechanism exists to copy data between the "flow scope" of a parent and child flow.
Orchestra supports named scopes; many conversations can be concurrently active. There is no explicit concept of a "subflow"; when one sequence of views (a flow) wants to call into another sequence (a subflow), the beans used by views in the subflow are simply configured using a different conversation name. In a few cases ("re-entrant screens") it really is necessary to stack conversation scopes; Orchestra will hopefully support this in the near future.
The use of the term "flash scope" appears somewhat different. For WebFlow, data in flash scope is kept "until the next external user event is signaled" (whatever that means). For Orchestra, a named scope can have a "flash lifecycle" meaning the entire scope is discarded if nothing in the scope is referenced during the processing of a single request.
Multiple Window Support
Both WebFlow and Orchestra support a single user (ie single http session) using the application from multiple browser windows concurrently. In Orchestra this is called a "Conversation Context". WebFlow calls this a "flow execution repository".
While the terminology used by both projects is quite different, the underlying implementation seems to be basically equivalent.
Back Button Support
There are three possible approaches to back-button support:
- Display an error page always, telling the user effectively "don't do that".
- Go back to the previous URL, but otherwise discard all context data.
- Go back to the previous URL, and try to restore the previous context data.
Option 1 is fairly easy to implement.
Option 2 makes sense in some cases, where there is no real "user state" associated with the previous page. However it does not work when the previous page needs data held in memory related to earlier operations the user performed. And in particular this applies to "workflow" type stuff, as almost by definition the page is showing "work in progress" data that is only held in memory. In workflows, therefore, the best that can be done when that state is not available is to redirect the user back to the first page of the workflow.
Option 3 means caching a copy of the "user state" at the start of each request, and keeping it for an indefinite amount of time. When back is selected by the user, the old copy of the state can be restored and the page re-rendered as it was. This is sometimes called a "continuation" (although that word does have a number of meanings).
Issues with Option 3
Caching of user state can be done in the client, by embedding the entire "user state" into an http form so that when "back" is selected the user's browser resubmits the state that needs to be used to render the page correctly. The client therefore bears the impact of data storage for old states, not the server. However more network bandwidth is needed as the pages are bigger. In addition, all the user state must be serializable (in some manner) as it needs to be streamed to the user and sent back. And it doesn't work with GET operations, only with POST.
Caching of user state can be done in the server, but of course keeping N old states around is very memory intensive. Some value of N also has to be chosen, meaning that only a fixed number of "back" operations can be supported.
More importantly, there is a major limitation: the in-memory user state can be restored to old values but other data, particularly database state, cannot. So for some pages going "back" will cause errors anyway as the in-memory data no longer matches external reality. In these (fairly common) cases, option 2 is better.
Things get really interesting here if ORM "conversation scoped persistence" is being used. Clearly any persistence-context that exists must also be restored to its previous state otherwise it may have "dirty" entities waiting to be written to the database and all sorts of other interesting things. But a persistence context can be a *very* large and tricky object to serialize and later deserialize. Note that a "flush" operation would greatly complicate things as data would exist within the Transaction waiting to be committed, but would not be within the persistence-context. This scenario is not valid for conversations, though: a Transaction never stays open across two requests, so by definition flushes cannot be used within a conversation except as the final operation.
Option 3 is really hard to implement when Session-scoped data is used extensively, as it is difficult to determine which bits need to be saved for later restore. Using conversations of any sort really help, as the objects held in a conversation are clearly the right ones to save.
Note that JSF implementations (Sun RI and MyFaces at least) provide "option 3" support for the actual JSF component objects, so that "back" restores a valid JSF component tree. However all the backing beans also need to be saved/restored as described above for this to be useful.
WebFlow appears to support option 3, ie stores away snapshots of the current state after each request, and using a hidden id in the request to select the appropriate state when a request is received.
WebFlow offers three "Execution Repository" implementations:
Simple --> no back-button support
- Server Continuation (server-side data saving)
- Client Continuation (client-side state saving)
The "simple" option basically does nothing special, and provides no back-button support. The "client continuation" option serializes all the conversation state in the rendered page. The "continuation" option stores the data on the server (probably in the http session)
WebFlow invalidates server-side "continuation" data when the associated flow (conversation) terminates. This means that it is never possible to go "back" into a conversation after it has ended; an error is immediately displayed.
Orchestra does not currently support either "client continuation" or "server continuation", but it should be fairly easy to implement the same sort of features as WebFlow (with the same limitations); all the necessary data structures are present within Orchestra.
!Webflow allows actions (method invocations) to be associated with "state transitions". Associating an action with entry to the "start state" allows per-workflow initialization, and associating them with a specific state allows per-view initialization.
For Orchestra, the UI framework's native lifecycle management should be used if possible. However Orchestra also provides a ViewController framework which can either be configured via a file, or via bean annotations in order to get callbacks on significant lifecycle events.
The WebFlow documentation is currently significantly better than the Orchestra documentation. Orchestra isn't bad but WebFlow's docs are excellent.
Orchestra provides a DynaForm component that allows easy display and edit of persistent entities.