Tuesday, September 18, 2012

JSF custom components: the role of componentType and componentFamily properties

The component-type property is very important element of definition of each JSF component. When you provide a custom component, you need to produce a tag library descriptor file that specifies component-type value. Any error in component-type value leads to fatal errors. But, what actually is the role of this element ?
<!--e x a m p l e   o f   t a g   l i b r a r y   d e s c r i p t o r-->

<facelet-taglib ...>
   <namespace>http://my-java-planet.pl</namespace>

   <tag>

      <tag-name>customTable</tag-name>

      <component>

         <component-type>pl.my-java-planet.customTable</component-type>        

      </component>      

   </tag> 

</facelet-taglib> 

As JSF specification says: component-type attribute specifies the logical name for a UI component. The
logical name is used to register the UI component with JSF so that it can later be accessed by the same logical name. In other words the component type is an identifier for the component class that must get mapped to the actual class. While not a property of UIComponent , the component-type property is used by tag library to force instatiating of concrete component instance.

To create an instance of JSF UI Component a tag library uses the Application object which resides "between" JSP (facelets) page and a sublass of UIComponent. Technically the createComponent() method of this object is used as shown below:

Application.createComponent("componen-type-value");

When there isn't possible to find appropriate component-type value, an exception is raised.
You can set the compponent-type ID with an annotation of the component class:

@FacesComponent("pl.my-java-planet.customTable") 
public class UICustomTable extends UIComponent

@FacesComponent annotation is an alternative for the following XML in the  faces-config.xml  file::

<faces-config version="1.2" 
    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">

  <component>

    <component-type>pl.my-java-planet.customTable</component-type>

    <component-class>pl.myjavaplanet.jsf.components.UICustomTable</component-class>

  </component>

</faces-config>

The component-family is the next mysterious attribute which defines behaviour of each JSF component. 
If your custom component class delegates rendering, it needs to override the getFamily method of UIComponent to return the identiier of a component family, which is used to refer to a component or set of components that can be rendered by a renderer or set of renderers.  

Definition says: The  component-family element specifies the component family that a renderer will be linked to. Component families are used to group families of components together and are used along with renderer types to uniquely identify a specific rendering. But ... what does this mean?

Component-family attribute is used to link components (one or more) with renderer. Cause one renderer can be in theory used for more than one component, the logical construct of family is used. To bind component-family with renderer you can use faces-config.xml as shown below:


<faces-config version="1.2" 
    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">
    <render-kit>
        <renderer> 


             <component-family>
                 pl.my-java-planet.CustomTableFamily
             </component-family> 

             <renderer-type>pl.my-java-planet.CustomTableRenderer</renderer-type>
             <renderer-class>
                  pl.myjavaplanet.UICustomTableRenderer
             </renderer-class> 

         </renderer>
    </render-kit>

</faces-config>

But. How we should "inform" the Component to which family belongs? To bind component-family with Component, the getFamily method of custom component class should be used. Value returned by getFamily method must match with component-family value from renderer definition:

public String getFamily() {

    return ( "pl.my-java-planet.CustomTableFamily");

}

Value returned by getFamily method must match with component-family in faces-config.xml

1 comment:

  1. Thanks Mariusz, This article is very helpful for understanding these two messy variables. Can you please provide a complete step by step tutorial of creating JSF custom component.Thanks in advance

    ReplyDelete