Skip to main content

JSF2 Facelet Tag Handlers

Posted by edburns on October 15, 2009 at 12:47 PM PDT

The following topics and more will be covered in detail in my
upcoming book with Neil Griffin, href="http://bit.ly/edburnsjsf2">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 href="https://jsftemplating.dev.java.net/">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 href="https://mojarra.dev.java.net/svn/mojarra/trunk/jsf-demo/jsf2-facelet-custom-tag">Mojarra
svn repo), the file lives at
WEB-INF/classes/META-INF/custom.taglib.xml.

src="http://mediacast.sun.com/users/edburns00/media/01_ComponentHandlerCtor.png" alt="callstack to custom tag handler constructor" />

  1. version="1.0" encoding="UTF-8"?>
  2.  
  3. xmlns="http://java.sun.com/xml/ns/javaee"
  4.              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  5.              xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
    http://java.sun.com/xml/ns/javaee/web-facelettaglibrary_2_0.xsd"
  6.              version="2.0">
  7.     >http://mojarra.dev.java.net/custom>
  8.     >
  9.         >custom1>
  10.         >
  11.             >javax.faces.Input>
  12.             >javax.faces.Text>
  13.             >com.sun.faces.facelets.custom.CustomComponentHandler1>
  14.         >
  15.     >
  16.    
  17.  
  18.  
  19. >
  20.  

The java code for this class is shown next. At right, you see the
stack trace for a breakpoint set on line 8.

  1. package com.sun.faces.facelets.custom;
  2.  
  3. import javax.faces.view.facelets.ComponentConfig;
  4. import javax.faces.view.facelets.ComponentHandler;
  5.  
  6. public class CustomComponentHandler1 extends ComponentHandler {
  7.     public CustomComponentHandler1(ComponentConfig config) {
  8.         super(config);
  9.     }
  10. }
  11.  

The preceding code does nothing. But it's a start. Particurlarly
useful is that href="http://java.sun.com/javaee/javaserverfaces/2.0/docs/api/javax/faces/view/facelets/ComponentConfig.html">ComponentConfig
argument. This gives you access to a whole bunch of useful stuff. Note
also that extends href="http://java.sun.com/javaee/javaserverfaces/2.0/docs/api/javax/faces/view/facelets/ComponentHandler.html">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 the onComponentCreated()
method, as shown in the next class.

  1. package com.sun.faces.facelets.custom;
  2.  
  3. import javax.faces.component.UIComponent;
  4. import javax.faces.view.facelets.ComponentConfig;
  5. import javax.faces.view.facelets.ComponentHandler;
  6. import javax.faces.view.facelets.FaceletContext;
  7.  
  8. public class CustomComponentHandler2 extends ComponentHandler {
  9.  
  10.     public CustomComponentHandler2(ComponentConfig config) {
  11.         super(config);
  12.     }
  13.  
  14.     @Override
  15.     public void onComponentCreated(FaceletContext ctx, UIComponent c, UIComponent parent) {
  16.         super.onComponentCreated(ctx, c, parent);
  17.     }
  18.  
  19.     @Override
  20.     public void onComponentPopulated(FaceletContext ctx, UIComponent c, UIComponent parent) {
  21.         super.onComponentPopulated(ctx, c, parent);
  22.     }
  23.  
  24. }
  25.  

The stack traces for onComponentCreated and
onComponentPopulated are href="http://mediacast.sun.com/users/edburns00/media/02_onComponentCreated.png">here
and href="http://mediacast.sun.com/users/edburns00/media/03_onComponentPopulated.png">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() and
createMetaRuleset(). The stack trace for
apply() is shown at left, while the one for
createMetaRuleset() is available href="http://mediacast.sun.com/users/edburns00/media/05_createMetaRuleSet.png">here.

