Sun Java Solaris Communities My SDN Account Join SDN
 
Java Platform, Enterprise Edition (Java EE)

Creating and Using a Custom Render Kit

 
By Roger Kitain and Jennifer Ball, August 2005  
JavaServer Faces technology offers a rich set of components as well as a standard render kit to render these components to an HTML client. At the same time, the design of JavaServer Faces technology is flexible enough to allow component writers to create their own render kit in order to render components to non-HTML clients, whether these components are the standard ones that come with JavaServer Faces technology or if they are custom components that the component writers have created themselves. This document describes how to create your own render kit and how to use a custom render kit in an application both by itself and with other render kits.  It assumes you have some basic knowledge of JavaServer Faces technology, particularly the page life cycle.

Steps for Creating and Using a Custom Render Kit


Before going into detail on how to create and use a custom render kit, let's summarize the tasks involved.  The following list outlines the steps required to create a custom render kit and to use its renderers to display components on the target client.
  1. Creating custom component classes or determining which standard components to use
  2. Creating renderer classes to render the components on the target client
  3. Creating a RenderKit class
  4. Creating a ResponseWriter class
  5. Creating a ResponseStateManager class
  6. Creating a SerializedView class
  7. Creating custom tags and tag handlers for the renderer and component combinations
  8. Registering the render kit, renderers, and components with the application
  9. Using the custom tags in the page
This document uses the lifecycle demo to explain the concepts around creating and using a render kit.

Understanding the lifecycle Demo


The lifecycle demo uses Scalable Vector Graphics (SVG) and the XML User Interface Language (XUL) along with JavaServer Faces technology to render an animated diagram of the JavaServer Faces life cycle.  SVG is an XML-based language that is used to describe two-dimensional graphics, such as lines, rectangles, text, and images and to allow animation of these graphic elements.  XUL is an XML-based language for creating dynamic user interfaces.  As JavaServer Faces technology does, XUL supports a rich set of UI components.

The lifecycle demo doesn't require the use of XUL; it is there just to demonstrate how easy it is for a JavaServer Faces application to utilize more than one render kit.  In fact, when using releases of JavaServer Faces technology prior to 1.2, you had to implement a custom ViewHandler class (see Creating a RenderKit Class for more information on this class) to handle multiple render kits, in addition to doing everything outlined in this document.

The lifecycle demo application uses three render kits: the standard HTML render kit, a custom SVG render kit, and a custom XUL render kit.They each render their respective markups. The following diagram shows the flow of the demo between the pages of different markup.

Page Flow of the Demo
Figure 1: Page Flow of the Demo
 

The markup for the first page is produced with the standard HTML render kit, which means it contains HTML markup.  From there, you navigate to the SVG page, which displays a diagram of the JavaServer Faces life cycle phases. Pressing any of the buttons on the lower left corner of the diagram produces an animation that shows how a request flows through each of the phases. The SVG markup for the SVG page is produced from an SVG render kit.

Clicking on any of the life cycle phase boxes causes an HTTP post to the JavaServer Faces controller, which in turn causes the next view to be returned. The response that is returned is the markup and a view identifier for an XUL page.  This page provides more detailed information about the selected phase.

This demo also shows how to deal with the fact that SVG and XUL do not support the notion of an HTTP post mechanism as does HTML.  Instead, the developer must use JavaScript to register an event handler so that when a button is pressed, an event is generated. The JavaScript onclick event handler collects the form input data, builds a post data string, and sends it to the server.

From SVG there are a couple of different ways to post data to a server from JavaScript. This demo relies on browsers that have built-in SVG support, such as in Deer Park Alpha 2, which makes available theXMLHttpRequest object as the posting mechanism.  The demo uses this object to post the request to the JavaServer Faces controller. It also uses this object to handle the response from the JavaServer Faces controller back to the client.

The problem with using the XMLHttpRequest object is that the response it generates will not include the view identifier of the view to send with the response.  This demo includes an implementation of a ResponsePhaseListener instance that will access the view identifier from the FacesContext instance and add it to the response.  This way, the JavaScript callback handling the response knows what view to display next.  The following figure illustrates how this process works.  See Handling Submits Generated from a non-HTML Page for more details on implementing theResponsePhaseListener class.

Handling Submits from a non-HTML Page
Figure 2: Handling Submits Generated from a non-HTML Page

