In our last article - "JSF and AJAX" (JDJ, Vol. 11, issue 1) - we discussed how JavaServer Faces component writers can take advantage of the new Weblets Open Source project (http://weblets.dev.java.net) to serve resources such as JavaScript libraries, icons, and CSS files directly from a Java Archive (JAR) without impacting the application developer.
In this article we'll address the need to fetch data using AJAX with JavaServer Faces (JSF) components. The most common use cases for fetching data with AJAX are to populate dropdown lists and add type-ahead functionality in text fields. In contrast to using AJAX postbacks for events, fetching data shouldn't affect the surrounding components on the page. And if fetching data isn't affecting other parts of the DOM tree, then you don't have to go through the full JSF lifecycle just to get the data, right?
This article introduces for the first time a new Open Source project called Mabon hosted on the Java.net Web site (http://mabon.dev.java.net). Mabon stands for Managed Bean Object Notation and its goal is to provide component writers of AJAX-enabled JSF components to access JSF managed beans outside the scope of the standard JSF lifecycle by using a JSON-syntax communication channel.
In essence, Mabon provides application developers with a standard and easy way to provide data to AJAX-enabled components using the managed bean facility provided by the JSF specification.
Fetching Data with AJAX
Fetching data the conventional way versus using AJAX follows the same basic concept - it shouldn't have the side effect of changing the state of surrounding components.
Figure 1 shows an AJAX sequence diagram using the HTTP GET method. The W3C recommends that you use the HTTP GET method to fetch data when there are no side effects requested by the user (for example, Google Suggest).
Different JSF AJAX Approaches
If you get no side effects, then there's no change to the JSF component hierarchy; so there's no need to go through the JSF lifecycle. But, if you want to reuse a managed bean method, the easiest way to get to it is via the JSF MethodBinding facility. Three solutions exist to support this - adding functionality to the Renderer, using a PhaseListener, and providing a new JSF Lifecycle.
The Renderer Approach
The Renderer approach adds functionality to the Renderer to detect the AJAX request. The JSF default lifecycle first restores the component hierarchy during the Restore View phase and the Renderer takes control during the Apply Request Values phase. After the AJAX request has been processed, the Renderer calls responseComplete() on the FacesContext to terminate processing of remaining phases of the Lifecycle. On the surface this may seem like the preferred approach, but it has some severe drawbacks.
A component hierarchy is required, which can incur additional overhead for each request, especially when client-side state saving is used. Calling the response-Complete() method will take effect only after this phase is done processing. The Apply Request Values phase calls the decode() method on all Renderers in the view, which can cause undesired side effects that are out of your control, such as a <h:commandButton> set to immediate="true" by the application developer. This causes the application logic to be called before the Apply Request Values phase is complete.
This approach also usually requires HTTP POST to send the state string back to the server.
The PhaseListener Approach
The PhaseListener approach adds a PhaseListener (PhaseId.RESTORE_VIEW) that short-circuits the Lifecycle and does all the processing in the PhaseListener itself. When it's done, it calls responseComplete() on the FacesContext.
For this approach to work, it has to render a reference containing information about the managed bean used in the initial request. The PhaseListener uses this information during postback to create a MethodBinding that can then be used to invoke a method on the managed bean and return data to the client. Since no component hierarchy is created, and thus no Renderers, there's no risk that command components with immediate set to true will cause any side effects.
But this approach has one issue; there's no way to prevent application developers from attaching additional PhaseListeners at the same phase, which can cause undesirable side effects. You also have no way of knowing in which order these PhaseListeners will be executed.
The Lifecycle Approach
This Lifecycle approach adds a new Lifecycle that's mapped to an AJAX request and contains only the lifecycle phases needed to process the request, invokes the application logic defined by a MethodBinding, and renders the response. This eliminates the overhead of creating and restoring the component tree, and so no Renderers are required. You also won't encounter any issues with immediate="true".
Another positive side effect of using a custom Lifecycle is that any PhaseListener added by the application developer will have no impact on this solution; application developers can even add PhaseListeners to this custom Lifecycle. However, if a custom PhaseListener is used to put additional managed beans on the request, you can run into issues, unless they're registered for the custom Lifecycle as well.
Select a JSF AJAX Approach
Here we use the Lifecycle approach, since it has no application logic side effects and low overhead. It's here that the Mabon Open Source project can help you focus on the design of your AJAX-enabled component.
Let us explain in a little about what Mabon is and what it can provide component writers interested in AJAX data fetch (Figure 2).
What Is Mabon?
Mabon offers a convenient way to hook in a specially designed lifecycle that's ideal for AJAX-enabled components that have to fetch data directly from a backing bean, without the overhead of a full JSF lifecycle. It also provides a Mabon protocol - mabon:/ - used to reference the backing bean and a JavaScript convenience function used to send the target URL and any arguments needed and then get data asynchronously from the managed bean.
Mabon and JSON
As you know, the XMLHttpRequest provides two response types - responseText and responseXML - that can be used to fetch data. The question to ask is; when should I use which? Answers to this question can differ depending on whom you ask, but we can recommend one guideline. Ask yourself if you control the syntax of the response.
The responseXML type returns a complete DOM object (which gives you ample ways of walking the DOM tree), letting you find the information needed and apply changes to the current document. This is useful when your component will impact surrounding elements and you don't control the response (for example, when you're communicating with a Web Service).
The MabonLifecycle Class
The MabonLifecycle consists of three phases - ApplyRequestValuesPhase, InvokeApplicationPhase, and RenderResponsePhase. The MabonLifecycle is responsible for executing these three phases. It's also responsible for handling any PhaseListeners attached to the MabonLifecycle.
The LifecyclePhase Class
The Mabon LifecyclePhase is the base class for all lifecycle phases.
Code Sample 1: The Mabon Protocol:
mabon:/managedBean.getValidDates
Code Sample 2: String returned after Mabon has evaluated the Mabon Protocol:
/<context-root>/<mabon-servlet-mapping>/managedBean.getValidDates
During an AJAX request, this URL is sent on the request and intercepted by the FacesLifecycleServlet.
Mabon: Initial Request
The Mabon implementation is designed specifically for AJAX requests and implements a communication channel using JSON syntax. This solution lets AJAX components that use managed beans fetch data and communicate with the server without having to go through a full JSF lifecycle. So how does it work? At application start-up Mabon will add the MabonLifecycle as part of the JSF LifecycleFactory context.
On the initial request, Mabon is just delegating through to the underlying JSF implementation and is active only during the Render Response phase, if needed.
In the Figure 3 sequence diagram, a page that contains a custom AJAX component is executed. To work, the AJAX component has to get data from an underlying backing bean. During encodeBegin(), the AJAX Renderer for that component will use the Mabon protocol - mabon:/ - to write out a target URL that references the backing bean. To get this URL, the Renderer will call the getResourceURL() on the ViewHandler. It will pass a string matching the method binding expression for the backing bean (for example, mabon:/managedBean.getSuggestions). The getResourceURL() method in MabonViewHandler will return a full path - /<context-root>/<mabon-servlet-mapping>/managedBean.getSuggestions - that can be written out to the document.
Mabon: Data Fetch Request
After the page has been rendered to the client, it contains a target URL to the backing bean that's needed by the AJAX component to fetch data (for example, /<context-root>/<mabon mapping>/managedBean.getValidDates). In subsequent AJAX requests, this string will be intercepted by the Mabon implementation and used to invoke the backing bean and return the result to the client (Figure 4).
On submit an AJAX-enabled component creates a new XMLHttpRequest object, which communicates asynchronously with the server to get data from the managed bean. This request is intercepted by the FacesLifecycleServlet, which routes the request through the Mabon Lifecycle instead of the default JSF Lifecycle.
When the FacesLifecycleServlet intercepts the request, the request processing starts by calling each Mabon lifecycle phase in sequence. First, you execute the ApplyRequestValuesPhase, which will decode the request and get the managed bean reference and method arguments needed for the managed bean off the request. Second, you execute the InvokeApplicationPhase that will create a MethodBinding based on the managed bean reference, invoke this MethodBinding passing any arguments, and return the result. Third, the Render-ResponsePhase takes the result and writes it back to the client.
Mabon APIs
The following sections cover the available APIs and how to register Mabon with an application.
Mabon Servlet Configuration
If you're planning to use Mabon for your AJAX-enabled components, you should be aware that it adds an extra step for the application developer using your JSF component library. The application developer needs to add the entry shown in Listing 1 to the Web application configuration file web.xml.
The servlet class net.java.dev.mabon.webapp.Faces-LifecycleServlet and the initialization parameter (for example, net.java.dev.mabon) is part of the Mabon contract. The application developer can decide to set the mapping to the same URL-pattern(s) as defined by default (for example, /mabon/*) or override the default URL mapping in case it's colliding with resources used by the Web application. Mabon automatically consumes this URL mapping change without requiring any code changes.
Mabon JavaScript APIs
The Mabon project provides a convenience JavaScript library that you can use to send your request to the server. The Mabon send() function leverages the Dojo toolkit's bind() function to communicate asynchronously with the server. For more information about the Dojo Toolkit visit the Dojo Web site at http://dojotoolkit.org/.
Listing 2 shows the source of the Mabon JavaScript library.
The Mabon send() function takes one argument - a Map. To call the mabon.send() function from your AJAX implementation, you have to construct the Map using JavaScript Map syntax as shown in the following code:
mabon.send(
{ url: targetURL,
args: [item1, item2],
callback: callback_function }
);
The targetURL is the resource URL that's written to the client (for example, /context-root/mabon-servlet-mapping/managedBean.methodName). The targetURL will be intercepted by the FacesLifecycleServlet and deciphered by the Mabon Apply Request Values phase.
Mabon Protocol
Now that you know how to configure Mabon, it's time to look at how you can reference the managed beans needed to fetch data. The Mabon protocol-like syntax is convenient and easy to understand. The syntax starts with mabon:/ followed by the managed bean name and finally the method name, as shown below:
ViewHandler.getResourceURL(context, "mabon:/<managed bean name>.<method>");
The syntax uses a prefix to indicate this is a Mabon-managed request, the managed bean name, and the method needed. This syntax - mabon:/<managed bean><method> - defined by the Mabon contract is used to return a target URL referencing the managed bean.
Summary
This article discussed how you can use AJAX to fetch data and leverage the JSF managed bean facility as a data source. It also covered the different XMLHttpRequest response types - responseText and responseXML - that you can use to return the result from the server. We also showed you how to use the eval() function to parse JSON-syntax responses efficiently.
We covered a new Open Source project called Mabon that extends JSF to provide a custom lifecycle that invokes a managed bean method remotely and then transfers the result to the client using JSON syntax.
In our next article in this series of building Rich Internet Components with JavaServer Faces, we're going to look at how we can bring the knowledge from the three previous articles together and create a <jdj:inputSuggest> component that leverages AJAX, Mabon, and Weblets.
This article is based on, and contains excerpts from, the book Pro JSF and Ajax: Building Rich Internet Components by Jonas Jacobi and John Fallows, to be published by Apress in February 2006.