The Java EE 5 Tutorial

Chapter 13 Creating Custom UI Components

JavaServer Faces technology offers a basic set of standard, reusable UI components that enable page authors and application developers to quickly and easily construct UIs for web applications. But often an application requires a component that has additional functionality or requires a completely new component. JavaServer Faces technology allows a component writer to extend the standard components to enhance their functionality or create custom components.

In addition to extending the functionality of standard components, a component writer might want to give a page author the ability to change the appearance of the component on the page. Or the component writer might want to render a component to a different client. Enabled by the flexible JavaServer Faces architecture, a component writer can separate the definition of the component behavior from its appearance by delegating the rendering of the component to a separate renderer. In this way, a component writer can define the behavior of a custom component once but create multiple renderers, each of which defines a different way to render the component to a particular kind of client device.

As well as providing a means to easily create custom components and renderers, the JavaServer Faces design also makes it easy to reference them from the page through JSP custom tag library technology.

This chapter uses the image map custom component from the Duke’s Bookstore application (see The Example JavaServer Faces Application) to explain how a component writer can create simple custom components, custom renderers, and associated custom tags, and take care of all the other details associated with using the components and renderers in an application.

If you are creating components intended for use with an IDE such as Sun Java Studio Creator, you might need to take some extra steps beyond what this chapter details to get your components to work with the IDE. For information on how to make your custom components work with Sun Java Studio Creator, see the article Writing Custom Components for Java Studio Creator Part 1: Developing a Component Library, located at http://developers.sun.com/prodtech/javatools/jscreator/reference/techart/2/writing_custom_components.html.

Determining Whether You Need a Custom Component or Renderer

The JavaServer Faces implementation supports a rich set of components and associated renderers, which are enough for most simple applications. This section helps you decide whether you need a custom component or custom renderer or instead can use a standard component and renderer.

When to Use a Custom Component

A component class defines the state and behavior of a UI component. This behavior includes converting the value of a component to the appropriate markup, queuing events on components, performing validation, and other functionality.

You need to create a custom component in these situations:

You do not need to create a custom component in these cases:

When to Use a Custom Renderer

If you are creating a custom component, you need to ensure, among other things, that your component class performs these operations:

The JavaServer Faces specification supports two programming models for handling encoding and decoding:

By delegating the operations to the renderer, you have the option of associating your custom component with different renderers so that you can represent the component in different ways on the page. If you don’t plan to render a particular component in different ways, it’s simpler to let the component class handle the rendering.

If you aren’t sure whether you will need the flexibility offered by separate renderers but you want to use the simpler direct-implementation approach, you can actually use both models. Your component class can include some default rendering code, but it can delegate rendering to a renderer if there is one.

Component, Renderer, and Tag Combinations

When you create a custom component, you will usually create a custom renderer to go with it. You will also need a custom tag to associate the component with the renderer and to reference the component from the page.

In rare situations, however, you might use a custom renderer with a standard component rather than a custom component. Or you might use a custom tag without a renderer or a component. This section gives examples of these situations and summarizes what’s required for a custom component, renderer, and tag.

You would use a custom renderer without a custom component if you wanted to add some client-side validation on a standard component. You would implement the validation code with a client-side scripting language, such as JavaScript, and then render the JavaScript with the custom renderer. In this situation, you need a custom tag to go with the renderer so that its tag handler can register the renderer on the standard component.

Custom components as well as custom renderers need custom tags associated with them. However, you can have a custom tag without a custom renderer or custom component. For example, suppose that you need to create a custom validator that requires extra attributes on the validator tag. In this case, the custom tag corresponds to a custom validator and not to a custom component or custom renderer. In any case, you still need to associate the custom tag with a server-side object.

Table 13–1 summarizes what you must or can associate with a custom component, custom renderer, or custom tag.

Table 13–1 Requirements for Custom Components, Custom Renderers, and Custom Tags

Custom Item 

Must Have 

Can Have 

Custom component 

Custom tag 

Custom renderer or standard renderer 

Custom renderer 

Custom tag 

Custom component or standard component 

Custom JavaServer Faces tag 

Some server-side object, like a component, a custom renderer, or custom validator 

Custom component or standard component associated with a custom renderer 

Understanding the Image Map Example

Duke’s Bookstore includes a custom image map component on the chooselocale.jsp page. This image map displays a map of the world. When the user clicks one of a particular set of regions in the map, the application sets the locale on the UIViewRoot component of the current FacesContext to the language spoken in the selected region. The hotspots of the map are the United States, Spanish-speaking Central and South America, France, and Germany.

Why Use JavaServer Faces Technology to Implement an Image Map?

JavaServer Faces technology is an ideal framework to use for implementing this kind of image map because it can perform the work that must be done on the server without requiring you to create a server-side image map.

In general, client-side image maps are preferred over server-side image maps for several reasons. One reason is that the client-side image map allows the browser to provide immediate feedback when a user positions the mouse over a hotspot. Another reason is that client-side image maps perform better because they don’t require round-trips to the server. However, in some situations, your image map might need to access the server to retrieve data or to change the appearance of non-form controls, tasks that a client-side image map cannot do.