We recommend that you download the demo, take a look at the code, and run it. You will need to register at java.net to download the example, but registration is free of charge. To download the example, go to https://javaserverfaces.dev.java.net/ , go to the section that lists the nightly bundles, and follow the instructions for downloading and unpacking the samples bundle.  The lifecycle demo is packaged as the jsf-renderkits.war. This demo runs on Sun's Java System Application Server PE 9.0, code-named glassfish. You can download glassfish from https://glassfish.dev.java.net/.  Please read the README file for information on software requirements and how to deploy and run the demo.  If you would like to see the source code of the demo, you can unpack the WAR file using the command

 jar -xvf jsf-renderkits.war

The rest of this document describes how to create and use a render kit using the SVG render kit included in the lifecycle demo.

Creating the Component Classes or Determining Which Standard Components to Use


A component class defines the state and behavior of a UI component.  Before creating any new component classes, you should determine if some of the standard components provided by JavaServer Faces technology already meet your needs and can be rendered to your target client.  In the case of the lifecycle demo, the standardUIOutput and UICommand components are sufficient for use as labels and buttons, respectively.  The lifecycle demo need only provide custom renderers for these components so that they can be rendered in an SVG-enabled browser.

On the other hand, JavaServer Faces technology does not provide any components that represent the shapes that SVG can render.  Therefore, the lifecycle demo needs custom components to represent the shapes that it uses.

If you decide that you do need a custom component, then you must decide whether it should extend a standard component or directly extend UIComponentBase, the base class for all the standard components.  This base class, along with the standard component classes, are located in the javax.faces.component package and their names begin with UI.

If your custom component serves the same purpose as a standard component, you should extend that standard component rather than directly extend UIComponentBase. For example, the UIOutput component is intended for displaying something.  Therefore, the Shape component class used by the lifecycle demo extends UIOutput rather than UIComponentBase.

The dynamic lifecycle example defines three component classes: ShapeLine, and Rectangle. The Line and Rectangle classes both extend Shape, which makes sense because lines and rectangles are shapes.
The sole purpose of theLine and Rectangle components is to represent the appropriate shapes on the page; they don't hold any data that needs to be validated, respond to events, or do anything that a component class usually needs to define for the component.   On the other hand, there is a lot of work to be done to render the components, which is what the corresponding renderer classes do.

The job of the Line and Rectangle component classes is to identify the component type and the component family for the purpose of registering the components with the application and delegating the rendering of the components to the appropriate renderers.   To delegate rendering, a component class must override the getFamily method of UIComponent to return the identifier of a component family.  A component family is used to refer to a component or set of components that can be rendered by a set of renderers.  Here is the getFamily method fromLine.java:

        public String getFamily() {
             return (COMPONENT_FAMILY);
      }
The Line class sets the static variable COMPONENT_FAMILY to"Line".  This identifier must match that defined by the configuration of the Line component of the application configuration resource file, as described in Registering the Render Kit, Renderers, and Components in the Configuration File.

The Line class also sets a static variable called COMPONENT_TYPE to "Line".  This value must match that returned by thegetComponentType method of the tag handler that implements the component tag representing the Line component on the page.  The implementation of the tag handler is described in Creating Custom Tags and Tag Handlers.

The Rectangle class also implements getFamily and sets theCOMPONENT_TYPE variable to match that defined in the RectangleTag tag handler class.

The Shape class does not define a component type or component family; it is merely the base class for Rectangle and Line.

After creating the component classes, you can create renderers for them.

Creating the Renderer Classes to Render Components on the Target Client


JavaServer Faces technology offers two techniques for performing rendering: direct implementation and delegated implementation.  By using direct implemenation, a component writer includes all rendering code in the component class.  Conversely,delegated implementation involves the component class delegating the task of rendering the component to a separate renderer.

By delegating the rendering to a separate renderer, the component makes itself more versatile because multiple renderers would be able to render it to different clients. This is why the custom components of the lifecycle demo all delegate their rendering to separate renderers. LineRenderer renders the Line component, andRectangleRenderer renders the Rectangle component.

The lifecycle demo also provides renderers for the standard components that it uses so that it can render them to the SVG client. ButtonRenderer renders the UICommand component as a button. FormRenderer renders the UIForm component as an HTML form. TextRenderer renders the UIOutput component as a label.

This section uses LineRenderer and ButtonRenderer to explain the basic requirements for writing a renderer class for custom and standard components.   At the very least, a renderer class must perform the encoding of a response.  This is the process of generating the markup for the target client.

A renderer class might also need to do some decoding, which involves taking the component's local value from the  request and converting it to a type acceptable to the component class--essentially the reverse of encoding.   A renderer is required to perform decoding only if it needs to retrieve a component's local value or if it needs to queue an event onto the component.

Let's first talk about how to perform encoding.

Performing Encoding

During the render response phase of the JavaServer Faces life cycle, the implementation processes the encoding methods of all components in the view and their associated renderers. The encoding methods convert the current local value of the component into the corresponding markup that represents it in the response.

