Skip to main content

JSF for nonbelievers: JSF component development

Time-saving moves make building JSF components a snap

Rick Hightower serves as chief technology officer for ArcMind Inc. He is coauthor of the popular book Java Tools for Extreme Programming, about applying extreme programming to J2EE development, as well as co-author of Professional Struts.

Summary:  In this final installment in his four-part JSF for nonbelievers series, Rick Hightower shows you the time-saving moves that will convince you, for once and for all, that JSF component development is easier than you think.

Editor's notes: Since publication, Sun has open sourced JSF 1.2 under their CDDL license. See Resources for a link to the new project page.
For details on getting started with JSF 1.2, now integrated in JEE 5, see Richard Hightower's tutorial series.

View more content in this series

Date:  26 Jul 2005
Level:  Intermediate PDF:  A4 and Letter (442KB)Get Adobe® Reader®
Activity:  25755 views

The litmus test for a component model is: Can you buy components from third-party vendors and plug them into your application? Similar to the way you can buy visual Swing components, you can buy Java ServerFaces (JSF) components! Need a fancy calendar? You have your choice among open source implementations and commercial components. You now have the option of buying instead of building complex Web-based GUI components.

JSF has a component model similar to AWT's GUI component model. You can create reusable components with JSF. Unfortunately, there is a misconception that creating components with JSF is difficult. Don't believe the FUD from people that have never tried it! Developing JSF components is not difficult. You can save time by not repeating the same code over and over. Once you create a component, it is easy to drop that component onto any JSP, or rather into any JSF form, which is important if you are working on a 250-page site. Most of JSF's functionality comes from base classes. JSF makes creating components easy because all the heavy lifting is done by API and base classes.

Throughout this series I've tried to help you overcome the FUD that has caused many Java developers to shy away from using JSF technology. I've discussed the basic misconceptions about the technology and served as a tour guide to both its underlying framework and its most worthy development features. With all the background work out of the way, I think you're ready to take the plunge and develop your own custom JSF components. As with all things JSF, I promise it will be easier than you think and far too rewarding, in terms of time and headache saved, to ignore.

Examples in this article have been developed using JDK 1.5 and Tomcat. Click on Sample code at the top of the page to download the example source. Note that there is no build file associated with this article, unlike the previous ones; I've left it to you as an exercise. Simply set your IDE or compiler to compile the classes in /src to /webapp/WEB-INF/classes and include all of the JAR files in /webapp/WEB-INF/lib (along with servlet-api.jar and jsp-api.jar, which are included with Tomcat).

The JSF component model

The JSF component model is similar to the AWT GUI component model. It has events and properties just like the Swing component model. It also has containers that contain components, and that also are components that can be contained by other containers. In theory, the JSF component model is divorced from HTML and JSP. The standard set of components that ships with JSF has JSP bindings and generates HTML renderings.

Examples of JSF components include calendar input components and rich HTML input components. You may never have time to write such components, but what if they already existed? The component model lowers the barrier to entry to add more functionality to Web applications by making a commodity of common functionality.

Component functionality typically centers around two actions: decoding and encoding data. Decoding is the process of converting incoming request parameters to the values of the component. Encoding is converting the current values of the component into the corresponding markup, that is, HTML.

The JSF framework gives you two options for encoding and decoding data. With a direct implementation approach, the component itself implements decoding and encoding. With a delegated implementation approach, the component delegates to a renderer that does the encoding and decoding. If you choose a delegated implementation you can associate your component with different renderers that will represent it in different ways on the page; for example a multi-select list box versus a list of check boxes.

Thus, JSF components consist of two parts: the component and the renderer. The JSF Component class defines the state and behavior of a UI component; a renderer defines how the component will be read from the request and how it will be displayed -- usually though HTML.The renderer converts the values of the component to appropriate markup. Event queuing and performance validation happen inside the component.

In Figure 1 you can see where data encoding and decoding occur in the (by now, I hope, familiar!) JSF lifecycle.


Figure 1. JSF lifecycle and JSF components
JSF components and the JSF lifecycle

Tip!

In many cases, you can simplify your development process by changing the rendering while leaving the component itself unchanged. In these cases you can write a custom renderer instead of a custom component.

Further component concepts

The base class for all JSF components is UIComponent. When you develop your own components you will subclass UIComponentBase, which extends UIComponent, and provides default implementations of the all of the abstract methods in UIComponent.

