Creating Custom UI Components and Other Custom Objects

We are working on a fresh, updated Jakarta EE Tutorial. This section hasn’t yet been updated.

This chapter describes creating custom components for applications that have additional functionality not provided by standard Jakarta Faces components.

Introduction to Creating Custom Components

Jakarta Faces technology offers a basic set of standard, reusable UI components that enable quick and easy construction of user interfaces for web applications. These components mostly map one-to-one to the elements in HTML 4. But often an application requires a component that has additional functionality or requires a completely new component. Jakarta Faces technology allows extension of standard components to enhance their functionality or to create custom components. A rich ecosystem of third-party component libraries is built on this extension capability, but it is beyond the scope of this tutorial to examine them. A web search for "Faces Component Libraries" (or "JSF Component Libraries") is a good starting point to learn more about this important aspect of using Jakarta Faces technology.

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 to alter listener behavior. Alternatively, the component writer might want to render a component to a different kind of client device type, such as a smartphone or a tablet instead of a desktop computer. Enabled by the flexible Jakarta 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.

A jakarta.faces.component.UIComponent is a Java class that is responsible for representing a self-contained piece of the user interface during the request-processing lifecycle. It is intended to represent the meaning of the component; the visual representation of the component is the responsibility of the jakarta.faces.render.Renderer. There can be multiple instances of the same UIComponent class in any given Jakarta Faces view, just as there can be multiple instances of any Java class in any given Java program.

Jakarta Faces technology provides the ability to create custom components by extending the UIComponent class, the base class for all standard UI components. A custom component can be used anywhere an ordinary component can be used, such as within a composite component. A UIComponent is identified by two names: component-family specifies the general purpose of the component (input or output, for instance), and component-type indicates the specific purpose of a component, such as a text input field or a command button.

A Renderer is a helper to the UIComponent that deals with how that specific UIComponent class should appear in a specific kind of client device. Renderers are identified by two names: render-kit-id and renderer-type. A render kit is just a bucket into which a particular group of renderers is placed, and the render-kit-id identifies the group. Most Jakarta Faces component libraries provide their own render kits.

A jakarta.faces.view.facelets.Tag object is a helper to the UIComponent and Renderer that allows the page author to include an instance of a UIComponent in a Jakarta Faces view. A tag represents a specific combination of component-type and renderer-type.

See Component, Renderer, and Tag Combinations for information on how components, renderers, and tags interact.

This chapter uses the image map component from the Duke’s Bookstore case study example to explain how you 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. See Duke’s Bookstore Case Study Example for more information about this example.

The chapter also describes how to create other custom objects: custom converters, custom listeners, and custom validators. It also describes how to bind component values and instances to data objects and how to bind custom objects to managed bean properties.

Determining Whether You Need a Custom Component or Renderer

The Jakarta Faces implementation supports a very basic set of components and associated renderers. This section helps you to decide whether you can use standard components and renderers in your application or need a custom component or custom 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 any other behavior related to how the component interacts with the browser and the request-processing lifecycle.

You need to create a custom component in the following situations.

  • You need to add new behavior to a standard component, such as generating an additional type of event (for example, notifying another part of the page that something changed in this component as a result of user interaction).

  • You need to take a different action in the request processing of the value of a component from what is available in any of the existing standard components.

  • You want to take advantage of an HTML capability offered by your target browser, but none of the standard Jakarta Faces components take advantage of the capability in the way you want, if at all. The current release does not contain standard components for complex HTML components, such as frames; however, because of the extensibility of the component architecture, you can use Jakarta Faces technology to create components like these. The Duke’s Bookstore case study creates custom components that correspond to the HTML map and area tags.

  • You need to render to a non-HTML client that requires extra components not supported by HTML. Eventually, the standard HTML render kit will provide support for all standard HTML components. However, if you are rendering to a different client, such as a phone, you might need to create custom components to represent the controls uniquely supported by the client. For example, some component architectures for wireless clients include support for tickers and progress bars, which are not available on an HTML client. In this case, you might also need a custom renderer along with the component, or you might need only a custom renderer.

You do not need to create a custom component in the following cases.

  • You need to aggregate components to create a new component that has its own unique behavior. In this situation, you can use a composite component to combine existing standard components. For more information on composite components, see Composite Components and Composite Components: Advanced Topics and an Example.

  • You simply need to manipulate data on the component or add application-specific functionality to it. In this situation, you should create a managed bean for this purpose and bind it to the standard component rather than create a custom component. See Managed Beans in Jakarta Faces Technology for more information on managed beans.

  • You need to convert a component’s data to a type not supported by its renderer. See Using the Standard Converters for more information about converting a component’s data.

  • You need to perform validation on the component data. Standard validators and custom validators can be added to a component by using the validator tags from the page. See Using the Standard Validators and Creating and Using a Custom Validator for more information about validating a component’s data.

  • You need to register event listeners on components. You can either register event listeners on components using the f:valueChangeListener and f:actionListener tags, or you can point at an event-processing method on a managed bean using the component’s actionListener or valueChangeListener attributes. See Implementing an Event Listener and Writing Managed Bean Methods for more information.

When to Use a Custom Renderer

A renderer, which generates the markup to display a component on a web page, allows you to separate the semantics of a component from its appearance. By keeping this separation, you can support different kinds of client devices with the same kind of authoring experience. You can think of a renderer as a "client adapter." It produces output suitable for consumption and display by the client and accepts input from the client when the user interacts with that component.

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

  • Decoding: Converting the incoming request parameters to the local value of the component

  • Encoding: Converting the current local value of the component into the corresponding markup that represents it in the response

