February 2011

Thu Feb 10 21:55:42 PST 2011

Writing Your Own Facelets Components

Introduction

I am writing here of components written in the same way as those shipped with JSF are, in Java. There’s plenty out there on how to do the much simpler process of writing so-called composite components strictly in HTML and XML. Composite components have their limitations, however, so they are not complete substitutes for doing things the “hard way.” (Which really isn’t all that hard once one figures it out.)

This is something that there is virtually no decent documentation on anywhere, so I figured I’d write it here.

By “decent documentation,” I mean any series of instructions that is both reasonably complete and correct. By “anywhere” I mean literally anywhere, so far as I have been able to tell. Even in supposedly up-to-date and comprehensive books (which for Java Server Facelets are very rare) end up being less than comprehensive and have significant inaccuracies; there is no way that following the instructions therein will result in successfully implementing even the simplest component. It’s the bane of open-source software: coding is fun, but documenting things isn’t so much fun. So lots of coding gets done, not much documentation, and one ends up with huge complex software systems and only the spottiest of documentation.

But I digress. Onward.

Files

First, there are a minimum of four files you must modify or create in order to create a custom component:

*taglib.xml
The file that describes your tag(s) to JSF. The asterisk can be replaced by any sequence of characters, but the file itself should end in taglib.xml. It’s customary to use hyphens or dots to set off the suffix, e.g. gunk.taglib.xml or gunk-taglib.xml.
faces-config.xml
This ties the component types referenced in the taglib file to Java classes which implement them.
web.xml
This is where JSF is made aware of the taglib file.
*.java
The code that implements your tag.

The web.xml and faces-config.xml files live in the WEB-INF directory. It is traditional (but not required) to put *taglib.xml there as well.

For each such file, I will provide an example. Taken together, they will result in a web app which defines and uses a very simple tag which simply outputs a bit of fixed text inside a <span> element.

*.java

(A sample *.java file may be found here.)

This must be a subclass of javax.faces.component.UIComponent; typically this will be a subclass of javax.faces.component.UIInput (your component will render HTML that accepts input from the end user’s browser within a form) or more commonly javax.faces.component.UIOutput (your component does not accept any user input).

Output-only tags are by far the most common, so that’s what I’m covering here for now. Not surprisingly, the core of defining a tag is telling JSF what HTML to render in its output when it encounters your tag in the input file. The class library documentation does not explicitly make it clear in any one place, but there are five key methods you need to be concerned with:

A constructor
Actually, I’m not 100% sure you need to bother with this; it might be acceptably defined for you by a parent class. But I ended up doing so, based on some examples I found. The trivial constructor I copied should serve most simple tags just fine.
encodeBegin
This method is responsible for outputting whatever HTML is needed when the opening tag for your component is encountered. If you are writing a component that uses no closing tag, you can put all of your rendering code in this method (which is what my example does).
encodeChildren
If your component does involve a starting and an ending tag, and you want to do something other than the standard rendering for what appears between the tags, this method needs to be defined. (See also the next item in this list.)
getRendersChildren
If this component is responsible for rendering its children, you must define this method to return true. If not, define it to return false. Note that this implies you should always define this to return true if you’re defining an encodeChildren method. Note also that a parent class of UIOutput defines a standard encodeChildren method, so it is meaningful to have this method return true even if you are not supplying your own encodeChildren method.
encodeEnd
This method is responsible for outputting whatever HTML is needed when the closing tag for your component is encountered. If your component has no closing tag (i.e. invoked like <prefix:tag attribute="value" />), then this method will be called by JSF immediately after encodeBegin returns.

Note that it is not always necessary to (re)define all five methods. For example, if your tag is not going to ever have enclosed content, or separate beginning or ending tags, then you need not implement encodeChildren or encodeEnd; you can put all your rendering into encodeBegin and be done with it.

The easiest (and safest) way to generate HTML in the rendering methods is to use the javax.faces.context.ResponseWriter object returned by the getResponseWriter() method of the passed javax.faces.context.FacesContext object. (It is also possible to use the stream returned by getResponseStream(), but this is not so safe or easy as using a ResponseWriter, because the latter takes pains to ensure that the generated HTML is valid.)

Note that you do not need to explicitly do anything to handle any attributes for your tag. JSF will automatically collect these for you and make them available in a Map<String,Object> returned by the getAttributes() method, which is already defined for you by a parent class of UIOutput.

The bad news about attributes is that there doesn’t seem to be any graceful way to make them mandatory or otherwise enforce restrictions about them. The <attribute> tag should allow one to do this in the taglib file, but it doesn’t seem to be honored very well. At least, it failed to trigger any errors when I attempted to use this feature to make an attribute mandatory (and then deliberately omitted it in a Facelets file) in Apache Myfaces.

Note that your tag will automatically support some attributes common to all Facelets tags, such as the rendered attribute, which controls if it renders as output at all, because parent classes of UIOutput already support such attributes.

*taglib.xml

(A sample *taglib.xml file may be found here.)

This file defines one or more Facelets tags by describing the name space for one or more tags and the component type for each tag. (See next section for how the component type is associated with a class.) Note that the arbitrary prefix represented by an asterisk in this name need not match the file name represented by the asterisk in *.java. Although the example file describes only a single tag, it is possible for a taglib file to describe more than one tag.

Although this file can reside in any web application directory, it is customary to place taglib files in the WEB-INF directory.

faces-config.xml

(A sample faces-config.xml file may be found here.)

The <component> entries of this file associate the component types mentioned in the taglib file with Java classes, which must appear in a jar file under WEB-INF/lib or a class file under WEB-INF/classes.

web.xml

(A sample web.xml file may be found here.)

The facelets.LIBRARIES context parameter in this file defines a semicolon-delimited list of taglib files. If your taglib file(s) are not mentioned here, JSF will be unaware of your tags and they will not render properly.

The name of this context parameter is not well-standardized and may be different; facelets.LIBRARIES appears to be the most common value (at least, it’s what both Mojarra and Apache Myfaces use). If that does not work, try javax.faces.FACELETS_LIBRARIES.

Using Your Tag

There’s at least one more file in the picture, of course, and that’s the Facelets file(s) which contain your tag. If you’ve done everything else in this guide correctly, at this point you’ve managed to create a tag which JSF recognizes just like any other Facelets tag. In other words, to use your tag, all you need to do is include an xmlns attribute in the <html> tag to associate a name with the URL name space of your tag, and then use your tag, prefixed with that name.

A sample which uses the tag I’ve defined may be found here. Since the tags in that will actually render in your browser (and moreover, if this blog is migrated to a Facelets-aware web server, the Facelets tag will render to HTML), here is a version as a plain-text file.

Sun Feb 27 21:04:14 PST 2011

Radio from an Alternate Reality Zone

At long last (it's taken over a year since I first thought of sharing some of what can be heard on the shortwave bands via the Internet):

Monthly Index for 2011 | Index of Years


Last updated: Tue Sep 13 16:14:11 PDT 2011