Components have parents and identifiers. Each components is associated with a component type, which is used to register the component in the faces context configuration file (faces-config.xml). You can use the JSF-EL (expression language) to bind JSF components to managed bean properties. You can associate expressions to any property on a component, thus allowing the component's property value to be set with JSF-EL. When you create component properties that are bound with JSF-EL, you need to create a value binding expression. When the getter method of the bound property is called it must use the value binding to get the value unless the setter method has set the value already.

A component can be a ValueHolder or an EditableValueHolder. A ValueHolder is associated with one or more Validators and a Converter; thus, JSF UI components are associated with Validators and a Converter (see Resources for more on JSF validation and conversion.)

Components like form field components have a ValueBinding that must be bound to a JavaBean read-write property. Components can access their parent by calling the getParent method, and can access their children by calling getChildren. Components can also have facet components, which are subcomponents of the current component and can be accessed by calling getFacets, which returns a map. Facets are named subcomponents.

Many of the component concepts outlined here will be part of the hands-on examples that follow, so keep them in mind!


Hello World, JSF style!

Let's start your foray into JSF component development with a nice and easy example: I'll show you how to render the Label tag, (example: <label>Form Test</label>).

Here are the steps I'll walk you through:

  1. Extend a UIComponent
    • Create a class that extends UIComponent
    • Save the component state
    • Register the component with faces-config.xml
  2. Define the renderer or implement inline
    • Override encode
    • Override decode
    • Register the renderer with faces-config.xml
  3. Create a custom tag that subclasses UIComponentTag
    • Return the renderer type
    • Return the component type
    • Set up properties that might use JSF expressions

The Label example will demonstrates the following aspects of JSF component development:

  • Creating a component
  • Directly implementing a renderer
  • Encoding output
  • Associating a custom tag with a component

Referring back to Figure 1 you can see the two lifecycle properties that will be active in this example. They are Apply Request Values and Render Response.

In Figure 2 you can see how the Label tag (<label>Form Test</label>) would be used in a JSP.


Figure 2. Using a JSF tag in a JSP
Using tag in JSP

Step 1: Extend a UIComponent

The first step is to create a component that subclasses UIOutput, which is a subclass of UIComponent. In addition to subclassing the class, I will add a label property, which the component will display as shown in Listing 1:


Listing 1. Subclass UIComponent and add label
                
import java.io.IOException;

import javax.faces.component.UIOutput;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;

public class LabelComponent extends UIOutput{

	private String label;

	public String getLabel() {
		return label;
	}
	public void setLabel(String label) {
		this.label = label;
	}
...

The next thing to do is save the component state. JSF does the actual storage and state management, typically though a session, a hidden form field, cookies, etc. (This is usually a setting that you configure.). To save the component state override the component's saveState and restoreState methods, as shown in Listing 2:


Listing 2. Saving component state
                
    @Override
    public Object saveState(FacesContext context) {
        Object values[] = new Object[2];
        values[0] = super.saveState(context);
        values[1] = label;
        return ((Object) (values));
    }

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

You'll notice that I'm using JDK 1.5. I set up the compiler so I have to specify the override annotation so as to clarify which methods are overriding base class methods. Doing this makes it easier to identify where the hooks for JSF exist.

The final step of creating the component is to register it with the faces-config.xml, as shown below:

<faces-config>

   <component>
      <component-type>simple.Label</component-type>
      <component-class>
         arcmind.simple.LabelComponent
      </component-class>
   </component>
...

Step 2: Define the renderer

The next thing to do is define the renderer functionality inline. Later I'll show you how to create a separate renderer. For now, you start by encoding the Label component output to display the label, as shown in Listing 3:


Listing 3. Encode the component output
                
public class LabelComponent extends UIOutput{
	...
	public void encodeBegin(FacesContext context) 
					throws IOException {

		ResponseWriter writer = 
			context.getResponseWriter();

		writer.startElement("label", this);
        	            writer.write(label);
        	            writer.endElement("label");
        	            writer.flush();
	}
	...
}

Notice that the response writer (javax.faces.context.ResponseWriter) makes it easy to work with markup languages like HTML. The code in Listing 3 outputs the value of the label in the body of the <label> element.

The family property shown below is used to associate the Label component with a renderer. While you don't need this property for the Label component per se (because it doesn't have a separate renderer) you will need it later in the article, when I show you how to create a separate renderer.

