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.

Table of Contents


Write the UIComponent Class

  • Extend the appropriate UIComponent class
  • Define the following properties, changing the the package com.foo to your package, and the component name, Foo to the name of your component:
public static final String COMPONENT_TYPE = "com.foo.Foo";
public static final String RENDERER_TYPE = "com.foo.FooRenderer";
  • Define public String getFamily () { return COMPONENT_TYPE; }
  • Define default constructor which calls setRendererType(RENDERER_TYPE);
  • Define your properties and their getters and setters.

For 1.1:

    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:

    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;
    }
  • Write the state saving and restoring code:
    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;
    }


Write the Renderer Class

  • Extend Renderer
  • 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.


Write the Tag Class

For 1.1

  • Extend UIComponentTag
  • Define all the properties as String (?)
  • Define the following methods (where Foo is your component class):
    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

  • Extend UIComponentElTag
  • Define properties as ValueExpression
  • Define these methods (where Foo is your component class):
    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);
        }
    }


Write the Tag Library Description (.tld)

For 1.1:

<?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]]></description>
    <tag>
        <name>foo</name>
        <tag-class>com.foo.Foo</tag-class>
        <body-content>JSP</body-content>
        <description><![CDATA[Your description here]]></description>
        <attribute>
            <name>id</name>
            <required>false</required>
            <rtexprvalue>true</rtexprvalue>
            <description>
                <![CDATA[Your description here.]]>
            </description>
        </attribute>
        <attribute>
            <name>bar</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
             <description>
                <![CDATA[Your description here.]]>
            </description>
        </attribute>
    </tag>
</taglib>

For 1.2:

<?xml version="1.0" encoding="UTF-8"?>

<taglib xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
    version="2.1">
    <description><![CDATA[Your description here]]></description>
    <tlib-version>1.0</tlib-version>
    <short-name>foo</short-name>
    <uri>http://foo.com/foo</uri>
    <tag>
        <description><![CDATA[Your description here]]></description>
        <name>foo</name>
        <tag-class>com.foo.Foo</tag-class>
        <body-content>JSP</body-content>
        <attribute>
            <description><![CDATA[Your description here]]></description>
            <name>id</name>
            <required>false</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
        <attribute>
            <description><![CDATA[Your description here]]></description>
            <name>bar</name>
            <required>true</required>
            <deferred-value><type>java.lang.Object</type></deferred-value>
        </attribute>
</taglib>


Write the faces-config.xml Entries

For 1.1:

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE faces-config PUBLIC
  "-//Sun Microsystems, Inc.//DTD JSF Config 1.1//EN"
  "http://java.sun.com/dtd/web-facesconfig_1_1.dtd">
<faces-config>
    <component>
        <component-type>com.foo.Foo</component-type>
        <component-class>com.foo.component.Foo</component-class>
        <component-extension>
            <renderer-type>com.foo.FooRenderer</renderer-type>
        </component-extension>
    </component>
    <render-kit>
        <renderer>
            <component-family>com.foo.Foo</component-family>
            <renderer-type>com.foo.FooRenderer</renderer-type>
            <renderer-class>com.foo.render.FooRenderer</renderer-class>
        </renderer>
    </render-kit>
</faces-config>

For 1.2:

<?xml version="1.0"?>
<faces-config
	xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd"
	version="1.2">
    <component>
        <description><![CDATA[Your description here]]></description>
        <display-name>Foo</display-name>
        <component-type>com.foo.Foo</component-type>
        <component-class>com.foo.component.Foo</component-class>
        <component-extension>
            <renderer-type>com.foo.FooRenderer</renderer-type>
        </component-extension>
    </component>

    <render-kit>
        <description>Renderkit implementation for the Download component</description>
        <renderer>
            <component-family>>com.foo.Foo</component-family>
            <renderer-type>com.foo.FooRenderer</renderer-type>
            <renderer-class>com.foo.render.FooRenderer</renderer-class>
        </renderer>
    </render-kit>
</faces-config>


Write Facelets .taglib.xml File

Optional, but recommended
Name this /META-INF/<component-lib-name>.taglib.xml:

<?xml version="1.0"?>
<!DOCTYPE facelet-taglib PUBLIC
  "-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN"
  "http://java.sun.com/dtd/facelet-taglib_1_0.dtd">

<facelet-taglib>
    <namespace>http://foo.com/foo</namespace>
    <tag>
        <tag-name>foo</tag-name>
        <component>
            <component-type>com.foo.Foo</component-type>
            <renderer-type>com.foo.component.Foo</renderer-type>
        </component>
    </tag>
</facelet-taglib>


Conclusion

That, in a nutshell is what it takes to build a JSF component. I have left out, of course, many, many details in terms of “why” things should be done the way they are. I’m also cognizant that there are differing opinions on how to build a component (for instance, does every component need a separate Renderer? To date, all of my components do, but there’s no reason they have to). Once you throw Ajax into the mix, things get a bit more complicated. My hope is that this helps more people get started. Where there are omissions in these steps, my first suggestion is the excellent JSF: The Complete Reference by Chris Schalk and Ed Burns.

Popularity: 100% [?]

18 Comments to JSF Component Writing Check List

  1. December 15, 2006   #

    Thank you, this was very helpful to have everything in one place instead of searching everywhere else.

  2. December 26, 2006   #

    Hello,

    This is great information. I am confused by your variable “bar” – I don’t see where that is declared.

    Can you explain that?

    Thanks.

  3. December 26, 2006   #

    Thanks – that helped clarify things. But I still am having issues getting my component spun up.

    I am building a JAVA class that extends HtmlPanelGroup that will set properties on the children. I can’t seem to get this to load up? I don’t need a custom renderer and am only using the encodeBegin method. I am using facelets and don’t need JSP support

    Do you have a sample JSF 1.2 custom component I could download to follow all the steps?

    Thanks.

  4. December 26, 2006   #

    I think I have it working. I changed from where I was extending to simply be UIComponentBase and I think I am OK now.

    Thanks

  5. January 5, 2007   #

    I am new to JSF, and custom JSF – is J2EE (Enterprise Edition) a requirement? I would like to run on Tomcat using J2SE if possible, but when I try to extend UIComponentTag I get compiler error “javax.servlet.jsp.tagext.JspIdConsumer cannot be resolved. It is indirectly referenced from required .class files”.

  6. January 29, 2007   #

    Hello Jason,

    watz the best way to ensure that the script & link rel tags are placed in html head. My renderer helper method pushes them in the body. I wish to get those , etc in the HTML head , any ideas?

    thanks
    Satish

  7. February 6, 2007   #

    Hello! the key points u have described there are simple… can you please tell something about creating custom controls with are inherited from UICommand class and details about implementing actionListener.

    Thanks a lot!

  8. February 23, 2007   #

    Here i feel the document is Very help full for the Beginners who are involved in designing a new Components ! nice Work done by the Author

  9. February 23, 2007   #

    Nice work.

    One question tho, related to a previous one about Facelets is:

    Great, I already use Facelets, but how the heck can I actually get my JSF 1.2 based component tag to compile at all? Clearly I’m missing a jar file here, but which one? Would it be the JSP 2.1 related one? If so do I get this from Glassfish or simply grab a copy of JEE 5 from javasoft?

    Thanks

  10. February 23, 2007   #

    Actually, a bit of digging into Facelets showed be how to create a TagLibrary implementation for my components to work without writing UIComponentELTag implementations.

  11. March 20, 2007   #

    Hey Jason,

    In your response to Brian, you mention that the “javax.servlet.jsp.tagext.JspIdConsumer cannot be resolved.” issue due to the dependency on JSP 2.1 would be resolved by using Facelets…
    Even with facelets the error still occurs. Is there a simple way to modify the component to be independent of JSP? If im just using facelets that is.

  12. June 6, 2007   #

    HI,

    i am trying to write a component for file Upload. The code developed complies with all the steps mentioned here with. But as i understand the setPorperties(..) of xxxTag sub class is not being called. Can you please help me with some check points etc.

    The diffarent methods of UploadTag, Upload, UploadRenderer are getting called except the UploadTag.setProperties(..) which is being called when the page is getting loaded.

    Please help me with what ever information you have.

    Regards,
    Ravi C