The Java EE 5 Tutorial

Chapter 12 Developing with JavaServer Faces Technology

Chapter 11, Using JavaServer Faces Technology in JSP Pages shows how the page author can bind components to server-side objects by using the component tags and core tags on the JSP page. The application developer’s responsibility is to program the server-side objects of a JavaServer Faces application. These objects include backing beans, converters, event handlers, and validators.

This chapter uses the Duke’s Bookstore application (see The Example JavaServer Faces Application) to explain all of the application developer’s responsibilities, including

Writing Bean Properties

As explained in Backing Beans, a backing bean property can be bound to one of the following items:

These properties follow JavaBeans component conventions (see JavaBeans Components).

The UI component’s tag binds the component’s value to a property using its value attribute and binds the component’s instance to a property using its binding attribute, as explained in Binding Component Values and Instances to External Data Sources. Likewise, all the converter, listener, and validator tags use their binding attributes to bind their associated implementations to backing bean properties, as explained in Binding Converters, Listeners, and Validators to Backing Bean Properties.

To bind a component’s value to a backing bean property, the type of the property must match the type of the component’s value to which it is bound. For example, if a backing bean property is bound to a UISelectBoolean component’s value, the property should accept and return a boolean value or a Boolean wrapper Object instance.

To bind a component instance, the property must match the component type. For example, if a backing bean property is bound to a UISelectBoolean instance, the property should accept and return UISelectBoolean.

Similarly, in order to bind a converter, listener, or validator implementation to a property, the property must accept and return the same type of converter, listener, or validator object. For example, if you are using the convertDateTime tag to bind a DateTime converter to a property, that property must accept and return a DateTime instance.

The rest of this section explains how to write properties that can be bound to component values, to component instances for the component objects described in Adding UI Components to a Page Using the HTML Component Tags, and to converter, listener, and validator implementations.

Writing Properties Bound to Component Values

To write a backing bean property bound to a component’s value, you must know the types that the component’s value can be so that you can make the property match the type of the component’s value.

Table 12–1 lists all the component classes described in Adding UI Components to a Page Using the HTML Component Tags and the acceptable types of their values.

When page authors bind components to properties using the value attributes of the component tags, they need to ensure that the corresponding properties match the types of the components’ values.

Table 12–1 Acceptable Types of Component Values

Component 

Acceptable Types of Component Values 

UIInput, UIOutput, UISelectItem, UISelectOne

Any of the basic primitive and numeric types or any Java programming language object type for which an appropriate Converter implementation is available.

UIData

array of beans, List of beans, single bean, java.sql.ResultSet, javax.servlet.jsp.jstl.sql.Result, javax.sql.RowSet.

UISelectBoolean

boolean or Boolean.

UISelectItems

java.lang.String, Collection, Array, Map.

UISelectMany

array or List. Elements of the array or List can be any of the standard types.

UIInput and UIOutput Properties

The following tag binds the name component to the name property of CashierBean.

<h:inputText id="name" size="50"
    value="#{cashier.name}"
    required="true">
     <f:valueChangeListener
         type="com.sun.bookstore6.listeners.NameChanged" />
 </h:inputText>

Here is the bean property bound to the name component:

protected String name = null;
 public void setName(String name) {
    this.name = name;
}
public String getName() {
    return this.name;
}

As Using the Standard Converters describes, to convert the value of a UIInput or UIOutput component, you can either apply a converter or create the bean property bound to the component with the desired type. Here is the example tag explained in Using DateTimeConverter that displays the date books will be shipped:

<h:outputText value="#{cashier.shipDate}">
    <f:convertDateTime dateStyle="full" />
</h:outputText>

The application developer must ensure that the property bound to the component represented by this tag has a type of java.util.Date. Here is the shipDate property in CashierBean:

protected Date shipDate;
public Date getShipDate() {
    return this.shipDate;
}
public void setShipDate(Date shipDate) {
    this.shipDate = shipDate;
}

See Binding Component Values and Instances to External Data Sources for more information on applying a Converter implementation.

UIData Properties

UIData components must be bound to one of the types listed in Table 12–1. The UIData component from the bookshowcart.jsp page of the Duke’s Bookstore example is discussed in the section Using Data-Bound Table Components. Here is part of the start tag of dataTable from that section:

<h:dataTable  id="items"
    ...
    value="#{cart.items}"
    var="item" >

The value expression points to the items property of the ShoppingCart bean. The ShoppingCart bean maintains a map of ShoppingCartItem beans.

The getItems method from ShoppingCart populates a List with ShoppingCartItem instances that are saved in the items map from when the customer adds books to the cart:

public synchronized List getItems() {
    List results = new ArrayList();
    results.addAll(this.items.values());
    return results;
}

All the components contained in the UIData component are bound to the properties of the ShoppingCart bean that is bound to the entire UIData component. For example, here is the outputText tag that displays the book title in the table:

<h:commandLink action="#{showcart.details}">
    <h:outputText value="#{item.item.title}"/>
</h:commandLink>

The book title is actually a hyperlink to the bookdetails.jsp page. The outputText tag uses the value expression #{item.item.title} to bind its UIOutput component to the title property of the Book bean. The first item in the expression is the ShoppingCartItem instance that the dataTable tag is referencing while rendering the current row. The second item in the expression refers to the item property of ShoppingCartItem, which returns a Book bean. The title part of the expression refers to the title property of Book. The value of the UIOutput component corresponding to this tag is bound to the title property of the Book bean:

private String title = null;
public String getTitle() {
    return this.title;
}
public void setTitle(String title) {
    this.title=title;
}

UISelectBoolean Properties

