ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.


AddThis Social Bookmark Button

Handling Events in JavaServer Faces, Part 1
Pages: 1, 2, 3

The Request Processing Lifecycle Phases

So far, so good. If you've programmed with GUI frameworks or in other event-driven environments, the JSF event model should look familiar. But as I mentioned in the introduction, the fact that JSF operates with a disconnected client that only occasionally communicates with the server where the application runs requires a few twists to the model.

First of all, the application may declare that the component instances should not be saved on the server between requests, due to memory usage concerns. JSF must then save enough information somewhere (e.g., in the response) to be able to reconstruct the component tree and restore all component state when it receives the next request. In other words, component instances may come and go in a JSF application, as opposed to a GUI application where they remain in memory as long as the application runs.

JSF must also deal with the fact that value changes happen in the client and that the server can't detect the changes until it receives a new request with the new values. One possible work-around is to add client-side scripting to the mix, so that value changes causes an immediate request; unfortunately, that solution can make the application feel sluggish, and it doesn't work at all if the client doesn't support scripting.

Button and link clicks are a bit easier to deal with, because they cause a new request always to be sent. Events corresponding to these user actions can, however, be classified further into events that affect only the user interface (e.g., show the next set of rows in a table or change from a brief to a detailed display) and events that must be processed by backend code (e.g., permanently save the values entered in a form or finalize a reservation request). For user interface events, the backend typically shouldn't be bothered at all. An event that involves the backend, on the other hand, must not be processed until all model properties have been updated with the new values received with the request.

JSF deals with all these concerns by defining a request processing lifecycle with well-defined phases (shown in Figure 8-1), where different activities take place and events fire in an orderly manner at the appropriate time.

Figure 8-1. Request processing lifecycle phases

The phases are described in detail in Appendix C, but here's a quick rundown:

  • Next, in the Apply Request Values phase, each component in the view looks for its own value in the request and saves it.

  • When the model properties have been populated with the latest values, event listeners may call backend code to process the new data in the Invoke Application phase.

  • Finally, a response to the request is sent, using the same view or a new one. This happens in the Render Response.

If the user triggered the request by clicking a button or a link, the corresponding UICommand component discovers this in the Apply Request Values phase when it finds a request parameter with a name that matches its ID. It creates an ActionEvent to represent the event, but doesn't notify the listeners immediately, because the rest of the components in the tree may not have picked up their values yet. Instead it adds it to a queue by calling its queueEvent() method.

At the end of the Apply Request Values phase, when all components know about their new values, it's safe to fire an ActionEvent that affects the user interface. JSF asks the UICommand that queued it to notify all listeners by calling its broadcast() method. But as you'll see soon, that's not the whole story: sometimes this event must not be broadcast until the Invoke Application phase.

Value change event handling happens in the Process Validations phase in a similar fashion, by default. A component that discovers that a value in the request is both valid and different from its previous value queues a ValueChangeEvent. Because the user may have changed a number of values since the previous request, it is likely that more than one event is queued in this phase. At the end of the phase, JSF scans the queue and calls the broadcast( ) method on the component that queued a ValueChangeEvent.

A listener processing an event may in turn do things that cause other events to be queued, so JSF continues to scan the queue at the end of a phase until the event queue is empty before moving on to the next phase.

Handling Application Backend Events

Separate the queuing from the firing of the event is the easy part. Dealing with the two subcategories of ActionEvent requires one more twist. From what I've said so far, it looks like an ActionEvent is always processed at the end of the Apply Request Values phase, but that's only appropriate for user interface events. As you may recall, an ActionEvent that requires backend processing must not be handled until the model properties are updated, i.e., after the Update Model Values phase at the earliest.

Let's look at some real examples of action handling using the report entry form in the sample application. We start with events that invoke backend logic in this section and look at user interface events in the next.

As you may recall, the report entry form has three fields for entering a date, an expense type and an amount for a report entry, and an Add button. Figure 8-2 shows the form produced by the version of the page we use in this section.

Figure 8-2
Figure 8-2. The report entry form area with a few entries

The Add button is a typical example of the most common type of event handling, namely a button that fires an event that requires processing by the application backend. When the user clicks this button, a new entry with the values entered in the fields is added to the current report.

Example 8-1 shows a version of the JSP page where the Add button is bound to an event handling method that invokes the backend code for adding the entry.

Example 8-1. Entry form area JSP page with an Add button action reference (expense/stage2/entryFormArea.jsp)

<%@ page contentType="text/html" %>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>

    <h:inputText id="title" size="30" required="true"
      value="#{reportHandler.currentReport.title}" />
    <h:message for="title" />
    <h:inputText id="date" size="8" required="true"
      <f:convertDateTime dateStyle="short" />
    <h:message for="date" />
    <h:selectOneMenu id="type" required="true"
      <f:selectItems value="#{entryHandler.expenseTypeChoices}"/>
    <h:message for="type" />
    <h:inputText id="amount" size="8" required="true"
      <f:convertNumber pattern="#,##0.00" />
      <f:validateDoubleRange minimum="1"/>
    <h:message for="amount" />
    <h:commandButton value="Add"
      action="#{entryHandler.add}" />
  <h:messages globalOnly="true" />

  <%-- Loop to verify that it works --%>
  <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
    <c:forEach items="${reportHandler.currentReportEntries}" var="e">
      <li>Date: ${e.date}, Type: ${e.type}, Amount: ${e.amount}</li>

The only real difference compared to the version of the page we used in Chapter 7 is the action attribute for the Add button <h:commandButton> action element. Of less interest at this point is the JSTL <c:forEach> action that lists all entries in the current report at the end of the page. I added the loop just to verify that the Add button's event handler really does what it's supposed to do. This loop gets the report entries from the report handler we looked at in Chapter 6. I'm not showing you the details here, but I promise to return to them when we replace the plain list with a real report entries table in Chapter 10.

The action attribute contains a method binding expression. The method binding expression syntax is similar to that of a value binding expression. It's a subset of the JSF EL that evaluates to a bean method with a certain signature. In Example 8-1, the method binding expression points to a method named add in the EntryHandler instance available through the entryHandler variable. Before we look at the add() method, let's see how it relates to the event listeners and the request processing lifecycle we discussed earlier.

Pages: 1, 2, 3

Next Pagearrow