A first look at JavaServer Faces, Part 2

Explore JavaServer Faces components

Recall from Part 1 that JavaServer Faces (JSF) is conceptually a mixture of Struts (Apache's popular open source JSP framework) and Swing (Java's standard desktop application framework). Like Struts, JSF provides a well-defined lifecycle, shown in Figure 1, for processing requests.

Figure 1. The JavaServer Faces lifecycle. Click on thumbnail to view full-size image.

In Part 1, the JSF lifecycle was discussed in detail and therefore is not discussed here; however, this article refers to the JSF lifecycle, and Figure 1 is repeated for convenience.

Like Swing, JSF specifies a rich component hierarchy for implementing server-side user interface (UI) components. That hierarchy lets developers implement components from scratch with various renderers that can render different markup languages. The JSF component hierarchy and related objects such as validators, event handlers, and model objects are the focus of this article's following sections:

Read the whole series, "A First Look at JavaServer Faces:"

JSF components

JSF's component hierarchy separates JSF from other Web application frameworks like Struts. For example, both Struts and JSF have a set of custom JavaServer Pages (JSP) tags that represent HTML components, such as text fields and option lists, but the Struts tags generate HTML directly, whereas the JSF tags create server-side components that generate HTML. At first glance, the JSF approach might not have an obvious advantage until you realize that JSF components can be fitted with renderers that generate markup languages other than HTML. So, you can implement a renderer for your markup language of choice and associate that renderer with an existing JSF component. Also, the rich JSF component hierarchy makes it relatively easy to create custom components like a tree viewer or a query builder.

Figure 2 shows the class diagram for JSF components.

Figure 2. JSF UI components class diagram. Click on thumbnail to view full-size image.

JSF components implement the javax.faces.component.UIComponent interface, which specifies a whopping 46 methods that define the essence of a JSF component; Figure 2 lists only a handful of those methods. Luckily, your components can extend the abstract class javax.faces.component.UIComponentBase, which implements sane defaults for all UIComponent methods except for getComponentType(). That means you can implement a custom component simply by extending UIComponentBase and implementing getComponentType().

Each JSF component has:

  • A list of child components
  • A hashmap of attributes
  • One or more validators
  • One or more event handlers
  • An identifier for an optional renderer

All JSF components are potential containers, by virtue of a child components list maintained by every JSF component. That lets you nest components—even if they contain other components—inside other components. This crucial ability, which is the impetus behind the Composite design pattern, is routinely implemented by object-oriented graphical user interfaces (GUIs), such as Swing or VisualWorks Smalltalk.

JSF components also maintain a list of attributes. Those attributes store component-specific information. For example, you may want to store the URL associated with an image used by a component; so you could store that URL—or the image itself—in the component's list of attributes. Component attributes are stored by name in a hash map.

All JSF components perform three fundamental tasks:

  • Render the component, typically by generating markup
  • Handle the component's events
  • Validate the component's values

JSF components can render themselves or delegate rendering to a renderer. The boolean UIComponent.rendersSelf() method tells the JSF implementation whether or not a component renders itself; if not, the JSF implementation obtains a reference to the component's renderer with the UIComponent.getRendererType() method's help and then calls on the renderer to produce markup for the component.

JSF component event handling can also be managed directly by a component, or components can delegate to an event handler. One or more event handlers can be registered for a component, typically by a component's renderer or the component itself, during the JSF lifecycle's Apply Request Values phase.

Finally, JSF components can have one or more validators that validate input. Those validators, which are usually created by the JSF implementation, are stored by components in an array list.

Now that we have a basic understanding of JSF components, let's look at implementing a custom validator and associating it with a component.

Implement custom validators

In Part 1, we discussed using built-in validators to validate input for JSF components. If you use JSP for your Web application's views—which is typically the case—you can specify validators for JSF components with a <faces:validator> tag, like this:

<faces:textentry_input id='name'> 
   <faces:validator className='javax.faces.validator.RequiredValidator'/>
</faces:textentry_input>

The code fragment attaches a validator to a text field; you just specify the validator's class name with the <faces:validate> tag's className attribute. The validator specified above is a JSF built-in validator that checks to make sure a component's value is not null. As discussed in Part 1, JSF provides a handful of built-in validators, but you can also implement your own validators and associate them with a JSF component. For example, the application shown in Figure 3 uses a custom validator to validate a username.

Figure 3. Use a custom validator. Click on thumbnail to view full-size image.

The JSP page displayed in Figure 3 is listed in Listing 1.

Listing 1

<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.0 Transitional//EN'>
<html>
   <head>
      <title>A Simple JavaServer Faces Application</title>
   </head>
   <body>
      <%@ taglib uri='http://java.sun.com/j2ee/html_basic/' prefix='faces' %>
      <font size='4'>Please enter your name and password</font>
      <faces:usefaces>
         <faces:form id='simpleForm' formName='simpleForm'>
            <table>
               <tr>
                  <td>Name:</td>
                  <td>
                     <faces:textentry_input id='name'> 
                           <faces:validator 
                                  className='com.sabreware.validators.NameValidator'/>
                           <faces:validator 
                                  className='javax.faces.validator.LengthValidator'/>
                           <faces:attribute 
                                       name='javax.faces.validator.LengthValidator.MINIMUM'
                                  value='3'/>
                       </faces:textentry_input>
                  </td>
                  <td>
                     <faces:validation_message componentId='name'/>
                  </td>
               </tr>
               <tr>
                  <td>Password:</td>
                  <td>
                     <faces:textentry_secret id='password'/> 
                  </td>
               </tr>
            </table>
            <p><faces:command_button id='submit' commandName='Log In'/>
         </faces:form>
      </faces:usefaces>
   </body>