Properties that hold the UISelectBoolean component’s data must be of boolean or Boolean type. The example selectBooleanCheckbox tag from the section Rendering Components for Selecting One Value binds a component to a property. Here is an example that binds a component value to a property:

<h:selectBooleanCheckbox title="#{bundle.receiveEmails}"
     value="#{custFormBean.receiveEmails}" >
</h:selectBooleanCheckbox>
<h:outputText value="#{bundle.receiveEmails}">

Here is an example property that can be bound to the component represented by the example tag:

    protected boolean receiveEmails = false;
        ...
    public void setReceiveEmails(boolean receiveEmails) {
        this.receiveEmails = receiveEmails;
    }
    public boolean getReceiveEmails() {
        return receiveEmails;
    }

UISelectMany Properties

Because a UISelectMany component allows a user to select one or more items from a list of items, this component must map to a bean property of type List or array. This bean property represents the set of currently selected items from the list of available items.

Here is the example selectManyCheckbox tag from Rendering Components for Selecting Multiple Values:

<h:selectManyCheckbox
    id="newsletters"
    layout="pageDirection"
    value="#{cashier.newsletters}">
    <f:selectItems value="#{newsletters}"/>
</h:selectManyCheckbox>

Here is a bean property that maps to the value of this selectManyCheckbox example:

protected String newsletters[] = new String[0];

public void setNewsletters(String newsletters[]) {
    this.newsletters = newsletters;
}
public String[] getNewsletters() {
    return this.newsletters;
}

As explained in the section Rendering Components for Selecting Multiple Values, the UISelectItem and UISelectItems components are used to represent all the values in a UISelectMany component. See UISelectItem Properties and UISelectItems Properties for information on how to write the bean properties for the UISelectItem and UISelectItems components.

UISelectOne Properties

UISelectOne properties accept the same types as UIInput and UIOutput properties. This is because a UISelectOne component represents the single selected item from a set of items. This item can be any of the primitive types and anything else for which you can apply a converter.

Here is the example selectOneMenu tag from Displaying a Menu Using the selectOneMenu Tag:

<h:selectOneMenu   id="shippingOption"
    required="true"
    value="#{cashier.shippingOption}">
    <f:selectItem
        itemValue="2"
        itemLabel="#{bundle.QuickShip}"/>
    <f:selectItem
        itemValue="5"
        itemLabel="#{bundle.NormalShip}"/>
    <f:selectItem
        itemValue="7"
        itemLabel="#{bundle.SaverShip}"/>
 </h:selectOneMenu>

Here is the property corresponding to this tag:

protected String shippingOption = "2";

public void setShippingOption(String shippingOption) {
    this.shippingOption = shippingOption;
}
public String getShippingOption() {
    return this.shippingOption;
}

Note that shippingOption represents the currently selected item from the list of items in the UISelectOne component.

As explained in the section Displaying a Menu Using the selectOneMenu Tag, the UISelectItem and UISelectItems components are used to represent all the values in a UISelectOne component. See UISelectItem Properties and UISelectItems Properties for information on how to write the backing bean properties for the UISelectItem and UISelectItems components.

UISelectItem Properties

A UISelectItem component represents one value in a set of values in a UISelectMany or UISelectOne component. The backing bean property that a UISelectItem component is bound to must be of type SelectItem. A SelectItem object is composed of an Object representing the value, along with two Strings representing the label and description of the SelectItem object.

The Duke’s Bookstore application does not use any UISelectItem components whose values are bound to backing beans. The example selectOneMenu tag from Displaying a Menu Using the selectOneMenu Tag contains selectItem tags that set the values of the list of items in the page. Here is an example bean property that can set the values for this list in the bean:

SelectItem itemOne = null;

SelectItem getItemOne(){
    return itemOne;

}

void setItemOne(SelectItem item) {
    itemOne = item;
}

UISelectItems Properties

UISelectItems components are children of UISelectMany and UISelectOne components. Each UISelectItems component is composed of either a set of SelectItem instances or a set of SelectItemGroup instances. As described in Using the selectItems Tag, a SelectItemGroup is composed of a set of SelectItem instances. This section describes how to write the properties for selectItems tags containing SelectItem instances and for selectItems tags containing SelectItemGroup instances.

Properties for SelectItems Composed of SelectItem Instances

Using the selectItems Tag describes how the newsletters list of the Duke’s Bookstore application is populated using the application configuration resource file. You can also populate the SelectItems with SelectItem instances programmatically in the backing bean. This section explains how to do this.

In your backing bean, you create a list that is bound to the SelectItem component. Then you define a set of SelectItem objects, set their values, and populate the list with the SelectItem objects. Here is an example code snippet that shows how to create a SelectItems property:

import javax.faces.component.SelectItem;
...
protected ArrayList options = null;
protected SelectItem newsletter0 =
     new SelectItem("200", "Duke’s Quarterly", "");
...
//in constructor, populate the list
options.add(newsletter0);
options.add(newsletter1);
options.add(newsletter2);
...
public SelectItem getNewsletter0(){
    return newsletter0;
}

void setNewsletter0(SelectItem firstNL) {
    newsletter0 = firstNL;
}
// Other SelectItem properties

public Collection[] getOptions(){
    return options;
}
public void setOptions(Collection[] options){
    this.options = new ArrayList(options);
}

The code first initializes options as a list. Each newsletter property is defined with values. Then, each newsletter SelectItem is added to the list. Finally, the code includes the obligatory setOptions and getOptions accessor methods.

Properties for SelectItems Composed of SelectItemGroup Instances

The preceding section explains how to write the bean property for a SelectItems component composed of SelectItem instances. This section explains how to change the example property from the preceding section so that the SelectItems is composed of SelectItemGroup instances.

