2009/05/20 - Apache Shale has been retired.

For more information, please explore the Attic.

Shale View Controller

Introduction

Shale supports a mechanism that provides a 1:1 relationship between a view tier presentation technology, which is responsible for creating an HTTP response (such as a JSP page), and a corresponding JavaBean class containing event handling logic, (optionally) values used in the dynamic rendering of the response, and (optionally) bindings to the individual user interface components included in the response page. Such a JavaBean class is known (in JavaServer Faces terminology) as a backing bean. In most circumstances, such a bean will be registered as a managed bean (configured for creation in request scope).

JavaServer Faces does not require that a backing bean implement any particular interface, or extend any particular base class. Therefore, Shale does not impose any such restriction either. Instead, it promises that if an application's backing bean implements the ViewController interface, then certain extra services will be provided "for free."

Provided Services

Each backing bean that implements ViewController will support a boolean property postback, which will be set to true if this view is the one that is processing a form submit previously rendered by the same page, or false if this view was newly navigated to. The property will be set before any of the lifecycle methods described below are called, so that application logic may perform conditional tasks based on this state.

As part of the standard JavaServer Faces managed beans processing, any <managed-property> elements in the configuration file, that are nested inside the <managed-bean> element for this backing bean, will also be processed when a new bean instance is created. You can use either literal values or value binding expressions to customize properties on your backing bean class. Fans of Dependency Injection will see that the managed beans facility provides support for such a framework, using Setter Injection as the mechanism for injecting dependencies.

In addition, the following lifecycle events are called, by the framework, at certain points in the JavaServer Faces request processing lifecycle:

  • init() - Called immediately after the view that this backing bean is associated with is created. (Technically, it happens when ViewManager.createView() is called.) For a postback, this happens during the Restore View phase of the request processing lifecycle, once it has been determined which view should be restored. If your application navigates from one page to another, the init() method of the second page will be called as part of the NavigationHandler.handleNavigation() processing.
    Use this method to acquire resources that you will need, no matter whether this is a postback request, a rendering request, or both.
  • preprocess() - Called after the Restore View phase has been completed, but before Apply Request Values phase starts, only for the view that will be processing the postback.
    Use this method to acquire resources (such as database connections) that you will need to process the postback.
  • prerender() - Called immediately before the Render Response phase that actually causes the view to be rendered. This method will only be called for the view that is actually rendered (the current view if you are redisplaying, or the new view if you perform navigation).
    Use this method to acquire resources (such as database connections, or performing queries) that you will need if this view is the one to be rendered. NOTE - when portlet support is integrated, all of the portlets on the current page will receive this event.
  • destroy() - If init() was ever called for a ViewController, then it is guaranteed that destroy() will be called as well.
    Use this method to release any resources acquired during an earlier event handler.
It can be difficult initially to visualize the order that events will occur, based solely on the descriptions above. Let's look at some common scenarios and see what happens when:
  • Initial navigation to welcome page, or a page directly accessed via a URL:
    • Backing bean is instantiated during Restore View phase, which notices that there is no previous state to restore.
    • The setPostback(false) and init() methods on your backing bean are called.
    • Because there is no previous state to restore, there will be no form submit processing.
    • The prerender() method is called.
    • Standard Render Response phase processing occurs, which causes the view to be rendered.
    • The destroy() method is called.
  • Postback processed by a page that then redisplays itself (by returning null from the action handler.):
    • Backing bean is instantiated during Restore View phase, which notices that there is previous state to restore.
    • The setPostback(true) and init() methods on your backing bean are called.
    • Standard Apply Request Values through Invoke Application phase processing occurs, which includes calling your action and value change event handlers.
    • The prerender() method is called.
    • Standard Render Response phase processing occurs, which causes the view to be rendered.
    • The destroy() method is called.
  • Postback processed by page A, which then navigates to page B:
    • Backing bean for page A is instantiated during Restore View phase, which notices that there is previous state to restore.
    • The setPostback(true) and init() methods on your backing bean for page A are called.
    • Standard Apply Request Values through Invoke Application phase processing occurs for page A, which includes calling your action and value change event handlers.
    • As part of the navigation process, the backing bean for page B is created.
    • The prerender() method for page B is called.
    • Standard Render Response phase processing occurs, which causes the view to be rendered.
    • The destroy() method is called for both page B and page A (since two backing beans were instantiated).

Using View Controller

For each JavaServer Faces view that you wish to associate with a ViewController backing bean, you must:
  • Implement the ViewController interface. The most convenient way to do this is likely to be extending the convenience base class (org.apache.shale.view.AbstractViewController).
  • As required by the JavaBeans specification, provide a no-args public constructor.
  • Declare your backing bean as a <managed-bean>, using a <managed-bean-name> value that can be mapped from the view identifier. (See DefaultViewControllerMapper for the details of the default mapping.) In nearly all circumstances, you will want the bean to be placed in request scope.
  • (Optional) Provide public JavaBean properties that may be set to customize the functionality provided by this bean. Typically, such properties will be configured based on the managed beans configuration defined in the JavaServer Faces configuration resources for your application.
  • Ensure that the functionality of your backing bean is not dependent upon the order in which property setters are called, since managed beans are configured via the "setter injection" pattern.
  • Create event handlers for the lifecycle events of interest, plus standard JavaServer Faces event handlers for things like actions and value changes.
  • (Optional but recommended) Write a corresponding JUnit test case using the Shale Test Framework. Because nearly all of your property and event handling methods have no JavaServer Faces API dependencies for their parameter signatures, it is generally quite easy to write such tests.