Tuesday, January 2, 2007

Using JavaServer Faces Technology with AJAX

Problem Description

The Java 2 Enterprise Edition includes JavaServer Faces technology, which provides a mature and extensible user interface component model. The design of this model makes it easy for application developers to create custom components by extending the standard components included with JavaServer Faces technology and to reuse these components across applications.

The example featured in this entry includes a JavaServer Faces text field component that takes the name of a city. When the user begins entering characters in the field, the application uses AJAX to perform auto completion on the data by matching the user's input to a list of cities stored on the server. In two of the versions of this example, the text field component is a custom JavaServer Faces component that provides the AJAX support. This component shields the page author from the complexities of AJAX by rendering all the HTML and JavaScript code that is required to incorporate AJAX capabilities into an application.

A third version of this example adds AJAX support to a standard JavaServer Faces component through a servlet that interacts with the JavaServer Faces component and other JavaServer Faces server-side objects to generate the appropriate auto completion data.

This entry focuses primarily on the question, "How can I incorporate AJAX functionality into a JavaServer Faces application?"

Among the other questions that this entry considers are the following:

* Should AJAX request handling be done in a separate servlet controller or should it be handled by the JavaServer Faces technology life cycle?
* How can existing components be AJAX-enabled without rewriting them?
* Should JavaScript code be embedded in Java code or externalized in a separate script file?
* How does the programming model differ between component writer and page developer?
* How should I organize the project structure in my workspace?
* How should I package the JavaServer Faces objects and JavaScript artifacts into a WAR file or EAR file?

Solution

Developers who want to include AJAX support in JavaServer Faces applications have more than one strategy to choose from. Which strategy they choose depends on the needs of their situation. Developers might ask themselves the following questions to help them decide which strategy to select:

* Is this a new application or is the development team adding AJAX support to an existing JavaServer Faces application?
* Is the page author capable of adding the JavaScript and AJAX code to the page, or should it be handled by a server-side component so that the page author can simply add the component tag to the page?
* Must the AJAX code be reusable?
* Must the AJAX code be customizable?
* Must the AJAX code be usable outside of the JavaServer Faces technology runtime?
* Is the application developer capable of doing the extra programming required to synchronize data with the JavaServer Faces objects if the development team decides to de-couple the AJAX code from the JavaServer Faces runtime?

This entry discusses three strategies for incorporating AJAX support into a JavaServer Faces application. The first two strategies involve creating a custom component to generate the necessary JavaScript and to make AJAX interactions with another server-side component. In Strategy 1, the JavaServer Faces technology life cycle and the custom component handle the AJAX requests. In Strategy 2, the custom component communicates with a special servlet, which handles all the AJAX requests. Strategy 3 does not require any custom components; all the additional AJAX support is performed by a servlet provided by the developer.

Strategy 1 and Strategy 2 require knowledge of the JavaServer Faces technolgy life cycle to implement them. Strategy 3 requires knowledge of the Java servlet API.

The following sections explain the three strategies and provide more detail on their advantages and disadvantages. After reading about these strategies and answering the list of questions above, developers should be able to select the strategy that is appropriate for their particular situation. The Comparison of Strategies section includes a chart that summarizes the characteristics of each strategy, which makes it even easier to make the decision.

Strategy 1: A Custom JavaServer Faces Component Renders Client-side AJAX JavaScript and Processes AJAX Requests

In this strategy the JavaServer Faces component does three things:
o Renders the HTML for the form elements
o Renders the links to the JavaScript code that handles the form events
o Processes AJAX requests

Figure 1 illustrates how this strategy works. The numbered steps following the figure explain what is happening in the figure.


Figure 1: Architecture of a JavaServer Faces Component that Renders Client-Side AJAX JavaScript and Processes AJAX Requests

The following steps explain the architecture illustrated in Figure 1:

1. The page called Enter Address Page contains an HTML script element rendered by the renderer, AutoCompleteTextRenderer.
2. A call is made to the URL, faces/autocomplete-script, which is mapped to a FacesServlet instance. This instance processes the RenderPhaseListener instance, which recognizes the URI and returns the component.js page containing the client-side JavaScript code necessary for the AJAX interactions. After the component.js page is returned, the RenderPhaseListener instance stops taking part in the JavaServer Faces technology life cycle processing for now.
3. When the user starts typing in the City text field, an onkeypress event occurs. The JavaScript function mapped to this event creates an XMLHttpRequest object and configures it with the URL to the FacesServlet instance. This URL is faces/autocomplete&id=San. The id=San part of the URL is a URL parameter in which San is the user input that is matched against the list of cities. The XMLHttpRequest object makes a call to the FacesServlet instance. HTML page events continue to be processed by the JSP page.
4. The FacesServlet instance processes the RenderPhaseListener instance that recognizes the URL faces/autocomplete and looks up the AutoCompleteTextField component, which provides the completion logic.
5. The RenderPhaseListener instance generates an XML document containing the potential completion items and returns it to the XMLHttpRequest object, which then calls the XMLHttpRequest callback function.
6. The XMLHttpRequest callback function updates the HTML DOM based on the contents of the XML document that was returned.
7. After entering the form data, the user clicks the Update button and the form is posted using an HTTP POST to the FacesServlet instance, which updates SessionBean with the address information entered by the user.

The remainder of this section provides further details on how this strategy works.

In Figure 1, the page called Enter Address Page is a JavaServer Faces page. This page contains a tag that represents the AutocompleteTextField component, which is a JavaServer Faces component. This component renders two things: an HTML text field, into which the user enters the name of a city, and the necessary client-side JavaScript AJAX code that fetches possible city names from a server-side managed bean. This managed bean contains a method that implements the algorithm for completing city names.

To add the AJAX-enabled component to the application, the page author need only include the following tag in the page:

completionMethod="#{AutoCompleteTextfield.completeCity}"
value="#{SessionBean.city}" required="true"
/>

Code Example 1: Declaring an AJAX-enabled JavaServer Faces Component in a JavaServer Faces page

The AutoCompleteTextFieldTag tag handler implements the ajaxTags:completionField tag, which is rendered to HTML by the renderer, AutoCompleteTextFieldRenderer. The first time the component is encountered in the page, AutoCompleteTextFieldRenderer renders a reference to external JavaScript code, as shown in this HTML fragment:

script type="text/javascript" src="faces/autocomplete-script">

Code Example 2: Rendered HTML that references JavaScript to be returned by RenderPhaseListener

When the reference to the external script file is reached, the browser makes a request to the URL, faces/autocomplete-script. The request first goes to the FacesServlet instance and is then received by the RenderPhaseListener instance, which returns the client-side JavaScript relevant to the AJAX interactions of the component. In the case of this example, the component.js file is included with the component and returned using the class loader. Bundling the relevant JavaScript code with the component is a good practice because it keeps the script and component close to each other but keeps the JavaScript out of the component code. The AJAX client-side JavaScript contains the XMLHttpRequest callback method, which includes the event mappings to the form elements rendered by AutoCompleteTextFieldRenderer.

When the target onkeypress event is encountered in the City form field, the client-side XMLHttpRequest object makes a HTTP GET call with the partial text input from the field to the FacesServlet instance, which in turn calls the RenderPhaseListener instance. The RenderPhaseListener instance looks up the AutoCompleteTextField component and gets a set of city names that match partial text input. For example, if the user entered "San" into the field, the component would fetch the set of city names that start with the string "San".

At this point, the RenderPhaseListener instance can access the view state associated with the page if it is needed to process the interaction. When processing AJAX requests with JavaServer Faces components, a developer can treat each AJAX request as a new request by using simple HTTP GET calls. In this case, every request is a new Faces request. Or the developer can use POST calls, which give access to the view state associated with the JavaServer Faces page. Strategy 1A provides further details on using POST calls to access view state. When using AJAX GET requests with JavaServer Faces technology, a developer has access to managed bean components and has the ability to evaluate expressions but does not have access to the view state.

In the case of this example, each auto complete AJAX request is a GET request and is therefore a new Faces request. The RenderPhaseListener instance renders an XML response based on the results obtained from the AutoCompleteTextField component.