The Jakarta Faces specification supports two programming models for handling encoding and decoding.

  • Direct implementation: The component class itself implements the decoding and encoding.

  • Delegated implementation: The component class delegates the implementation of encoding and decoding to a separate renderer.

By delegating the operations to the renderer, you have the option of associating your custom component with different renderers so that you can render the component on different clients. If you don’t plan to render a particular component on different clients, it may be simpler to let the component class handle the rendering. However, a separate renderer enables you to preserve the separation of semantics from appearance. The Duke’s Bookstore application separates the renderers from the components, although it renders only to HTML 4 web browsers.

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 can create a custom renderer to go with it. To associate the component with the renderer and to reference the component from the page, you will also need a custom tag.

Although you need to write the custom component and renderer, there is no need to write code for a custom tag (called a tag handler). If you specify the component and renderer combination, Facelets creates the tag handler automatically.

In rare situations, 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 is 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.

Requirements for Custom Components, Custom Renderers and Custom Tags summarizes what you must or can associate with a custom component, custom renderer, or custom tag.

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 Jakarta 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 index.xhtml page. This image map displays a selection of six book titles. When the user clicks one of the book titles in the image map, the application goes to a page that displays the title of the selected book as well as information about a featured book. The page allows the user to add either book (or none) to the shopping cart.

Why Use Jakarta Faces Technology to Implement an Image Map?

Jakarta 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 nonform controls, tasks that a client-side image map cannot do.

Because the image map custom component uses Jakarta 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="j_idt13" name="j_idt13" method="post"
        action="/dukesbookstore/index.xhtml" ...>
    ...
    <img id="j_idt13:mapImage"
         src="/dukesbookstore/jakarta.faces.resource/book_all.jpg?ln=images"
         alt="Choose a Book from our Catalog"
         usemap="#bookMap" />
    ...
    <map name="bookMap">
       <area alt="Duke"
          coords="67,23,212,268"
          shape="rect"
          onmouseout="document.forms[0]['j_idt13:mapImage'].src='resources/images/book_all.jpg'"
          onmouseover="document.forms[0]['j_idt13:mapImage'].src='resources/images/book_201.jpg'"
          onclick="document.forms[0]['bookMap_current'].value='Duke'; document.forms[0].submit()"
       />
    ...
       <input type="hidden" name="bookMap_current">
    </map>
    ...
</form>

The img tag associates an image (book_all.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 bookMap_current and set the locale in the jakarta.faces.context.FacesContext instance according to the region that was selected.

Understanding the Facelets Page

Here is an abbreviated form of the Facelets page that the image map component uses to generate the HTML page shown in the preceding section. It uses custom bookstore:map and bookstore:area tags to represent the custom components:

<h:form>
    ...
        <h:graphicImage id="mapImage"
                        name="book_all.jpg"
                        library="images"
                        alt="#{bundle.ChooseBook}"
                        usemap="#bookMap" />
        <bookstore:map id="bookMap"
                       current="map1"
                       immediate="true"
                       action="bookstore">
            <f:actionListener
                type="dukesbookstore.listeners.MapBookChangeListener" />
            <bookstore:area id="map1" value="#{Book201}"
                            onmouseover="resources/images/book_201.jpg"
                            onmouseout="resources/images/book_all.jpg"
                            targetImage="mapImage" />
            <bookstore:area id="map2" value="#{Book202}"
                            onmouseover="resources/images/book_202.jpg"
                            onmouseout="resources/images/book_all.jpg"
                            targetImage="mapImage"/>
            ...
        </bookstore:map>
    ...
</h:form>

The alt attribute of the h:graphicImage tag maps to the localized string "Choose a Book from our Catalog".

The f:actionListener tag within the bookstore:map tag points to a listener class for an action event. The processAction method of the listener places the book ID for the selected map area into the session map. The way this event is handled is explained more in Handling Events for Custom Components.

The action attribute of the bookstore:map tag specifies a logical outcome String, "bookstore", which by implicit navigation rules sends the application to the page bookstore.xhtml. For more information on navigation, see Configuring Navigation Rules.

The immediate attribute of the bookstore:map tag is set to true, which indicates that the default jakarta.faces.event.ActionListener implementation should execute during the Apply Request Values phase of the request-processing lifecycle, 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 bookstore:map tag is set to the default area, which is map1 (the book My Early Years: Growing Up on Star7, by Duke).

Notice that the bookstore: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 dukesbookstore.renderers.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.

Summary of the Image Map Application Classes

Image Map Classes summarizes all the classes needed to implement the image map component.

Image Map Classes
Class Function

AreaSelectedEvent

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

AreaComponent

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

MapComponent

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

AreaRenderer

This jakarta.faces.render.Renderer performs the delegated rendering for AreaComponent.

ImageArea

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

MapBookChangeListener

The action listener for the MapComponent.

The Duke’s Bookstore source directory, called bookstore-dir, is jakartaee-examples/tutorial/case-studies/dukes-bookstore/src/main/java/jakarta/tutorial/dukesbookstore/. The event and listener classes are located in bookstore-dir/listeners/. 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/.

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 jakarta.faces.render.Renderer.

    2. Register the renderer to a render kit.

  3. Register the component.

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

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

See Registering a Custom Component and Registering a Custom Renderer with a Render Kit for information on registering the custom component and the renderer. The section Using a Custom Component discusses how to use the custom component in a Jakarta 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:

  • Decoding (converting the request parameter to the component’s local value)

  • Encoding (converting the local value into the corresponding markup)

  • Saving the state of the component

  • Updating the bean value with the local value

  • Processing validation on the local value

  • Queueing events

The jakarta.faces.component.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 jakarta.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 defined in the jakarta.faces.component package:

  • ActionSource: Indicates that the component can fire a jakarta.faces.event.ActionEvent

  • ActionSource2: Extends ActionSource and allows component properties referencing methods that handle action events to use method expressions as defined by the EL

  • EditableValueHolder: Extends ValueHolder and specifies additional features for editable components, such as validation and emitting value-change events

  • NamingContainer: Mandates that each component rooted at this component has a unique ID

  • StateHolder: Denotes that a component has state that must be saved between requests

  • ValueHolder: Indicates that the component maintains a local value as well as the option of accessing data in the model tier

If your component extends UIComponentBase, it automatically implements only StateHolder. Because all components directly or indirectly extend UIComponentBase, they all implement StateHolder. Any component that implements StateHolder also implements the StateHelper interface, which extends StateHolder and defines a Map-like contract that makes it easy for components to save and restore a partial view state.

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 Jakarta Faces API documentation 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 Duke’s Bookstore 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 @FacesComponent annotation registers the components with the Jakarta Faces implementation:

@FacesComponent("DemoMap")
public class MapComponent extends UICommand {...}

@FacesComponent("DemoArea")
public class AreaComponent extends UIOutput {...}

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

<bookstore:map id="bookMap"
               current="map1"
               immediate="true"
               action="bookstore">
    ...
</bookstore:map>

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

<bookstore:area id="map1" value="#{Book201}"
                onmouseover="resources/images/book_201.jpg"
                onmouseout="resources/images/book_all.jpg"
                targetImage="mapImage"/>

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

  • Retrieving the value of the currently selected area

  • Defining the properties corresponding to the component’s values

  • Generating an event when the user clicks on the image map

  • Queuing the event

  • Saving its state

  • Rendering the HTML map tag and the HTML input tag

MapComponent delegates the rendering of the HTML map and input tags to the MapRenderer class.

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

  • Retrieving the shape and coordinate data from the bean

  • Setting the value of the hidden tag to the id of this component

  • Rendering the area tag, including the JavaScript for the onmouseover, onmouseout, and onclick functions

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:

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 Jakarta 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. Alternatively, you can use the encodeALL method, which encompasses all the methods.

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 Jakarta 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:

@Override
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");
}