Because the image map custom component uses JavaServer Faces technology, it has the best of both styles of image maps: It can handle the parts of the application that need to be performed on the server, while allowing the other parts of the application to be performed on the client side.

Understanding the Rendered HTML

Here is an abbreviated version of the form part of the HTML page that the application needs to render:

<form id="_id38" method="post"
     action="/bookstore6/chooselocale.faces" ... >
    ...
    <img id="_id38:mapImage"
         src="/bookstore6/template/world.jpg"
         alt="Choose Your Preferred Locale from the Map"
         usemap="#worldMap" />
         <map name="worldMap">
            <area alt="NAmerica"                        
                 coords="53,109,1,110,2,167,,..."
                shape="poly"
                 onmouseout=
                    "document.forms[0][’_id_id38:mapImage’].src=
                        ’/bookstore6/template/world.jpg’"
                onmouseover=
                    "document.forms[0][’_id_id38:mapImage’].src=
                        ’/bookstore6/template/world_namer.jpg’"
                onclick=
                    "document.forms[0][’worldMap_current’].
                        value=
                            ’NAmerica’;document.forms[0].submit()"
            />
            <input type="hidden" name="worldMap_current">
        </map>
        ...
</form>

The img tag associates an image (world.jpg) with the image map referenced in the usemap attribute value.

The map tag specifies the image map and contains a set of area tags.

Each area tag specifies a region of the image map. The onmouseover, onmouseout, and onclick attributes define which JavaScript code is executed when these events occur. When the user moves the mouse over a region, the onmouseover function associated with the region displays the map with that region highlighted. When the user moves the mouse out of a region, the onmouseout function redisplays the original image. If the user clicks on a region, the onclick function sets the value of the input tag to the ID of the selected area and submits the page.

The input tag represents a hidden control that stores the value of the currently selected area between client-server exchanges so that the server-side component classes can retrieve the value.

The server-side objects retrieve the value of worldMap_current and set the locale in the FacesContext instance according to the region that was selected.

Understanding the JSP Page

Here is an abbreviated form of the JSP page that the image map component will use to generate the HTML page shown in the preceding section:

<f:view>
    <f:loadBundle basename="messages.BookstoreMessages"
         var="bundle"/>
    <h:form>
        ...
        <h:graphicImage id="mapImage" url="/template/world.jpg"
             alt="#{bundle.ChooseLocale}"
            usemap="#worldMap" />
         <bookstore:map id="worldMap" current="NAmericas"
             immediate="true" action="bookstore"
            actionListener="#{localeBean.chooseLocaleFromMap}">
            <bookstore:area id="NAmerica" value="#{NA}"
                 onmouseover="/template/world_namer.jpg"
                 onmouseout="/template/world.jpg"
                 targetImage="mapImage" />
            <bookstore:area id="SAmerica" value="#{SA}"
                onmouseover="/template/world_samer.jpg"
                onmouseout="/template/world.jpg"
                 targetImage="mapImage" />
            <bookstore:area id="Germany" value="#{gerA}"
                onmouseover="/template/world_germany.jpg"
                 onmouseout="/template/world.jpg"
                 targetImage="mapImage" />
            <bookstore:area id="France" value="#{fraA}"
                 onmouseover="/template/world_france.jpg"
                onmouseout="/template/world.jpg"
                 targetImage="mapImage" />
            </bookstore:map>
        ...
    </h:form>
</f:view>

The alt attribute of graphicImage maps to the localized string "Choose Your Locale from the Map".

The actionListener attribute of the map tag points at a method in LocaleBean that accepts an action event. This method changes the locale according to the area selected from the image map. The way this event is handled is explained more in Handling Events for Custom Components.

The action attribute specifies a logical outcome String, which is matched against the navigation rules in the application configuration resource file. For more information on navigation, see the section Configuring Navigation Rules.

The immediate attribute of the map tag is set to true, which indicates that the default ActionListener implementation should execute during the apply request values phase of the request-processing life cycle, instead of waiting for the invoke application phase. Because the request resulting from clicking the map does not require any validation, data conversion, or server-side object updates, it makes sense to skip directly to the invoke application phase.

The current attribute of the map tag is set to the default area, which is NAmerica.

Notice that the area tags do not contain any of the JavaScript, coordinate, or shape data that is displayed on the HTML page. The JavaScript is generated by the AreaRenderer class. The onmouseover and onmouseout attribute values indicate the image to be loaded when these events occur. How the JavaScript is generated is explained more in Performing Encoding.

The coordinate, shape, and alternate text data are obtained through the value attribute, whose value refers to an attribute in application scope. The value of this attribute is a bean, which stores the coords, shape, and alt data. How these beans are stored in the application scope is explained more in the next section.

Configuring Model Data

In a JavaServer Faces application, data such as the coordinates of a hotspot of an image map is retrieved from the value attribute through a bean. However, the shape and coordinates of a hotspot should be defined together because the coordinates are interpreted differently depending on what shape the hotspot is. Because a component’s value can be bound only to one property, the value attribute cannot refer to both the shape and the coordinates.