The server-side AJAX request processing is provided by a RenderPhaseListener instance. From the perspective of a server-side developer, the requests are a little different from other AJAX requests in that they usually do not go through all the phases of the JavaServer Faces technology life cycle. The following code shows how the RenderPhaseListener instance handles an AJAX request:

private void handleAjaxRequest(PhaseEvent event) {
FacesContext context = event.getFacesContext();
HttpServletResponse response =
(HttpServletResponse)context.getExternalContext().getResponse();
..
if ("complete".equals(action)) {
String method = request.getParameter("method");
try {
//get completion items
response.setContentType("text/xml");
response.setHeader("Cache-Control", "no-cache");
//Code to write XML response goes here...

event.getFacesContext().responseComplete();
return;
} catch (EvaluationException ee) {
ee.printStackTrace();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}


Code Example 3: PhaseListener code to handle AJAX requests

One thing the developer needs to remember is that the content type must be set to "text/xml" and the Cache-Control header also needs to be set to "no-cache" as shown in the following code snippet, which is taken from Code Example 3.

response.setContentType("text/xml");
response.setHeader("Cache-Control", "no-cache");

The call to event.getFacesContext().responseComplete() following the creation of the XML response causes the rest of the JavaServer Faces life cycle to be skipped.

The response is returned to the XMLHttpRequest callback method, which updates the HTML DOM with the list of potential city names.

The end user might then select a relevant city name from the list returned from the AutoCompleteTextField component and click on the Update button, which causes the form to be submitted to the FacesServlet instance, after which the SessionBean object is updated.

This strategy makes it easy for page authors to add AJAX-enabled components to a JavaServer Faces page. As long as the component is added to the application, all they need to do is include the corresponding component tag in the page.

The component developer can make the component more customizable by allowing external JavaScript files to override the embedded file as well as allowing the use of external style sheets. One way to do this is to include JavaScript and CSS properties on the component that the user can override. For example, the AutocompletionTextField component can have an ondisplay property that lets users of the component specify JavaScript code that is executed when an item is selected from the popup. Similarly, a selectedItemStyle property could let the user specify a CSS or a CSS style class to be applied to selected items in the popup.

Strategy 1A: Doing AJAX processing against the full View

This approach is used by the AJAX Progress Bar for JavaServer Faces Technology. As with Strategy 1, FacesServlet is used to orchestrate all the processing of AJAX requests and all the serving of script files. The main difference between Strategy 1 and Strategy 1A is the use of POST to submit the actual view state back to the server. Doing this requires the AJAX-enabled components to abort normal page processing to prevent rendering of the entire JSP page for AJAX requests. This is done the same way as in Strategy 1, using the responseComplete method on the FacesContext instance. Another difference in the architecture of the progress bar example is that the PhaseListener instance is responsible only for rendering the script, whereas ProgressBarRenderer writes out the response XML that is handled by the AJAX JavaScript code.

Another complexity resulting from the polling nature of the progress bar is the need for a closure to the function passed to the setTimeout JavaScript method. The progress bar uses the technique described by Richard Cornford at http://jibbering.com/faq/faq_notes/closures.html. This is a very handy technique when coding AJAX-enabled components.

Performance should be considered when determining how much view state is associated with each request relative to the volume of AJAX requests being processed when using this approach. While this approach is more tightly coupled with the JavaServer Faces life cycle and therefore provides ready access to Faces objects, this tight coupling could result in a performance degradation compared to the strategy of using a separate AJAX controller, which would not have a tight coupling to the view state.

Strategy 2: A Custom JavaServer Faces Component with Separate AJAX Controller

In this strategy the JavaScript rendered by the JavaServer Faces component communicates with a separate servlet. All asynchronous requests will therefore be handled outside of the JavaServer Faces technology life cycle processing. However, the separate servlet can look up the FacesContext instance and evaluate value binding and method binding expressions so that it can do such things as find managed beans and make calls to them.

Figure 2 illustrates this strategy. The numbered steps following the figure explain what is happening in the figure.


Figure 2: Architecture of a JavaServer Faces Component with separate AJAX Controller

The following list describes the interactions shown in Figure 2:
1. The page, called Enter Address Page, contains an HTML script element that is rendered by the renderer, AutoCompleteTextRenderer.
2. A call is made to the URL, faces/autocomplete-script, which is mapped to the FacesServlet instance. This instance processes the RenderPhaseListener instance, which recognizes the URL and returns the component.js page containing the client-side JavaScript code necessary for the AJAX interactions. After the component.js page is returned, the RenderPhaseListener instance stops contributing to the JavaServer Faces technology life cycle processing for now.
3. When the user enters text into the City text field, an onkeypress event is generated. The JavaScript function mapped to this event creates an XMLHttpRequest object and configures it with the URL to the AjaxControllerServlet instance. This URL is autocomplete&id=San. The id=San part of the URL is a URL parameter in which San is the user input that is matched against the list of cities.
4. The XMLHttpRequest object makes the call to the AjaxControllerServlet instance, and HTML page events continue to be processed by the page. The AjaxControllerServlet instance looks up the AutoCompleteTextField component and gets a list of potential completion items.
5. The AjaxControllerServlet instance generates an XML document containing the potential completion items and returns it to the XMLHttpRequest object. The XMLHttpRequest object then calls the XMLHttpRequest callback function.
6. The XMLHttpRequest callback function updates the HTML DOM with the list of city names, which are contained in the returned XML document. At this point, users can select a city name, and when they do, the form element's value is set with the selected value.
7. After entering the form data, the user clicks the Update button and the form is POSTed to the FacesServlet instance, which updates the SessionBean object with the address information entered by the user.

This strategy provides a clean separation of the AJAX interaction logic and the traffic that is usually handled by the FacesServlet instance. Each AJAX request is a new request that the JavaServer Faces technology life cycle does not see, meaning that AjaxControllerServlet does not have access to the JavaServer Faces page view state. The AJAX servlet can communicate with the managed beans and evaluate expressions by looking up the FacesContext instance and calling the corresponding code.

The strategy is similar to Strategy 1 in that all "plumbing" is generated by the JavaServer Faces component itself. In this strategy, the JavaServer Faces component is depending on a separate dedicated servlet to handle the JavaScript events it is adding to the rendered HTML. Updates to the JavaServer Faces model data occur outside the scope of the JavaServer Faces technology life cycle request/response processing, and so care should be taken that model data is consistent.

Strategy 3: Retrofitting an Existing JavaServer Faces Application

In this strategy, no custom JavaServer Faces components are used. As in Strategy 2, a separate dedicated AJAX servlet is used, and custom JavaScript code is added to the JSP page for the purpose of communicating with the servlet. Developers are responsible for all the "plumbing". This means that they must provide the code that handles JavaScript events associated with the JavaServer Faces components, makes asynchronous calls, and updates the HTML document when responses arrive.

Figure 3 illustrates this strategy. The numbered steps following the figure explain what is happening in the figure.


Figure 3: Retrofitting an Existing JavaServer Faces Application

The following steps explain the architecture of the JavaServer Faces application shown in Figure 3:

1. The page, called Enter Address Page, contains the client-side JavaScript code necessary for the AJAX interactions.
2. When the user types in the City text field, an onkeypress event is generated. The JavaScript function mapped to this event creates an XMLHttpRequest object and configures it as a URL to the AjaxControllerServlet instance. This URL is autocomplete&id=San. The id=San part of the URL is a URL parameter in which San is the user input that is matched against the list of cities. After the XMLHttpRequest object makes the call to AjaxControllerServlet, the page continues to process HTML page events.
3. AjaxControllerServlet looks up the CitiesBean managed bean and gets a list of potential completion items.
4. The AjaxControllerServlet instance generates an XML document containing the potential completion items and returns it to the XMLHttpRequest object. The XMLHttpRequest object calls the XMLHttpRequest callback function.
5. The XMLHttpRequest callback function updates the HTML DOM based on the contents of the XML document that was returned.
6. After entering the form data, the user clicks the Update button and the form is POSTed to the FacesServlet instance, which updates the SessionBean object with the respective address information.

Figure 3 is very similar to Figure 2, which illustrates Strategy 2, with one exception: In Strategy 2, the servlet is provided with the JavaServer Faces component and handles "generic" traffic. In Strategy 3, the JavaScript and the servlet are provided by the application developer. Notice that the servlet talks about the application-specific list of cities, whereas in Strategy 2 the servlet handles generic items. This is because strategy 2 is designed to be reusable in that the solution can be utilitzed to handle various auto completion use cases, such as completing city names, state names, or people names, whereas Strategy 3 is bound to the specific use case of completing city names.

This strategy has the advantage of not relying on any special AJAX-enabled JavaServer Faces components, and therefore, any existing JavaServer Faces application can be retrofitted with custom JavaScript and a special servlet to handle AJAX behavior. However, like Strategy 2, the use of a separate servlet makes calling JavaServer Faces methods more difficult. Furthermore, it is impossible to get access to the view state of the JavaServer Faces page.

This strategy allows for more customization of the JavaScript code that provides the AJAX interaction, but it is also more difficult to maintain and requires that the page author be familiar with AJAX interactions and JavaScript. The page author is responsible for matching the events generated by the HTML elements to the JavaScript functions. Doing this might be difficult for the average page author because the HTML elements are generated by a JavaServer Faces component renderer, and therefore the page author must be somewhat familiar with the server-side renderer code. In addition, the page author is also responsible for making sure that the AJAX updates to the HTML DOM match the HTML elements rendered by a JavaServer Faces component.

The application developer is responsible for the server-side processing of AJAX using a web component, such as a servlet. Updates to the JavaServer Faces model data occur outside the scope of the JavaServer Faces technology life cycle request/response processing, so care should be taken to ensure that model data updated by a server-side component is consistent with that managed by the JavaServer Faces technology runtime.

Rendering JavaScript Exactly Once

The JavaScript should be placed in a separate file as mentioned above, to ensure that the JavaScript contents can be cached by the browser.

While the JavaServer Faces component can be used multiple times in a page, the JavaScript inclusion reference should be emitted exactly once -- before the first usage. A good way to do this is to modify the renderer that emits the script tag so that it stores a flag for itself in the request map. This flag records whether the renderer has written the script out for the current page yet. The following code snippet demonstrates how to add code to the renderer that will store the flag and check its value to determine if the script tag has been rendered:

...
private void renderScriptOnce(ResponseWriter writer, UIComponent component, FacesContext context)
throws IOException {
// Store attribute in request map when we've rendered the script such
// that we only do this once per page
Map requestMap = context.getExternalContext().getRequestMap();
Boolean scriptRendered = (Boolean)requestMap.get(RENDERED_SCRIPT_KEY);

if (scriptRendered == Boolean.TRUE) {
return;
}

// Render script and CSS here
...
}

private static final String RENDERED_SCRIPT_KEY = "bpcatalog-ajax-script-rendered";

Project Structure and Packaging

One issue that developers must deal with is how to organize their workspace. What should the project conventions be? Generally, this is covered by existing project conventions for JavaServer Faces technology modules and applications. For example, the faces-config.xml file and the TLD files for JavaServer Faces technology are usually kept in the src/conf directory.

But where should you keep the JavaScript files? Should they be kept with the source code files? In most cases, we recommend that the JavaScript files be kept in the src/resources directory. In some cases, you might be using a pre-existing library of JavaScript files, which might be reusable even without the JavaServer Faces components, such as in the case of making a JavaServer Faces component that uses some of those existing JavaScript files. In this instance, the JavaScript library should be viewed as a separate project and should be used only by the JavaServer Faces application.

Another issue to consider is how to package these JavaServer Faces components and JavaScript artifacts into a WAR file or EAR file. The packaging is mostly the same as for other JavaServer Faces modules and applications, but you need to consider where to put the AJAX artifacts, which include the javascript files. Generally, it is recommended that you place the AJAX artifacts with the compiled classes so that the JavaServer Faces components can load them by calling getClass().getResourceAsStream().

In order to use the components within an IDE, there are additional packaging considerations, such as how to make properties visible in IDE property sheets. These considerations differ from IDE to IDE (until this hopefully gets standardized with JSR 273). For a description of how to package your JavaServer Faces component for Sun Java Studio Creator