The UIComponentBase class defines a set of methods for rendering markup: encodeBegin, encodeChildren, and encodeEnd. If the component has child components, you might need to use more than one of these methods to render the component.  For example, if the tag for one component is nested within a tag for another component, you need to render the parent component's start tag with the encodeBegin method and render its end tag with the encodeEnd method.  The nested component's renderer takes over rendering the nested component after the parent component's start tag is rendered and before its end tag it rendered.  If the parent component must perform the rendering for child components, it does this in its encodeChildren method.

The lifecycle demo components don't have any child components.  Therefore, rendering these components is a bit simpler.  The renderer class can perform the rendering with itsencodeBegin method, encodeEnd method, or both methods.  In fact,LineRenderer uses both methods: encodeBegin to render most of the start tag and encodeEnd to render the end tag.  Here are the encodeBegin and encodeEnd methods of LineRenderer:

public void encodeBegin(FacesContext context, UIComponent component)
        throws IOException {
        if (context == null || component == null) {
            // PENDING - i18n
            throw new NullPointerException("'context' and/or 'component' is null");
        }
        if (log.isTraceEnabled()) {
            log.trace("Begin encoding component " + component.getId());
        }
        // suppress rendering if "rendered" property on the component is
        // false.
        if (!component.isRendered()) {
            if (log.isTraceEnabled()) {
                log.trace("End encoding component " + component.getId() +
                          " since rendered attribute is set to false ");
            }
            return;
        }
        
        ResponseWriter writer = context.getResponseWriter();

        writer.startElement("line", component);
        writeIdAttributeIfNecessary(context, writer, component);
        String x1 = (String)component.getAttributes().get("x1");
        if (x1 != null) {
            writer.writeAttribute("x1", x1, "x1");
        }
        String y1 = (String)component.getAttributes().get("y1");
        if (y1 != null) {
            writer.writeAttribute("y1", y1, "y1");
        }
        String x2 = (String)component.getAttributes().get("x2");
        if (x2 != null) {
            writer.writeAttribute("x2", x2, "x2");
        }
        String y2 = (String)component.getAttributes().get("y2");
        if (y2 != null) {
            writer.writeAttribute("y2", y2, "y2");
        }
        String style = (String)component.getAttributes().get("style");
        if (style != null) {
            writer.writeAttribute("style", style, "style");
        }
        writer.writeText("\n    ", null);
    }

    public void encodeEnd(FacesContext context, UIComponent component)
        throws IOException {
        if (context == null || component == null) {
            // PENDING - i18n
            throw new NullPointerException("'context' and/or 'component' is null");
        }
        ResponseWriter writer = context.getResponseWriter();

        writer.endElement("line");
        writer.writeText("\n", null);
    }
Notice that encodeBegin renders the beginning line tag. The encodeEnd method renders the ending line tag.

The encoding methods accept a UIComponent argument and a FacesContext argument. The FacesContext instance contains all the information associated with the current request. The UIComponent argument is the component that needs to be rendered.

The methods render the markup to the ResponseWriter instance, which writes out the markup to the current response. This basically involves passing the HTML tag names and attribute names to the ResponseWriter instance as strings, retrieving the values of the component attributes, and passing these values to the ResponseWriter instance.

The startElement method takes a String (the name of the tag) and the component to which the tag corresponds (in this case, line).  Passing this information to the ResponseWriter instance helps design-time tools know which portions of the generated markup are related to which components.

After calling startElement, the encodeBegin method calls writeIdAttributeIfNecessary (defined inBaseRenderer), which tries to get the ID of the component in order to write it out.  After rendering the ID, the method calls writeAttribute to render all the tag's attributes. The writeAttribute method takes the name of the attribute, its value, and the name of the corresponding property or attribute of the containing component. The last parameter can be null, and it won't be rendered.

As explained in Creating the Renderer Classes, your renderer might also need to supply adecode method.  The next section describes the decode method included in ButtonRenderer.

Performing Decoding

During the apply request values phase, the JavaServer Faces implementation processes the decode methods of all components in the tree. The decode method extracts a component's local value from incoming request parameters and converts the value to a type that is acceptable to the component class.  The process of

decoding is more bound to the concept of HTTP than it is to a specific rendering technology.

