What is JavaServer Faces?  
JavaServer Faces (JSF) is an application framework for  creating Web-based user interfaces. If you are familiar with Struts (a popular  open source JSP-based Web application framework) and Swing (the standard Java  user interface framework for desktop applications), think of JavaServer Faces as  a combination of those two frameworks. Like Struts, JSF provides Web application  lifecycle management through a controller servlet; and like Swing, JSF provides  a rich component model complete with event handling and component rendering.  
In a nutshell, JSF eases Web-based application development because it:
- Lets you create user interfaces from a set of standard, reusable server-side components
- Provides a set of JSP tags to access those components
- Transparently saves state information and repopulates forms when they redisplay
- Provides a framework for implementing custom components
- Encapsulates event handling and component rendering so you can use standard JSF components or custom components to support markup languages other than HTML
- Lets tool vendors develop IDEs for a standard Web application framework
Besides being a conceptual combination of Struts and Swing, JSF is a direct competitor to Microsoft's WebForms. The frameworks are very similar, both in concept and implementation. And because JSF represents a standard for Java-based Web application frameworks, tool vendors can concentrate on developing IDEs for JSF instead of developing an IDE for one of approximately 35 existing Java-based Web application frameworks, including Struts.
Note: Struts developers needn't worry; although JSF and Struts have much in common, JSF will not make Struts obsolete. See Resources for a discussion of an integration strategy for Struts and JavaServer Faces.
Currently, JSF is an early access (EA) release, and, as a result, is somewhat immature. The specification leaves some functionality unspecified, and the specification and reference implementation are currently out of sync, with the former specifying new syntaxes and functionality not yet implemented in the latter. On the other hand, JSF is mature enough for you to write code against—although much of that code is guaranteed to be obsolete (see the disclaimer below)—and the reference implementation is fairly complete and relatively bug-free. You can download the JSF specification, the reference implementation, two sample applications, and a JSF tutorial from Resources.
The two articles in this series provide a code-intensive introduction to JavaServer Faces. In this article, I begin with a short discussion of the JSF lifecycle and then dive into some example code that illustrates implementation of Web-based user interfaces with JSF and how you can take advantage of built-in validation. In Part 2, I will explain more advanced JSF concepts such as: implementing custom validation; using model objects; internationalization; creating custom components; and finally, delegating event handling and rendering so you can use components to generate markup languages other than HTML.
Disclaimer: The code discussed in this article was written against the EA2 JSF reference implementation. As mentioned above, the specification and reference implementation are in a state of flux, and therefore, the code in this article is guaranteed to be obsolete in the near future; however, the code works as advertised with the EA2 reference implementation and was tested with both Tomcat 4.0.6 (the latest production release of Tomcat) and Resin 2.1.6. Furthermore, you can read the JSF specification until the cows come home, but to really grasp the concepts, you must ruminate over some code.
The JavaServer Faces lifecycle 
JSF  handles HTTP requests with seven distinct phases, as shown in Figure 1. The  normal flow of control is shown with solid lines, whereas dashed lines show  alternate flows depending on whether a component requests a page redisplay or  validation or conversion errors occur.  
| 
 
 
 Figure 1. The JavaServer Faces lifecycle. Click on thumbnail to view full-size image. | 