@Override
public void encodeEnd(FacesContext context, UIComponent component)
        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 jakarta.faces.context.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 jakarta.faces.context.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 Jakarta 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 jakarta.faces.convert.Converter implementation 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 component queues the event by calling queueEvent.

Here is the decode method of MapRenderer:

@Override
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 Jakarta 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 it gives you much more flexibility when you write Facelets pages.

To enable the attributes to accept expressions, the component class must implement getter and setter methods for the component properties. These methods can use the facilities offered by the StateHelper interface to store and retrieve not only the values for these properties but also the state of the components across multiple requests.

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. Similarly, the UIOutput class that AreaComponent extends already obtains the ValueExpression instances for its supported attributes. For both components, the simple getter and setter methods store and retrieve the key values and state for the attributes, as shown in this code fragment from AreaComponent:

enum PropertyKeys {
    alt, coords, shape, targetImage;
}
public String getAlt() {
    return (String) getStateHelper().eval(PropertyKeys.alt, null);
}
public void setAlt(String alt) {
    getStateHelper().put(PropertyKeys.alt, alt);
}
...

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, you could 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

As described in Enabling Component Properties to Accept Expressions, use of the StateHelper interface facilities allows you to save the component’s state at the same time you set and retrieve property values. The StateHelper implementation allows partial state saving; it saves only the changes in the state since the initial request, not the entire state, because the full state can be restored during the Restore View phase.

Component classes that implement StateHolder may prefer to implement the saveState(FacesContext) and restoreState(FacesContext, Object) methods to help the Jakarta Faces implementation save and restore the state of components across multiple requests.

To save a set of values, you can 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 a hypothetical method from MapComponent, which has only one attribute, current:

@Override
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 the component.

A component that implements StateHolder may 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 Jakarta 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 a hypothetical 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.

Whether or not you implement these methods in your component class, you can use the jakarta.faces.STATE_SAVING_METHOD context parameter 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. By default, the state is saved on the server.

The web applications in the Duke’s Forest case study save their view state on the client.

Saving state on the client uses more bandwidth as well as more client resources, whereas saving it on the server uses more server resources. You may also want to save state on the client if you expect your users to disable cookies.

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 the tasks described in the following topics:

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.

The renderer class begins with a @FacesRenderer annotation:

@FacesRenderer(componentFamily = "Area", rendererType = "DemoArea")
public class AreaRenderer extends Renderer { }

The @FacesRenderer annotation registers the renderer class with the Jakarta Faces implementation as a renderer class. The annotation identifies the component family as well as the renderer type.

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 "book203". The following line from encodeEnd gets the value of the attribute "book203" 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 book203 AreaComponent instance.

After retrieving the ImageArea object, the method 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 Facelets page needs to provide only the path to the images that are to be loaded during an onmouseover or onmouseout action:

<bookstore:area id="map3" value="#{Book203}"
                onmouseover="resources/images/book_203.jpg"
                onmouseout="resources/images/book_all.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 Jakarta Faces lifecycle 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 Jakarta 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.

The @FacesRenderer annotation registers the renderer class with the Jakarta Faces implementation as a renderer class. The annotation identifies the component family as well as the renderer type.