A renderer must implement the decode method only if it must retrieve the local value or if it needs to queue events. The decode method of ButtonRenderer does both of these things.  It first retrieves the client ID of the button component that it rendered previously and checks if it matches the client ID for the button the user has just clicked.  If the IDs match, the decode method calls the component's queueEvent method to queue on ActionEvent onto the component.  Here is thedecode method from ButtonRenderer:

   public void decode(FacesContext context, UIComponent component) {
        if (context == null || component == null) {            
         throw new NullPointerException("'context' and/or 'component is null");
        }
        if (log.isTraceEnabled()) {
            log.trace("Begin decoding component " + component.getId());
        }
          
        String clientId = component.getClientId(context);
        Map requestParameterMap = context.getExternalContext()
            .getRequestParameterMap();
        String value = (String) requestParameterMap.get(clientId);
        if (value == null) {
            if (requestParameterMap.get(clientId + ".x") == null &&
                requestParameterMap.get(clientId + ".y") == null) {
                return;
            }
        }

        ActionEvent actionEvent = new ActionEvent(component);
        component.queueEvent(actionEvent);

        if (log.isDebugEnabled()) {
            log.debug("This command resulted in form submission " +
                      " ActionEvent queued " + actionEvent);
        }
        if (log.isTraceEnabled()) {
            log.trace("End decoding component " + component.getId());
        }
        return;
    }

Note the renderer classes extend BaseRenderer, which in turn extends Renderer. The BaseRenderer class contains definitions of the Renderer class methods so that you don't have to include them in your renderer class.

Now that you have your set of renderers, it's time to add them to your render kit.  Before you do that, you need to create a RenderKit class.

Creating a RenderKit Class


Before going into how to create a RenderKit class, it's worthwhile explaining what exactly a render kit does and what is its role in the application life cycle.  This will help you better understand the pieces of the RenderKit implementation that you need to create.

As you've guessed by now, a render kit defines a set of renderers that have the ability to render a set of components to one particular kind of client.  For example, this section shows you how to create a render kit for an SVG client.

Much more than just defining a set of renderers, a RenderKit instance also takes part in rendering the response, re-building the component tree structure, and saving and restoring component state.  It does this in partnership with the default ViewHandler implementation, which allows applications to control what happens in the restore view and render response phases of the life cycle.

In a nutshell, the life cycle implementation uses ViewHandler to get information and necessary objects from the render kit so that it can determine whether ViewHandler should create or restore a view, save or restore state, or render the response, and can instruct ViewHandler to do one of these things. The primary objects that participate with ViewHandler in building the view, rendering it, and restoring state are summarized in the following figure.

Objects Used in Rendering
Figure 3: Objects Used in Rendering
 

One of the objects that ViewHandler obtains from the render kit is the ResponseStateManager object, which knows the rendering technology used by the render kit and can perform rendering-specific state-management duties.  It might not be immediately obvious what the relationship is between rendering and state-management.  The two are actually related because state is saved on the client by default.  In order to save the state on the client, it must be rendered using the rendering technology specified by the render kit.

Another object the ViewHandler instance obtains from the render kit is the ResponseWriter object. It implements methods for writing out markup to a specific client.

Let's take a look at how these objects are used during the life cycle of a JavaServer Faces page.

During the restore view phase of the life cycle, ViewHandler retrieves the ResponseStateManager object in order to test if the request is a postback or an initial request.  The ResponseStateManager object is needed in this case because it is the only one that knows what rendering technology is being used and is therefore the only one that can look at a request, which is rendering-technology specifiec.

If the request is an initial request, the life cycle implementation jumps to the render response phase. During the render response phase, theViewHandler instance uses the ResponseStateManager object to save the state of the tree for the benefit of subsequent requests.  Additionally, the renderView method of ViewHandler is called.  This method callsencodeAll on the UIViewRoot instance, which represents the root of the component tree.

The encodeAll method traverses the tree of components, calling each component's encoding methods. These methods use the component family and renderer type specified in the application's configuration file to find the renderer associated with the component.  Once the renderer is found, it calls the methods of the ResponseWriter object to render the view.

If a request is a postback, therestoreView method of ViewHandler is called.  This method uses theResponseStateManager object to re-build the component tree and restore state.  After the tree is built and state is restored, theViewHandler instance is not needed until the render response phase occurs again.

Meanwhile, the render kit and renderers are needed to decode the request values during the apply request values phase.  To do this, the FacesContext instance calls the processDecodes method on the UIView instance.  This method traverses the component tree, calling processDecodes on all the components in the tree.  The components' processDecodes methods call the associated decoding methods of the renderers.  The renderers then call the methods of the ResponseWriter object that write out the markup to the client.

Subsequently, the rest of the life cycle phases are executed, including render response, during which the same process occurs as did during the render response phase that was executed for the initial request.

When using JavaServer Faces technology, version 1.2,  you do not have to create a custom ViewHandler to handle multiple render kits in an application, as you did when using prior versions.  You need only specify the render kit to be used for each view using the view tag on the corresponding page, as explained in Using the Render Kit in the Page.

