JSF2 Facelet Tag Handlers Blog
The following topics and more will be covered in detail in my upcoming book with Neil Griffin, JavaServer Faces 2.0: The Complete Reference. Please enjoy this early access content!
One challenging aspect of designing JSF 2.0 was how to standardize Facelets. We wanted to standardize only the minimum amount that would still allow developers get the job done. Initially, we did not include binary custom tag handlers in the standard because most users of Facelets were simply using it to declare pages of existing UI components. Andy Schwartz and others advocated for the inclusion of a custom tag handler feature in the standard but I didn't want to just standardize what Jacob had initially done.
While Jacob's initial work for custom tag handlers was certainly effective, EG discussions with Ken Paulsen, creator of the JSFTemplating View Declaration Language led the EG to conclude that the standardization work for some of Facelets would best be left to JSF 2.1. In particular, Ken and EG member Imre Oßwald came up with something they called View Abstract Syntax Tree that would handle deeper aspects of Faces templating that they felt solved some of the flaws in the implementation of Facelets. Rather than overspecify, we came up with a simpler solution that still enables the most common usecases for custom tag handlers.
First,
let's take a look at the custom.taglib.xml file. The manner and location
for this file is exactly the same as before. In this example,
(available in the Mojarra svn repo), the file lives atWEB-INF/classes/META-INF/custom.taglib.xml
.
- <?xml version="1.0" encoding="UTF-8"?>
- <facelet-taglibxmlns="http://java.sun.com/xml/ns/javaee"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- version="2.0">
- <tag>
- <tag-name>custom1</tag-name>
- <component>
- <component-type>javax.faces.Input</component-type>
- <renderer-type>javax.faces.Text</renderer-type>
- <handler-class>com.sun.faces.facelets.custom.CustomComponentHandler1</handler-class>
- </component>
- </tag>
- </facelet-taglib>
The java code for this class is shown next. At right, you see the stack trace for a breakpoint set on line 8.
- packagecom.sun.faces.facelets.custom;
- importjavax.faces.view.facelets.ComponentConfig;
- importjavax.faces.view.facelets.ComponentHandler;
- publicclassCustomComponentHandler1 extendsComponentHandler {
- publicCustomComponentHandler1(ComponentConfig config) {
- super(config);
- }
- }
The preceding code does nothing. But it's a start. Particurlarly useful is that ComponentConfig argument. This gives you access to a whole bunch of useful stuff. Note also that extends javax.faces.view.facelets.ComponentHandler. There are handlers for all the kinds of tags in JSF: converter, validator, component, and behavior.
In
many cases, the only reason people were doing custom Facelet tags was
so they could be notified when the component is built. To get this in
JSF 2.0, just override theonComponentCreated()
method, as shown in the next class.
- packagecom.sun.faces.facelets.custom;
- importjavax.faces.component.UIComponent;
- importjavax.faces.view.facelets.ComponentConfig;
- importjavax.faces.view.facelets.ComponentHandler;
- importjavax.faces.view.facelets.FaceletContext;
- publicclassCustomComponentHandler2 extendsComponentHandler {
- publicCustomComponentHandler2(ComponentConfig config) {
- super(config);
- }
- @Override
- public voidonComponentCreated(FaceletContext ctx, UIComponent c, UIComponent parent) {
- super.onComponentCreated(ctx, c, parent);
- }
- @Override
- public voidonComponentPopulated(FaceletContext ctx, UIComponent c, UIComponent parent) {
- super.onComponentPopulated(ctx, c, parent);
- }
- }
The stack traces for onComponentCreated
andonComponentPopulated
are here and here, respectively.
If you really must have access to the apply
method, and indeed override it, you can still do so. However, to
preserve a clean separation between interface and implementation there
is a little extra syntatic sugar you must endure. Sorry. Here's the code
for a custom tag handler that overrides apply()
andcreateMetaRuleset()
. The stack trace forapply()
is shown at left, while the one forcreateMetaRuleset()
is available here.
- packagecom.sun.faces.facelets.custom;
- importjava.io.IOException;
- importjavax.faces.component.UIComponent;
- importjavax.faces.view.facelets.ComponentConfig;
- importjavax.faces.view.facelets.ComponentHandler;
- importjavax.faces.view.facelets.FaceletContext;
- importjavax.faces.view.facelets.MetaRuleset;
- importjavax.faces.view.facelets.TagHandlerDelegate;
- publicclassCustomComponentHandler3 extendsComponentHandler {
- publicCustomComponentHandler3(ComponentConfig config) {
- super(config);
- }
- @Override
- public voidonComponentCreated(FaceletContext ctx, UIComponent c, UIComponent parent) {
- super.onComponentCreated(ctx, c, parent);
- }
- @Override
- public voidonComponentPopulated(FaceletContext ctx, UIComponent c, UIComponent parent) {
- super.onComponentPopulated(ctx, c, parent);
- }
- @Override
- protectedTagHandlerDelegate getTagHandlerDelegate(){
- finalTagHandlerDelegate parent =super.getTagHandlerDelegate();
- TagHandlerDelegate result = newTagHandlerDelegate() {
- @Override
- public MetaRuleset createMetaRuleset(Class type) {
- returnparent.createMetaRuleset(type);
- }
- @Override
- parent.apply(ctx, comp);
- }
- };
- return result;
- }
- }
As
mentioned previously, there are handlers for all the different kinds of
JSF artifacts that may appear in a Facelet page. The syntax in the .taglib.xml
file is rather similar for all of them, but the one for the validator is shown next.
- <tag>
- <tag-name>validator</tag-name>
- <validator>
- <validator-id>javax.faces.Required</validator-id>
- <handler-class>com.sun.faces.facelets.custom.CustomValidatorHandler</handler-class>
- </validator>
- </tag>
Finally, here's the code for this custom validator.
- packagecom.sun.faces.facelets.custom;
- importjava.io.IOException;
- importjavax.faces.component.UIComponent;
- importjavax.faces.view.facelets.FaceletContext;
- importjavax.faces.view.facelets.MetaRuleset;
- importjavax.faces.view.facelets.TagHandlerDelegate;
- importjavax.faces.view.facelets.ValidatorConfig;
- importjavax.faces.view.facelets.ValidatorHandler;
- publicclassCustomValidatorHandler extendsValidatorHandler {
- publicCustomValidatorHandler(ValidatorConfig config) {
- super(config);
- }
- @Override
- protectedTagHandlerDelegate getTagHandlerDelegate(){
- finalTagHandlerDelegate parent =super.getTagHandlerDelegate();
- TagHandlerDelegate result = newTagHandlerDelegate() {
- @Override
- public MetaRuleset createMetaRuleset(Class type) {
- returnparent.createMetaRuleset(type);
- }
- @Override
- parent.apply(ctx, comp);
- }
- };
- return result;
- }
- }
The callstacks for apply()
andcreateMetaRuleset()
are here and here
80% of the time, there is no need for any custom tag handlers. If you're doing your job right, the logic should be in the UIComponent, Validator, Converter, etc. However, for that extra 20% of the time, you can use the above practices to get the job done.
Technorati Tags: edburns
4 Comments
-
Everything is cool except the fact that the CompositeComponentTagHandler class has a private constructor and cannot be extended easily. //
-
Hello, While using JSF2, I am writing a CustomComponent with an associated ComponentHandler. I couldnt get it to work. Problem is its not recognizing the URI of the taglib. So, I wrote a simple, very simple CustomComponent with a ComponentHandler. Still the same problem. I deploy the app in tomcat 6 and I am using Java 6. I have been on this issue for almost 3 weeks now. I also emailed about it to you. Please help if you can. Regards, Tahir //
rdelaplante Oct 16, 2009 12:08 AM