src="http://mediacast.sun.com/users/edburns00/media/04_apply.png" alt="callstack to custom tag handler constructor" />

  1. package com.sun.faces.facelets.custom;
  2.  
  3. import java.io.IOException;
  4. import javax.faces.component.UIComponent;
  5. import javax.faces.view.facelets.ComponentConfig;
  6. import javax.faces.view.facelets.ComponentHandler;
  7. import javax.faces.view.facelets.FaceletContext;
  8. import javax.faces.view.facelets.MetaRuleset;
  9. import javax.faces.view.facelets.TagHandlerDelegate;
  10.  
  11. public class CustomComponentHandler3 extends ComponentHandler {
  12.  
  13.     public CustomComponentHandler3(ComponentConfig config) {
  14.         super(config);
  15.     }
  16.  
  17.     @Override
  18.     public void onComponentCreated(FaceletContext ctx, UIComponent c, UIComponent parent) {
  19.         super.onComponentCreated(ctx, c, parent);
  20.     }
  21.  
  22.     @Override
  23.     public void onComponentPopulated(FaceletContext ctx, UIComponent c, UIComponent parent) {
  24.         super.onComponentPopulated(ctx, c, parent);
  25.     }
  26.  
  27.     @Override
  28.     protected TagHandlerDelegate getTagHandlerDelegate() {
  29.         final TagHandlerDelegate parent = super.getTagHandlerDelegate();
  30.         TagHandlerDelegate result = new TagHandlerDelegate() {
  31.  
  32.             @Override
  33.             public MetaRuleset createMetaRuleset(Class type) {
  34.                 return parent.createMetaRuleset(type);
  35.             }
  36.  
  37.             @Override
  38.             public void apply(FaceletContext ctx, UIComponent comp) throws IOException {
  39.                 parent.apply(ctx, comp);
  40.             }
  41.         };
  42.         return result;
  43.     }
  44. }
  45.  

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.

  1. >
  2.     >validator>
  3.     >
  4.         >javax.faces.Required>
  5.         >com.sun.faces.facelets.custom.CustomValidatorHandler>
  6.     >
  7. >
  8.  

Finally, here's the code for this custom validator.

  1. package com.sun.faces.facelets.custom;
  2.  
  3. import java.io.IOException;
  4. import javax.faces.component.UIComponent;
  5. import javax.faces.view.facelets.FaceletContext;
  6. import javax.faces.view.facelets.MetaRuleset;
  7. import javax.faces.view.facelets.TagHandlerDelegate;
  8. import javax.faces.view.facelets.ValidatorConfig;
  9. import javax.faces.view.facelets.ValidatorHandler;
  10.  
  11. public class CustomValidatorHandler extends ValidatorHandler {
  12.  
  13.     public CustomValidatorHandler(ValidatorConfig config) {
  14.         super(config);
  15.     }
  16.  
  17.     @Override
  18.     protected TagHandlerDelegate getTagHandlerDelegate() {
  19.         final TagHandlerDelegate parent = super.getTagHandlerDelegate();
  20.         TagHandlerDelegate result = new TagHandlerDelegate() {
  21.  
  22.             @Override
  23.             public MetaRuleset createMetaRuleset(Class type) {
  24.                 return parent.createMetaRuleset(type);
  25.             }
  26.  
  27.             @Override
  28.             public void apply(FaceletContext ctx, UIComponent comp) throws IOException {
  29.                 parent.apply(ctx, comp);
  30.             }
  31.         };
  32.         return result;
  33.     }
  34. }
  35.  

The callstacks for apply() and
createMetaRuleset() are href="http://mediacast.sun.com/users/edburns00/media/06_validator_apply.png">here
and href="http://mediacast.sun.com/users/edburns00/media/07_validator_createMetaRuleset.png">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:

Comments

Writing a simple CustomComponent with ComponentHandler

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

Writing a simple CustomComponent with ComponentHandler

Notice that the first file (WEB-INF/classes/META-INF/custom.taglib.xml) has to be declared in web.xml, e.g.

<context-param>
<param-name>javax.faces.FACELETS_LIBRARIES</param-name>
<param-value>/WEB-INF/classes/META-INF/custom.taglib.xml</param-value>
</context-param>


Best regards, Panu

CustomCompositeComponentHandler

Everything is cool except the fact that the CompositeComponentTagHandler class has a private constructor and cannot be extended easily.

Your book

I pre-ordered your book, really looking forward to its release! PS: Please make sure JSF 2.1 includes a radio button component that lets me place the buttons in a group arbitrarily on the page