Some features brought in with Beehive Page Flow
Declarative programming model using Java 5 annotations. Navigation, exception handling, validation, etc. are all defined in a single Java class: the page flow "controller" class that drives a piece of your web application. See StrutsTi/ControllerMock, or actual running samples at wars/samples/src/java/pageflow in the tree ('maven dist' at the root will build wars that you can deploy).
Modular Page Flows
An application can have multiple page flows within it, allowing you to break it up into separate, self-contained chunks of flow. This is like modules in Struts Classic, but it goes beyond breaking up the config. Read on...
Stateful Page Flows
When a user enters a page flow (by hitting an URL in the page flow's URL space), an instance of the page flow's controller class is created. While the user is in the page flow, the controller instance simply stores the flow-related state in member variables. Methods within the class -- particularly action methods and exception handlers -- have access to the member state. By default, the state is automatically cleaned up when the user leaves the page flow to enter another one. This behavior can be configured per-page flow, but using auto-cleanup helps keep your session small, and focused on the task at hand.
Controller classes are driven through a lifecycle, with callbacks for create/before-action/after-action/destroy.
The storage location (user session by default) is pluggable.
"Target scopes" are the foundation for multiple page flows living in the same session -- in portlets or in multiple browser windows/frames.
Page Flow inheritance provides a way to share actions, exception handlers, configuration, etc. among controller classes. When you extend a controller class you inherit/merge its configuration (annotations) as well as everything else you would inherit normally.
Shared Flow provides another way to make actions, exception handlers, *and state* available to multiple page flows. The feature is useful for accessing shared state, to provide actions/exception-handlers for shared user interface (e.g., menu bars, page headers), and when you cannot change your controller class hierarchy. (This feature isn't currently completely hooked up in Ti.)
On an as-needed basis, Page Flow tracks the most recently-shown views (along with the data used to initialize them), as well as the most recently-run action. This allows you to write generic actions (in base classes and in shared flows) that can navigate symbolically, rather than absolutely.
Nested Page Flows
An entire page flow can be inserted, or "nested", inside of another page flow. At its heart, nesting is a way of pushing aside the current page flow temporarily and transferring control to another (nested) page flow with the intention of coming back to the original one. Nesting is useful when you want to do one of the following tasks:
- gather data from the user, for use in the current page flow;
- allow the user to correct errors or supply additional information en route to executing a desired action;
- show an alternate view of data represented in the current page flow;
- bring the user through a "wizard";
- show the user information that will be useful in the current page flow (e.g., help screens can be easily implemented as nested page flows); and
- in general, to further break up your application into separate (and in many cases reusable) pieces.
Nested page flows define entry points and exit points, and are analogous to method invocations. You can pass data to them, and receive data when they return. Upon return, you can use Symbolic Navigation (above) to get back to a recent page, or to rerun a recent action.
The framework is reasonably smart about the back button; for instance, it crawls up the nesting stack to find the appropriate page flow when you execute an action from the browser's history. It is also tolerant of rapid-click: changes to the nesting stack are deferred from session commit until the end of a request, so that multiple nesting actions will not collide in the middle of a set of requests.
Declarative Exception Handling and Validation
Exception handling and data validation are configured through annotations declared in the controller class (and additionally in form bean classes). Exception handlers can be instance methods on the class, with access to controller state. Both features work well with inheritance. Messages in both features can be JSP 2.0-style expressions as well as simple message keys. (Note that validation is currently built on Commons Validator, but the annotations could also drive validation in XWork.)
First-class Integration with JavaServer Faces
Integration with JSF as *view tier* technology (it doesn't take advantage of the more frameworky aspects of JSF like Shale does):
- You can raise Page Flow actions directly from JSF components, and also from JSF command handlers ("actions" in JSF parlance). Both can pass along form beans to the raised actions.
- Page Flow can manage the lifecycle of "backing beans" associated with JSF pages.
- A set of implicit objects ("backing", "pageInput", "pageFlow", "sharedFlow", etc.) are provided, and can be used in JSF databinding expressions.
- When a JSF page is restored through the Symbolic Navigation feature (above), its component tree is restored along with its backing bean (...along with the inputs used to initialize the page).
Page Flow allows you to establish a contract between the controller and the view through "action outputs" and "page inputs", with optional annotations that cause runtime checks for type-match and required values.
Pluggable base handlers for login/logout/roles, bean storage, classloading, forward/redirect, exception-handling (e.g., unwrapping exceptions).
There's overlap here with XWork, so some of this feature will go away. But one additional piece is that you can dynamically inject an *entire nested page flow* to run before or after an action, and you can change the destination or result of an action based on the return value from the injected page flow.
Layer for URL rewriting and templating -- key for portal support.
While you can post/store data directly in page flow controllers, form beans (POJOs) are also supported, via arguments to action methods. By default they're request-scoped, but you can also make them "page flow-scoped" by tying into a member variable instance using the useFormBean attribute on the @ti.action annotation.