To solve this problem, the application encapsulates all of this information in a set of ImageArea objects. These objects are initialized into application scope by the managed bean creation facility (see Backing Beans). Here is part of the managed bean declaration for the ImageArea bean corresponding to the South America hotspot:

<managed-bean>
    ...
    <managed-bean-name>SA</managed-bean-name>
    <managed-bean-class>
        components.model.ImageArea
    </managed-bean-class>
    <managed-bean-scope>application</managed-bean-scope>
    <managed-property>
        <property-name>shape</property-name>
        <value>poly</value>
    </managed-property>
    <managed-property>
        <property-name>alt</property-name>
        <value>SAmerica</value>
    </managed-property>
    <managed-property>
        <property-name>coords</property-name>
        <value>89,217,95,100...</value>    
    </managed-property>
</managed-bean>

For more information on initializing managed beans with the managed bean creation facility, see the section Application Configuration Resource File.

The value attributes of the area tags refer to the beans in the application scope, as shown in this area tag from chooselocale.jsp:

<bookstore:area id="NAmerica"
         value="#{NA}"
         onmouseover="/template/world_namer.jpg"
         onmouseout="/template/world.jpg" />

To reference the ImageArea model object bean values from the component class, you implement a getValue method in the component class. This method calls super.getValue. The superclass of tut-install/javaeetutorial5/examples/web/bookstore6/src/java/com/sun/bookstore6/components/AreaComponent.java, UIOutput, has a getValue method that does the work of finding the ImageArea object associated with AreaComponent. The AreaRenderer class, which needs to render the alt, shape, and coords values from the ImageArea object, calls the getValue method of AreaComponent to retrieve the ImageArea object.

ImageArea iarea = (ImageArea) area.getValue();

ImageArea is only a simple bean, so you can access the shape, coordinates, and alternative text values by calling the appropriate accessor methods of ImageArea. Creating the Renderer Class explains how to do this in the AreaRenderer class.

Summary of the Application Classes

Table 13–2 summarizes all the classes needed to implement the image map component.

Table 13–2 Image Map Classes

Class 

Function 

AreaSelectedEvent

The ActionEvent indicating that an AreaComponent from the MapComponent has been selected.

AreaTag

The tag handler that implements the area custom tag.

MapTag

The tag handler that implements the map custom tag.

AreaComponent

The class that defines AreaComponent, which corresponds to the area custom tag.

MapComponent

The class that defines MapComponent, which corresponds to the map custom tag.

AreaRenderer

This Renderer performs the delegated rendering for AreaComponent.

ImageArea

The bean that stores the shape and coordinates of the hotspots. 

LocaleBean

The backing bean for the chooselocale.jsp page.

The Duke's Bookstore source directory, called bookstore-dir, is tut-install/javaeetutorial5/examples/web/bookstore6/src/java/com/sun/bookstore6/. The event and listener classes are located in bookstore-dir/listeners/. The tag handlers are located in bookstore-dir/taglib/. The component classes are located in bookstore-dir/components/. The renderer classes are located in bookstore-dir/renderers/. ImageArea is located in bookstore-dir/model/. LocaleBean is located in bookstore-dir/backing/.

Steps for Creating a Custom Component

    You can apply the following steps while developing your own custom component.

  1. Create a custom component class that does the following:

    1. Overrides the getFamily method to return the component family, which is used to look up renderers that can render the component.

    2. Includes the rendering code or delegates it to a renderer (explained in step 2).

    3. Enables component attributes to accept expressions.

    4. Queues an event on the component if the component generates events.

    5. Saves and restores the component state.

  2. Delegate rendering to a renderer if your component does not handle the rendering. To do this:

    1. Create a custom renderer class by extending javax.faces.render.Renderer.

    2. Register the renderer to a render kit.

    3. Identify the renderer type in the component tag handler.

  3. Register the component.

  4. Create an event handler if your component generates events.

  5. Write a tag handler class that extends javax.faces.webapp.UIComponentELTag. In this class, you need a getRendererType method, which returns the type of your custom renderer if you are using one (explained in step 2); a getComponentType method, which returns the type of the custom component; and a setProperties method, with which you set all the new attributes of your component.

  6. Create a tag library descriptor (TLD) that defines the custom tag.

The application architect does the work of registering the custom component and the renderer. See Registering a Custom Converter and Registering a Custom Renderer with a Render Kit for more information. Using a Custom Component discusses how to use the custom component in a JavaServer Faces page.

Creating Custom Component Classes

As explained in When to Use a Custom Component, a component class defines the state and behavior of a UI component. The state information includes the component’s type, identifier, and local value. The behavior defined by the component class includes the following:

The UIComponentBase class defines the default behavior of a component class. All the classes representing the standard components extend from UIComponentBase. These classes add their own behavior definitions, as your custom component class will do.

Your custom component class must either extend UIComponentBase directly or extend a class representing one of the standard components. These classes are located in the javax.faces.component package and their names begin with UI.