Now that you understand the role of the render kit and itsResponseStateManager and ResponseWriter objects in the life cycle, you're ready to implement a RenderKit class, which must perform the following tasks:

  • Define  the content types and character encodings that the render kit class supports
  • Implement  the following methods:
    • An addRenderers method to add renderers to the render kit
    • getRenderer method to retrieve a renderer from the render kit.
    • getResponseStateManager method that returns a ResponseStateManager instance, which will handle rendering-related state management
    • createResponseWriter that  determines the content type and returns an appropriate ResponseWriter instance that can generate the appropriate markup.
The first thing to do is to add some private, static variables that define the content type and character encoding that thisRenderKit class supports:
        private static String SVG_CONTENT_TYPE = "image/svg+xml";
       private static String APPLICATION_XML_CONTENT_TYPE = "application/xml";
 private static String TEXT_XML_CONTENT_TYPE = "text/xml";
       private static String CHAR_ENCODING = "ISO-8859-1";
     private static String CONTENT_TYPE_IS_SVG = "ContentTypeIsSVG";
The RenderKit class will use these variables to test against the current content type and encoding to ensure that they match the ones that this render kit supports.

The JavaServer Faces  implementation invokes theaddRenderer method of RenderKit at application startup time as it processes the application configuration file, in which the render kit and set of renderers are configured.  You'll see in Configuring the Render Kit, Components, and Renderers how to configure the render kit, renderers, and components included in your application.  The addRenderer method populates a map with the renderer, as shown here:

 public void addRenderer(String family, String rendererType,
                            Renderer renderer) {
        if (family == null || rendererType == null || renderer == null) {
            // PENDING - i18n
            String message = "Argument Error: One or more parameters are null."; 
            message = message + " family " + family + " rendererType " +
                rendererType + " renderer " + renderer;
            throw new NullPointerException(message);
                
        }
         HashMap renderers = null;

        synchronized (rendererFamilies) {
        if (null == (renderers = (HashMap) rendererFamilies.get(family))) {
        rendererFamilies.put(family, renderers = new HashMap());
        }
            renderers.put(rendererType, renderer);
        }
    }
 

 

The getRenderer method returns a renderer that can render the components of the specified component family and has the specified renderer type.  The component family is defined in the component class.  The renderer type is defined by the tag handler implementing the tag  that renders the component.
 

    public Renderer getRenderer(String family, String rendererType) {
       if (rendererType == null || family == null) {
            // PENDING - i18n
            String message =
"Argument Error: One or more parameters are null."; 
            message = message + " family " + family + " rendererType " +
                rendererType;
            throw new NullPointerException(message);
        }
       HashMap renderers = null;
       Renderer renderer = null;
       if (null != (renderers = (HashMap) rendererFamilies.get(family))) {
             renderer = (Renderer) renderers.get(rendererType);
      }
       return renderer;
   }

The JavaServer Faces life cycle implementation invokes the getResponseStateManager method to retrieve the ResponseStateManager instance associated with the render kit.  Here is thegetResponseStateManager method from SVGRenderKit:

The following method returns a ResponseStateManager instance:

    public synchronized ResponseStateManager getResponseStateManager() {
        if (responseStateManager == null) {
            responseStateManager = new SVGResponseStateManager();
        }
        return responseStateManager;
    }
The createResponseWriter method returns aResponseWriter instance that provides the content type (in this caseimage/svg+xml).  The ViewHandler instance associated with the current view invokes this method during the render response phase to obtain a ResponseWriter object so that the current view can be written to this object and subsequently rendered to the client.  The developer of the render kit is also responsible for creating the customResponseWriter class.

The createResponseWriter method takes a Writer object, a String that contains a list of content types, and the character encoding, all of which are used to render the response.  The method first loads the supported content types defined by the RenderKit class into an array.  The method then checks if the String of content types passed to the method is null.  If it is, the method looks for a content type in the context representing the response and in the request header.  If the method finds a content type, it looks for a matching content type in the array of supported types.  If no match is found, the content type is set to SVG.  The method then returns a new SVGRenderKit instance with the specified writer, and an acceptable content type and character encoding.

Creating a ResponseWriter Class


The ResponseWriter class defines methods for rendering to the target client.  A ResponseWriter object is used by the renderView method of ViewHandler to render the view during the render response phase of the life cycle.

The default ResponseWriter that comes with JavaServer Faces technology extends the Writer class from the Java 2 Standard Edition to add special methods for producing elements and attributes for markup languages, such as XML and HTML.  It also defines the content type for the render kit that creates it.

