Thursday 27 January 2011

Custom components

This post gives an overview of the functionality if JSF 2.0 for creating custom and composite components.

JSF 1.x

Creating a custom component for JSF 1.x wasn't that easy. Well, the
most 'difficult' task was that you had to create so many files and that
they should all have the correct 'anchor' names so that they are linked
correctly. The content of those files itself wasn't too difficult. But
creating so many files was a burden for most of us.

To be able to make a comparison between the way of working in JSF
2.0, i briefly give an overview of those files required for JSF
1.x.

  • Component java class: It is the instance of the object which is
    stored in the JSF view tree. It contain the data of your
    component.
  • Component renderer java class: It is a good practice to have the
    decoding and encoding functionality (transfer data from post requests
    and create HTML code) of your custom component in a seperate class.
    That way it is possible to define another render kit and output
    something else (like WML).
  • Component Tag java class: This class will populate the component
    with the attributes that you specify in the view files. It thus
    converts the value of the attributes of your new tag in the JSP files
    to the component instance.
  • The TLD file : Here you specify the name of your new tag and
    which attributes are available.

The last thing that you had to do then was putting the correct
XML snippet in the faces-config file, so that it can be used by
JSF.

With facelets, it became mush easier to create some custom
components, but it had also the capability to create for instance a custom
validator


JSF 2.0

With JSF 2.0, there was not only the inclusion of facelets into the
standard. Some new concepts are also introduced that build further on the
principles of facelets. One of these is the possiblity to create composite
components in a declarative way that also allows to define which
attributes are required. That is one of the 3 kind of custom components
that I would like to discuss today in this text. The other one is the
creation of a custom component in the JSF 2.0 way. And you will see, it
requires less files then described above. The last example describes the
usage of a TagHandler and how you could use it.


Composite components

With JSF 1.x you have probably also multiple times the situation
where you had a snippet in your JSP file, where only some of the
attributes where different. Most of the time the only thing which is
different is some id of the component and the value expression that
binds the value to the backing bean. A snippet like the following can be
found in any project

<h:outputLabel for="fieldId" value="field Label :"/>
<h:inputText id="fieldId" value="#{bean.property}" label="field Label" />

And some variations could be found where we have some
additional attributes set on the input field.

Snippets like that can now be reused much easier by defining a
composite component. And the best thing of all is that it can be in a
declarative way, so no Java coding, not even an XML file or TLD. We have
even some control over the usage of the attributes. Let me give an
example of the composite component fieldwithLabel, I have now in all my
JSF projects:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:c="http://java.sun.com/jsp/jstl/core"
      xmlns:composite="http://java.sun.com/jsf/composite">
<composite:interface name="fieldwithLabel">
   <composite:attribute name="fieldId" required="true"/>
   <composite:attribute name="field" required="true"/>
   <composite:attribute name="fieldLabel" required="true"/>
   <composite:attribute name="passwordField" required="false" default="false"/>
   <composite:attribute name="size" type="java.lang.Integer" required="false" default="20"/>
</composite:interface>
<composite:implementation>
   <tr>
      <td style="text-align: right;">
         <h:outputLabel for="#{cc.attrs.fieldId}" value="#{cc.attrs.fieldLabel} :"/>
      </td>
      <td>
         <c:if test="#{not cc.attrs.passwordField}">
            <h:inputText id="#{cc.attrs.fieldId}" value="#{cc.attrs.field}" 
label="#{cc.attrs.fieldLabel}" rendered="#{not cc.attrs.passwordField}" size="#{cc.attrs.size}"/>
         </c:if>
         <c:if test="#{cc.attrs.passwordField}">
            <h:inputSecret id="#{cc.attrs.fieldId}" value="#{cc.attrs.field}" 
label="#{cc.attrs.fieldLabel}" rendered="#{cc.attrs.passwordField}" size="#{cc.attrs.size}"/>
         </c:if>
      </td>
   </tr>
</composite:implementation>
</html> 

Let me explain the major parts of this file
before I explain you how you can use it. First of all you can
distinguish 2 major parts, the interface and the implementation. And
they resemble very much the concepts form the Java language. In the
interface you specify which attributes can be supplied to the composite
component. And you don't just specify the name but also the type, if a
value is required and some default value when you don't specify
one.

In the implementation section, you define the HTML snippet (with
JSF 2.0, XHTML is the default language for describing the view and no
longer JSP) that must be used. And in that 'template' part, you can
refer to the attributes that you have defined in de interface section.
You can even define some logic, as in the example. Based on the
attribute passwordField we can render an inputText or an inputSecret
component.