Identifying the Renderer Type

Register the renderer with a render kit by using the @FacesRenderer annotation (or by using the application configuration resource file, as explained in Registering a Custom Renderer with a Render Kit). During the Render Response phase, the Jakarta Faces implementation calls the getRendererType method of the component’s tag handler to determine which renderer to invoke, if there is one.

You identify the type associated with the renderer in the rendererType element of the @FacesRenderer annotation for AreaRenderer as well as in the renderer-type element of the tag library descriptor.

Implementing an Event Listener

The Jakarta Faces technology supports action events and value-change events for components.

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

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

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

This section explains how to implement the two listener classes.

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

You do not need to 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 jakarta.faces.event.ValueChangeListener implementation must include a processValueChange(ValueChangeEvent) method. This method processes the specified value-change event and is invoked by the Jakarta 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.

In the Duke’s Bookstore case study, the NameChanged listener implementation is registered on the name UIInput component on the bookcashier.xhtml page. This listener stores into session scope the name the user entered in the field corresponding to the name component.

The bookreceipt.xhtml subsequently retrieves the name from the session scope:

<h:outputFormat title="thanks"
                value="#{bundle.ThankYouParam}">
    <f:param value="#{sessionScope.name}"/>
</h:outputFormat>

When the bookreceipt.xhtml page is loaded, it displays the 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 {

    @Override
    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 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, and 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

A jakarta.faces.event.ActionListener implementation must include a processAction(ActionEvent) method. The processAction(ActionEvent) method processes the specified action event. The Jakarta Faces implementation invokes the processAction(ActionEvent) method when the ActionEvent occurs.

The Duke’s Bookstore case study uses two ActionListener implementations, LinkBookChangeListener and MapBookChangeListener. See Handling Events for Custom Components for details on MapBookChangeListener.

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

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 that represents the event of clicking on the map and how to write the method that processes this event.

As explained in Understanding the Facelets Page, the actionListener attribute of the bookstore:map tag points to the MapBookChangeListener class. The listener class’s processAction method processes the event of clicking the image map. Here is the processAction method:

@Override
public void processAction(ActionEvent actionEvent)
        throws AbortProcessingException {

    AreaSelectedEvent event = (AreaSelectedEvent) actionEvent;
    String current = event.getMapComponent().getCurrent();
    FacesContext context = FacesContext.getCurrentInstance();
    String bookId = books.get(current);
    context.getExternalContext().getSessionMap().put("bookId", bookId);
}

When the Jakarta 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 jakartaee-examples/tutorial/case-studies/dukes-bookstore/src/main/java/jakarta/tutorial/dukesbookstore/listeners/AreaSelectedEvent.java). Then this method gets the MapComponent associated with the event. Next, it 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 attribute to get the book’s ID value from a HashMap object, which is constructed elsewhere in the MapBookChangeListener class. Finally, the method places the ID obtained from the HashMap object into the session map for the application.

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.

Defining the Custom Component Tag in a Tag Library Descriptor

To use a custom tag, you declare it in a Tag Library Descriptor (TLD). The TLD file defines how the custom tag is used in a Jakarta Faces page. 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, available in the Jakarta Faces standard HTML tag library.

The TLD file name must end with taglib.xml. In the Duke’s Bookstore case study, the custom tags area and map are defined in the file web/WEB-INF/bookstore.taglib.xml.

All tag definitions must be nested inside the facelet-taglib element in the TLD. Each tag is defined by a tag element. Here are the tag definitions for the area and map components:

<facelet-taglib xmlns="https://jakarta.ee/xml/ns/jakartaee"
...>
    <namespace>http://dukesbookstore</namespace>
    <tag>
        <tag-name>area</tag-name>
        <component>
            <component-type>DemoArea</component-type>
            <renderer-type>DemoArea</renderer-type>
        </component>
    </tag>
    <tag>
        <tag-name>map</tag-name>
        <component>
            <component-type>DemoMap</component-type>
            <renderer-type>DemoMap</renderer-type>
        </component>
    </tag>
</facelet-taglib>

The component-type element specifies the name defined in the @FacesComponent annotation, and the renderer-type element specifies the rendererType defined in the @FacesRenderer annotation.

The facelet-taglib element must also include a namespace element, which defines the namespace to be specified in pages that use the custom component. See Using a Custom Component for information on specifying the namespace in pages.

The TLD file is located in the WEB-INF directory. In addition, an entry is included in the web deployment descriptor (web.xml) to identify the custom tag library descriptor file, as follows:

    <context-param>
        <param-name>jakarta.faces.FACELETS_LIBRARIES</param-name>
        <param-value>/WEB-INF/bookstore.taglib.xml</param-value>
    </context-param>

Using a Custom Component

To use a custom component in a page, you add the custom tag associated with the component to the page.

As explained in Defining the Custom Component Tag in a Tag Library Descriptor, you must ensure that the TLD that defines any custom tags is packaged in the application if you intend to use the tags in your pages. TLD files are stored in the WEB-INF/ directory or subdirectory of the WAR file or in the META-INF/ directory or subdirectory of a tag library packaged in a JAR file.

You also need to include a namespace declaration in the page so that the page has access to the tags. The custom tags for the Duke’s Bookstore case study are defined in bookstore.taglib.xml. The ui:composition tag on the index.xhtml page declares the namespace defined in the tag library:

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
                xmlns:ui="jakarta.faces.facelets"
                xmlns:h="jakarta.faces.html"
                xmlns:f="jakarta.faces.core"
                xmlns:bookstore="http://dukesbookstore"
                template="./bookstoreTemplate.xhtml">