public class LabelComponent extends UIOutput{
	...
	public String getFamily(){
		return "simple.Label";
	}
	...
}

A side step: Hacking the JSF-RI

If you're using the JSF reference implementation from Sun Microsystems (and not the MyFaces implementation), you'll have to add the following to your component-creation code:

public void encodeEnd(FacesContext context) 
			throws IOException {
	return;
}

public void decode(FacesContext context) {
	return;
}

Sun's JSF RI expects a renderer will send a null pointer exception if your component doesn't have a renderer. The MyFaces implementation doesn't require you to work around this requirement, but it's still a good idea to include the above methods in your code, so that your component works in both the MyFaces and JSF RI environments.

MyFaces is better!

If you're using the Sun JSF RI or some derivative thereof, do yourself a favor and switch to MyFaces. While MyFaces wasn't always the better implementation, today it is. Its error messages are better than those of the Sun JSF RI and the framework is comparatively more rigorous.

Step 3: Create a custom tag

JSF components are not inherently tied to JSP. To bridge from the JSP world to the JSF world you need a custom tag that returns the component type (which you'll then register in the faces-context file) and a renderer, as Figure 3 demonstrates.


Figure 3. Bridging JSF and JSP
Bridging JSF and JSP

Note that since you don't have a separate renderer, you can return a null value for getRendererType(). Note also that you have to have set the value of the label property from the custom tag to the component, as shown here:

[LabelTag.java]

public class LabelTag extends UIComponentTag {
…
protected void setProperties(UIComponent component) {
	/* you have to call the super class */
	super.setProperties(component);
	((LabelComponent)component).setLabel(label);
}


Remember that the Tag class sets up the binding from the JSP to the Label component, as shown in Figure 4.


Figure 4. Binding JSF and JSP
Binding JSF and JSP

Now all you have to do is create a TLD (Tag Library Descriptor) file to register your custom tag, as shown in Listing 4:


Listing 4. Registering a custom tag
                
[arcmind.tld]

<taglib>
   <tlib-version>0.03</tlib-version>
   <jsp-version>1.2</jsp-version>
   <short-name>arcmind</short-name>
   <uri>http://arcmind.com/jsf/component/tags</uri>
   <description>ArcMind tags</description>
   
   <tag>
      <name>slabel</name>
      <tag-class>arcmind.simple.LabelTag</tag-class>
      <attribute> 
         <name>label</name> 
         <description>The value of the label</description>
      </attribute> 
   </tag>
...

Once you've defined a TLD file, you can start using the tag in JSPs, as in the following example:

[test.jsp]
<%@ taglib prefix="arcmind" 
         uri="http://arcmind.com/jsf/component/tags" %>
            ...
	<arcmind:slabel label="Form Test"/>

And that's it -- not much more is needed to develop a simple JSF component. But what if you wanted to create a slightly more complex component, for a slightly more complex usage scenario? Keep reading, then.


A composite component

For the next example I'll show you how to create a component (and tag) that picks up where the last one left off. The Field component combines the work of several components into one. Composite components are a mainstay of JSF component development and will save you lots of time!

The Field component combines label, text input, and message functionality into one component. Field's text input functionality allows users to input text. Its label functionality shows up red if there is a problem (such as incorrect input) and also presents an asterisk (*) to denote required fields. Its message functionality lets it write out error messages when required.

The Field component example demonstrates the following:

  • The UIInput component
  • Working with value bindings and component attributes
  • Decoding values from request parameters
  • Working with error messages

Unlike the Label component, the Field component uses a separate renderer. If you are developing components for one HTML-based application, don't bother with separate renderers. Doing so is extra work without extra bang for your buck. If you're developing a lot of JSF components that you plan on selling to clients that want to target to more than one client, then you do want a separate renderer. In short, renderers are good for commercial-frameworks developers and not so good for application developers developing in-house Web applications.

Follow the code!

Since I've already gone over the basic steps of creating a component, defining the renderer, and creating a custom tag, this time I'll let the code speak for itself and only point out the more important details. In Listing 5 you can see how the Field tag is used in a typical application example:


Listing 5. The Field tag
                
<f:view>
  <h2>CD Form</h2>
      
  <h:form id="cdForm">
        
    <h:inputHidden id="rowIndex" value="#{CDManagerBean.rowIndex}" /> 
      
      	
        <arcmind:field id="title"
                           value="#{CDManagerBean.title}"  
                           label="Title:"
                           errorStyleClass="errorText"
                           required="true" /> <br />
		
        <arcmind:field id="artist"
                           value="#{CDManagerBean.artist}"  
                           label="Artist:"
                           errorStyleClass="errorText"
                           required="true" /> <br />
      	
        <arcmind:field id="price"
                           value="#{CDManagerBean.price}"  
                           label="CD Price:"
                           errorStyleClass="errorText"
                           required="true">
           <f:validateDoubleRange maximum="1000.0" minimum="1.0"/>
        </arcmind:field>

The above tags output the following HTML:

<label style="" class="errorText">Artist*</label>
<input type="text" id="cdForm:artist " 
       name=" cdForm:artist " />
Artist is blank, it must contain characters

And Figure 5 shows what all this might look like in your browser.


Figure 5. The Field component
The Field component

Listing 6 shows the code to create the Field component. Because this component inputs text rather than just outputting it (as Label did) you start by subclassing UIInput rather than UIOutput.


Listing 6. Field subclasses UIInput
                
package com.arcmind.jsfquickstart;

import javax.faces.component.UIInput;
import javax.faces.context.FacesContext;


/**
 * @author Richard Hightower
 *  
 */
public class FieldComponent extends UIInput {

	private String label;

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

    @Override
    public void restoreState(FacesContext context, Object state) {
        Object values[] = (Object[])state;
        super.restoreState(context, values[0]);
        label = (String)values[1];
    }
    
	public FieldComponent (){
		this.setRendererType("arcmind.Field");
	}

	/**
	 * @return Returns the label.
	 */
	public String getLabel() {
		return label;
	}

	/**
	 * @param label
	 *  The label to set.
	 */
	public void setLabel(String label) {
		this.label = label;
	}

	
	@Override
	public String getFamily() {
		return "arcmind.Field";
	}


	public boolean isError() {
		return !this.isValid();
	}

}

You'll notice the encoding method is missing from this snippet. That's because the encoding and decoding occur in a separate renderer. I'll cover this a bit later.

Value bindings and component attributes

Whereas the Label component had only one property (JSP attribute) the Field component has several, namely label, errorStyle, errorStyleClass, and value. The label and value properties are at the core of the Field component, but errorStyle and errorStyleClass are HTML specific. Because these attributes are HTML specific, you do not need to have them as properties in the Field component; instead you will pass them around as component attributes that only the renderer knows about.

As with the Label component, you will need a custom tag to bind the Field component to a JSP, as shown in Listing 7:


Listing 7. Creating a custom tag for FieldComponent
                
/*
 * Created on Jul 19, 2004
 *
 */
package com.arcmind.jsfquickstart;

import javax.faces.application.Application;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.el.ValueBinding;
import javax.faces.webapp.UIComponentTag;


/**
 * @author Richard Hightower
 *
 */
public class FieldTag extends UIComponentTag {

     private String label;
     private String errorStyleClass="";
     private String errorStyle="";
     private boolean required;
     private String value="";
     
     /**
      * @return Returns the label.
      */
     public String getLabel() {
          return label;
     }
     /**
      * @param label The label to set.
      */
     public void setLabel(String label) {
          this.label = label;
     }
     /**
      * @see javax.faces.webapp.UIComponentTag#setProperties
      * (javax.faces.component.UIComponent)
      */
     @Override
     protected void setProperties(UIComponent component) {
          /* You have to call the super class */
          super.setProperties(component);
          ((FieldComponent)component).setLabel(label);
          component.getAttributes().put("errorStyleClass",
            errorStyleClass);
          component.getAttributes().put("errorStyle",errorStyle);
          ((FieldComponent)component).setRequired(required);
     
     
         FacesContext context = FacesContext.getCurrentInstance();
         Application application = context.getApplication();
         ValueBinding binding = application.createValueBinding(value);
         component.setValueBinding("value", binding);
          
     }
     /**
      * @see javax.faces.webapp.UIComponentTag#getComponentType()
      */
     @Override
     public String getComponentType() {
          return "arcmind.Field";     
     }

     /**
      * @see javax.faces.webapp.UIComponentTag#getRendererType()
      */
     @Override
     public String getRendererType() {
          return "arcmind.Field";     
     }

     /**
      * @return Returns the errorStyleClass.
      */
     public String getErrorStyleClass() {
          return errorStyleClass;
     }
     /**
      * @param errorStyleClass The errorStyleClass to set.
      */
     public void setErrorStyleClass(String errorStyleClass) {
          this.errorStyleClass = errorStyleClass;
     }
     
     /**
      * @return Returns the errorStyle.
      */
     public String getErrorStyle() {
          return errorStyle;
     }
     /**
      * @param errorStyle The errorStyle to set.
      */
     public void setErrorStyle(String errorStyle) {
          this.errorStyle = errorStyle;
     }

     /**
      * @return Returns the required.
      */
     public boolean isRequired() {
          return required;
     }
     /**
      * @param required The required to set.
      */
     public void setRequired(boolean required) {
          this.required = required;
     }
     
     /**
      * @return Returns the value.
      */
     public String getValue() {
          return value;
     }
     /**
      * @param value The value to set.
      */
     public void setValue(String value) {
          this.value = value;
     }
}

Conceptually, you will not find much difference between the above code and the Label component. The setProperties method does differ slightly in this instance, however:

protected void setProperties(UIComponent component) {
    /* You have to call the super class */
    super.setProperties(component);
    ((FieldComponent)component).setLabel(label);
    component.getAttributes().put("errorStyleClass", 
      errorStyleClass);
    component.getAttributes().put("errorStyle",errorStyle);

    ((FieldComponent)component).setRequired(required);

Whereas the label property is passed through just as it was in the previous example, the errorStyleClass and errorStyle properties are not passed through. Instead, they are added to the attributes map of the JSF component. The Renderer class will then use the attributes map to render class and style attributes. This setup allows the HTML-specific code to be absent from your component.

The actual value binding code for this revised setProperties method is also somewhat different, as shown here.

protected void setProperties(UIComponent component) {
      ...	
	
     FacesContext context = FacesContext.getCurrentInstance();
     Application application = context.getApplication();
     ValueBinding binding = application.createValueBinding(value);
     component.setValueBinding("value", binding);

This code allows the value property of the Field component to be bound to a backing bean. For the sake of example, I've bound the CDManagerBean's title property to a Field component as follows: value="#{CDManagerBean.title}. Value bindings are created with the Application object. The Application object is the factory that creates value bindings. The component has a special method to store value bindings, namely setValueBinding; you can have more than one value binding.

The separate renderer

Last but not least is the renderer. The main thing the separate renderer has to worry about is decoding (input) and encoding (output). The Field component does a lot more encoding than decoding, so its renderer has many encoding methods but just one decoding method. In Listing 8 you can see the Field component's renderer:


Listing 8. FieldRenderer extends Renderer
                
package com.arcmind.jsfquickstart;

import java.io.IOException;
import java.util.Iterator;
import java.util.Map;

import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.component.UIInput;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;
import javax.faces.el.ValueBinding;
import javax.faces.render.Renderer;

/**
 * @author Richard Hightower
 *
 */
public class FieldRenderer extends Renderer {


  @Override 
   public Object getConvertedValue(FacesContext facesContext, UIComponent component, 
     Object submittedValue) throws ConverterException {
        

    //Try to find out by value binding
    ValueBinding valueBinding = component.getValueBinding("value");
    if (valueBinding == null) return null;

    Class valueType = valueBinding.getType(facesContext);
    if (valueType == null) return null;

    if (String.class.equals(valueType)) return submittedValue;    
    if (Object.class.equals(valueType)) return submittedValue;    

    Converter converter = ((UIInput) component).getConverter();
    converter =  facesContext.getApplication().createConverter(valueType);
    if (converter != null ) {
        return converter.getAsObject(facesContext, component, (String) submittedValue);
    }else {
        return submittedValue; 
    }
		
    }

   @Override
    public void decode(FacesContext context, UIComponent component) {
        /* Grab the request map from the external context */
       Map requestMap = context.getExternalContext().getRequestParameterMap();
        /* Get client ID, use client ID to grab value from parameters */
       String clientId = component.getClientId(context);
       String value = (String) requestMap.get(clientId);
		
        FieldComponent fieldComponent = (FieldComponent)component;
          /* Set the submitted value */
        ((UIInput)component).setSubmittedValue(value);
    }
	
   @Override
    public void encodeBegin(FacesContext context, UIComponent component)
        throws IOException {
        FieldComponent fieldComponent = (FieldComponent) component;
        ResponseWriter writer = context.getResponseWriter();
        encodeLabel(writer,fieldComponent);
        encodeInput(writer,fieldComponent);
        encodeMessage(context, writer, fieldComponent);
        writer.flush();
    }

	
	
    private void encodeMessage(FacesContext context, ResponseWriter writer, 
      FieldComponent fieldComponent) throws IOException {
        Iterator iter = context.getMessages(fieldComponent.getClientId(context));
        while (iter.hasNext()){
           FacesMessage message = (FacesMessage) iter.next();
           writer.write(message.getDetail());
        }
    }

    private void encodeLabel(ResponseWriter writer, FieldComponent 
      fieldComponent) throws IOException{
        writer.startElement("label", fieldComponent);
        if (fieldComponent.isError()) {
            String errorStyleClass = (String) fieldComponent.getAttributes().get("errorStyleClass");
            String errorStyle = (String) fieldComponent.getAttributes().get("errorStyle");

            writer.writeAttribute("style", errorStyle, "style");
            writer.writeAttribute("class", errorStyleClass, "class");
        }
        writer.write("" + fieldComponent.getLabel());
        if (fieldComponent.isRequired()) {
            writer.write("*");
        }
       writer.endElement("label");
    }
	
    private void encodeInput(ResponseWriter writer, FieldComponent 
      fieldComponent) throws IOException{
        FacesContext currentInstance = FacesContext.getCurrentInstance();
        writer.startElement("input", fieldComponent);
        writer.writeAttribute("type", "text", "type");
        writer.writeAttribute("id", fieldComponent.getClientId(currentInstance), "id");
		writer.writeAttribute("name", fieldComponent.getClientId(currentInstance), "name");
        if(fieldComponent.getValue()!=null)
            writer.writeAttribute("value", fieldComponent.getValue().toString(), "value");
        writer.endElement("input");
    }

}

Encoding and decoding

As previously mentioned, the main thing this renderer does is decode input and encode output. I'll start with the decoding because it's the easiest. The FieldRenderer's decode method is as follows:

@Override
public void decode(FacesContext context, UIComponent component) {
       /* Grab the request map from the external context */
     Map requestMap = context.getExternalContext().getRequestParameterMap();
       /* Get client ID, use client ID to grab value from parameters */
     String clientId = component.getClientId(context);
     String value = (String) requestMap.get(clientId);
		
     FieldComponent fieldComponent = (FieldComponent)component;
       /* Set the submitted value */
     ((UIInput)component).setSubmittedValue(value);
}

The Label component didn't need to decode because it was a UIOutput component. The Field component is a UIInput component, which means it accepts input so it does have to decode. A decode method could read values from session, cookies, headers, request, etc. In most cases, the decode method will just read values from request parameters like the above. The Field Renderer's decode method grabs the clientId from the component to identify the request parameter to be looked up. The clientId is calculated as the fully qualified name of the component given its container path. Thus, because the example component is in a form (a container), its clientid would be as follows: nameOfForm:nameOfComponent or in the example cdForm:artist, cdForm:price, cdForm:title. The last step of the decode method is to store the submitted value in the component (later it will be converted then validated; see Resources for more on validation and conversion).

The encoding methods offer no real surprises. They are similar to what you saw with the Label component. The first method, encodeBegin, delegates to the three helper methods encodeLabel, encodeInput, and encodeMessage, as shown here:

@Override
public void encodeBegin(FacesContext context, UIComponent component)
       throws IOException {
     FieldComponent fieldComponent = (FieldComponent) component;
     ResponseWriter writer = context.getResponseWriter();
     encodeLabel(writer,fieldComponent);
     encodeInput(writer,fieldComponent);
     encodeMessage(context, writer, fieldComponent);
     writer.flush();
}

The encodeLabel method is responsible for changing the color of the label to red (or whatever color you specify in your style sheet styles) in the case of errors and using asterisks (*) to denote required fields, as you can see below:

private void encodeLabel(ResponseWriter writer, FieldComponent fieldComponent) throws IOException{
     writer.startElement("label", fieldComponent);
     if (fieldComponent.isError()) {
          String errorStyleClass = (String) fieldComponent.getAttributes().get("errorStyleClass");
          String errorStyle = (String) fieldComponent.getAttributes().get("errorStyle");

          writer.writeAttribute("style", errorStyle, "style");
          writer.writeAttribute("class", errorStyleClass, "class");
     }
     writer.write("" + fieldComponent.getLabel());
     if (fieldComponent.isRequired()) {
          writer.write("*");
     }
     writer.endElement("label");
}

First, the encodeLabel method checks to see if there are errors, and if so it outputs the errorStyle and errorStyleClass (a better version would only output them if they were not blank -- but I'll leave that as an exercise for you!). The helper method then checks to see if the component is a required field, and if so it outputs an asterisk. The encodeMessages and encodeInput methods do what you would expect; namely output error messages and generate the HTML input text field for the Field component.

Behold, the mystery method!

You may have noticed that there is a method I haven't covered. This method is the dark horse of the methods in this class. If you read the javadocs for Renderer (an abstract class that all renderers extend), you may feel that such a method was not needed, which would be fair enough: That's what I thought at first, too. But you, like me, would be wrong!

In fact, the base class Renderer does not automatically invoke the associated converters of the subclass Renderer -- even though the javadocs for Renderer and the JSF specification suggest that it does. MyFaces and the JSF RI have classes (specific to their implementation) that perform this magic for their renderers, but the functionality is not covered in the core JSF API.

Instead, you will need to use the method getConvertedValues to look up the associated converter and invoke it. The method shown in Listing 9 grabs the right converter based on the type of the value binding:


Listing 9. The getConvertedValues method
                
@Override
 public Object getConvertedValue(FacesContext facesContext, 
   UIComponent component, Object submittedValue) throws ConverterException {
        
     //Try to find out by value binding
     ValueBinding valueBinding = component.getValueBinding("value");
     if (valueBinding == null) return null;

     Class valueType = valueBinding.getType(facesContext);
     if (valueType == null) return null;

     if (String.class.equals(valueType)) return submittedValue;    
     if (Object.class.equals(valueType)) return submittedValue;    

     Converter converter = ((UIInput) component).getConverter();
     converter =  facesContext.getApplication().createConverter(valueType);
     if (converter != null ) {
          return converter.getAsObject(facesContext, component, (String) submittedValue);
     }else {
          return submittedValue; 
     }
		
}

The code in Listing 9 adds the functionality that both the Render javadoc and the JSF specification would have you believe is automatic: It isn't. On the other hand, note that if you do not have a separate Renderer, you do not need the above (getConvertedValues) method. The UIComponentBase class (a superclass of the Field component), provides this functionality in the case of a direct renderer. Take my advice and only bother with renderers if you have to target many devices or if you're writing a commercial framework. In other cases they're just not worth the extra headache.

If you're wondering how the component is associated with the renderer, simply take a look at Figure 6.


Figure 6. Mapping a renderer to a component
Mapping a renderer to a component

The custom tag has two methods that return the component type and the renderer type. These are used to look up the correct renderer and the component that is configured in faces-config.xml. Note (although it is not pictured) that the component must return the right family type.


Conclusion

And with that you really do know the essentials of JSF component development. Of course, there are plenty of other topics to be covered in this area -- including emitting component events, internationalizing components, creating UICommand-style components, and more. See Resources for a JSF reading list!

In the writing of this article I encountered a technical snag with the Renderer, which resulted in my discovering the workaround of the getConvertedValues method. While I had encountered the problem with the Converter and worked through it before, I'd done so on a tight (production) schedule. Hacks that work for production aren't necessarily the same as the ones that get proliferated in how-to articles; so this time I had to learn to not only fix the problem but how to do it right. In the course of all that I ended up learning and experiencing how JSF component handling works at a very deep level; so sometimes the detour is worth the scenery.

I hope that in this four-part series you've learned enough about the advantages of using JSF, as well as the basics of how it works, that you'll feel comfortable going deeper into the technology on your own. And when you get lost -- because you probably sometimes will -- don't give in to FUD. Instead, just remember what I said about detours and scenery, and keep going.



Download

DescriptionNameSizeDownload method
Source codej-jsf4comps.zip4 MB HTTP

Information about download methods


Resources

About the author

Rick Hightower serves as chief technology officer for ArcMind Inc. He is coauthor of the popular book Java Tools for Extreme Programming, about applying extreme programming to J2EE development, as well as co-author of Professional Struts.

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Java technology
ArticleID=89457
ArticleTitle=JSF for nonbelievers: JSF component development
publish-date=07262005
author1-email=rhightower@arc-mind.com
author1-email-cc=jaloi@us.ibm.com

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Rate a product. Write a review.

Special offers