Coming Up for Air

JSF Component Writing Check List

I’ve been doing a fair amount of JSF component writing of late, and, invariably, I miss one or more "minor" details, causing my component to explode in spectacular fashion at runtime. What follows, then, is a check list for writing JSF components, with notes on the differences between the 1.1 and 1.2 versions of the specification. <h3>Table of Contents</h3> <ul> <li>Write the UIComponent Class</li> <li>Write the Renderer Class</li> <li>Write the Tag Class</li> <li>Write the Tag Library Description (.tld)</li> <li>Write the faces-config.xml Entries</li> <li>Write Facelets .taglib.xml File</li> <li>Conclusion</li> </ul> <a name="uicomponent"></a><h3>Write the UIComponent Class</h3> <ul> <li>Extend the appropriate UIComponent class</li> <li>Define the following properties, changing the the package com.foo to your package, and the component name, Foo to the name of your component:</li> </ul>

1
2
public static final String COMPONENT_TYPE = "com.foo.Foo";
public static final String RENDERER_TYPE = "com.foo.FooRenderer";

<ul> <li>Define public String getFamily () { return COMPONENT_TYPE; } <li>Define default constructor which calls setRendererType(RENDERER_TYPE); <li>Define your properties and their getters and setters.</li> </ul> For 1.1:

1
2
3
4
5
6
7
8
    protected String bar;
    public String getBar() {
        if (null != this.bar) {
            return this.bar ;
        }
        ValueBinding _vb = getValueBinding("bar");
        return (_vb != null) ? (bar) _vb.getValue(getFacesContext()) : null;
    }

For 1.2:

1
2
3
4
5
6
7
8
    protected String bar;
    public String getBar() {
        if (null != this.bar) {
            return this.bar;
        }
        ValueExpression _ve = getValueExpression("bar");
        return (_ve != null) ? (String) _ve.getValue(getFacesContext().getELContext()) : null;
    }

<ul> <li>Write the state saving and restoring code:</li> </ul>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    private Object[] _state = null;
    public void restoreState(FacesContext _context, Object _state) {
        this._state = (Object[]) _state;
        super.restoreState(_context, this._state[0]);
        bar = (String) this._state[1];
    }
    public Object saveState(FacesContext _context) {
        if (_state == null) {
            _state = new Object[2];
        }
        _state[0] = super.saveState(_context);
        _state[1] = bar;
        return _state;
    }

<a name="renderer"></a><h3>Write the Renderer Class</h3> <ul> <li>Extend Renderer</li> <li>Implement encodeBegin(), and/or encodeEnd(). Maybe others, depending on the component’s needs. All of my components, for example, have only need to extend one or the other. </li> </ul> <a name="tag"></a><h3>Write the Tag Class</h3> For 1.1 <ul> <li>Extend UIComponentTag</li> <li>Define all the properties as String (?)</li> <li>Define the following methods (where Foo is your component class):</li> </ul>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    protected String bar;
    public String getRendererType() {
        return Foo.RENDERER_TYPE;
    }
    public String getComponentType() {
        return Foo.COMPONENT_TYPE;
    }
    protected void setProperties(UIComponent component) {
        super.setProperties(component);
        if (!(component instanceof Foo)) {
            throw new IllegalStateException("Component " + component.toString() +
                " not expected type.  Expected: com.foo.Foo.  Perhaps you're missing a tag?");
        }
        Foo foo = (Foo)component;
        if (bar!= null) {
            if (isValueReference(bar)) {
                ValueBinding vb = Util.getValueBinding(bar);
                foo.setValueBinding("bar", vb);
            } else {
                throw new IllegalStateException("The value for 'bar' must be a ValueBinding.");
            }
        }
    }

For 1.2 <ul> <li>Extend UIComponentElTag</li> <li>Define properties as ValueExpression</li> <li>Define these methods (where Foo is your component class):</li> </ul>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    protected ValueExpression bar;
    public String getComponentType() {
        return Foo.COMPONENT_TYPE;
    }
    public String getRendererType() {
        return Foo.RENDERER_TYPE;
    }
    public ValueExpression getBar() {
        return bar;
    }
    protected void setProperties(UIComponent component) {
        super.setProperties(component);
        Foo download = null;
        try {
            foo = (Foo) component;
        } catch (ClassCastException cce) {
            throw new IllegalStateException("Component " + component.toString() +
                " not expected type.  Expected: com.foo.Foo.  Perhaps you're missing a tag?");
        }
        if (bar != null) {
            foo.setValueExpression("bar", bar);
        }
    }

</li> <a name="tld"></a><h3>Write the Tag Library Description (.tld)</h3> For 1.1:

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
    <tlib-version>1.0</tlib-version>
    <jsp-version>1.2</jsp-version>
    <short-name>foo</short-name>
    <uri>http://foo.com/foo</uri>
    <description><![CDATA[Your description here]]

Quotes

Sample quote

Quote source