Finally, to use a custom component in a page, you add the component’s tag to the page.

The Duke’s Bookstore case study includes a custom image map component on the index.xhtml page. This component allows you to select a book by clicking on a region of the image map:

...
<h:graphicImage id="mapImage"
                name="book_all.jpg"
                library="images"
                alt="#{bundle.chooseLocale}"
                usemap="#bookMap" />
<bookstore:map id="bookMap"
               current="map1"
               immediate="true"
               action="bookstore">
    <f:actionListener
        type="ee.jakarta.tutorial.dukesbookstore.listeners.MapBookChangeListener" />
    <bookstore:area id="map1" value="#{Book201}"
                    onmouseover="resources/images/book_201.jpg"
                    onmouseout="resources/images/book_all.jpg"
                    targetImage="mapImage" />
    ...
    <bookstore:area id="map6" value="#{Book207}"
                    onmouseover="resources/images/book_207.jpg"
                    onmouseout="resources/images//book_all.jpg"
                    targetImage="mapImage" />
</bookstore:map>

The standard h:graphicImage tag associates an image (book_all.jpg) with an image map that is referenced in the usemap attribute value.

The custom bookstore:map tag that represents the custom component, MapComponent, specifies the image map and contains a set of bookstore:area tags. Each custom bookstore:area tag represents a custom AreaComponent and specifies a region of the image map.

On the page, the onmouseover and onmouseout attributes specify the image that is displayed when the user performs the actions described by the attributes. The custom renderer also renders an onclick attribute.

In the rendered HTML page, 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. When the user clicks a region, the onclick function sets the value of a hidden input tag to the ID of the selected area and submits the page.

When the custom renderer renders these attributes in HTML, it also renders the JavaScript code. The custom renderer also renders the entire onclick attribute rather than letting the page author set it.

The custom renderer that renders the HTML map tag also renders a hidden input component that holds the current area. The server-side objects retrieve the value of the hidden input field and set the locale in the FacesContext instance according to which region was selected.

Creating and Using a Custom Converter

A Jakarta Faces converter class converts strings to objects and objects to strings as required. Several standard converters are provided by Jakarta Faces for this purpose. See Using the Standard Converters for more information on these included converters.

As explained in Conversion Model, if the standard converters included with Jakarta Faces cannot perform the data conversion that you need, you can create a custom converter to perform this specialized conversion. This implementation, at a minimum, must define how to convert data both ways between the two views of the data described in Conversion Model.

All custom converters must implement the jakarta.faces.convert.Converter interface. This section explains how to implement this interface to perform a custom data conversion.

The Duke’s Bookstore case study uses a custom Converter implementation, located in jakarta-examples/tutorial/case-studies/dukes-bookstore/src/main/java/jakarta/tutorial/dukesbookstore/converters/CreditCardConverter.java, to convert the data entered in the Credit Card Number field on the bookcashier.xhtml page. It strips blanks and hyphens from the text string and formats it so that a blank space separates every four characters.

Another common use case for a custom converter is in a list for a nonstandard object type. In the Duke’s Tutoring case study, the Student and Guardian entities require a custom converter so that they can be converted to and from a UISelectItems input component.

Creating a Custom Converter

The CreditCardConverter custom converter class is created as follows:

@FacesConverter("ccno")
public class CreditCardConverter implements Converter {
    ...
}

The @FacesConverter annotation registers the custom converter class as a converter with the name of ccno with the Jakarta Faces implementation. Alternatively, you can register the converter with entries in the application configuration resource file, as shown in Registering a Custom Converter.

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:

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

    if (newValue.isEmpty()) {
        return null;
    }
    // Since this is only a String to String conversion,
    // this conversion does not throw ConverterException.

    String convertedValue = newValue.trim();
    if ( (convertedValue.contains("-")) || (convertedValue.contains(" "))) {
        char[] input = convertedValue.toCharArray();
        StringBuilder builder = new StringBuilder(input.length);
        for (int i = 0; i < input.length; ++i) {
            if ((input[i] == '-') || (input[i] == ' ')) {
            } else {
                builder.append(input[i]);
            }
        }
        convertedValue = builder.toString();
    }
    return convertedValue;
}

During the Apply Request Values phase, when the components' decode methods are processed, the Jakarta Faces implementation looks up the component’s local value in the request and calls the getAsObject method. When calling this method, the Jakarta 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 an implementation of this method:

@Override
public String getAsString(FacesContext context,
        UIComponent component, Object value)
        throws ConverterException {

    String inputVal = null;
    if ( value == null ) {
        return "";
    }
    // value must be of a type that can be cast to a String.
    try {
        inputVal = (String)value;
    } catch (ClassCastException ce) {
        FacesMessage errMsg = new FacesMessage(CONVERSION_ERROR_MESSAGE_ID);
        FacesContext.getCurrentInstance().addMessage(null, errMsg);
        throw new ConverterException(errMsg.getSummary());
    }
    // insert spaces after every four characters for better
    // readability if they are not already present.
    char[] input = inputVal.toCharArray();
    StringBuilder builder = new StringBuilder(input.length + 3);
    for (int i = 0; i < input.length; ++i) {
        if ((i % 4) == 0 && (i != 0)) {
            if ((input[i] != ' ') || (input[i] != '-')){
                builder.append(" ");
                // if there are any "-"'s convert them to blanks.
            } else if (input[i] == '-') {
                builder.append(" ");
            }
         }
         builder.append(input[i]);
    }
    String convertedValue = builder.toString();
    return convertedValue;
}