Because SVG is derived from XML, SVGResponseWriter pretty much re-uses the code from the default response writer class.  The only real difference is that SVGResponseWriter defines a content type of SVG, as shown by this line of code from SVGResponseWriter:

    private String contentType = "image/svg+xml";
If you are creating a custom render kit and the client for which it defines renderers is XML-based then you will be able to simply copy over the SVGResponseWriter code to your ResponseWriter class.

Creating a ResponseStateManager Class


As explained in Creating a RenderKit Class, the ResponseStateManager class defines methods for re-building the component tree structure and restoring state during the restore view phase and for saving state during the render response phase.  If you are creating your own render kit, you also need to provide an implementation of ResponseStateManager that is aware of the rendering technology that your render kit uses.

Creating a ResponseStateManager class involves providing implementations for the following methods:
 

  • An isPostBack method that returns true if the current request is a postback. The ResponseStateManager makes this determination by checking if it has already written out the component tree state during a previous request.
  • getState method that retrieves the component tree state from the current request.  This method is called by the restoreView method of ViewHandler.
  • writeState method that writes out the state to the client.  This method is called by the renderView method of ViewHandler during the render response phase.  Its purpose is to write out a serialized state of the component tree using a SerializedView object. SerializedView is  a serializeable object that contains the structure and state of the component tree.  See Creating a SerializedView Class for more information.  The writeState method receives either a SerializedView object or a two-element object array containing the structure of the component tree and its state, respectively.  If the argument is not a SerializedView object, it will create a SerializedView object from the array.  In either case, the method calls another writeState method that takes the serialized view and does the work of writing out the state of the tree.


Much of the SVGResponseStateManager class is copied directly from theResponseStateManagerImpl class that is part of the JavaServer Faces reference implementation.  The writeState method is the only one that has code that is specific to rendering SVG.

More specifically, the writeState method does the following:

  • Gets the ResponseWriter instance associated with this render kit.
  • Gets the component tree state that this ResponseStateManager instance had saved previously.
  • Determines if the application is configured to save state in the client (which is the default)
  • If the application is performing client-side state saving, compresses the state and secures it, and writes out the state to the client in a hidden field using the ResponseWriter instance.
  • If the application is performing server-side state saving, writes out the tree structure.
  • Finally, gets the ID of the render kit (which is SVG in this case), and renders it to a hidden field.
Please refer to SVGResponseStateManager.java for the details on implementing all of the required methods.

Creating a SerializedView Class


Recall from the previous section that the writeState method of ResponseStateManager writes out the component state to the client.  To do this, it requires a SerializedView object that encapsulates the state and structure of the component tree.  Therefore, if you are creating a custom render kit, you must provide a SerializedView class.  The following code is the entire SerializedView class included in the lifecycle demo:
 
public class SerializedView extends Object implements Serializable {
   private Object structure = null;
   private Object state = null;

   public SerializedView(Object newStructure, Object newState) {
      structure = newStructure;
      state = newState;
   }
   public Object getStructure() {
      return structure;
   }
   public Object getState() {
      return state;
   }
}

When you create your own render kit and use it in an application, you can copy this class as it is to your application.

Registering the Render Kit, Renderers, and Components in the Configuration File


After you've created the render kit, renderers, and components, you can register them with the application using the application's configuration file.  This file is usually called faces-config.xml, as it is in the lifecycle demo.

The following piece of the lifecycle demo's configuration file show's how to register the SVG render kit, the Line component, theLineRenderer renderer, and the ButtonRenderer renderer, which renders a UICommand component to an SVG client.  The rest of the configuration information is ommitted. Please refer to the lifecycle demo's faces-config.xml file to find out how the other components and renderers are configured.
 

  ...
  <component>
      <component-type>Line</component-type>
      <component-class>
renderkits.components.svg.Line
</component-class>
  </component>
  ...
  <render-kit>
    <render-kit-id>SVG</render-kit-id>
    <render-kit-class>
renderkits.renderkit.svg.SVGRenderKit
</render-kit-class>
    ...
    <renderer>
      <component-family>
javax.faces.Command
</component-family>
      <renderer-type>
renderkit.svg.Button
</renderer-type>
      <renderer-class>
renderkits.renderkit.svg.ButtonRenderer
</renderer-class>
    </renderer>
    <renderer>
      <component-family>Line</component-family>
      <renderer-type>
renderkit.svg.Line
</renderer-type>
      <renderer-class>
renderkits.renderkit.svg.LineRenderer
</renderer-class>
    </renderer>
  </render-kit>
