JSF2 Facelet Tag Handlers

Posted by: Ed Burns on 10/15/2009

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 at WEB-INF/classes/META-INF/custom.taglib.xml.

callstack to custom tag handler constructor
  1. <?xml version="1.0" encoding="UTF-8"?>
  2.  
  3. <facelet-taglib 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.     <namespace>http://mojarra.dev.java.net/custom</namespace>
  8.     <tag>
  9.         <tag-name>custom1</tag-name>
  10.         <component>
  11.             <component-type>javax.faces.Input</component-type>
  12.             <renderer-type>javax.faces.Text</renderer-type>
  13.             <handler-class>com.sun.faces.facelets.custom.CustomComponentHandler1</handler-class>
  14.         </component>
  15.     </tag>
  16.    
  17.  
  18.  
  19. </facelet-taglib>
  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 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 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 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() and createMetaRuleset(). The stack trace for apply() is shown at left, while the one for createMetaRuleset() is available here.

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. <tag>
  2.     <tag-name>validator</tag-name>
  3.     <validator>
  4.         <validator-id>javax.faces.Required</validator-id>
  5.         <handler-class>com.sun.faces.facelets.custom.CustomValidatorHandler</handler-class>
  6.     </validator>
  7. </tag>
  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 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:


  • Currently 3.7/5
  • 1
  • 2
  • 3
  • 4
  • 5
3.7 rating out of 3 votes

About Ed Burns

Ed Burns

Ed Burns is currently a Senior Staff Engineer at Sun Microsystems, Inc. At Sun, Ed leads a team of web experts from across the industry in developing JavaServerâ„¢ Faces Technology through the Java Community Process and in open source. His areas of professional interests include web application frameworks, AJAX, reducing complexity, test driven development, requirements gathering, and computer supported collaborative work. Before working on JavaServer Faces, Ed worked on a wide variety of client and server side web technologies since 1994, including NCSA Mosaic, Mozilla, the Sun Java Plugin, Jakarta Tomcat, the Cosmo Create HTML authoring tool, and the web transport layer in the Irix operating system from Silicon Graphics.

Ed has a Bachelor of Computer Science degree from the University of Illinois at Urbana Champaign. While at UIUC, Ed took a minor in Germanic Studies and worked for IBM in the co-op program, where he first aquired a fondness for computer history by working on System 370 Office Software.

Ed has presented many times at Sun's JavaOne conference, given a keynote address at the W-JAX conference in Munich, Germany, and also has spoken at numerous Java User Group meetings. Further information and blogs may be found at http://purl.oclc.org/NET/edburns/.

More About Ed »

NFJS, the Magazine

August Issue Now Available
  • Google Your Persistent Domain Model
    by John Griffin
  • Get Cooking in the Cloud with Chef, Part 2
    by Michael Nygard
  • Making Java Bearable with Guava
    by Daniel Hinojosa
  • HTML 5 Update
    by Brian Sletten
Learn More »