If your custom component serves the same purpose as a standard component, you should extend that standard component rather than directly extend UIComponentBase. For example, suppose you want to create an editable menu component. It makes sense to have this component extend UISelectOne rather than UIComponentBase because you can reuse the behavior already defined in UISelectOne. The only new functionality you need to define is to make the menu editable.

Whether you decide to have your component extend UIComponentBase or a standard component, you might also want your component to implement one or more of these behavioral interfaces:

If your component extends UIComponentBase, it automatically implements only StateHolder. Because all components directly or indirectly extend UIComponentBase, they all implement StateHolder.

If your component extends one of the other standard components, it might also implement other behavioral interfaces in addition to StateHolder. If your component extends UICommand, it automatically implements ActionSource2. If your component extends UIOutput or one of the component classes that extend UIOutput, it automatically implements ValueHolder. If your component extends UIInput, it automatically implements EditableValueHolder and ValueHolder. See the JavaServer Faces API Javadoc to find out what the other component classes implement.

You can also make your component explicitly implement a behavioral interface that it doesn’t already by virtue of extending a particular standard component. For example, if you have a component that extends UIInput and you want it to fire action events, you must make it explicitly implement ActionSource2 because a UIInput component doesn’t automatically implement this interface.

The image map example has two component classes: AreaComponent and MapComponent. The MapComponent class extends UICommand and therefore implements ActionSource2, which means it can fire action events when a user clicks on the map. The AreaComponent class extends the standard component UIOutput.

The MapComponent class represents the component corresponding to the map tag:

<bookstore:map id="worldMap" current="NAmericas"
     immediate="true"
     action="bookstore"
    actionListener="#{localeBean.chooseLocaleFromMap}">

The AreaComponent class represents the component corresponding to the area tag:

<bookstore:area id="NAmerica" value="#{NA}"
    onmouseover="/template/world_namer.jpg"
     onmouseout="/template/world.jpg"
     targetImage="mapImage" />

MapComponent has one or more AreaComponent instances as children. Its behavior consists of the following

The rendering of the map and input tags is performed by tut-install/javaeetutorial5/examples/web/bookstore6/src/java/com/sun/bookstore6/renderers/MapRenderer.java, but MapComponent delegates this rendering to MapRenderer.

AreaComponent is bound to a bean that stores the shape and coordinates of the region of the image map. You’ll see how all this data is accessed through the value expression in Creating the Renderer Class. The behavior of AreaComponent consists of the following

Although these tasks are actually performed by AreaRenderer, AreaComponent must delegate the tasks to AreaRenderer. See Delegating Rendering to a Renderer for more information.

The rest of this section describes the tasks that MapComponent performs as well as the encoding and decoding that it delegates to MapRenderer. Handling Events for Custom Components details how MapComponent handles events.

Specifying the Component Family

If your custom component class delegates rendering, it needs to override the getFamily method of UIComponent to return the identifier of a component family, which is used to refer to a component or set of components that can be rendered by a renderer or set of renderers. The component family is used along with the renderer type to look up renderers that can render the component.

Because MapComponent delegates its rendering, it overrides the getFamily method:

public String getFamily() {
    return ("Map");
}

The component family identifier, Map, must match that defined by the component-family elements included in the component and renderer configurations in the application configuration resource file. Registering a Custom Renderer with a Render Kit explains how to define the component family in the renderer configuration. Registering a Custom Component explains how to define the component family in the component configuration.

Performing Encoding

During the render response phase, the JavaServer Faces implementation processes the encoding methods of all components and their associated renderers in the view. The encoding methods convert the current local value of the component into the corresponding markup that represents it in the response.

The UIComponentBase class defines a set of methods for rendering markup: encodeBegin, encodeChildren, and encodeEnd. If the component has child components, you might need to use more than one of these methods to render the component; otherwise, all rendering should be done in encodeEnd.

Because MapComponent is a parent component of AreaComponent, the area tags must be rendered after the beginning map tag and before the ending map tag. To accomplish this, the MapRenderer class renders the beginning map tag in encodeBegin and the rest of the map tag in encodeEnd.

The JavaServer Faces implementation automatically invokes the encodeEnd method of AreaComponent’s renderer after it invokes MapRenderer’s encodeBegin method and before it invokes MapRenderer’s encodeEnd method. If a component needs to perform the rendering for its children, it does this in the encodeChildren method.

Here are the encodeBegin and encodeEnd methods of MapRenderer:

public void encodeBegin(FacesContext context,
     UIComponent component) throws IOException {
    if ((context == null)|| (component == null)){
        throw new NullPointerException();
    }
    MapComponent map = (MapComponent) component;
    ResponseWriter writer = context.getResponseWriter();
    writer.startElement("map", map);
    writer.writeAttribute("name", map.getId(),"id");
}
public void encodeEnd(FacesContext context) throws IOException {
    if ((context == null) || (component == null)){
        throw new NullPointerException();
    }
    MapComponent map = (MapComponent) component;
    ResponseWriter writer = context.getResponseWriter();
    writer.startElement("input", map);
    writer.writeAttribute("type", "hidden", null);
    writer.writeAttribute("name",
         getName(context,map), "clientId");(
    writer.endElement("input");
    writer.endElement("map");
}

Notice that encodeBegin renders only the beginning map tag. The encodeEnd method renders the input tag and the ending map tag.

The encoding methods accept a UIComponent argument and a FacesContext argument. The FacesContext instance contains all the information associated with the current request. The UIComponent argument is the component that needs to be rendered.

The rest of the method renders the markup to the ResponseWriter instance, which writes out the markup to the current response. This basically involves passing the HTML tag names and attribute names to the ResponseWriter instance as strings, retrieving the values of the component attributes, and passing these values to the ResponseWriter instance.

The startElement method takes a String (the name of the tag) and the component to which the tag corresponds (in this case, map). (Passing this information to the ResponseWriter instance helps design-time tools know which portions of the generated markup are related to which components.)

After calling startElement, you can call writeAttribute to render the tag’s attributes. The writeAttribute method takes the name of the attribute, its value, and the name of a property or attribute of the containing component corresponding to the attribute. The last parameter can be null, and it won’t be rendered.

The name attribute value of the map tag is retrieved using the getId method of UIComponent, which returns the component’s unique identifier. The name attribute value of the input tag is retrieved using the getName(FacesContext, UIComponent) method of MapRenderer.

If you want your component to perform its own rendering but delegate to a renderer if there is one, include the following lines in the encoding method to check whether there is a renderer associated with this component.

if (getRendererType() != null) {
    super.encodeEnd(context);
    return;
}

If there is a renderer available, this method invokes the superclass’s encodeEnd method, which does the work of finding the renderer. The MapComponent class delegates all rendering to MapRenderer, so it does not need to check for available renderers.

In some custom component classes that extend standard components, you might need to implement other methods in addition to encodeEnd. For example, if you need to retrieve the component’s value from the request parameters, you must also implement the decode method.

Performing Decoding

During the apply request values phase, the JavaServer Faces implementation processes the decode methods of all components in the tree. The decode method extracts a component’s local value from incoming request parameters and uses a Converter class to convert the value to a type that is acceptable to the component class.

A custom component class or its renderer must implement the decode method only if it must retrieve the local value or if it needs to queue events. The MapRenderer instance retrieves the local value of the hidden input field and sets the current attribute to this value by using its decode method. The setCurrent method of MapComponent queues the event by calling queueEvent, passing in the AreaSelectedEvent instance generated by MapComponent.

Here is the decode method of MapRenderer:

public void decode(FacesContext context, UIComponent component) {
    if ((context == null) || (component == null)) {
        throw new NullPointerException();
    }
    MapComponent map = (MapComponent) component;
    String key = getName(context, map);
    String value = (String)context.getExternalContext().
        getRequestParameterMap().get(key);
    if (value != null)
         map.setCurrent(value);
    }
}

The decode method first gets the name of the hidden input field by calling getName(FacesContext, UIComponent). It then uses that name as the key to the request parameter map to retrieve the current value of the input field. This value represents the currently selected area. Finally, it sets the value of the MapComponent class’s current attribute to the value of the input field.

Enabling Component Properties to Accept Expressions

Nearly all the attributes of the standard JavaServer Faces tags can accept expressions, whether they are value expressions or method expressions. It is recommended that you also enable your component attributes to accept expressions because this is what page authors expect, and it gives page authors much more flexibility when authoring their pages.

Creating the Component Tag Handler describes how MapTag, the tag handler for the map tag, sets the component’s values when processing the tag. It does this by providing the following:

To retrieve the expression objects that setProperties stored, the component class must implement a method for each property that accesses the appropriate expression object, extracts the value from it and returns the value.

Because MapComponent extends UICommand, the UICommand class already does the work of getting the ValueExpression and MethodExpression instances associated with each of the attributes that it supports.

However, if you have a custom component class that extends UIComponentBase, you will need to implement the methods that get the ValueExpression and MethodExpression instances associated with those attributes that are enabled to accept expressions. For example, if MapComponent extended UIComponentBase instead of UICommand, it would need to include a method that gets the ValueExpression instance for the immediate attribute:

public boolean isImmediate() {
    if (this.immediateSet) {
        return (this.immediate);
    }
    ValueExpression ve = getValueExpression("immediate");
    if (ve != null) {
        Boolean value = (Boolean) ve.getValue(
            getFacesContext().getELContext());
        return (value.booleanValue());
    } else {
        return (this.immediate);
    }
}

The properties corresponding to the component attributes that accept method expressions must accept and return a MethodExpression object. For example, if MapComponent extended UIComponentBase instead of UICommand, it would need to provide an action property that returns and accepts a MethodExpression object:

public MethodExpression getAction() {
    return (this.action);
}
public void setAction(MethodExpression action) {
    this.action = action;
}

Saving and Restoring State

Because component classes implement StateHolder, they must implement the saveState(FacesContext) and restoreState(FacesContext, Object) methods to help the JavaServer Faces implementation save and restore the state of components 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. Here is the method from MapComponent:

public Object saveState(FacesContext context) {
    Object values[] = new Object[2];
    values[0] = super.saveState(context);
    values[1] = current;
    return (values);
}

This method initializes an array, which will hold the saved state. It next saves all of the state associated with MapComponent.