The Reconstitute Request Tree phase creates a component tree for the requested page. If that page previously displayed and JSF saved the page's state information, the state information is added to the request. This means that JSF automatically retains form information when a form redisplays; for example, when a user does not correctly fill out a form. This handy feature is a fundamental capability provided by Microsoft's WebForms, but is otherwise absent from J2EE.
During the Apply Request Values phase, the JSF implementation  iterates over the components in the component tree and calls each component's  decode() method. That method extracts information from the request  and stores it in the component. Optionally, components may delegate decoding to  a renderer.  
In addition to decoding request information during the Apply Request Values phase, components or their renderers may create request events. Typically, request events signal a visual change for one or more components; for example, clicking on a graphic in a tree control may expand a branch of the tree. Alternatively, an event in one component may update the visual representation of another component; for example, clicking on a leaf node in a tree may cause an associated list to change its contents and redisplay. In either situation, a request event is generated and added to the JSF context.
Request events, which are generated during the Apply Request Values phase,  are handled during the Handle Request Events phase. During the Handle  Request Events phase, the JSF implementation calls the  processEvents() method for each component that has one or more  request events. Components may handle request events themselves, or they may  choose to delegate event handling to an event handler. The  processEvents() method is a boolean() method. If that  method returns false, lifecycle processing advances to the  Process Validations phase; otherwise, lifecycle processing advances  directly to the Render Response phase.  
During the Reconstitute Request Tree phase, the JSF implementation may  register one or more validators for any of the components in the component tree.  In the Process Validations phase, the JSF implementation invokes the  validate() method for each validator. Validators perform  correctness checks and return a boolean value from their  validate() method; if that method returns true, the  JSF lifecycle proceeds normally; otherwise, the JSF implementation invokes the  Render Response phase directly.  
Each JSF user interface component can be associated with a field in a Java  object (known as a model object). During the Update Model phase,  component values are copied to the component's model object. A component's  updateModel() method carries out that data transfer. Conversion  errors can occur during this phase because request parameters are strings, but  model values can represent any type of Java object. If a conversion error  occurs, the JSF implementation invokes the Render Response phase directly.  
In a JSF application, if you submit a form or click on a link (both of which must be represented by JSF components), the JSF implementation creates a form event or a command event, respectively. Those events are handled in the Invoke Application phase by an application-specific handler. Typically, those handlers specify a URL, and the JSF implementation forwards the request to that URL. Currently, application-specific handlers handle form and command events in a single method, typically with a switch statement. The JSF expert group is aware of this approach's ugliness, and therefore, it's almost certain to change in the JavaServer Faces 1.0 release.
Finally, the Render Response phase creates a response component tree and forwards the response. When a user submits a form, clicks on a link, or otherwise generates a request, the cycle starts anew.
Now that we have a general overview of JavaServer Faces and a rudimentary understanding of the JSF lifecycle, let's take a look at some code.
A simple JavaServer Faces example  
Figures 2a, 2b, and 2c show a very simple JSF application.  The application's opening page contains a link that starts the application. That  link points to a JSP page that displays a simple form. Because this simple  application does not perform validation, you can click the Log In button without  filling in the name and password fields, which will transport you to another JSP  page that welcomes you to JavaServer Faces.  
| 
 
 
 Figure 2a. A simple JavaServer Faces application. Click on thumbnail to view full-size image. | 
| 
 
 
 Figure 2b. JavaServer Faces login screen. Click on thumbnail to view full-size image. | 
| 
 
 
 Figure 2c. Welcome to JavaServer Faces. Click on thumbnail to view full-size image. | 
First, let's explore the logistics of implementing this simple application,  and JavaServer Faces applications in general. JSF requires the following jar  files in the WEB-INF/lib directory:  
- WEB-INF/lib/commons-beanutils.jar
- WEB-INF/lib/commons-collections.jar
- WEB-INF/lib/commons-digester.jar
- WEB-INF/lib/commons-logging-api.jar
- WEB-INF/lib/jsf-api.jar
- WEB-INF/lib/jsf-ri.jar
- WEB-INF/lib/jstl.jar
- WEB-INF/lib/standard.jar
The jar files listed above are all you need for JSF applications. Even though, as we will see shortly, JSF applications typically use JSP tags implemented by the JSF implementation, there are no separate tag library descriptor (TLD) files because that information is contained in the jar files.
Here's a listing of the other files that comprise the application shown in Figure 2:
- WEB-INF/web.xml
- WEB-INF/classes/com/sabreware/listeners/SimpleContextListener.java
- WEB-INF/classes/com/sabreware/appHandlers/SimpleApplicationHandler.java
- /index.html
- /index.jsp
Example 1 lists the deployment descriptor (WEB-INF/web.xml).  
Example 1. WEB-INF/web.xml 
 
   
   
     
         com.sabreware.listeners.SimpleServletContextListener
     
   
  
   
     
     
     
   
  
   
     
     
   
   
     
   
