Handling Events in JavaServer Faces, Part 1
by Hans BergstenEditor's note: O'Reilly's JavaServer Faces offers developers a guide to learning how to use the JSF framework to build web applications. In this excerpt from the book, author Hans Bergsten looks at the JSF event model, using examples to help explain what's going on "under the hood." Next week, in part two of this excerpt, Hans implements event handling for parts of the sample application discussed here.
|
Related Reading
JavaServer Faces |
Handling Events
When the user clicks a button or link, changes a value in a field, or makes a selection in a list, the application may need to react. JSF user interface components signal user actions by firing an event handled by application code that has registered itself to be notified of the event. It's a model borrowed from traditional GUI frameworks, making it easy to develop and maintain the code for each specific user action in a separate code module. You can even use multiple event handling modules for different aspects of the processing, such as one that logs the action and another that acts on it.
On the surface, the JSF model looks the same as the event model used for standalone applications, but there's a twist: with JSF, the user actions take place in a client (e.g., a browser) that has no permanent connection to the server, so the delivery of some types of event is delayed until a new connection is established (e.g., when the user submits a form). To deal with this difference, JSF defines a strict request processing lifecycle, where events are generated and handled in different phases.
In this chapter, we first look at the event model and how it relates to the request processing lifecycle to understand what's going on. We then implement event handling for parts of the sample application.
Understanding the JSF Event Model
The JSF event model is based on the event model defined by the JavaBeans specification. In this model, an event is represented by an instance of an event class. An event source object fires an event by calling an event notification method on event listener objects registered to receive the event, passing a reference to the event object as a notification method argument.
Let's look at what this means in more detail. All
JSF event classes extend the
javax.faces.event.FacesEvent class:
package javax.faces.event;
import java.util.EventObject;
import javax.faces.component.UIComponent;
...
public abstract class FacesEvent extends EventObject {
public FacesEvent(UIComponent component) {
super(component);
}
public UIComponent getComponent( ) {
return ((UIComponent) getSource( ));
}
...
}
The FacesEvent class extends
the standard Java event superclass
java.util.EventObject and has a constructor that
takes the UIComponent event source object as an
argument. It also implements a type-safe accessor method for the
event source object.
When a user clicks a button, it triggers an event represented by the
javax.faces.event.ActionEvent class:
package javax.faces.event;
import javax.faces.component.UIComponent;
public class ActionEvent extends FacesEvent {
public ActionEvent(UIComponent component) {
super(component);
}
...
}
Other events are represented by similar concrete subclasses, such as
the javax.faces.event.ValueChangeEvent, which
signals a value change.
Along with the event classes, there are listener interfaces declaring
the methods that the event source calls to notify listeners of the
event. A listener interface can contain methods for many related
events, but for the JSF component events, there's a
separate interface per event. Here's the
javax.faces.event.ActionListener interface:
package javax.faces.event;
import javax.faces.component.UIComponent;
public interface ActionListener extends FacesListener {
public void processAction(ActionEvent event)
throws AbortProcessingException;
}
The ActionListener interface
extends the
javax.faces.event.FacesListener interface and
defines one method, taking an ActionEvent instance
as the single argument.
Classes that want to be informed about events are called
event listeners. They declare which events they
are interested in by implementing the corresponding
listener interfaces. Hence,
an event listener that wants to deal with the
ActionEvent fired by a command component declares
its intent like this:
package com.mycompany.expense.ui;
import javax.faces.event.ActionListener;
public class ReportHandler implements ActionListener {
...
public void processAction(ActionEvent e)
throws AbortProcessingException {
...
}
}
To prevent other listeners from seeing an event, all JSF
event-processing methods can throw a
javax.faces.event.AbortProcessingException. This
is rarely needed, but can come in handy when serious problems occur
while processing the event. If the event notification method throws
this exception, JSF stops the event processing immediately.
Event source classes, like UICommand, declare
the type of events they can fire by
providing methods for registering and deregistering the corresponding
event listeners:
public void addActionListener(ActionListener listener) {
addFacesListener(listener);
}
public void removeActionListener(ActionListener listener) {
removeFacesListener(listener);
}
The methods follow the JavaBeans conventions: the method names are
made from the words add and
remove followed by the listener interface name,
and both methods take an instance of the listener as the single
argument.
The addFacesListener() and
removeFacesListener() methods called by the
registration and deregistration methods are protected methods
implemented by UIComponentBase, so that the task
of maintaining the listener list doesn't have to be
implemented by all subclasses.
When a component notices that a user event has happened, it creates an instance of the corresponding event class and adds it to an event list. Eventually, JSF tells the component to fire the event, i.e., loop through the list of listeners for that event and call the event notification method on each one.