Let’s separate the newsletters into two groups: One group includes Duke’s newsletters, and the other group includes the Innovator’s Almanac and Random Ramblings newsletters.

In your backing bean, you need a list that contains two SelectItemGroup instances. Each SelectItemGroup instance contains two SelectItem instances, each representing a newsletter:

import javax.faces.model.SelectItemGroup;
...
private ArrayList optionsGroup = null;

optionsGroup = new ArrayList(2);

private static final SelectItem options1[] = {
    new SelectItem("200", "Duke’s Quarterly", "");
    new SelectItem("202",
         "Duke’s Diet and Exercise Journal", "");
};
private static final SelectItem options2[] = {
    new SelectItem("201", "Innovator’s Almanac", "");
    new SelectItem("203", "Random Ramblings", "");
};

SelectItemGroup group1 =
     new SelectItemGroup("Duke’s", null, true, options1);
SelectItemGroup group2 =
    new SelectItemGroup("General Interest", null, true,
         options2);

optionsGroup.add(group1);
optionsGroup.add(group2);
...
public Collection getOptionsGroup() {
    return optionsGroup;
}
 public void setOptionsGroup(Collection newGroupOptions) {
    optionsGroup = new ArrayList(newGroupOptions);
}

The code first initializes optionsGroup as a list. The optionsGroup list contains two SelectItemGroup objects. Each object is initialized with the label of the group appearing in the list or menu; a value; a Boolean indicating whether or not the label is disabled; and an array containing two SelectItem instances. Then each SelectItemGroup is added to the list. Finally, the code includes the setOptionsGroup and getOptionsGroup accessor methods so that the tag can access the values. The selectItems tag references the optionsGroup property to get the SelectItemGroup objects for populating the list or menu on the page.

Writing Properties Bound to Component Instances

A property bound to a component instance returns and accepts a component instance rather than a component value. Here are the tags described in Binding a Component Instance to a Bean Property that bind components to backing bean properties:

<h:selectBooleanCheckbox
     id="fanClub"
    rendered="false"
    binding="#{cashier.specialOffer}" />
<h:outputLabel for="fanClub"
    rendered="false"
    binding="#{cashier.specialOfferText}"  >
    <h:outputText id="fanClubLabel"
        value="#{bundle.DukeFanClub}" />
</h:outputLabel>

As Binding a Component Instance to a Bean Property explains, the selectBooleanCheckbox tag renders a check box and binds the fanClub UISelectBoolean component to the specialOffer property of CashierBean. The outputLabel tag binds the fanClubLabel component (which represents the check box’s label) to the specialOfferText property of CashierBean. If the user orders more than $100 (or 100 euros) worth of books and clicks the Submit button, the submit method of CashierBean sets both components’ rendered properties to true, causing the check box and label to display when the page is re-rendered.

Because the components corresponding to the example tags are bound to the backing bean properties, these properties must match the components’ types. This means that the specialOfferText property must be of UIOutput type, and the specialOffer property must be of UISelectBoolean type:

UIOutput specialOfferText = null;

public UIOutput getSpecialOfferText() {
    return this.specialOfferText;
}
public void setSpecialOfferText(UIOutput specialOfferText) {
    this.specialOfferText = specialOfferText;
}

UISelectBoolean specialOffer = null;

public UISelectBoolean getSpecialOffer() {
    return this.specialOffer;
}
public void setSpecialOffer(UISelectBoolean specialOffer) {
    this.specialOffer = specialOffer;
}

See Backing Beans for more general information on component binding.

See Referencing a Method That Performs Navigation for information on how to reference a backing bean method that performs navigation when a button is clicked.

See Writing a Method to Handle Navigation for more information on writing backing bean methods that handle navigation.

Writing Properties Bound to Converters, Listeners, or Validators

All of the standard converter, listener, and validator tags that are included with JavaServer Faces technology support binding attributes that allow page authors to bind converter, listener, or validator implementations to backing bean properties.

The following example from Binding Converters, Listeners, and Validators to Backing Bean Properties shows a standard convertDateTime tag using a value expression with its binding attribute to bind the DateTimeConverter instance to the convertDate property of LoginBean:

<h:inputText value="#{LoginBean.birthDate}">
    <f:convertDateTime binding="#{LoginBean.convertDate}" />
</h:inputText>

The convertDate property must therefore accept and return a DateTimeConverter object, as shown here:

private DateTimeConverter convertDate;
public DateTimeConverter getConvertDate() {
    ...
    return convertDate;
{
public void setConvertDate(DateTimeConverter convertDate) {
    convertDate.setPattern("EEEEEEEE, MMM dd, yyyy");
    this.convertDate = convertDate;
}

Because the converter is bound to a backing bean property, the backing bean property is able to modify the attributes of the converter or add new functionality to it. In the case of the preceding example, the property sets the date pattern that the converter will use to parse the user’s input into a Date object.

The backing bean properties that are bound to validator or listener implementations are written in the same way and have the same general purpose.

Performing Localization

As mentioned in Rendering Components for Selecting Multiple Values, data and messages in the Duke’s Bookstore application have been localized for French, German, Spanish, and American English.

This section explains how to produce the localized error messages as well as how to localize dynamic data and messages.

Rendering Components for Selecting Multiple Values describes how page authors access localized data from the page.

If you are not familiar with the basics of localizing web applications, see Chapter 15, Internationalizing and Localizing Web Applications.

Creating a Resource Bundle

A ResourceBundle contains a set of localized messages. To learn how to create a ResourceBundle, see http://java.sun.com/docs/books/tutorial/i18n/index.html.

After you create the ResourceBundle, put it in the same directory as your classes. Much of the data for the Duke’s Bookstore application is stored in a ResourceBundle called BookstoreMessages, located in tut-install/javaeetutorial5/examples/web/bookstore/src/com/sun/bookstore/messages/.

Localizing Dynamic Data

The Duke’s Bookstore application has some data that is set dynamically in backing beans. Because of this, the beans must load the localized data themselves; the data can’t be loaded from the page.

The message method in tut-install/javaeetutorial5/examples/web/bookstore6/src/java/com/sun/bookstore6/backing/AbstractBean.java is a general-purpose method that looks up localized data used in the backing beans:

protected void message(String clientId, String key) {
    // Look up the requested message text
    String text = null;
    try {
        ResourceBundle bundle =
            ResourceBundle.getBundle("messages.BookstoreMessages",
                context().getViewRoot().getLocale());
        text = bundle.getString(key);
    } catch (Exception e) {
        text = "???" + key + "???";
    }
    // Construct and add a FacesMessage containing it
    context().addMessage(clientId, new FacesMessage(text));
}

This method gets the current locale from the UIViewRoot instance of the current request and loads the localized data for the messages using the getBundle method, passing in the path to the ResourceBundle and the current locale.

The other backing beans call this method by using the key to the message that they are trying to retrieve from the resource bundle. Here is a call to the message method from tut-install/javaeetutorial5/examples/web/bookstore6/src/java/com/sun/bookstore6/backing/ShowCartBean.java:

message(null, "Quantities Updated");

Localizing Messages

The JavaServer Faces API provides two ways to create messages from a resource bundle:

Registering Custom Error Messages includes an example of registering a ResourceBundle in the application configuration resource file.

Creating a Message with a Message Factory

    To use a message factory to create a message, follow these steps:

  1. Register the ResourceBundle instance with the application. This is explained in Registering Custom Error Messages.

  2. Create a message factory implementation. You can simply copy the MessageFactory class included with the Duke’s Bookstore application to your application.

  3. Access a message from your application by calling the getMessage(FacesContext, String, Object) method of the MessageFactory class. The MessageFactory class uses the FacesContext to access the Application instance on which the messages are registered. The String argument is the key that corresponds to the message in the ResourceBundle. The Object instance typically contains the substitution parameters that are embedded in the message. For example, the custom validator described in Implementing the Validator Interface will substitute the format pattern for the {0} in this error message:


    Input must match one of the following patterns {0}

Implementing the Validator Interface gives an example of accessing messages.

Using FacesMessage to Create a Message

Instead of registering messages in the application configuration resource file, you can access the ResourceBundle directly from the code. The validateEmail method from the Coffee Break example does this:

...
String message = "";
...
message = CoffeeBreakBean.loadErrorMessage(context,
    CoffeeBreakBean.CB_RESOURCE_BUNDLE_NAME,
         "EMailError");
context.addMessage(toValidate.getClientId(context),
    new FacesMessage(message));
...

These lines also call the loadErrorMessage to get the message from the ResourceBundle. Here is the loadErrorMessage method from CoffeeBreakBean:

public static String loadErrorMessage(FacesContext context,
     String basename, String key) {
    if ( bundle == null ) {
         try {
            bundle = ResourceBundle.getBundle(basename,
                 context.getViewRoot().getLocale());
        } catch (Exception e) {
            return null;
        }
    }
    return bundle.getString(key);
}

Creating a Custom Converter

As explained in Conversion Model, if the standard converters included with JavaServer Faces technology don’t perform the data conversion that you need, you can easily create a custom converter to perform this specialized conversion.

All custom converters must implement the Converter interface. This implementation, at a minimum, must define how to convert data both ways between the two views of the data described in Conversion Model.

This section explains how to implement the Converter interface to perform a custom data conversion. To make this implementation available to the application, the application architect registers it with the application, as explained in Registering a Custom Converter. To use the implementation, the page author must register it on a component, as explained in Registering a Custom Converter.

The Duke’s Bookstore application uses a custom Converter implementation, called tut-install/javaeetutorial5/examples/web/bookstore6/src/java/com/sun/bookstore6/converters/CreditCardConverter.java, to convert the data entered in the Credit Card Number field on the bookcashier.jsp page. It strips blanks and hyphens from the text string and formats it so that a blank space separates every four characters.

To define how the data is converted from the presentation view to the model view, the Converter implementation must implement the getAsObject(FacesContext, UIComponent, String) method from the Converter interface. Here is the implementation of this method from CreditCardConverter:

public Object getAsObject(FacesContext context,
     UIComponent component, String newValue)
         throws ConverterException {

    String convertedValue = null;
    if ( newValue == null ) {
        return newValue;
    }
    // Since this is only a String to String conversion,
     // this conversion does not throw ConverterException.
    
    convertedValue = newValue.trim();
    if ( (convertedValue.contains("-")) ||
         (convertedValue.contains(" "))) {
        char[] input = convertedValue.toCharArray();
        StringBuffer buffer = new StringBuffer(input.length);
        for ( int i = 0; i < input.length; ++i ) {
            if ( input[i] == ’-’ || input[i] == ’ ’  ) {
                continue;
            } else {
                buffer.append(input[i]);
            }
        }
        convertedValue = buffer.toString();
    }
        return convertedValue;
}

During the apply request values phase, when the components’ decode methods are processed, the JavaServer Faces implementation looks up the component’s local value in the request and calls the getAsObject method. When calling this method, the JavaServer Faces implementation passes in the current FacesContext instance, the component whose data needs conversion, and the local value as a String. The method then writes the local value to a character array, trims the hyphens and blanks, adds the rest of the characters to a String, and returns the String.

To define how the data is converted from the model view to the presentation view, the Converter implementation must implement the getAsString(FacesContext, UIComponent, Object) method from the Converter interface. Here is the implementation of this method from CreditCardConverter:

public String getAsString(FacesContext context,
     UIComponent component, Object value)
     throws ConverterException {
    
    String inputVal = null;
    if ( value == null ) {
        return null;
    }
    // value must be of the type that can be cast to a String.
    try {
        inputVal = (String)value;
    } catch (ClassCastException ce) {
        FacesMessage errMsg = MessageFactory.getMessage(
        CONVERSION_ERROR_MESSAGE_ID,
         (new Object[] { value, inputVal }));
        throw new ConverterException(errMsg.getSummary());
    }
    // insert spaces after every four characters for better
     // readability if it doesn’t already exist.
     char[] input = inputVal.toCharArray();
     StringBuffer buffer = new StringBuffer(input.length + 3);
    for ( int i = 0; i < input.length; ++i ) {
        if ( (i % 4) == 0 && i != 0) {
            if (input[i] != ’ ’ || input[i] != ’-’){
                 buffer.append(" ");
                 // if there are any "-"’s convert them to blanks.
             } else if (input[i] == ’-’) {
                buffer.append(" ");
             }
         }
         buffer.append(input[i]);
    }
     String convertedValue = buffer.toString();
    return convertedValue;
}

During the render response phase, in which the components’ encode methods are called, the JavaServer Faces implementation calls the getAsString method in order to generate the appropriate output. When the JavaServer Faces implementation calls this method, it passes in the current FacesContext, the UIComponent whose value needs to be converted, and the bean value to be converted. Because this converter does a String-to-String conversion, this method can cast the bean value to a String.

If the value cannot be converted to a String, the method throws an exception, passing the error message from the ResourceBundle, which is registered with the application. Registering Custom Error Messages explains how to register the error messages with the application. Performing Localization explains more about working with localized messages.

If the value can be converted to a String, the method reads the String to a character array and loops through the array, adding a space after every four characters.

Implementing an Event Listener

As explained in Event and Listener Model, JavaServer Faces technology supports action events and value-change events.

Action events occur when the user activates a component that implements ActionSource. These events are represented by the class javax.faces.event.ActionEvent.

Value-change events occur when the user changes the value of a component that implements EditableValueHolder. These events are represented by the class javax.faces.event.ValueChangeEvent.

One way to handle these events is to implement the appropriate listener classes. Listener classes that handle the action events in an application must implement the interface javax.faces.event.ActionListener. Similarly, listeners that handle the value-change events must implement the interface javax.faces.event.ValueChangeListener.

This section explains how to implement the two listener classes.

If you need to handle events generated by custom components, you must implement an event handler and manually queue the event on the component as well as implement an event listener. See Handling Events for Custom Components for more information.


Note –

You need not create an ActionListener implementation to handle an event that results solely in navigating to a page and does not perform any other application-specific processing. See Writing a Method to Handle Navigation for information on how to manage page navigation.


Implementing Value-Change Listeners

A ValueChangeListener implementation must include a processValueChange(ValueChangeEvent) method. This method processes the specified value-change event and is invoked by the JavaServer Faces implementation when the value-change event occurs. The ValueChangeEvent instance stores the old and the new values of the component that fired the event.

The NameChanged listener implementation is registered on the name UIInput component on the bookcashier.jsp page. This listener stores into session scope the name the user entered in the text field corresponding to the name component. When the bookreceipt.jsp page is loaded, it displays the first name inside the message:


"Thank you, {0} for purchasing your books from us."

Here is part of the NameChanged listener implementation:

...
public class NameChanged extends Object implements
     ValueChangeListener {

     public void processValueChange(ValueChangeEvent event)
        throws AbortProcessingException {
    
        if (null != event.getNewValue()) {
            FacesContext.getCurrentInstance().
                getExternalContext().getSessionMap().
                    put("name", event.getNewValue());
        }
    }
}

When the user enters the name in the text field, a value-change event is generated, and the processValueChange(ValueChangeEvent) method of the NameChanged listener implementation is invoked. This method first gets the ID of the component that fired the event from the ValueChangeEvent object. Next, it puts the value, along with an attribute name, into the session map of the FacesContext instance.

Registering a Value-Change Listener on a Component explains how to register this listener onto a component.

Implementing Action Listeners

An ActionListener implementation must include a processAction(ActionEvent) method. The processAction(ActionEvent) method processes the specified action event. The JavaServer Faces implementation invokes the processAction(ActionEvent) method when the ActionEvent occurs.

The Duke’s Bookstore application does not use any ActionListener implementations. Instead, it uses method expressions from actionListener attributes to refer to backing bean methods that handle events. This section explains how to turn one of these methods into an ActionListener implementation.

The chooselocale.jsp page allows the user to select a locale for the application by clicking on one of a set of hyperlinks. When the user clicks one of the hyperlinks, an action event is generated, and the chooseLocaleFromLink(ActionEvent) method of LocaleBean is invoked. Instead of implementing a bean method to handle this event, you can create a listener implementation to handle it. To do this, you do the following:

The listener implementation would look something like this:

...
public class LocaleChangeListener extends Object implements
     ActionListener {

    private HashMap<String, Locale> locales = null;
    
     public LocaleChangeListener() {
        locales = new HashMap<String, Locale>(4);
        locales.put("NAmerica", new Locale("en", "US"));
        locales.put("SAmerica", new Locale("es", "MX"));
        locales.put("Germany", new Locale("de", "DE"));
        locales.put("France", new Locale("fr", "FR"));
    }

    public void processAction(ActionEvent event)
        throws AbortProcessingException {
        
        String current = event.getComponent().getId();
        FacesContext context = FacesContext.getCurrentInstance();
        context.getViewRoot().setLocale((Locale)
         locales.get(current));
     }
}

Registering an Action Listener on a Component explains how to register this listener onto a component.

Creating a Custom Validator

If the standard validators don’t perform the validation checking you need, you can easily create a custom validator to validate user input. As explained in Validation Model, there are two ways to implement validation code:

Writing a Method to Perform Validation explains how to implement a backing bean method to perform validation. The rest of this section explains how to implement the Validator interface.

If you choose to implement the Validator interface and you want to allow the page author to configure the validator’s attributes from the page, you also must create a custom tag for registering the validator on a component.

If you prefer to configure the attributes in the Validator implementation, you can forgo creating a custom tag and instead let the page author register the validator on a component using the validator tag, as described in Using a Custom Validator.

You can also create a backing bean property that accepts and returns the Validator implementation you create as described in Writing Properties Bound to Converters, Listeners, or Validators. The page author can use the validator tag’s binding attribute to bind the Validator implementation to the backing bean property.

Usually, you will want to display an error message when data fails validation. You need to store these error messages in resource bundle, as described in Creating a Resource Bundle.

After creating the resource bundle, you have two ways to make the messages available to the application. You can queue the error messages onto the FacesContext programmatically. Or, you can have the application architect register the error messages using the application configuration resource file, as explained in Registering Custom Error Messages.

The Duke’s Bookstore application uses a general-purpose custom validator (called tut-install/javaeetutorial5/examples/web/bookstore6/src/java/com/sun/bookstore6/validators/FormatValidator.java) that validates input data against a format pattern that is specified in the custom validator tag. This validator is used with the Credit Card Number field on the bookcashier.jsp page. Here is the custom validator tag:

<bookstore:formatValidator
     formatPatterns="9999999999999999|9999 9999 9999 9999|
        9999-9999-9999-9999"/>

According to this validator, the data entered in the field must be one of the following:

The rest of this section describes how this validator is implemented and how to create a custom tag so that the page author can register the validator on a component.

Implementing the Validator Interface

A Validator implementation must contain a constructor, a set of accessor methods for any attributes on the tag, and a validate method, which overrides the validate method of the Validator interface.

The FormatValidator class also defines accessor methods for setting the formatPatterns attribute, which specifies the acceptable format patterns for input into the fields. In addition, the class overrides the validate method of the Validator interface. This method validates the input and also accesses the custom error messages to be displayed when the String is invalid.

The validate method performs the actual validation of the data. It takes the FacesContext instance, the component whose data needs to be validated, and the value that needs to be validated. A validator can validate only data of a component that implements EditableValueHolder.

Here is the validate method from FormatValidator:

public void validate(FacesContext context, UIComponent component, Object toValidate) {

    boolean valid = false;
    String value = null;
    if ((context == null) || (component == null)) {
        throw new NullPointerException();
    }
    if (!(component instanceof UIInput)) {
        return;
    }
    if ( null == formatPatternsList || null == toValidate) {
        return;
    }
    value = toValidate.toString();
    //validate the value against the list of valid patterns.
    Iterator patternIt = formatPatternsList.iterator();
    while (patternIt.hasNext()) {
        valid = isFormatValid(
            ((String)patternIt.next()), value);
        if (valid) {
            break;
        }
    }
    if ( !valid ) {
        FacesMessage errMsg =
            MessageFactory.getMessage(context,
                FORMAT_INVALID_MESSAGE_ID,
                     (new Object[] {formatPatterns}));
            throw new ValidatorException(errMsg);
    }
}

This method gets the local value of the component and converts it to a String. It then iterates over the formatPatternsList list, which is the list of acceptable patterns as specified in the formatPatterns attribute of the custom validator tag.

While iterating over the list, this method checks the pattern of the component’s local value against the patterns in the list. If the pattern of the local value does not match any pattern in the list, this method generates an error message. It then passes the message to the constructor of ValidatorException. Eventually the message is queued onto the FacesContext instance so that the message is displayed on the page during the render response phase.

The error messages are retrieved from the Application instance by MessageFactory. An application that creates its own custom messages must provide a class, such as MessageFactory, that retrieves the messages from the Application instance. When creating your own application, you can simply copy the MessageFactory class from the Duke’s Bookstore application to your application.

The getMessage(FacesContext, String, Object) method of MessageFactory takes a FacesContext, a static String that represents the key into the Properties file, and the format pattern as an Object. The key corresponds to the static message ID in the FormatValidator class:

public static final String FORMAT_INVALID_MESSAGE_ID =
     "FormatInvalid";
}

When the error message is displayed, the format pattern will be substituted for the {0} in the error message, which, in English, is


Input must match one of the following patterns {0}

JavaServer Faces applications can save the state of validators and components on either the client or the server. Specifying Where State Is Saved explains how to configure your application to save state on either the client or the server.

If your JavaServer Faces application saves state on the client (which is the default), you need to make the Validator implementation implement StateHolder as well as Validator. In addition to implementing StateHolder, the Validator implementation needs to implement the saveState(FacesContext) and restoreState(FacesContext, Object) methods of StateHolder. With these methods, the Validator implementation tells the JavaServer Faces implementation which attributes of the Validator implementation to save and restore across multiple requests.

To save a set of values, you must implement the saveState(FacesContext) method. This method is called during the render response phase, during which the state of the response is saved for processing on subsequent requests. When implementing the saveState(FacesContext) method, you need to create an array of objects and add the values of the attributes you want to save to the array. Here is the saveState(FacesContext) method from FormatValidator:

public Object saveState(FacesContext context) {
    Object values[] = new Object[2];
    values[0] = formatPatterns;
    values[1] = formatPatternsList;
    return (values);
}

To restore the state saved with the saveState(FacesContext) method in preparation for the next postback, the Validator implementation implements restoreState(FacesContext, Object). The restoreState(FacesContext, Object) method takes the FacesContext instance and an Object instance, which represents the array that is holding the state for the Validator implementation. This method sets the Validator implementation’s properties to the values saved in the Object array. Here is the restoreState(FacesContext, Object) method from FormatValidator:

public void restoreState(FacesContext context, Object state) {
     Object values[] = (Object[]) state;
    formatPatterns = (String) values[0];
    formatPatternsList = (ArrayList) values[1];
}

As part of implementing StateHolder, the custom Validator implementation must also override the isTransient and setTransient(boolean) methods of StateHolder. By default, transientValue is false, which means that the Validator implementation will have its state information saved and restored. Here are the isTransient and setTransient(boolean) methods of FormatValidator:

private boolean transientValue = false;

public boolean isTransient() {
    return (this.transientValue);
}

public void setTransient(boolean transientValue) {
    this.transientValue = transientValue;
}

Saving and Restoring State describes how a custom component must implement the saveState(FacesContext) and restoreState(FacesContext, Object) methods.

Creating a Custom Tag

If you implemented a Validator interface rather than implementing a backing bean method that performs the validation, you need to do one of the following:

To create a custom tag, you need to do two things:

Using a Custom Validator explains how to use the custom validator tag on the page.

Writing the Tag Handler

The tag handler associated with a custom validator tag must extend the ValidatorELTag class. This class is the base class for all custom tag handlers that create Validator instances and register them on UI components. The FormatValidatorTag class registers the FormatValidator instance onto the component.

The FormatValidatorTag tag handler class does the following:

The formatPatterns attribute of the formatValidator tag supports literals and value expressions. Therefore, the accessor method for this attribute in the FormatValidatorTag class must accept and return an instance of ValueExpression:

protected ValueExpression formatPatterns = null;

public void setFormatPatterns(ValueExpression fmtPatterns){
    formatPatterns = fmtPatterns;
}

Finally, the createValidator method creates an instance of FormatValidator, extracts the value from the formatPatterns attribute’s value expression and sets the formatPatterns property of FormatValidator to this value:

the formatPatterns property of FormatValidator to this value:

protected Validator createValidator() throws JspException {
    FacesContext facesContext =
         FacesContext.getCurrentInstance();
    FormatValidator result = null;
    if(validatorID != null){
        result = (FormatValidator) facesContext.getApplication()
            .createValidator(validatorID);
    }
    String patterns = null;
    if (formatPatterns != null) {
        if (!formatPatterns.isLiteralText()) {
            patterns = (String)
             formatPatterns.getValue(facesContext.getELContext());
        } else {
            patterns = formatPatterns.getExpressionString();
        }

Writing the Tag Library Descriptor

To define a tag, you declare it in a tag library descriptor (TLD), which is an XML document that describes a tag library. A TLD contains information about a library and each tag contained in it. See Tag Library Descriptors for more information about TLDs.

The custom validator tag is defined in bookstore.tld, located in the tut-install/javaeetutorial5/examples/web/bookstore6/web/WEB-INF/ directory. It contains a tag definition for formatValidator:

<tag>
    <name>formatValidator</name>
    ...
    <tag-class>
        com.sun.bookstore6.taglib.FormatValidatorTag</tag-class>
    <attribute>
        <name>formatPatterns</name>
        <required>true</required>
        <deferred-value>
            <type>String</type>
        </deferred-value>
    </attribute>
</tag>

The name element defines the name of the tag as it must be used in the page. The tag-class element defines the tag handler class. The attribute elements define each of the tag’s attributes. The formatPatterns attribute is the only attribute that the tag supports. The deferred-value element indicates that the formatPatterns attribute accepts deferred value expressions. The type element says that the expression resolves to a property of type String.

Writing Backing Bean Methods

Methods of a backing bean perform application-specific functions for components on the page. These functions include performing validation on the component’s value, handling action events, handling value-change events, and performing processing associated with navigation.

By using a backing bean to perform these functions, you eliminate the need to implement the Validator interface to handle the validation or the Listener interface to handle events. Also, by using a backing bean instead of a Validator implementation to perform validation, you eliminate the need to create a custom tag for the Validator implementation. Creating a Custom ValidatorCreating a Custom Validator describes implementing a custom validator. Implementing an Event Listener describes implementing a listener class.

In general, it’s good practice to include these methods in the same backing bean that defines the properties for the components referencing these methods. The reason is that the methods might need to access the component’s data to determine how to handle the event or to perform the validation associated with the component.

This section describes the requirements for writing the backing bean methods.

Writing a Method to Handle Navigation

A backing bean method that handles navigation processing, called an action method, must be a public method that takes no parameters and returns an Object, which is the logical outcome that the navigation system uses to determine what page to display next. This method is referenced using the component tag’s action attribute.

The following action method in CashierBean is invoked when a user clicks the Submit button on the bookcashier.jsp page. If the user has ordered more than $100 (or 100 euros) worth of books, this method sets the rendered properties of the fanClub and specialOffer components to true. This causes them to be displayed on the page the next time the page is rendered.

After setting the components’ rendered properties to true, this method returns the logical outcome null. This causes the JavaServer Faces implementation to re-render the bookcashier.jsp page without creating a new view of the page. If this method were to return purchase (which is the logical outcome to use to advance to bookcashier.jsp, as defined by the application configuration resource file), the bookcashier.jsp page would re-render without retaining the customer’s input. In this case, you want to re-render the page without clearing the data.

If the user does not purchase more than $100 (or 100 euros) worth of books or the thankYou component has already been rendered, the method returns receipt.

The default NavigationHandler provided by the JavaServer Faces implementation matches the logical outcome, as well as the starting page (bookcashier.jsp) against the navigation rules in the application configuration resource file to determine which page to access next. In this case, the JavaServer Faces implementation loads the bookreceipt.jsp page after this method returns.

public String submit() {
    ...
    if(cart().getTotal() > 100.00 &&
         !specialOffer.isRendered())
    {
        specialOfferText.setRendered(true);
        specialOffer.setRendered(true);
        return null;
    } else if (specialOffer.isRendered() &&
         !thankYou.isRendered()){
        thankYou.setRendered(true);
        return null;
    } else {
        clear();
        return ("receipt");
    }
}

Typically, an action method will return a String outcome, as shown in the previous example. Alternatively, you can define an Enum class that encapsulates all possible outcome strings and then make an action method return an enum constant, which represents a particular String outcome defined by the Enum class. In this case, the value returned by a call to the Enum class’s toString method must match that specified by the from-outcome element in the appropriate navigation rule configuration defined in the application configuration file.

The Duke’s Bank example uses an Enum class to encapsulate all logical outcomes:

public enum Navigation  {
    main, accountHist, accountList, atm, atmAck, transferFunds,
     transferAck, error
}

When an action method returns an outcome, it uses the dot notation to reference the outcome from the Enum class:

public Object submit(){
    ...
    return Navigation.accountHist;
}

Referencing a Method That Performs Navigation explains how a component tag references this method. Binding a Component Instance to a Bean Property discusses how the page author can bind these components to bean properties. Writing Properties Bound to Component Instances discusses how to write the bean properties to which the components are bound. Configuring Navigation Rules provides more information on configuring navigation rules.

Writing a Method to Handle an Action Event

A backing bean method that handles an action event must be a public method that accepts an action event and returns void. This method is referenced using the component tag’s actionListener attribute. Only components that implement ActionSource can refer to this method.

The following backing bean method from LocaleBean of the Duke’s Bookstore application processes the event of a user clicking one of the hyperlinks on the chooselocale.jsp page:

public void chooseLocaleFromLink(ActionEvent event) {
    String current = event.getComponent().getId();
    FacesContext context = FacesContext.getCurrentInstance();
    context.getViewRoot().setLocale((Locale)
        locales.get(current));
}

This method gets the component that generated the event from the event object. Then it gets the component’s ID. The ID indicates a region of the world. The method matches the ID against a HashMap object that contains the locales available for the application. Finally, it sets the locale using the selected value from the HashMap object.

Referencing a Method That Handles an Action Event explains how a component tag references this method.

Writing a Method to Perform Validation

Rather than implement the Validator interface to perform validation for a component, you can include a method in a backing bean to take care of validating input for the component.

A backing bean method that performs validation must accept a FacesContext, the component whose data must be validated, and the data to be validated, just as the validate method of the Validator interface does. A component refers to the backing bean method by using its validator attribute. Only values of UIInput components or values of components that extend UIInput can be validated.

Here is the backing bean method of CheckoutFormBean from the Coffee Break example:

public void validateEmail(FacesContext context,
     UIComponent toValidate, Object value) {
    
    String message = "";
    String email = (String) value;
    if (email.contains(’@’)) {
        ((UIInput)toValidate).setValid(false);
        message = CoffeeBreakBean.loadErrorMessage(context,
             CoffeeBreakBean.CB_RESOURCE_BUNDLE_NAME,
            "EMailError");
        context.addMessage(toValidate.getClientId(context),
            new FacesMessage(message));
    }
}

The validateEmail method first gets the local value of the component. It then checks whether the @ character is contained in the value. If it isn’t, the method sets the component’s valid property to false. The method then loads the error message and queues it onto the FacesContext instance, associating the message with the component ID.

See Referencing a Method That Performs Validation for information on how a component tag references this method.

Writing a Method to Handle a Value-Change Event

A backing bean that handles a value-change event must be a public method that accepts a value-change event and returns void. This method is referenced using the component’s valueChangeListener attribute.

The Duke’s Bookstore application does not have any backing bean methods that handle value-change events. It does have a ValueChangeListener implementation, as explained in the Implementing Value-Change Listeners section.

For illustration, this section explains how to write a backing bean method that can replace the ValueChangeListener implementation.

As explained in Registering a Value-Change Listener on a Component, the name component of the bookcashier.jsp page has a ValueChangeListener instance registered on it. This ValueChangeListener instance handles the event of entering a value in the field corresponding to the component. When the user enters a value, a value-change event is generated, and the processValueChange(ValueChangeEvent) method of the ValueChangeListener class is invoked.

Instead of implementing ValueChangeListener, you can write a backing bean method to handle this event. To do this, you move the processValueChange(ValueChangeEvent) method from the ValueChangeListener class, called NameChanged, to your backing bean.

Here is the backing bean method that processes the event of entering a value in the name field on the bookcashier.jsp page:

public void processValueChange(ValueChangeEvent event)
    throws AbortProcessingException {
    if (null != event.getNewValue()) {
        FacesContext.getCurrentInstance().
            getExternalContext().getSessionMap().
                put("name", event.getNewValue());
    }
}    

The page author can make this method handle the ValueChangeEvent object emitted by a UIInput component by referencing this method from the component tag’s valueChangeListener attribute. See Referencing a Method That Handles a Value-change Event for more information.