The declarative way of defining the composite component, means
that a few conventions and rules should be followed before it will work.
First, the above snippet must be placed in a file which is located in a
subdirectory of webapp/resources. An example makes it probably clear.
This snippet is found in the file fieldWithLabel.xhtml located in
webapp/resources/inputField. And the name of the file and the
subdirectory are important. The following example shows you how you can
use the composite component.

<html ...
    xmlns:field="http://java.sun.com/jsf/composite/inputField"
 ...>
<table>
   <field:fieldWithLabel fieldId="username" fieldLabel="#{msg['general.label.username']}"
size="30" field="#{credentials.userName}" />

</table>

The name of the subdirectory must be the last
part in the namespace declaration, in our case it is inputField. The
alias, here field, we are free to choose. Then, we can access the
composite component by using name of the file where we stored it as
element from that namespace, in our case fieldWithLabel. The attributes
are the ones we have defined in te interface section. When the JSF view
is build, the line

<field:fieldWithLabel fieldId="username" 
fieldLabel="#{msg['general.label.username']}" size="30" field="#{credentials.userName}" />

is
replaced by

<tr>
   <td style="text-align: right;">
      <h:outputLabel for="username" value="#{msg['general.label.username']} :"
   </td>
   <td>
      <h:inputText id="username" value="#{credentials.userName}" label="#{msg['general.label.username']}"
renderer="true" size="30"/>
   </td>
</tr>

which is much less readable, has more duplication and more places
where a typo error could make that your screen is not behaving the way
you intended it.


Custom components

The above described way of creating composite components allows
you to simplify the view files and improve your productivity. But
sometimes, there is no component available for the functionality you
need. Not so long ago, we had the need to show a JFreeChart image on the
screen. In the backing bean, all the code was in place that generates
the image, in the PNG codec, and we had a unique code so that we could
identify the image. so basicly, if we had something like this

<img:chart value="unique code to graph"/>

we would be able to show it on the screen.

Also this kind of requirements can now be easily developed. First
we need the UI component itself.

@FacesComponent(value = "graph")
public class Graph extends UIComponentBase {

    @Override
    public String getFamily() {
        return "customComponent";
    }

    @Override
    public String getRendererType() {
        return "customComponent.Graph"; 
    }
}

The only thing we need to specify are the identifiers so that the
renderer can be found which is responsible for generating the HTML code.
This is because we need only one attribute, value, and that is by
default supported by the UIComponentBase class. Other attrbiutes that
are common to all components like id and rendered, are also available if
we would like it.

The renderer needs to create the HTML code which is sent to the
browser, the main elements in this renderer are shown in the next code
snippet.

public class GraphRenderer extends Renderer {

   @Override
   public void encodeEnd(final FacesContext context, final UIComponent component) throws IOException {

      ResponseWriter writer = context.getResponseWriter();

      String value = (String) component.getValueExpression("value").getValue(context.getELContext());
      if (value != null) {

         String clientId = component.getClientId(context);
         String imageSrc = getImageSrc(context, value);

         writer.startElement("img", component);
         writer.writeAttribute("id", clientId, "id");
         writer.writeAttribute("src", imageSrc, null);

         renderPassThruAttributes(context, component, HTML.IMG_ATTRS);

         writer.endElement("img");
      }
   }    

   private String getImageSrc(final FacesContext context, final String value) {

      StringBuilder builder = new StringBuilder(getActionURL(context));
      if (builder.indexOf("?") == -1) {
          builder.append('?');
      } else {
          builder.append('&');
      }
      builder.append(GraphContentStreamer.DYNAMIC_CONTENT_PARAM).append("=").append(value);
      return builder.toString();
   }   
}

The encodeEnd method gets a reference to the component it needs to
render. And on that component, we can ask the value of the attribute
"value" (see component.getValueExpression("value").getValue()). With
that value, we create an URL which is tracked back into our application
(see getActionURL() in the method getImageSrc). In the URL, we have a
specific URL parameter (here specified by the constant
GraphContentStreamer.DYNAMIC_CONTENT_PARAM) so that we can identify a
request for the image by a JSF Phase listener. That listener can then
supply the contents of the image.

The above code is all that you need from the Java side. There are
just 2 configuration entries to make. The first one is in the
facesconfig file. We need to define the renderer which will be used in
conjunction with our custom components. This can be done as
follows:

