JSFsimpliying JSF

How to Write a Dynamic JSF 2.x Component

Actually I’m trying another approach to simplify my team’s JSF pages. The verbosity of JSF always puzzles me. My favorite example is the <h:inputText> component. In most cases you have to add a label and an <h:message> component, and you have to make sure every id is correct (read my blog post on this topic). JSF has been designed to make it simple to write your own component. That’s what I did, and I stumbled across some unexpected difficulties.

Preparations

It is really simple to write a JSF component. JSF has been designed with user-defined components in mind, and JSF 2.0 makes use of annotations to make things even simpler. The minimalist approach is to write component class, annotate it with @FacesComponent, write a tag library and you are done. You can also add a renderer, but that’s plain vanilla code, too. Write a little HTML code and be happy. That’s what I call a static component.

This article shows you how to compose a component from other JSF components. This turned out to be more difficult. On the other hand this approach offers a lot of possibilities. You can write a component that consists of a number of components that are added dynamically by the managed beans. That’s why I call these components “dynamic” even though my example is pretty static.

Before we start writing the component, let me show you what I want to do. I want to write a component that composes a label, an input field and a message tag. In other words, I just want to write my JSF files like so:

<f:view xmlns="http://www.w3c.org/1999/xhtml"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:ui="http://java.sun.com/jsf/ui"
    xmlns:bj="http://beyondjava.net/simplejsf" locale="en-us">
    <h:head>
	<link type="text/css" rel="stylesheet" href="themes/bluesky/skin.css" />
    </h:head>
    <h:body>
	<h:form>
  	    <h:panelGrid columns="3">
		<bj:inputText label="First number" value="#{additionBean.number1}" />
		<bj:inputText label="Second number" value="#{additionBean.number2}" />
		<h:commandButton type="submit" value="add"
			action="#{additionBean.add}" />
		<h:outputLabel value="" />
		<h:outputLabel value="" />
		<bj:inputText label="Result" value="#{additionBean.result}" />
	    </h:panelGrid>
	</h:form>
        <h:messages />
    </h:body>
</f:view>

I want this JSF file to be rendered like this:


Deriving from a Primefaces class

As I mentioned, this is pretty simple if you are ready to write some HTML code. My difficulties began when I decided not to invent the wheel from scratch. My team uses Primefaces as component library. Wouldn’t it be nice to derive <bj:inputText> from one of their classes?

At first, I had no luck. It was possible to derive from the Mojarra classes, but the Primefaces classes obvious didn’t like me. It took me a couple of days to find out that it is indeed possible to derive from a Primefaces component. All you have to do is override the getFamily() method. So the shortest working component class looks like this:

@FacesComponent("de.beyondjava.InputText")
public class BeyondInputText extends org.primefaces.component.inputtext.InputText implements SystemEventListener
{
   public static final String COMPONENT_FAMILY = "javax.faces.Input";

   @Override
   public String getFamily()
   {
      return COMPONENT_FAMILY;
   }
}

Composing JSF components

The second difficulty arose when I decided not to write HTML code. Instead, I decided to compose standard components like <p:inputText>, <p:outputLabel> and <p:messages>. The rationale behind this decision is that we want to benefit from new features of the third party JSF component library. So what we want is a very loose coupling.

There is an interesting annotation that allows a JSF component to receive messages. One of the messages is sent when a component has been added to the JSF component tree:

@ListenerFor(systemEventClass = PostAddToViewEvent.class)
@FacesComponent("de.beyondjava.InputText")
public class InputText extends org.primefaces.component.inputtext.InputText implements SystemEventListener
{
   ...

   public void processEvent(ComponentSystemEvent event) throws AbortProcessingException
   {
      if (event instanceof PostAddToViewEvent)
      {
         OutputLabel l = new OutputLabel();
         l.setFor(getId());
         l.setValue(getLabel());
         getParent().getChildren().add(l);
      }
      super.processEvent(event);
   }
}

This code works. Almost. It adds a label at the end of the form. All right, they just promised us to send the event after the component has been added to the component tree. Nobody promised us to send the event immediately…

This wasn’t exactly what I wanted to see. I want my labels to proceed their input field. Well, it wasn’t a big deal to fix it. But the weird thing is yet to come. Once you submit the form, the form looks like this:

I suspect this is a bug of the current JSF version (I used Mojarra 2.1.14 and Primeface 3.4.1). According to the JSF 2.2 spec, getParent().getChildren() has to be designed to work correctly if children are added or removed.

A long and tedious search followed. There is little information in the internet1 Hardly anybody seems to write dynamic JSF components. In the end I found a step-by-step instruction that works. If the author is right, this solution is officially supported by the Mojarra team. It still looks like a hack to me, but then, it works flawlessly:

@FacesComponent("de.beyondjava.InputText")
public class InputText extends org.primefaces.component.inputtext.InputText 
       implements SystemEventListener
{
   public static final String COMPONENT_FAMILY = "javax.faces.Input";

   @Override
   public String getFamily()
   {
      return COMPONENT_FAMILY;
   }

   public InputText()
   {
      FacesContext context = FacesContext.getCurrentInstance();
      UIViewRoot root = context.getViewRoot();
      root.subscribeToViewEvent(PreRenderViewEvent.class, this);
   }

   public void processEvent(SystemEvent event) throws AbortProcessingException
   {
      if (!FacesContext.getCurrentInstance().isPostback())
      {
         insertLabelBeforeThisInputField();
         insertLabelBehindThisInputField();
      }
   }

   private void insertLabelBeforeThisInputField()
   {
      OutputLabel l = new OutputLabel();
      l.setFor(getId());
      l.setValue("Label in front of " + getLabel());
      List<UIComponent> tree = getParent().getChildren();
      for (int i = 0; i < tree.size(); i++)
      {
         if (tree.get(i) == this)
         {
            tree.add(i, l);
            break;
         }
      }
   }

   private void insertLabelBehindThisInputField()
   {
      OutputLabel l = new OutputLabel();
      l.setFor(getId());
      l.setValue("Label behind " + getLabel());
      List<UIComponent> tree = getParent().getChildren();
      for (int i = 0; i < tree.size(); i++)
      {
         if (tree.get(i) == this)
         {
            tree.add(i + 1, l);
            break;
         }
      }
   }
}

Instead of annotating the class you have to make it implement the SystemEventListener interface, and you have to subscribe to the PreRenderViewEvent event of the root element of the JSF component tree. Now you can manipulate the tree freely in the processEvent method.

The final component

I’d like to conclude this article with the complete source code of the component I promised you at the beginning.

Put a tag library file in the META-INF subdirectory of your project:

<?xml version="1.0"?>
<facelet-taglib version="2.0"
	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">

  <namespace>http://beyondjava.net/simplejsf</namespace>
  <tag>
    <tag-name>inputText</tag-name>
	<component>
	  <component-type>de.beyondjava.InputText</component-type>
	  <renderer-type>de.beyondjava.InputText</renderer-type>
	</component>
	<attribute>
	  <description><![CDATA[Unique identifier.]]></description>
	  <name>id</name>
	  <required>false</required>
   	  <type>java.lang.String</type>
	</attribute>
        ...
  <tag>
</facelet-taglib>

You can safely omit the attribute list. JSF ignores the attribute list in the tag library definition. It simply uses reflection to set the attributes. However, your editor (e.g. Eclipse 3.7) may need the attribute list. Just unzip the component library your are using and copy the attribute list from the tld file you find in the META-INF subdirectory.

package de.beyondjava.jsfComponents;

import java.util.List;

import javax.faces.component.FacesComponent;
import javax.faces.component.UIComponent;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.PreRenderViewEvent;
import javax.faces.event.SystemEvent;
import javax.faces.event.SystemEventListener;

import org.primefaces.component.message.Message;
import org.primefaces.component.outputlabel.OutputLabel;

@FacesComponent("de.beyondjava.InputText")
public class InputText extends org.primefaces.component.inputtext.InputText implements SystemEventListener
{
   public static final String COMPONENT_FAMILY = "javax.faces.Input";

   @Override
   public String getFamily()
   {
      return COMPONENT_FAMILY;
   }

   public InputText()
   {
      FacesContext context = FacesContext.getCurrentInstance();
      UIViewRoot root = context.getViewRoot();
      root.subscribeToViewEvent(PreRenderViewEvent.class, this);
   }

   @Override
   public void processEvent(SystemEvent event) throws AbortProcessingException
   {
      if (!FacesContext.getCurrentInstance().isPostback())
      {
         insertLabelBeforeThisInputField();
         insertMessageBehindThisInputField();
      }
   }

   private void insertMessageBehindThisInputField()
   {
      Message l = new Message();
      l.setFor(getId());
      l.setDisplay("text");
      List<UIComponent> tree = getParent().getChildren();
      for (int i = 0; i < tree.size(); i++)
      {
         if (tree.get(i) == this)
         {
            tree.add(i + 1, l);
            break;
         }
      }
   }

   private void insertLabelBeforeThisInputField()
   {
      OutputLabel l = new OutputLabel();
      l.setFor(getId());
      l.setValue(getLabel());
      List<UIComponent> tree = getParent().getChildren();
      for (int i = 0; i < tree.size(); i++)
      {
         if (tree.get(i) == this)
         {
            tree.add(i, l);
            break;
         }
      }
   }

   @Override
   public boolean isListenerForSource(Object source)
   {
      return (source instanceof UIViewRoot);
   }
}

That’s all you need. Notice that we haven’t defined a renderer. We happily delegate the rendering to the Primefaces library. We didn’t define any properties, either. <bj:inputText> has exactly the same properties as <p:inputText>. Thus we can hope we won’t run into compatibility issues with new versions of JSF, Mojarra and Primefaces.


Further reading:

How to write a PrimeFaces component (free sample chapter of the PrimeFaces Cookbook)


  1. By the time you read this, this may have changed. As of October 2012, I suspect my blog almost doubles the number of posts on this topic :).

3 thoughts on “How to Write a Dynamic JSF 2.x Component

    1. Not quite. The example on javadomain.in is just a clever use of a data table. In a way, it’s dynamical: you can add and remove elements to a table. However, you can’t add arbitrary elements to the JSF page. In your example, you can display any number of outputTexts, but your can’t add inputTexts, images or something else. The javadomain article covers a completely different topic than my article.

  1. Its a very good tutorial . I found information which I couldn’t even referring 5 jsf text books. Thanks heaps for posting this wonderful tutorial.

Leave a Reply

Your email address will not be published.