A component that implements StateHolder must also provide an implementation for restoreState(FacesContext, Object), which restores the state of the component to that saved with the saveState(FacesContext) method. The restoreState(FacesContext, Object) method is called during the restore view phase, during which the JavaServer Faces implementation checks whether there is any state that was saved during the last render response phase and needs to be restored in preparation for the next postback. Here is the restoreState(FacesContext, Object) method from MapComponent:

public void restoreState(FacesContext context, Object state) {
    Object values[] = (Object[]) state;
     super.restoreState(context, values[0]);
    current = (String) values[1];
}

This method takes a FacesContext and an Object instance, representing the array that is holding the state for the component. This method sets the component’s properties to the values saved in the Object array.

When you implement these methods in your component class, be sure to specify in the deployment descriptor where you want the state to be saved: either client or server. If state is saved on the client, the state of the entire view is rendered to a hidden field on the page.

To specify where state is saved for a particular web application, you need to set the javax.faces.STATE_SAVING_METHOD context parameter to either client or server in your application’s deployment descriptor. See Saving and Restoring State for more information on specifying where state is saved in the deployment descriptor.

Delegating Rendering to a Renderer

Both MapComponent and AreaComponent delegate all of their rendering to a separate renderer. The section Performing Encoding explains how MapRenderer performs the encoding for MapComponent. This section explains in detail the process of delegating rendering to a renderer using AreaRenderer, which performs the rendering for AreaComponent.

To delegate rendering, you perform these tasks:

Creating the Renderer Class

When delegating rendering to a renderer, you can delegate all encoding and decoding to the renderer, or you can choose to do part of it in the component class. The AreaComponent class delegates encoding to the AreaRenderer class.

To perform the rendering for AreaComponent, AreaRenderer must implement an encodeEnd method. The encodeEnd method of AreaRenderer retrieves the shape, coordinates, and alternative text values stored in the ImageArea bean that is bound to AreaComponent. Suppose that the area tag currently being rendered has a value attribute value of "fraA". The following line from encodeEnd gets the value of the attribute "fraA" from the FacesContext instance.

ImageArea ia = (ImageArea)area.getValue();

The attribute value is the ImageArea bean instance, which contains the shape, coords, and alt values associated with the fraA AreaComponent instance. Configuring Model Data describes how the application stores these values.

After retrieving the ImageArea object, it renders the values for shape, coords, and alt by simply calling the associated accessor methods and passing the returned values to the ResponseWriter instance, as shown by these lines of code, which write out the shape and coordinates:

writer.startElement("area", area);
writer.writeAttribute("alt", iarea.getAlt(), "alt");
writer.writeAttribute("coords", iarea.getCoords(), "coords");
writer.writeAttribute("shape", iarea.getShape(), "shape");

The encodeEnd method also renders the JavaScript for the onmouseout, onmouseover, and onclick attributes. The page author need only provide the path to the images that are to be loaded during an onmouseover or onmouseout action:

<bookstore:area id="France" value="#{fraA}"
     onmouseover="/template/world_france.jpg"
     onmouseout="/template/world.jpg" targetImage="mapImage" />

The AreaRenderer class takes care of generating the JavaScript for these actions, as shown in the following code from encodeEnd. The JavaScript that AreaRenderer generates for the onclick action sets the value of the hidden field to the value of the current area’s component ID and submits the page.

sb = new StringBuffer("document.forms[0][’").
    append(targetImageId).append("’].src=’");
sb.append(getURI(context,
     (String) area.getAttributes().get("onmouseout")));
sb.append("’");
writer.writeAttribute("onmouseout", sb.toString(),
     "onmouseout");
sb = new StringBuffer("document.forms[0][’").
    append(targetImageId).append("’].src=’");
sb.append(getURI(context,
     (String) area.getAttributes().get("onmouseover")));
sb.append("’");
writer.writeAttribute("onmouseover", sb.toString(),
     "onmouseover");
sb = new StringBuffer("document.forms[0][’");
sb.append(getName(context, area));
sb.append("’].value=’");
sb.append(iarea.getAlt());
sb.append("’; document.forms[0].submit()");
writer.writeAttribute("onclick", sb.toString(), "value");
writer.endElement("area");

By submitting the page, this code causes the JavaServer Faces life cycle to return back to the restore view phase. This phase saves any state information, including the value of the hidden field, so that a new request component tree is constructed. This value is retrieved by the decode method of the MapComponent class. This decode method is called by the JavaServer Faces implementation during the apply request values phase, which follows the restore view phase.

In addition to the encodeEnd method, AreaRenderer contains an empty constructor. This is used to create an instance of AreaRenderer so that it can be added to the render kit.

Identifying the Renderer Type

During the render response phase, the JavaServer Faces implementation calls the getRendererType method of the component’s tag handler to determine which renderer to invoke, if there is one.

The getRendererType method of AreaTag must return the type associated with AreaRenderer. You identify this type when you register AreaRenderer with the render kit, as described in Registering a Custom Renderer with a Render Kit. Here is the getRendererType method from the AreaTag class:

public String getRendererType() { return ("DemoArea");}

Creating the Component Tag Handler explains more about the getRendererType method.

Handling Events for Custom Components

As explained in Implementing an Event Listener, events are automatically queued on standard components that fire events. A custom component, on the other hand, must manually queue events from its decode method if it fires events.

Performing Decoding explains how to queue an event on MapComponent using its decode method. This section explains how to write the class representing the event of clicking on the map and how to write the method that processes this event.

As explained in Understanding the JSP Page, the actionListener attribute of the map tag points to the chooseLocaleFromMap method of the bean LocaleBean. This method processes the event of clicking the image map. Here is the chooseLocaleFromMap method of LocaleBean:

public void chooseLocaleFromMap(ActionEvent actionEvent) {
    AreaSelectedEvent event = (AreaSelectedEvent) actionEvent;
    String current = event.getMapComponent().getCurrent();
    FacesContext context = FacesContext.getCurrentInstance();
    context.getViewRoot().setLocale((Locale)
        locales.get(current));
}

When the JavaServer Faces implementation calls this method, it passes in an ActionEvent object that represents the event generated by clicking on the image map. Next, it casts it to an AreaSelectedEvent object (see tut-install/javaeetutorial5/examples/web/bookstore6/src/java/com/sun/bookstore6/listeners/AreaSelectedEvent.java). Then this method gets the MapComponent associated with the event. It then gets the value of the MapComponent object’s current attribute, which indicates the currently selected area. The method then uses the value of the current property to get the Locale object from a HashMap object, which is constructed elsewhere in the LocaleBean class. Finally the method sets the locale of the FacesContext instance to the Locale obtained from the HashMap object.

In addition to the method that processes the event, you need the event class itself. This class is very simple to write: You have it extend ActionEvent and provide a constructor that takes the component on which the event is queued and a method that returns the component. Here is the AreaSelectedEvent class used with the image map:

public class AreaSelectedEvent extends ActionEvent {
    ...
    public AreaSelectedEvent(MapComponent map) {
        super(map);
    }
    public MapComponent getMapComponent() {
        return ((MapComponent) getComponent());
    }
}

As explained in the section Creating Custom Component Classes, in order for MapComponent to fire events in the first place, it must implement ActionSource. Because MapComponent extends UICommand, it also implements ActionSource.

Creating the Component Tag Handler

Now that you’ve created your component and renderer classes, you’re ready to define how a tag handler processes the tag representing the component and renderer combination. If you’ve created your own JSP custom tags before, creating a component tag handler should be easy for you.

In JavaServer Faces applications, the tag handler class associated with a component drives the render response phase of the JavaServer Faces life cycle. For more information on the JavaServer Faces life cycle, see The Life Cycle of a JavaServer Faces Page.

The first thing that the tag handler does is to retrieve the type of the component associated with the tag. Next, it sets the component’s attributes to the values given in the page. It then returns the type of the renderer (if there is one) to the JavaServer Faces implementation so that the component’s encoding can be performed when the tag is processed. Finally, it releases resources used during the processing of the tag.

The image map custom component includes two tag handlers: AreaTag and MapTag. To see how the operations on a JavaServer Faces tag handler are implemented, let’s take a look at MapTag.

The MapTag class extends UIComponentELTag, which supports jsp.tagext.Tag functionality as well as JavaServer Faces-specific functionality. UIComponentELTag is the base class for all JavaServer Faces tags that correspond to a component. Tags that need to process their tag bodies should instead subclass UIComponentBodyELTag.

Retrieving the Component Type

As explained earlier, the first thing MapTag does is to retrieve the type of the component. It does this by using the getComponentType operation:

    public String getComponentType() {
        return ("DemoMap");
    }

The value returned from getComponentType must match the value configured for the component with the component-type element of the application’s application configuration resource file. Registering a Custom Component explains how to configure a component.

Setting Component Property Values

After retrieving the type of the component, the tag handler sets the component’s property values to those supplied as tag attributes values in the page. This section assumes that your component properties are enabled to accept expressions, as explained in Enabling Component Properties to Accept Expressions.

Getting the Attribute Values

Before setting the values in the component class, the MapTag handler first gets the attribute values from the page by means of JavaBeans component properties that correspond to the attributes. The following code shows the property used to access the value of the immediate attribute.

private javax.el.ValueExpression immediate = null;

public void setImmediate(javax.el.ValueExpression immediate)
 {
    this.immediate = immediate;
}

As this code shows, the setImmediate method takes a ValueExpression object. This means that the immediate attribute of the map tag accepts value expressions.

Similarly, the setActionListener and setAction methods take MethodExpression objects, which means that these attributes accept method expressions. The following code shows the properties used to access the values of the actionListener and the action attributes

private javax.el.MethodExpression actionListener = null;

public void setActionListener(
    javax.el.MethodExpression actionListener) {
    
    this.actionListener = actionListener;
}
private javax.el.MethodExpression action = null;

public void setAction(javax.el.MethodExpression action) {
        this.action = action;
}

Setting the Component Property Values