During the Render Response phase, in which the components' encode methods are called, the Jakarta Faces implementation calls the getAsString method in order to generate the appropriate output. When the Jakarta 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 an error message from the resource bundle that is registered with the application. Registering Application Messages explains how to register custom error messages with the application.

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.

You can also create a custom converter with a @FacesConverter annotation that specifies the forClass attribute, as shown in the following example from the Duke’s Tutoring case study:

@FacesConverter(forClass=Guardian.class, value="guardian")
public class GuardianConverter extends EntityConverter implements Converter { ... }

The forClass attribute registers the converter as the default converter for the Guardian class. Therefore, whenever that class is specified by a value attribute of an input component, the converter is invoked automatically.

A converter class can be a separate Java POJO class, as in the Duke’s Bookstore case study. If it needs to access objects defined in a managed bean class, however, it can be a subclass of a Jakarta Faces managed bean, as in the address-book persistence example, in which the converters use an enterprise bean that is injected into the managed bean class.

Using a Custom Converter

To apply the data conversion performed by a custom converter to a particular component’s value, you must do one of the following.

  • Reference the converter from the component tag’s converter attribute.

  • Nest an f:converter tag inside the component’s tag and reference the custom converter from one of the f:converter tag’s attributes.

If you are using the component tag’s converter attribute, this attribute must reference the Converter implementation’s identifier or the fully-qualified class name of the converter. Creating and Using a Custom Converter explains how to implement a custom converter.

The identifier for the credit card converter class is ccno, the value specified in the @FacesConverter annotation:

@FacesConverter("ccno")
public class CreditCardConverter implements Converter {
    ...
}

Therefore, the CreditCardConverter instance can be registered on the ccno component as shown in the following example:

<h:inputText id="ccno"
             size="19"
             converter="ccno"
             value="#{cashierBean.creditCardNumber}"
             required="true"
             requiredMessage="#{bundle.ReqCreditCard}">
    ...
</h:inputText>

By setting the converter attribute of a component’s tag to the converter’s identifier or its class name, you cause that component’s local value to be automatically converted according to the rules specified in the Converter implementation.

Instead of referencing the converter from the component tag’s converter attribute, you can reference the converter from an f:converter tag nested inside the component’s tag. To reference the custom converter using the f:converter tag, you do one of the following.

  • Set the f:converter tag’s converterId attribute to the Converter implementation’s identifier defined in the @FacesConverter annotation or in the application configuration resource file. This method is shown in bookcashier.xhtml:

    <h:inputText id="ccno"
                 size="19"
                 value="#{cashierBean.creditCardNumber}"
                 required="true"
                 requiredMessage="#{bundle.ReqCreditCard}">
        <f:converter converterId="ccno"/>
        <f:validateRegex
           pattern="\d{16}|\d{4} \d{4} \d{4} \d{4}|\d{4}-\d{4}-\d{4}-\d{4}"/>
    </h:inputText>
  • Bind the Converter implementation to a managed bean property using the f:converter tag’s binding attribute, as described in Binding Converters, Listeners, and Validators to Managed Bean Properties.

The Jakarta Faces implementation calls the converter’s getAsObject method to strip spaces and hyphens from the input value. The getAsString method is called when the bookcashier.xhtml page is redisplayed; this happens if the user orders more than $100 worth of books.

In the Duke’s Tutoring case study, each converter is registered as the converter for a particular class. The converter is automatically invoked whenever that class is specified by a value attribute of an input component. In the following example, the itemValue attribute calls the converter for the Guardian class:

<h:selectManyListbox id="selectGuardiansMenu"
                     title="#{bundle['action.add.guardian']}"
                     value="#{guardianManager.selectedGuardians}"
                     size="5"
                     converter="guardian">
    <f:selectItems value="#{guardianManager.allGuardians}"
                   var="selectedGuardian"
                   itemLabel="#{selectedGuardian.name}"
                   itemValue="#{selectedGuardian}" />
</h:selectManyListbox>

Creating and Using a Custom Validator

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

  • Implement a managed bean method that performs the validation.

  • Provide an implementation of the jakarta.faces.validator.Validator interface to perform the validation.

Writing a Method to Perform Validation explains how to implement a managed 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 specify a custom tag for registering the validator on a component.

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

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

Usually, you will want to display an error message when data fails validation. You need to store these error messages in 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 register the error messages in the application configuration resource file, as explained in Registering Application Messages.

For example, an e-commerce application might use a general-purpose custom validator called FormatValidator.java to validate input data against a format pattern that is specified in the custom validator tag. This validator would be used with a Credit Card Number field on a Facelets page. Here is the custom validator tag:

<mystore: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:

  • A 16-digit number with no spaces

  • A 16-digit number with a space between every four digits

  • A 16-digit number with hyphens between every four digits

The f:validateRegex tag makes a custom validator unnecessary in this situation. However, the rest of this section describes how this validator would be implemented and how to specify a custom tag so that the page author could 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 hypothetical FormatValidator class also defines accessor methods for setting the formatPatterns attribute, which specifies the acceptable format patterns for input into the fields. The setter method calls the parseFormatPatterns method, which separates the components of the pattern string into a string array, formatPatternsList.

public String getFormatPatterns() {
    return (this.formatPatterns);
}
public void setFormatPatterns(String formatPatterns) {
    this.formatPatterns = formatPatterns;
    parseFormatPatterns();
}

In addition to defining accessor methods for the attributes, 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 jakarta.faces.component.EditableValueHolder.