<render-kit>       
      <renderer>
         <component-family>customComponent</component-family>
         <renderer-type>customComponent.Graph</renderer-type>
         <renderer-class>be.rubus.web.jsf.view.renderer.GraphRenderer</renderer-class>
      </renderer>
   </render-kit>

The first 2 entries are the ones that
are defined in the component itself (and now the method name shows
clearly what value needs to be placed in the faces-config file) and the
last one is the fully qualified class name of the renderer.

The other configuration entry is that facelets (the name is still
widely used) recognizes the component in a XHTML file. Therefor we need
a file that ends on -taglib.xml (I mostly use the name
custom-taglib.xml) in the WEB-INF folder.

<facelet-taglib 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-facelettaglibrary_2_0.xsd"
                version="2.0">
    <namespace>http://www.rubus.be/demo/jsf/custom</namespace>
    <tag>
        <tag-name>chart</tag-name>
        <component>
            <component-type>graph</component-type>
        </component>
    </tag>

</facelet-taglib>

The import elements here are the namespace, that is the namespace
we need to use in the xhtml file, the tag-name, chart in this example,
it is the element we need to use to reference our custom component in
the xhtml file (we used <img:chart ...). The last piece that we can
see is that we link the element chart, with a component that is defined
with the name graph. That name is defined as the attribute of the
annotation @JSFComponent in the Java component class code.

I have to admit, the amount of lines that you have to write is
much more then is the case for a composite component. Also, there are
more points that you have to take into account, like constants that you
define in the Java class needs to corespond to the values that you
specify in the faces-config file and the facelets tag library. But it is
still easier then compared to the JSF 1.X situation.


TagHandler

Well, we will give a small example of a CompontHandler, which is a
subclass of the TagHandler. The first important question is when do we
need this? One situation where I used it, was to adjust the visibility
of any JSF component in relation to the security rules that should be
applied to it. With the onComponentPopulated method of the class, you
have easily access to the parent component during the building of the
view. The view is build for the first time during the render view phase
when the view is accessed for the first time. Then, in case of a
postback, the view is again created in the restore view phase. At those
times, you can manipulate the parent component. Let me give an
example.

In the facelets taglib file, I have an entry like this:

<tag>
        <tag-name>securedComponent</tag-name>
        <component>
            <component-type>securedComponent</component-type>
            <handler-class>org.os890.codi.addon.secure.component.SecuredComponentHandler</handler-class>
        </component>
        <attribute>
            <description>Defines the voter(s) that will be used for this. multiple names separated by a ,</description>
            <name>voter</name>
            <required>true</required>
            <type>java.lang.String</type>
        </attribute>
    ...
    </tag>

In comparison with the standard custom component we discussd
in the previous section, the most important change is the addition of a
handler-class. This class extends the ComponentHandler class and the
onComponentPopulated looks like this:


public void onComponentPopulated(FaceletContext ctx, UIComponent component, UIComponent parent)
    {
        super.onComponentPopulated(ctx, component, parent);
        String voter = findValue(component, "voter", String.class);
        ...
    }
    
    private <T> T findValue(UIComponent component, String valueName, Class<T> resultClass)
    {
        T result = null;

        if (component.getAttributes().containsKey(valueName))
        {
            if (Boolean.class.equals(resultClass))
            {
                result = (T) new Boolean((String) component.getAttributes().get(valueName));
            } else
            {
                result = (T) component.getAttributes().get(valueName);
            }
        }
        if (result == null)
        {
            ValueExpression ve = component.getValueExpression(valueName);
            if (ve != null)
            {
                result = (T) ve.getValue(FacesContext.getCurrentInstance().getELContext());
            }
        }
        return result;
    }

With the findValue we can retrieve the value (constant or the
evaluated ValueExpression) of the attribute name we have specified like
voter as we specified it in the facelets tag library. But the most
valuable part is that UIComponent parent as paremeter of the method,
that allows any manipulation we like. The next time i'll explain the
CODI security add-on I made with the help of the
componentHandler.


Conclusion

The composite component feature of JSF 2.0 is one step further in
the ability to define templating mechanism in your JSF view files. The
declarative way that allows precise controle over the parameters and their
usage is a great advantage. It is one of the new features of JSF 2.0 that
I like the most. Also the creation of the a custom component is easier
then before and I consider creating a new custom component much faster
then before.

1 comment:

  1. If you need supporting tools, JSF Components Wizard is for you, check out http://jcw.jsfportal.org

    ReplyDelete