To pass the value of the tag attributes to MapComponent, the tag handler implements the setProperties method. The way setProperties passes the attribute values to the component class depends on whether the values are value expressions or method expressions.

Setting Value Expressions on Component Properties

When the attribute value is a value expression, setProperties first checks if it is not a literal expression. If the expression is not a literal, setProperties stores the expression into a collection, from which the component class can retrieve it and resolve it at the appropriate time. If the expression is a literal, setProperties performs any required type conversion and then does one of the following:

The following piece of the MapTag handler’s setProperties method sets the renderer-dependent property, styleClass, and the renderer-independent property, immediate:

if (styleClass != null) {
    if (!styleClass.isLiteralText()) {
        map.setValueExpression("styleClass", styleClass);
    } else {
        map.getAttributes().put("styleClass",
             styleClass.getExpressionString());
    }
}
...
if (immediate != null) {
    if (!immediate.isLiteralText()) {
        map.setValueExpression("immediate", immediate);
    } else {
        map.setImmediate(new
             Boolean(immediate.getExpressionString()).
                booleanValue());
    }
}

Setting Method Expressions on Component Properties

The process of setting the properties that accept method expressions is done differently depending on the purpose of the method. The actionListener attribute uses a method expression to reference a method that handles action events. The action attribute uses a method expression to either specify a logical outcome or to reference a method that returns a logical outcome, which is used for navigation purposes.

To handle the method expression referenced by actionListener, the setProperties method must wrap the expression in a special action listener object called MethodExpressionActionListener. This listener executes the method referenced by the expression when it receives the action event. The setProperties method then adds this MethodExpressionActionListener object to the list of listeners to be notified when the event of a user clicking on the map occurs. The following piece of setProperties does all of this:

if (actionListener != null) {
    map.addActionListener(
        new MethodExpressionActionListener(actionListener));
}

If your component fires value change events, your tag handler’s setProperties method does a similar thing, except it wraps the expression in a MethodExpressionValueChangeListener object and adds the listener using the addValueChangeListener method.

In the case of the method expression referenced by the action attribute, the setProperties method uses the setActionExpression method of ActionSource2 to set the corresponding property on MapComponent:

if (action != null) {
    map.setActionExpression(action);
}

Providing the Renderer Type

After setting the component properties, the tag handler provides a renderer type (if there is a renderer associated with the component) to the JavaServer Faces implementation. It does this using the getRendererType method:

public String getRendererType() {return "DemoMap";}

The renderer type that is returned is the name under which the renderer is registered with the application. See Delegating Rendering to a Renderer for more information.

If your component does not have a renderer associated with it, getRendererType should return null. In this case, the renderer-type element in the application configuration file should also be set to null.

Releasing Resources

It’s recommended practice that all tag handlers implement a release method, which releases resources allocated during the execution of the tag handler. The release method of MapTag as follows:

public void release() {
    super.release();
    current = null;
    styleClass = null;
    actionListener = null;
    immediate = null;
    action = null;
}

This method first calls the UIComponentTag.release method to release resources associated with UIComponentTag. Next, the method sets all attribute values to null.

Defining the Custom Component Tag in a Tag Library Descriptor

To define a tag, you declare it in a TLD. The web container uses the TLD to validate the tag. The set of tags that are part of the HTML render kit are defined in the html_basic TLD.

The custom tags area and map are defined in bookstore.tld. The bookstore.tld file defines tags for all the custom components and the custom validator tag described in Creating a Custom Tag.

All tag definitions must be nested inside the taglib element in the TLD. Each tag is defined by a tag element. Here is part of the tag definition of the map tag:

<tag>
    <name>map</name>
    <tag-class>taglib.MapTag</tag-class>
    <attribute>
        <name>binding</name>
        <required>false</required>
        <deferred-value>
            <type>
                javax.faces.component.UIComponent
            </type>
        </deferred-value>
    </attribute>
    <attribute>
        <name>current</name>
        <required>false</required>
        <deferred-value>
            <type>
                java.lang.String
            </type>
        </deferred-value>
    </attribute>
    ...
    <attribute>
        <name>actionListener</name>
        <required>false</required>
        <deferred-method>
            <method-signature>
                void actionListener(javax.faces.event.ActionEvent)
            </method-signature>
        </deferred-method>
        <type>String</type>
    </attribute>
    ...
</tag>

At a minimum, each tag must have a name (the name of the tag) and a tag-class attribute, which specifies the fully-qualified class name of the tag handler.

Each attribute element defines one of the tag attributes. As described in Defining a Tag Attribute Type, the attribute element must define what kind of value the attribute accepts, which for JavaServer Faces tags is either a deferred value expression or a method expression.

To specify that an attribute accepts a deferred value expression, you define the type that the corresponding component property accepts using a type element nested inside of a deferred-value element, as shown for the binding and current attribute definitions in the preceding code snippet.

To specify that an attribute accepts a method expression, you define the signature of the method that expression references using a method-signature element nested inside a deferred-method element, as shown by the actionListener attribute definition in the preceding code snippet. The actual name of the method is ignored by the runtime.

For more information on defining tags in a TLD, consult the Tag Library Descriptors section of this tutorial.