Here is an implementation of the validate method:

@FacesValidator
public class FormatValidator implements Validator, StateHolder {
    ...
    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 =
                new FacesMessage(FORMAT_INVALID_MESSAGE_ID);
            FacesContext.getCurrentInstance().addMessage(null, errMsg);
            throw new ValidatorException(errMsg);
        }
    }
}

The @FacesValidator annotation registers the FormatValidator class as a validator with the Jakarta Faces implementation. The validate 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 that was parsed from 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 creates a jakarta.faces.application.FacesMessage and queues it on the FacesContext for display, using a String that represents the key in the Properties file:

public static final String FORMAT_INVALID_MESSAGE_ID =
     "FormatInvalid";
}

Finally, the method passes the message to the constructor of jakarta.faces.validator.ValidatorException.

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

Input must match one of the following patterns: {0}

You may wish to save and restore state for your validator, although state saving is not usually necessary. To do so, you will need to implement the StateHolder interface as well as the Validator interface. To implement StateHolder, you would need to implement its four methods: saveState(FacesContext), restoreState(FacesContext, Object), isTransient, and setTransient(boolean). See Saving and Restoring State for more information.

Specifying a Custom Tag

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

  • Allow the page author to specify the Validator implementation to use with the f:validator tag. In this case, the Validator implementation must define its own properties. Using a Custom Validator explains how to use the f:validator tag.

  • Specify a custom tag that provides attributes for configuring the properties of the validator from the page.

To create a custom tag, you need to add the tag to the tag library descriptor for the application, bookstore.taglib.xml:

<tag>
    <tag-name>validator</tag-name>
    <validator>
        <validator-id>formatValidator</validator-id>
        <validator-class>
            dukesbookstore.validators.FormatValidator
        </validator-class>
    </validator>
</tag>

The tag-name element defines the name of the tag as it must be used in a Facelets page. The validator-id element identifies the custom validator. The validator-class element wires the custom tag to its implementation class.

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

Using a Custom Validator

To register a custom validator on a component, you must do one of the following.

  • Nest the validator’s custom tag inside the tag of the component whose value you want to be validated.

  • Nest the standard f:validator tag within the tag of the component and reference the custom Validator implementation from the f:validator tag.

Here is a hypothetical custom formatValidator tag for the Credit Card Number field, nested within the h:inputText tag:

<h:inputText id="ccno" size="19"
  ...
  required="true">
  <mystore:formatValidator
  formatPatterns="9999999999999999|9999 9999 9999 9999|9999-9999-9999-9999"/>
</h:inputText>
<h:message styleClass="validationMessage" for="ccno"/>

This tag validates the input of the ccno field against the patterns defined by the page author in the formatPatterns attribute.

You can use the same custom validator for any similar component by simply nesting the custom validator tag within the component tag.

If the application developer who created the custom validator prefers to configure the attributes in the Validator implementation rather than allow the page author to configure the attributes from the page, the developer will not create a custom tag for use with the validator.

In this case, the page author must nest the f:validator tag inside the tag of the component whose data needs to be validated. Then the page author needs to do one of the following.

The following tag registers a hypothetical validator on a component using an f:validator tag and references the ID of the validator:

<h:inputText id="name" value="#{CustomerBean.name}"
            size="10" ...>
    <f:validator validatorId="customValidator" />
    ...
</h:inputText>

Binding Component Values and Instances to Managed Bean Properties

A component tag can wire its data to a managed bean by one of the following methods:

  • Binding its component’s value to a bean property

  • Binding its component’s instance to a bean property

To bind a component’s value to a managed bean property, a component tag’s value attribute uses an EL value expression. To bind a component instance to a bean property, a component tag’s binding attribute uses a value expression.

When a component instance is bound to a managed bean property, the property holds the component’s local value. Conversely, when a component’s value is bound to a managed bean property, the property holds the value stored in the managed bean. This value is updated with the local value during the Update Model Values phase of the lifecycle. There are advantages to both of these methods.

Binding a component instance to a bean property has the following advantages.

  • The managed bean can programmatically modify component attributes.

  • The managed bean can instantiate components rather than let the page author do so.

Binding a component’s value to a bean property has the following advantages.

  • The page author has more control over the component attributes.

  • The managed bean has no dependencies on the Jakarta Faces API (such as the component classes), allowing for greater separation of the presentation layer from the model layer.

  • The Jakarta Faces implementation can perform conversions on the data based on the type of the bean property without the developer needing to apply a converter.

In most situations, you will bind a component’s value rather than its instance to a bean property. You’ll need to use a component binding only when you need to change one of the component’s attributes dynamically. For example, if an application renders a component only under certain conditions, it can set the component’s rendered property accordingly by accessing the property to which the component is bound.

When referencing the property using the component tag’s value attribute, you need to use the proper syntax. For example, suppose a managed bean called MyBean has this int property:

protected int currentOption = null;
public int getCurrentOption(){...}
public void setCurrentOption(int option){...}

The value attribute that references this property must have this value-binding expression:

#{myBean.currentOption}

In addition to binding a component’s value to a bean property, the value attribute can specify a literal value or can map the component’s data to any primitive (such as int), structure (such as an array), or collection (such as a list), independent of a JavaBeans component. Examples of Value-Binding Expressions lists some example value-binding expressions that you can use with the value attribute.

Examples of Value-Binding Expressions
Value Expression

A Boolean

cart.numberOfItems > 0

A property initialized from a context initialization parameter

initParam.quantity

A bean property

cashierBean.name

A value in an array

