Download
FAQ
History
HomeHomeNext API
Search
Feedback
Divider

Delegating Rendering to a Renderer

For the purpose of illustrating delegated rendering, the image map example includes an AreaRenderer, which performs the rendering for the UIArea component.

To delegate rendering, you need to perform these tasks:

Create the Renderer Class

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

To delegate the encoding to AreaRenderer, the AreaRenderer needs to implement an encodeEnd method.

The encoding methods in a Renderer are just like those in a UIComponent class except that they accept a UIComponent argument as well as a FacesContext argument, whereas the encodeEnd method defined by UIComponentBase only takes a FacesContext. The UIComponent argument is the component that needs to be rendered. In the case of non-delegated rendering, the component is rendering itself. In the case of delegated rendering, the renderer needs to be told what component it is rendering. So you need to pass the component to the encodeEnd method of AreaRenderer:

public void encodeEnd(FacesContext context, 
  UIComponent component) { ... } 

The encodeEnd method of AreaRenderer must retrieve the shape, coordinates, and alt values stored in the ImageArea model object that is bound to the UIArea component. Suppose that the area tag currently being rendered has a valueRef attribute value of "fraA". The following line from encodeEnd gets the valueRef value of "fraA" and uses it to get the value of the attribute "fraA" from the FacesContext.

ImageArea ia = (ImageArea)
  context.getModelValue(component.getvalueRef()); 

The attribute value is the ImageArea model object instance, which contains the shape, coordinates, and alt values associated with the fraA UIArea component instance.

Simplifying the JSP Page describes how the application stores these values.

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

writer.write("<area shape=\"");
writer.write(ia.getShape());
writer.write("\"" );
writer.write(" coords=\"");
writer.write(ia.getCoords()); 

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

<d:area id="France" valueRef="fraA" 
  onmouseover="/cardemo/world_france.jpg" 
  onmouseout="/cardemo/world.jpg"  /> 

The AreaRenderer class takes care of generating the JavaScript for these actions, as shown in this code from encodeEnd:

writer.write(" onmouseover=\"");
writer.write("document.forms[0].mapImage.src='");
imagePath = (String) component.getAttribute("onmouseover");
if ('/' == imagePath.charAt(0)) {
  writer.write(imagePath);
} else {
  writer.write(contextPath + imagePath);
}
writer.write("';\"");
writer.write(" onmouseout=\"");
writer.write("document.forms[0].mapImage.src='");
imagePath = (String) component.getAttribute("onmouseout");
if ('/' == imagePath.charAt(0)) {
  writer.write(imagePath);
} else {
  writer.write(contextPath + imagePath);
} 

The JavaScript that AreaRenderer generates for the onclick action sets the value of the hidden variable, selectedArea, to the value of the current area's component ID and submits the page:

writer.write("\" 
  onclick=\"document.forms[0].selectedArea.value='");
writer.write(component.getComponentId());
writer.write("'; document.forms[0].submit()\"");
writer.write(" onmouseover=\"");
writer.write("document.forms[0].mapImage.src='"); 

By submitting the page, this code causes the JavaServer Faces lifecycle to return back to the Reconstitute Component Tree phase. This phase saves any state information--including the value of the selectedArea hidden variable--so that a new request component tree is constructed. This value is retrieved by the decode method of the UIMap component class. This decode method is called by the JavaServer Faces implementation during the Apply Request Values phase, which follows the Reconstitute Request Tree phase.

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

AreaRenderer also must implement the decode method and the other encoding methods, whether or not they are needed.

Finally, AreaRenderer requires an implementation of supportsComponentType:

public boolean supportsComponentType(String componentType) {
  if ( componentType == null ) {
    throw new NullPointerException();
  } 
  return (componentType.equals(UIArea.TYPE));
} 

This method returns true when componentType equals UIArea's component type, indicating that AreaRenderer supports the UIArea component.

Note that AreaRenderer extends BaseRenderer, which in turn extends Renderer. The BaseRenderer class is included in the RI of JavaServer Faces technology. It contains definitions of the Renderer class methods so that you don't have to include them in your renderer class.

Register the Renderer with a Render Kit

For every UI component that a render kit supports, the render kit defines a set of Renderer objects that can render the component in different ways to the client supported by the render kit. For example, the standard UISelectOne component class defines a component that allows a user to select one item out of a group of items. This component can be rendered with the Listbox renderer, the Menu renderer, or the Radio renderer. Each renderer produces a different appearance for the component. The Listbox renderer renders a menu that displays all possible values. The Menu renderer renders a subset of all possible values. The Radio renderer renders a set of radio buttons.

When you create a custom renderer, you need to register it with the appropriate render kit. Since the image map application implements an HTML image map, AreaRenderer should be registered with the HTML render kit.

You register the renderer using the application configuration file (see Application Configuration):

<render-kit>
  <renderer>
    <renderer-type>Area</renderer-type>
    <renderer-class>
      components.renderkit.AreaRenderer
    </renderer-class>
  </renderer>
</render-kit> 

The render-kit element represents a RenderKit implementation. If no render-kit-id is specified, the default HTML render kit is assumed. The renderer element represents a Renderer implementation. By nesting the renderer element inside the render-kit element, you are registering the renderer with the RenderKit associated with the render-kit element.

The renderer-type will be used by the tag handler, as explained in the next section. The renderer-class is the fully-qualified classname of the Renderer.

Identify the Renderer Type

During the Render Response phase, the JavaServer Faces implementation calls the getRendererType method of the component's tag to determine which renderer to invoke, if there is one.

The getRendererType method of AreaTag must return the type associated with AreaRenderer. Recall that you identified this type when you registered AreaRenderer with the render kit. Here is the getRendererType method from the cardemo application's AreaTag class:

public String getRendererType() { return "Area";} 
Divider
Download
FAQ
History
HomeHomeNext API
Search
Feedback
Divider

All of the material in The Java(TM) Web Services Tutorial is copyright-protected and may not be published in other works without express written permission from Sun Microsystems.