</html>

Like the code fragment shown at the beginning of this section, the preceding JSP page uses <faces:validator> tags to attach validators to a JSF component. In this case, the validators are a custom validator that authenticates a username and a JSF built-in validator that checks to make sure the username is at least three characters long.

The custom validator used in Listing 1 is listed in Listing 2.

Listing 2. WEB-INF/classes/com/sabreware/validators/NameValidator.java

package com.sabreware.validators;
import java.util.Iterator;
import javax.faces.component.UIComponent;
import javax.faces.component.AttributeDescriptor;
import javax.faces.context.FacesContext;
import javax.faces.context.Message;
import javax.faces.context.MessageImpl;
import javax.faces.validator.Validator;
public class NameValidator implements Validator {
   public AttributeDescriptor getAttributeDescriptor(String attributeName) {
      return null;   
   }
   public Iterator getAttributeNames() {
      return null;   
   }
   public void validate(FacesContext context, UIComponent component) {
      String name = (String)component.getValue();
      if(!"phillip".equalsIgnoreCase(name)) {
         context.addMessage(component,
            new MessageImpl(Message.SEVERITY_ERROR, "bad username",
                        "The username " + name + " is invalid"));
      }
   }
}

The preceding validator implements the javax.faces.validator.Validator interface, which defines the methods listed below:

  • void validate(FacesContext, UIComponent)
  • Iterator getAttributeNames(String)
  • AttributeDescriptor getAttributeDescriptor(String)

The validate() method performs the actual validation for a given component. The other two methods, defined by the Validator interface, are used by tools to discover attributes (and their descriptions) associated with a particular validator. In this case, we don't have any attributes for our validator, so the getAttributeDescriptor() and getAttributeNames() methods simply return null.

A validator's validate() method does nothing if the component's values are valid; if those values are invalid, the validator() method creates messages and adds them to the JSF context. All of that happens during the JSF lifecycle's Process Validations phase. If a component fails validation—meaning messages have been added to the JSF context—the JSF implementation proceeds directly to the Render Response phase; otherwise, the lifecycle proceeds to the Apply Model Values phase. (Refer to Figure 1 for more information about the JSF lifecycle phases.)

Model objects at work

In a Model-View-Controller (MVC) architecture, views, which are typically JSP pages for Java-based Web applications, display values contained in a model. JavaServer Faces makes it easy to connect UI components to fields stored in model objects. As we saw in the last section, if a request's values are all valid, the JSF lifecycle moves from the Process Validations phase to the Apply Model Values phase. During the latter phase, the JSF implementation copies component values to the model objects associated with those components. The HTML text fields shown in Figure 4a are connected to a model object. When the Log In button activates, the JSF implementation forwards control to a JSP page that uses that model object to display a personalized greeting (Figure 4b).

Figure 4a. HTML text fields connected to a model object. Click on thumbnail to view full-size image.
Figure 4b. Personalized greeting. Click on thumbnail to view full-size image.

Listing 3 shows the JSP page illustrated in Figure 4a.

SUBHEAD2: Listing 3. /index.jsp

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
   <head>
      <title>A Simple JavaServer Faces Application</title>
   </head>
   <body>
      <%@ taglib uri="http://java.sun.com/j2ee/html_basic/" prefix="faces" %>
      <font size="4">Please enter your name and password</font>
      
      <!-- We use jsp:useBean to create a model object, but
           real applications typically create model objects elsewhere:
           JSP pages are purely views of the model -->
      <jsp:useBean id='user' class='com.sabreware.beans.User'
                scope='session'/>
      <faces:usefaces>
         <faces:form id="simpleForm" formName="simpleForm">
            <table>
               <tr>
                  <td>Name:</td>
                  <td><faces:textentry_input id="name"
                                 modelReference="${user.name}"/></td>
               </tr>
               <tr>
                  <td>Password:</td>
                  <td><faces:textentry_secret id="password"
                                  modelReference="${user.password}"/></td>
              </tr>
            </table>
            <p><faces:command_button id="submit" commandName="Log In"/>
         </faces:form>
      </faces:usefaces>
   </body>
</html>

The preceding JSP page creates a session-scope variable of type com.sabreware.beans.User. JSP pages typically do not create model objects; instead, business objects, such as a servlet or a servlet filter, usually create model objects. The preceding JSP page uses the <faces:textentry_input> tags' modelReference attribute to specify a field in the user object. For example, the name field is associated with the user object's name property, and the password field is associated with the user object's password property.

The User class is demonstrated in Listing 4.

1 2 3 Page
Recommended
Join the discussion
Be the first to comment on this article. Our Commenting Policies
See more