books[3]

A value in a collection

books["fiction"]

A property of an object in an array of objects

books[3].price

The next two sections explain how to use the value attribute to bind a component’s value to a bean property or other data objects and how to use the binding attribute to bind a component instance to a bean property.

Binding a Component Value to a Property

To bind a component’s value to a managed bean property, you specify the name of the bean and the property using the value attribute.

This means that the first part of the EL value expression must match the name of the managed bean up to the first period (.) and the part of the value expression after the period must match the property of the managed bean.

For example, in the Duke’s Bookstore case study, the h:dataTable tag in bookcatalog.xhtml sets the value of the component to the value of the books property of the BookstoreBean backing bean, whose name is store:

<h:dataTable id="books"
             value="#{store.books}"
             var="book"
             headerClass="list-header"
             styleClass="list-background"
             rowClasses="list-row-even, list-row-odd"
             border="1"
             summary="#{bundle.BookCatalog}">

The value is obtained by calling the backing bean’s getBooks method, which in turn calls the BookRequestBean session bean’s getBooks method.

Binding a Component Value to an Implicit Object

One external data source that a value attribute can refer to is an implicit object.

The bookreceipt.xhtml page of the Duke’s Bookstore case study has a reference to an implicit object:

<h:outputFormat title="thanks"
                value="#{bundle.ThankYouParam}">
    <f:param value="#{sessionScope.name}"/>
</h:outputFormat>

This tag gets the name of the customer from the session scope and inserts it into the parameterized message at the key ThankYouParam from the resource bundle. For example, if the name of the customer is Gwen Canigetit, this tag will render:

Thank you, Gwen Canigetit, for purchasing your books from us.

Retrieving values from other implicit objects is done in a similar way to the example shown in this section. Implicit Objects lists the implicit objects to which a value attribute can refer. All of the implicit objects, except for the scope objects, are read-only and therefore should not be used as values for a UIInput component.

Implicit Objects
Implicit Object What It Is

applicationScope

A Map of the application scope attribute values, keyed by attribute name

cookie

A Map of the cookie values for the current request, keyed by cookie name

facesContext

The FacesContext instance for the current request

header

A Map of HTTP header values for the current request, keyed by header name

headerValues

A Map of String arrays containing all the header values for HTTP headers in the current request, keyed by header name

initParam

A Map of the context initialization parameters for this web application

param

A Map of the request parameters for this request, keyed by parameter name

paramValues

A Map of String arrays containing all the parameter values for request parameters in the current request, keyed by parameter name

requestScope

A Map of the request attributes for this request, keyed by attribute name

sessionScope

A Map of the session attributes for this request, keyed by attribute name

view

The root UIComponent in the current component tree stored in the FacesRequest for this request

Binding a Component Instance to a Bean Property

A component instance can be bound to a bean property using a value expression with the binding attribute of the component’s tag. You usually bind a component instance rather than its value to a bean property if the bean must dynamically change the component’s attributes.

Here are two tags from the bookcashier.xhtml page that bind components to bean properties:

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

The h:selectBooleanCheckbox tag renders a check box and binds the fanClub UISelectBoolean component to the specialOffer property of the cashier bean. The h:outputLabel tag binds the component representing the check box’s label to the specialOfferText property of the cashier bean. If the application’s locale is English, the h:outputLabel tag renders

I'd like to join the Duke Fan Club, free with my purchase of over $100

The rendered attributes of both tags are set to false to prevent the check box and its label from being rendered. If the customer makes a large order and clicks the Submit button, the submit method of CashierBean sets both components' rendered properties to true, causing the check box and its label to be rendered.

These tags use component bindings rather than value bindings because the managed bean must dynamically set the values of the components' rendered properties.

If the tags were to use value bindings instead of component bindings, the managed bean would not have direct access to the components and would therefore require additional code to access the components from the FacesContext instance to change the components' rendered properties.

Writing Properties Bound to Component Instances explains how to write the bean properties bound to the example components.

Binding Converters, Listeners, and Validators to Managed Bean Properties

As described in Adding Components to a Page Using HTML Tag Library Tags, a page author can bind converter, listener, and validator implementations to managed bean properties using the binding attributes of the tags that are used to register the implementations on components.

This technique has similar advantages to binding component instances to managed bean properties, as described in Binding Component Values and Instances to Managed Bean Properties. In particular, binding a converter, listener, or validator implementation to a managed bean property yields the following benefits.

  • The managed bean can instantiate the implementation instead of allowing the page author to do so.

  • The managed bean can programmatically modify the attributes of the implementation. In the case of a custom implementation, the only other way to modify the attributes outside of the implementation class would be to create a custom tag for it and require the page author to set the attribute values from the page.

Whether you are binding a converter, listener, or validator to a managed bean property, the process is the same for any of the implementations.

  • Nest the converter, listener, or validator tag within an appropriate component tag.

  • Make sure that the managed bean has a property that accepts and returns the converter, listener, or validator implementation class that you want to bind to the property.

  • Reference the managed bean property using a value expression from the binding attribute of the converter, listener, or validator tag.

For example, say that you want to bind the standard DateTime converter to a managed bean property because you want to set the formatting pattern of the user’s input in the managed bean rather than on the Facelets page. First, the page registers the converter onto the component by nesting the f:convertDateTime tag within the component tag. Then, the page references the property with the binding attribute of the f:convertDateTime tag:

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

The convertDate property would look something like this:

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

See Writing Properties Bound to Converters, Listeners, or Validators for more information on writing managed bean properties for converter, listener, and validator implementations.