The component element is fairly self-explanatory, as is the render-kit element.  The elements that might not be so obvious are thecomponent-family and renderer-type sub-elements of the renderer element.  The component-family element identifies a set of components that the specified renderer can render.  The value of this element must match that returned by the component class'sgetFamily method.  The value of the renderer-type element must match that returned by the getRendererType method of the tag handler class, which we'll talk about next.

Creating Custom Tags and Tag Handlers


In JavaServer Faces applications, the tag handler class associated with a component drives the rendering of that component.   The first thing that the tag handler does is to retrieve the type of the component associated with the tag. Next, it sets the component's attributes to the values given in the page. Finally, it returns the type of the renderer to the JavaServer Faces implementation so that the component's encoding can be performed when the tag is processed.  This section uses LineTag.java to explain how to implement a tag handler.  It also explains how to define the tag in a TLD.

To implement a tag handler, you need to create a class that extends UIComponentELTag and add the following to it:

  • Properties to access the values for all of the component and renderer attributes
  • setProperties method that passes the value of tag attributes to the component
  • getRendererType method that returns the type of the renderer that renders this tag
  • getComponentType method that returns the type of the component that this tag represents
  • release method that releases resources
The LineTag tag handler has several properties that are called by the life cycle implementation to set the component's attribute values to those supplied as tag attributes in the page.  The following property is used to access the value of the Line component's onClick attribute:
    // PROPERTY: onclick
    private javax.el.ValueExpression onclick;
    public void setOnclick(javax.el.ValueExpression onclick) {
        this.onclick = onclick;
    }
Notice that the property (and all of the properties in this tag handler) accepts a ValueExpression instance.  This is because the attributes of Line component only accept value expressions.

To pass the values of the tag attributes to Line component, the tag handler implements the setProperties method.  The following lines set the value on the onClick property:

       if (onclick != null) {
            if (!onclick.isLiteralText()) {
                line.setValueExpression("onclick", onclick);
            } else {
                line.getAttributes().
put("onclick", onclick.getExpressionString());
            }
        }
The tag handler also needs to retrieve the types of the component and renderer that the tag represents on the page.  It does this with the getComponentType and getRendererType methods:
    public String getRendererType() {
        return "renderkit.svg.Line";
    }

    public String getComponentType() {
        return "Line";
    }
The types that these methods return must match those under which the component and renderer are registered with the configuration file as shown in  Registering the Render Kit, Components, and Renderers in the Configuration File.

Finally, it's recommended that all tag handlers implement a release method, which releases resources allocated during the execution of the tag handler. Therelease method of LineTag is as follows:

public void release() {
        super.release();

        // rendered attributes
        this.onclick = null;
        this.onfocusin = null;
        this.onfocusout = null;
        this.onmousedown = null;
        this.onmousemove = null;
        this.onmouseout = null;
        this.onmouseover = null;
        this.onmouseup = null;
        this.style = null;
        this.x1 = null;
        this.y1 = null;
        this.x2 = null;
        this.y2 = null;

Now that the tag handler, component, and renderer are defined, the next step is to define the actual tag in a tag library descriptor (TLD).  The web container uses the TLD to validate the tag.  The set of tags that are part of the standard HTML render kit are defined in the html_basic TLD.

The custom tags used to render SVG components for the lifecycle demo are defined in the svg.tld file.  The following code snippet is part of the line tag's definition.  The definition must have the following sub-elements:

  • name element that defines the name of the tag
  • tag-class element that specifies the tag handler class
  • body-content element specified as JSP
  • An attribute element for each attribute of the line tag
  • rendered element that indicates whether or not the component should be processed during all phases of the life cycle or only during render response.  The default is true, meaning that the element should be rendered during all phases.
Every attribute element must have a name element defining the name of the attribute and a required element defining if the attribute is required for every instance of the tag.

All attribute elements except the one defining the id attribute must also include a deferred-value element that specifies what type of value that the attribute accepts.

    <tag>
       <name>line</name>
       <tag-class>
renderkits.taglib.svg.LineTag
</tag-class>
    <body-content>JSP</body-content>
        <attribute>
             ...
             <name>id</name>
         <required>false</required>
              <rtexprvalue>false</rtexprvalue>

        </attribute>
    <attribute>
             <name>rendered</name>
           <required>false</required>
              <deferred-value>
                        <type>boolean</type>
            </deferred-value>
       </attribute>
    <attribute>
              ...
            <name>x1</name>
         <required>false</required>
              <deferred-value>
                        <type>java.lang.String</type>
           </deferred-value>
       </attribute>
     ...
    </tag> 

Using the Render Kit and Associated Tags in the Page


After performing all the steps outlined so far of this document, you can now create pages using the custom render kit.  In order to use the render kit, your pages must include the following:
  • page directive that indicates the content type of the page, which matches the content type defined by the render kit's ResponseWriter object.
  • Tag library directives declaring the tag libraries that define the custom component tags that can display the components rendered by your custom render kit
  • A tag that indicates the rendering technology that the page uses (SVG, in this case)
  • renderKitId attribute on the view tag.  This attribute holds the ID of the render kit.
Once you've added these items to the page, you can add the custom tags you created.  This piece of the svg.jsp page shows the required elements of the page and a few of the component tags, namely those that display the graphic elements illustrating the restore view phase:
 
<%@ page contentType="image/svg+xml"%>
<%@ taglib uri="http://java.sun.com/jsf/svg" prefix="g" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
                                                                                      
<svg xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink">
    <style type="text/css">
      rect:hover {fill-opacity:0.3;}
    </style>
    <f:view renderKitId="SVG" > <g:form id="form">
           <g:outputText x="100" y="50" textAnchor="middle"
value="JSF Request Processing Lifecycle"
                style="stroke:black;
stroke-width:0.5;
fill:none; font-size:32pt;" />

           <!-- Restore View Graphic -->

           <g:line id="toRestore" x1="25" y1="125"
x2="100" y2="125"
style="stroke:black; fill:none;" />
           <g:commandButton id="restore" width="120"
height="50" x="100" y="100" type="submit"
action="xul-restore"
style="stroke:black; fill:#8470ff;" >
              <g:outputText x="130" y="120"
textAnchor="middle" value="Restore" />
              <g:outputText x="135" y="140"
textAnchor="middle" value="View" />
           </g:commandButton>
...
</g:form>
</f:view>
</svg>

Handling Submits Generated from a non-HTML Page


The SVG language does not support any notion of a form or submitting a form.  Therefore, the lifecycle demo handles submits from the SVG page using Ajax techniques, namely an XMLHttpRequest object, which allows performing a submit without refreshing an entire page.  When doing form submissions in this way, the request does not contain the view ID for the page that must be accessed next.

For this reason, the lifecycle demo requires a ResponsePhaseListener implementation to get the view ID after the invoke application phase and before the render response phase.  When theResponsePhaseListener object is notified that the invoke application phase has occurred, it retrieves the view ID from the FacesContext instance and adds it to the URL to be used to render the next page.  It then adds this URL to the response header so that the next view can be rendered in the subsequent render response phase.

The following code sample shows the lifecycle demo'sResponsePhaseListener.afterPhase method, which the JavaServer Faces life cycle implementation calls after the invoke application phase.  This method has the task of appending the URL of the new view to the response.

    public void afterPhase(PhaseEvent event) {
        // Disregard requests that are not XMLHttpRequest(s) 
        Map requestHeaderMap =
event.getFacesContext().getExternalContext().
            getRequestHeaderMap();
        if (requestHeaderMap.get(XML_HTTP) == null) {
            return;
        }
        // If we're dealing with an XMLHttpRequest...
        // Get the URI and stuff it in the response header.
        FacesContext context = event.getFacesContext();
        String viewId = context.getViewRoot().getViewId();
        String actionURL =
context.getApplication().getViewHandler().
getActionURL(context, viewId); 
        HttpServletResponse response =
(HttpServletResponse)context.getExternalContext().
getResponse();
        response.setHeader("Cache-Control", "no-cache");
        response.setHeader(VIEW_URI, actionURL);
    }
For more detail on AJAX, please see the AJAX entries of the blueprints solution catalog.

Conclusion


JavaServer Faces technology provides a rich, flexible component and rendering model that makes it easy to build custom render kits to render components to various kinds of clients.  You've also seen how easy it is to configure and use the render kit in a web application.  Finally, you've learned how to use the new Ajax techniques to handle form submits for rendering technologies that have no facility for form submits.  You now have the means to create and use your own render kit using JavaServer Faces technology.

Resources


For more information on this topic, visit the following links:

JavaServer Faces Technology
AJAX with the Java 2 Enterprise Edition
The Glassfish Project
Rate and Review
Tell us what you think of the content of this page.
Excellent   Good   Fair   Poor  
Comments:
Your email address (no reply is possible without an address):
Sun Privacy Policy

Note: We are not able to respond to all submitted comments.

Oracle is reviewing the Sun product roadmap and will provide guidance to customers in accordance with Oracle's standard product communication policies. Any resulting features and timing of release of such features as determined by Oracle's review of roadmaps, are at the sole discretion of Oracle. All product roadmap information, whether communicated by Sun Microsystems or by Oracle, does not represent a commitment to deliver any material, code, or functionality, and should not be relied upon in making purchasing decisions. It is intended for information purposes only, and may not be incorporated into any contract.