The deployment descriptor listed above declares four things:
- A servlet context listener
- A controller servlet
- A mapping for the controller servlet
- A welcome file
The deployment descriptor listed in Example 1 associates the JSF controller  servlet with the URL /faces/*, which causes the servlet container  to map all URLs that start with /faces to the JSF controller  servlet. JSF uses the controller servlet to control the JSF lifecycle.  
The servlet context listener is listed in Example 2.
Example 2.  WEB-INF/com/sabreware/listeners/SimpleServletContextListener 
 
package com.sabreware.listeners;
import javax.servlet.*;
import  javax.faces.*;
import javax.faces.lifecycle.*;
import  com.sabreware.appHandlers.SimpleApplicationHandler;
public class  SimpleServletContextListener implements ServletContextListener {
  public  void contextInitialized(ServletContextEvent e) {
     LifecycleFactory  factory =  (LifecycleFactory)
       FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY);
     Lifecycle  lifecycle =  factory.getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE);
     lifecycle.setApplicationHandler(new  SimpleApplicationHandler());
  }
  public void  contextDestroyed(ServletContextEvent e) {
     // Nothing to do here
   }
}
Servlet containers create servlet context listeners at application startup  and invoke the listener's contextInitialized() method. When the  application shuts down, the servlet container invokes the listener's  contextDestroyed() method. The servlet context listener listed  above creates an application handler and associates it with the JSF lifecycle.  Application handlers handle application events and specify a URL that the JSF  implementation subsequently forwards to, as illustrated by the application  handler created in Example 2 and listed in Example 3.  
Example 3. WEB-INF/com/sabreware/appHandlers/SimpleApplicationHandler  
 
package com.sabreware.appHandlers;
import  javax.faces.FactoryFinder;
import javax.faces.context.FacesContext;
import  javax.faces.event.FacesEvent;
import  javax.faces.lifecycle.ApplicationHandler;
import  javax.faces.tree.TreeFactory;
public class SimpleApplicationHandler  implements ApplicationHandler {
  public boolean processEvent(FacesContext  context, FacesEvent facesEvent) {
     TreeFactory treeFactory =  (TreeFactory)FactoryFinder.
       getFactory(FactoryFinder.TREE_FACTORY);
     context.setResponseTree(
       treeFactory.getTree(context.getServletContext(),
                           "/welcome.jsp"));
     return  true;
  }
}
In the JSF lifecycle's Render Response phase, the JSF implementation forwards  to a URL. That URL represents a component tree, which is known as the response  tree. The application handler listed above sets the response tree to  /welcome.jsp, and the JSF implementation subsequently forwards to  that URL after the application handler is invoked (in the Invoke  Application phase of the JSF lifecycle). Our simple example only generates  one application event—a form event generated when the Log In button is  activated. JSF applications can generate two types of application events: form  events and command events. Command events are generated when you click on a  link.  
The servlet context listener listed in Example 2 and the application handler listed in Example 3 work hand in hand. The context listener creates the application handler, and the application handler specifies a URL that the JSF implementation forwards to when the login form is submitted.
The welcome file, /index.html, is listed in Example 4.  
Example 4. /index.html 
 
 
Welcome to a simple JavaServer Faces Application
     faces/index.jsp'>Click here to start the  application
 
The welcome file listed above creates a link to the first JSP page displayed by the application. All JSF applications must route the first JSP page displayed by the application through the JSF controller servlet. You can either provide an HTML page that contains a link to that JSP page, as illustrated in this example, or you can rely on the user to type the correct URL to start the application. Unfortunately, neither solution is very appealing; hopefully, JSF 1.0 will provide a mechanism to obviate this requirement.
The application's initial JSP page is listed in Example 5.
Example 5. /index.jsp 
 
 
<%@ taglib uri="http://java.sun.com/j2ee/html_basic/" prefix="faces" %>
Please enter your name and password
<faces:usefaces>
<faces:form id="simpleForm" formName="simpleForm">
| Name: | |
| Password: | 
        
     </faces:usefaces>
  
The preceding JSP page is where most of the action takes place in our simple  application. JSF provides JSP tags for all of the standard components that JSF  supports. Those tags must be contained within the body of a  
When the form in the preceding JSP page is submitted, the JSF lifecycle  begins, and the application handler subsequently invokes. That handler specifies  /welcome.jsp as the response tree, and the JSF implementation  subsequently forwards to that JSP page, which is listed in Example 6.  
Example 6. /welcome.jsp 
 
 
Welcome to JavaServer Faces!
Now that we've seen how a simple JSF application works, let's extend that application by adding field validation.
Validation with JavaServer Faces  
JavaServer Faces provides built-in validation and a  framework for creating custom validators. Built-in validation is discussed  below; custom validation will be discussed in Part 2.  
Built-in validation 
JavaServer Faces provides the  following built-in validators:  
- DoubleRangeValidator
- LengthValidator
- LongRangeValidator
- RequiredValidator
- StringRangeValidator
The preceding list of validators represent class names. Those classes reside  in the javax.faces.validator package. The  DoubleRangeValidator and LongRangeValidator validate  that a request parameter (which is always a string) can convert to either a  double or long, respectively, and that those values  fall within a specified range. The LengthValidator checks the  string length of a request parameter against minimum or maximum values. The  RequiredValidator requires a non-null value for a  given field. The StringRangeValidator converts a string into either  a long or a double and checks that value against  specified minimum or maximum values.  
Figure 3 shows the length validator in action.
| 
 
 
 Figure 3. JavaServer Faces built-in validation. Click on thumbnail to view full-size image. | 
Figure 3 shows an error message generated by a length validator. The minimum length was specified as three, but the value entered in the corresponding field was only two characters, so a validation error and corresponding error message were generated when the field's corresponding form was submitted.
Example 7 lists the JSP page shown in Figure 3.
Example 7. Use the LengthValidator 
 
 
<%@ taglib uri='http://java.sun.com/j2ee/html_basic/' prefix='faces' %>
Please enter your name and password
| Name: | <faces:validator className='javax.faces.validator.LengthValidator'/> <faces:attributename= 'javax.faces.validator.LengthValidator.MINIMUM' value='3'/> | <faces:validation_message componentId='name'/> | 
| Password: |  | 
         
    
  
As evidenced by the preceding JSP page, JSF validators are easy to use:  simply add a 
 ecall 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.
ecall 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. | 
- JSF components
- Implement custom validators
- Model objects at work
- Internationalize Web apps with JSF
- Implement custom components
Read the whole series, "A First Look at JavaServer Faces:"
- Part 1: Learn how to implement Web-based user interfaces with JSF
- Part 2: Explore JavaServer Faces components
<>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. | 
- 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 perform three fundamental tasks:
- Render the component, typically by generating markup
- Handle the component's events
- Validate the component's values
<>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  
 
   
The code fragment attaches a validator to a text field; you just specify the  validator's class name with the 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 
 
   
<%@ taglib uri='http://java.sun.com/j2ee/html_basic/' prefix='faces' %>
Please enter your name and password
| Name: | value='3'/> |  | 
| Password: |  | 
          
Like the code fragment shown at the beginning of this section, the preceding  JSP page uses 
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.
The User class is demonstrated in Listing 4.  
Listing 4. The model object: com/sabreware/beans/User  
 
The preceding JSP page accesses the name property of the  user object created by the JSP page listed in Listing  3. That property is accessed with the JSP Standard Tag Library (JSTL). Note  the exact same syntax is used in Listing 3 to specify user properties with the  modelReference attribute.  
<>Internationalize Web apps with JSF  
By itself, JavaServer Faces does not provide much support  for internationalization; instead, JSF relies on JSTL for internationalization  and localization support.  
First, we store all localized text in a properties file:
Listing 6. /WEB-INF/classes/messages_en.properties  
 
login.window-title=Internationalization Javaserver  Faces
login.name=Name
login.password=Password
login.submit=Log  In
errors.bad-username=Bad username
errors.bad-username-details=The  username {0} is invalid
Then we rewrite Listing 3's JSP page to localize all text displayed to the user. Listing 7 shows that rewritten JSP page.
Listing 7. /index.jsp 
 
   
<%@ taglib uri='http://java.sun.com/jstl/fmt' prefix='fmt' %>
<%@ taglib uri='http://java.sun.com/j2ee/html_basic/' prefix='faces' %>
Please enter your name and password
| <fmt:message key='login.name'/> |  | |
| <fmt:message key='login.password'/> | 
          
The preceding JSP page uses two tags from the JSTL formatting library to set  a locale and resource bundle base name (
The validator listed below localizes error messages, also using JSTL.
Listing 8.  /WEB-INF/classes/com/sabreware/validators/NameValidator.java 
 
package com.sabreware.validators;
import java.util.*;
import  java.text.MessageFormat;
import javax.servlet.ServletContext;
import  javax.servlet.jsp.jstl.core.Config;
import  javax.servlet.jsp.jstl.fmt.LocalizationContext;
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))  {
         ServletContext app =  context.getServletContext();
         LocalizationContext lc =  (LocalizationContext)
                         Config.get(app,  Config.FMT_LOCALIZATION_CONTEXT);
         if(lc == null)  {
            context.addMessage(component,
               new  MessageImpl(Message.SEVERITY_ERROR, "bad  username",
                           "The username " + name + " is  invalid"));
         }
         else  {
            ResourceBundle rb =  lc.getResourceBundle();
            Object[] args = new Object[] { new  String(name) };
            String cs =  rb.getString("errors.bad-username-details");
            String s =  MessageFormat.format(cs,  args);
            context.addMessage(component,
               new  MessageImpl(Message.SEVERITY_ERROR,
                            rb.getString("errors.bad-username"), s));
          }
      }
   }
}
JSTL maintains a localization context that contains a resource  bundle and the locale used to locate that resource bundle. That localization  context is available through the JSTL Config class. The preceding  JSP page uses the context's resource bundle to localize messages.  
Before we move on to JSF custom components, you should note that  internationalization support for JSF is not complete. In Listing 7, the text  displayed by the Submit button is not localized because the JSF  
Note: Detailed coverage of JSTL internationalization is beyond the scope of this article. For more information about JSTL, see Resources.
<>Implement custom  components 
Besides providing a standard set of components, a  render kit that renders those components in HTML, and a set of corresponding JSP  tags, JavaServer Faces also lets you create custom components. The application  below contains two instances of a JSF custom component. That custom component  keeps track of two images. When you click on the component, it toggles the image  it currently displays.  
| 
 
 
 Figure 5a. Two custom components display their primary images. Click on thumbnail to view full-size image. | 
| 
 
 
 Figure 5b. Application changes after user clicks on left-hand component. Click on thumbnail to view full-size image. | 
| 
 
 
 Figure 5c. Application changes again after user clicks on right-hand component. Click on thumbnail to view full-size image. | 
Listing 9 lists the JSP page shown in Figure 5a.
Listing 10. /WEB-INF/tlds/example.tld 
 
Listing 11. /WEB-INF/classes/com/sabreware/tags/ToggleGraphicTag.java  
 
The custom component is demonstrated in Listing 12.
<>Listing 12.  /WEB-INF/classes/com/sabreware/components/UIToggleGraphic.java  
 
package com.sabreware.components;
import  javax.servlet.http.HttpServletRequest;
import  javax.faces.component.UIGraphic;
import  javax.faces.context.FacesContext;
import  javax.faces.context.ResponseWriter;
import  javax.faces.event.CommandEvent;
import  javax.faces.event.FacesEvent;
public class UIToggleGraphic  extends UIGraphic {
   // This component supports one  command: click.
   private static String clickCommand =  "click";
   // This method indicates whether this component renders  itself
   // or delegates rendering to a renderer.
   public boolean  getRendersSelf() {
      return true;
   }
   //  This method, which is called during the Render Response phase,
   //  generates the markup that represents the component.
   public void  encodeEnd(FacesContext context) throws java.io.IOException  {
      ResponseWriter writer =  context.getResponseWriter();
      HttpServletRequest request =  (HttpServletRequest)
                              context.getServletRequest();
      // Represent this component as an HTML  anchor element with an
      // image. When the image is clicked, the current  page is reloaded
      // with a request parameter named component whose  value is this
      // component's ID.
      writer.write("component=" + getComponentId() +  "'>");
      writer.write("");
      writer.write("");
   }
   // This  method, which is called during the Apply Request Values phase,
   // decodes  request parameters.
   public void decode(FacesContext  context) throws java.io.IOException {
      HttpServletRequest request =  (HttpServletRequest)
                              context.getServletRequest();
      // If there's a request parameter named  component whose value
      // matches this component's  ID...
      if(getComponentId().equals(request.getParameter("component")))  {
         // ...enqueue a command event on the FacesContext event  queue
         // for this component. The processEvent method, listed  below,
         // processes that event.
          context.addRequestEvent(this, new CommandEvent(this,  clickCommand));
      }
   }
   // This method,  which is called during the Handle Request Events phase,
   // processes  command events that were added to the FacesContext by
   // the decode  method.
   public boolean processEvent(FacesContext context,  FacesEvent event) {
      if(event instanceof CommandEvent) {
          CommandEvent cmdEvent = (CommandEvent)event;
         // If the event's  command name equals "click"...
          if(clickCommand.equals(cmdEvent.getCommandName()))  {
            // ...toggle the component's  image.
            toggleImage();
         }
          return false; // Go directly to render phase.
      }
      return true;  // Process request normally.
   }
   // This method returns a string  representing the component's type.
   public String  getComponentType() {
      return  "com.sabreware.components.UIToggleGraphic";
   }
   // This private  method toggles the component's image.
   private void toggleImage()  {
      String     imageOne =  (String)getAttribute("imageOne");
      String     imageTwo =  (String)getAttribute("imageTwo");
      String currentImage =  (String)getAttribute("image");
      if(imageTwo.equals(currentImage)) 
         setAttribute("image", imageOne);
      else                 
         setAttribute("image", imageTwo);
      // The setURL()  method is defined in the superclass  (UIGraphic).
      setURL((String)getAttribute("image"));
    }
}
In Listing 13, I've rewritten the component in Listing 12.
Listing 13.  /WEB-INF/classes/com/sabreware/
components/UIToggleGraphic.java  
 
The component's rendering code is encapsulated in a renderer, as shown in Listing 14.
Listing 14.  /WEB-INF/classes/com/sabreware/renderers/ToggleGraphicHTMLRenderer.java  
 
package com.sabreware.renderers;
import java.util.Iterator;
import  java.util.Vector;
import javax.servlet.http.HttpServletRequest;
import  javax.faces.component.AttributeDescriptor;
import  javax.faces.component.UIComponent;
import  javax.faces.component.UIGraphic;
import  javax.faces.context.FacesContext;
import  javax.faces.context.ResponseWriter;
import  javax.faces.event.CommandEvent;
import  javax.faces.event.FacesEvent;
import javax.faces.render.Renderer;
import  com.sabreware.components.UIToggleGraphic;
public class  ToggleGraphicHTMLRenderer extends Renderer {
   // This  vector's iterator is returned from the getAttributeNames methods.
   private  Vector emptyVector = new Vector();
   // This method, which is called  during the Apply Request Values phase,
   // decodes request  parameters.
   public void decode(FacesContext context,  UIComponent component)
                                       throws  java.io.IOException {
      HttpServletRequest request =  (HttpServletRequest)
                              context.getServletRequest();
      // If there's a request parameter named  component whose value
      // matches this component's  ID...
      if(component.getComponentId().equals(
                              request.getParameter("component")))  {
         // ...enqueue a command event on the FacesContext event  queue
         // for this component. The processEvent method, listed  below,
         // processes that event.
          context.addRequestEvent(component,
            new  CommandEvent(component,  ((UIToggleGraphic)component).
                                          getClickCommandName()));
      }
    }
   // This method, which is called during the Render Response  phase,
   // generates the markup that represents the component.
   public  void encodeEnd(FacesContext context, UIComponent component) 
                                       throws java.io.IOException  {
      UIToggleGraphic toggleGraphic =  (UIToggleGraphic)component;
      ResponseWriter writer =  context.getResponseWriter();
      HttpServletRequest request =  (HttpServletRequest)
                              context.getServletRequest();
      // Represent this component as an HTML  anchor element with an
      // image. When the image is clicked, the current  page is reloaded
      // with a request parameter named component whose  value is this
      // component's ID.
      writer.write("\component=" +  component.getComponentId() +  "'>");
      writer.write("\");
      writer.write("\");
   }
   public void  encodeBegin(FacesContext context, UIComponent component) 
                                       throws java.io.IOException {
    }
   public void encodeChildren(FacesContext context, UIComponent component) 
                                       throws java.io.IOException {
    }
   public AttributeDescriptor getAttributeDescriptor(String  componentType,
                                    String name)  {
      return null;
   }
   public AttributeDescriptor  getAttributeDescriptor(UIComponent  component,
                                    String name) {
      return  null;
   }
   public Iterator getAttributeNames(String componentType)  {
      return emptyVector.iterator();
   }
   public Iterator  getAttributeNames(UIComponent component) {
      return  emptyVector.iterator();
   }
   public boolean  supportsComponentType(String componentType) {
      return  "com.sabreware.components.UIToggleGraphic".equals(componentType);
   }
    public boolean supportsComponentType(UIComponent component) {
      return  supportsComponentType(component.getComponentType());
   }
}
Like Listing  12's original component, the preceding renderer implements the  decode() and encodeEnd() methods. The latter generates  HTML markup for the component. The last eight methods currently must be  implemented by all renderers, whether they are relevant or not, because those  methods are defined by the Renderer interface, which all renderers  must implement. Hopefully, JSF 1.0 will provide an abstract class that  implements the Renderer interface and provides sane defaults for  those methods.  
Listing 15 lists the component's event handler.
Listing 15.  /WEB-INF/classes/com/sabreware/eventHandlers/ToggleGraphicEventHandler.java  
 
package com.sabreware.eventHandlers;
import  javax.faces.event.RequestEventHandler;
import  javax.faces.component.UIComponent;
import  javax.faces.context.FacesContext;
import  javax.faces.event.CommandEvent;
import  javax.faces.event.FacesEvent;
import  com.sabreware.components.UIToggleGraphic;
public class  ToggleGraphicEventHandler extends RequestEventHandler {
    public boolean processEvent(FacesContext context, UIComponent  component,
                                           FacesEvent event)  {
      if(event instanceof CommandEvent) {
          CommandEvent cmdEvent = (CommandEvent)event;
         UIToggleGraphic  toggleGraphic = (UIToggleGraphic)component;
         // If the event's  command name equals "click"...
          if(toggleGraphic.getClickCommandName().
                      equals(cmdEvent.getCommandName()))  {
            // ...toggle the component's  image
            toggleGraphic.toggleImage();
            return  false; // go directly to render phase
         }
      }
      return  true; // process request normally
   }
}
Like the component in Listing  12, the preceding event handler implements the processEvent()  method to handle the component's request events. If that method returns  true, the JSF lifecycle proceeds normally; if not, the JSF  lifecycle proceeds directly to the Render Response phase.  
A facelift for Web apps 
JavaServer  Faces is a Web application framework that defines a request lifecycle and a rich  component hierarchy. The request lifecycle relieves developers from writing that  code in their own Web applications, which makes those applications much easier  to implement. The component hierarchy lets developers implement custom  components that render different markup types. Additionally, developers can  implement renderers for the JSF built-in components, so those components can  also generate different markup types. JavaServer Faces is expected to have a  major impact on the development of Java-based Web applications.
 
 
   
   
   
   
   
   
   
   
   
   
   
  
0 comments:
Post a Comment