Wouter has posted 5 posts at DZone. View Full User Profile

Creating a Custom JSF 1.2 Component - With Facets, Resource Handling, Events and Listeners

10.07.2009
| 29132 views |
  • submit to reddit

I occasionally create custom JavaServer Faces components. Just enough to sort of remember what the steps are, but not nearly frequently enough to quickly put a new component together. This article demonstrates the quick step approach to creating a new custom component in the old fashioned way (that means: it is not a Facelets template based or an ADF Faces 11g Declarative Component). Its primary purpose is to help me quickly retrace my steps. But perhaps it will benefit some of you as well.

The Shuffler component I will develop supports facets. It will render its facet children - one after the other. Which one is rendered first can be indicated through an attribute facetOrder (values normal, reverse and random), which is EL enabled. A shuffler-method-expression can optionally be set to provide the Shuffler with a shuffle-order-processor: the method is invoked with the list of facets to shuffle and will return it in the order in which to render the children.

The component can render with a shuffle icon that when pressed causes the children to be shuffled. The Shuffler component allows registration of Shuffle Event Listeners, custom listeners that are informed whenever the shuffle event occurs.

An example of how the Shuffler can be used inside a JSF page:

Some elements of custom JSF components that are explicitly discussed in this article:
  • dynamic attributes of type ValueExpression (EL enabled)
  • attributes of type MethodExpression (also EL enabled)
  • facets
  • (custom) events and listeners

Bare essentials for custom JSF components

A custom JSF component is represented by a Java Class - one that extends from UIComponentBase. An instance of this class is created whenever a new page is rendered that contains the component (and for each occurrence of the component in the page, a new instance of the class is created). The component class holds the attributes that are set by the page developer and that determine the behavior and appearance of the component. The component class has the internal logic of the component and it deals for example with events and listeners. This class may also render the markup (HTML) for the component - though it is a better practice to leave the actual rendering to a Renderer class.

public class Shuffler extends UIComponentBase {
...
}

Most custom JSF component will also have an associated Renderer class, that extends from Renderer. Note that some components will not actually be rendered (such as Listeners, Iterators or Parameters) and therefore will not have a Renderer class. The Renderer is not only responsible for rendering the HTML, it will also inspect (decode) the incoming request from the browser to see whether the request parameter map contains values that are of interest to the component - that indicate for example that a value has been entered or set on the component(’s representation in the browser) or an action has been executed against it. Note that one JSF component may have multiple Renderers, for example for different channels and protocols (to render a representation of the component in plain XML, in WML, in JavaFX or XUL) or for different user agents (Firefox, Internet Explorer) or themes (professional user, internet surfer).

public class ShufflerRenderer extends SuperRenderer {
...
}

JavaServer Faces pages can be created in various ways - including programmatically, using Facelets and using JSP pages. The latter option, through JSP, is still the most common one, though that is about to change with JSF 2.0 favoring Facelets. Page developers using JSPs will describe the JSF component tree that will need to be instantiated in memory for rendering a certain View using a plain JSP page. The tags in the JSP page are normal JSP tags - described by TLD (tag library descriptors) - corresponding to JSF components and therefore JSF component classes. Every JSF component that needs to be used in JSPs has to have a corresponding JSP tag-class, one that will typically extend from UIComponentELTag (or just from TagSupport when no JSF component is added to the component tree for a certain tag, for example when that tag represents a listener or parameter). The Tag Class specifies which JSF Component it represents. It also indicates which Renderer should be used to render the component. This means that one component can have multiple JSP tags associated with it, each providing a different way of rendering the component. Note: the renderer can also be specified dynamically - taking user preferences or characteristics into account

public class ShufflerTag extends UIComponentELTag {

public static final String COMPONENT_TYPE = "nl.amis.jsf.UIShuffler";
public static final String RENDERER_TYPE = "nl.amis.jsf.ShufflerRenderer";
public String getComponentType() {
return COMPONENT_TYPE;
}

public String getRendererType() {
return RENDERER_TYPE;
}
...
}

Tags representing JSF components need to be described in TLD files (Tag Library Descriptors) just like any other JSP tag.The entry in the TLD defines the tag label to use in the page, whether the tag can contain child-tags, some descriptive meta data and every attribute that can be configured in the tag. For each attribute the TLD-entry specifies the type, whether it is required and if the attribute can contain an EL expression passing in a value or an EL expression passing in a method; in the latter case, the entry also prescribes the signature of the method:

<?xml version = '1.0' encoding = 'windows-1252'?>
<taglib 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-jsptaglibrary_2_1.xsd"
version="2.1" xmlns="http://java.sun.com/xml/ns/javaee">
<display-name>ShufflerLib</display-name>
<tlib-version>1.0</tlib-version>
<short-name>ShufflerLib</short-name>
<uri>/nl.amis,jsf/ShufflerLib</uri>
<tag>
<description>Writes a DIV element that contains the facets in a specific order.</description>
<name>shuffler</name>
<tag-class>nl.amis.jsf.shuffler.ShufflerTag</tag-class>
<body-content>JSP</body-content>
<!-- standard UIComponent attributes -->
<attribute>
<name>id</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>rendered</name>
<required>false</required>
<deferred-value>
<type>boolean</type>
</deferred-value>
</attribute>
<attribute>
<name>binding</name>
<required>false</required>
<deferred-value>
<type>javax.faces.component.UIComponent</type>
</deferred-value>
</attribute>
<!-- custom attributes -->
<attribute>
<name>styleClass</name>
<required>false</required>
<deferred-value>
<type>java.lang.String</type>
</deferred-value>
</attribute>
....
</tag>

JSF components need to be registered in a special faces-config.xml file (special in the sense that it is not the faces-config.xml that drives a web application but rather one that acts like a repository of components and their renderers. Note however that all entries in this special faces-config.xml is merged together with the ‘normal’ faces-config.xml. That means in turn that while the special file is primarily seen as the registry of components, it can also configure PhaseListeners, Navigation Rules (hard to see the value in that) and Managed Beans (which can be very useful). 

The component registration in faces-config.xml consists of a component type that is associated with a the component class.

  <component>
<component-type>nl.amis.jsf.UIShuffler</component-type>
<component-class>nl.amis.jsf.shuffler.Shuffler</component-class>
</component>

Renderers can also be registered in this file. A renderer entry registers a renderer-type (corresponding to the value returned by the getRendererType() method in the tag class) associated with the RendererClass. Based on the value (rendererType) returned by the tag class, the correct class to instantiate can be determined from this entry:

  <render-kit>
<renderer>
<component-family>nl.amis.Shuffler</component-family>
<renderer-type>nl.amis.jsf.ShufflerRenderer</renderer-type>
<renderer-class>nl.amis.jsf.shuffler.ShufflerRenderer</renderer-class>
</renderer>
</render-kit>

Implementing the Classes: Component, Renderer and TagHandler

The TagHandler ShufflerTag is the intermediary between the world of JSP pages (and the Servlet/JSP engine that translates the JSP file into a servlet class) and the JSF realm. Every tag in the JSP page needs to be turned into its corresponding JSF representation. The tag handler needs to override the setProperties() method inherited from the UIComponentELTag class; this method takes all the values set on the tag attributes in the page and passes them onwards to the Component. In our initial case, the tag is used in JSPs like this:

<shf:shuffler styleClass="h1" id="s1" rendered="true">
... other content
</shf:shuffler>

The styleClass attribute is the only one we defined - id and rendered are defined on every JSP-tag based on JSF’s UIComponentELTag. Thye styleClass attribute is also the only attribute we need to take responsibility for in the tag class, by providing a setter method that sets a private member and by passing the value of that private member to the component in the setProperties() method. The code for the ShufflerTag class now becomes:

package nl.amis.jsf.shuffler;

import javax.el.ValueExpression;

import javax.faces.component.UIComponent;
import javax.faces.webapp.UIComponentELTag;

public class ShufflerTag extends UIComponentELTag {

public static final String COMPONENT_TYPE = "nl.amis.jsf.UIShuffler";
public static final String RENDERER_TYPE = "nl.amis.jsf.ShufflerRenderer";

private ValueExpression styleClass;

public String getComponentType() {
return COMPONENT_TYPE;
}

public String getRendererType() {
return RENDERER_TYPE;
}

protected void setProperties(UIComponent component) {
super.setProperties(component);
processProperty(component, styleClass, Shuffler.STYLECLASS_ATTRIBUTE_KEY);
}

public void release() {
super.release();
styleClass= null;
}

protected final void processProperty(final UIComponent component, final ValueExpression property,
final String propertyName) {
if (property != null) {
if(property.isLiteralText()) {
component.getAttributes().put(propertyName, property.getExpressionString());
}
else {
component.setValueExpression(propertyName, property);
}
}
}

public void setStyleClass(ValueExpression styleClass) {
this.styleClass = styleClass;
}
}

We cater for the fact that styleClass can contain a ValueExpression - as all attributes can, starting from JSF 1.2. In the method processProperty we check whether the string passed for styleClass is a literal string or should be considered an EL expression. In the latter case, we pass a ValueExpression to the component, otherwise a ‘normal’ attribute. Also note that the super class takes care of the attributes id, rendered and binding. However, we do have to specify them in the tag-library.

The component class in our case leads a pretty comfortable life: the tag handler informs him of all the attribute values and the actual rendering is left to a special Renderer class. The component is a pretty passive element in this simple example:

package nl.amis.jsf.shuffler;

import javax.faces.component.UIComponentBase;
import javax.faces.context.FacesContext;

public class Shuffler extends UIComponentBase {

public static final String FAMILY = "nl.amis.Shuffler";
public static final String STYLECLASS_ATTRIBUTE_KEY = "styleClass";

public String getFamily() {
return FAMILY;
}

@Override
public Object saveState(FacesContext facesContext) {
Object values[] = new Object[2];
values[0] = super.saveState(facesContext);
values[1] = this.getAttributes().get(STYLECLASS_ATTRIBUTE_KEY);
return values;

}

@Override
public void restoreState(FacesContext facesContext, Object state) {
Object values[] = (Object[])state;
super.restoreState(facesContext, values[0]);
this.getAttributes().put(STYLECLASS_ATTRIBUTE_KEY, values[1]);
}
}

The only really useful thing the component does is implementing the saveState and restoreState methods. These methods play an important part in turning the state of the component into a serializable object array and restoring that state of the component in the RestoreView phase, based on the serialized array.

The Tag Handler specifies in its getRendererType() method that the renderer to use for this component when using the shuffler tag, is one called nl.amis.jsf.ShufflerRenderer. In the faces-config.xml file, we have indicated that this renderer type is associated with the class nl.amis.jsf.shuffler.ShufflerRenderer that extends Renderer. The renderers in JSF can override methods like encodeBegin(), encodeEnd(), encodeChildren() and decode() - the latter only when we have to process the incoming request, looking for new values set on or events that occurred on the component. In our case, we initially will simply have the ShufflerRenderer render a DIV element with a class attribute (based on the styleClass attribute). The DIV will allow the children of the Shuffler component to render - by not overriding the encodeChildren() method.

package nl.amis.jsf.shuffler;

import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.render.Renderer;

public class ShufflerRenderer extends Renderer {

@Override
public void encodeBegin(final FacesContext facesContext,
final UIComponent component) throws IOException {
super.encodeBegin(facesContext, component);
final ResponseWriter writer = facesContext.getResponseWriter();

writer.startElement("DIV", component);
String styleClass =
(String)attributes.get(Shuffler.STYLECLASS_ATTRIBUTE_KEY);
writer.writeAttribute("class", styleClass, null);
}

@Override
public void encodeEnd(final FacesContext facesContext,
final UIComponent component) throws IOException {
final ResponseWriter writer = facesContext.getResponseWriter();
writer.endElement("DIV");
}
}
Published at DZone with permission of its author, Wouter van Reeven.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Tags:

Comments

Jacek Furmankiewicz replied on Wed, 2009/10/07 - 12:25pm

Don't get me wrong, it's a nice article...but I think I'd rather just use jQuery UI which can do custom components using a plugin and 1 line of Javascript.

I think instead of looking at these heavy server-side frameworks Java programmers should look at jQuery UI instead (which makes modern Javascript easy) and just expose data from the server-side Java apps via REST or something like that.

Here's the jquery code to turn an order list of divs into a proper accordion component

<script type="text/javascript">
$(function() {
$("#accordion").accordion();
});
</script

 http://jqueryui.com/demos/accordion/

Compared to that, the JSF 1.2 approach looks insanely compilicated.

 

 

Nicolas Frankel replied on Thu, 2009/10/08 - 3:00am in response to: Jacek Furmankiewicz

Jacek,

Executing code client-side or server-side is a major architectural decision that you don't have the luxury to make! For most clients I have worked for, these choices were made before I came. It has nothing to do with this article whatsoever.

Besides, you'll get the same problem in developing from scratch for whatever framework you're using (Swing, .Net, what have you) if you don't have the component you want when needed, which is a common occurence. Making reusable components libraries for your organization is a big step in speeding up development.

I must concede though that JSF architecture favors ease of use over ease of development so new components are a bit complex to develop.

Michael Mosmann replied on Fri, 2009/10/09 - 2:54am in response to: Nicolas Frankel

Hi,

> Executing code client-side or server-side is a major architectural decision that you don't have the luxury to make!

And if you have the luxury sometimes you should not...

> Besides, you'll get the same problem in developing from scratch for whatever framework you're using (Swing, .Net, what have you) if you don't have the component you want when needed, which is a common occurence.

Sure.. but: JSF makes this too heavy.. 

I wonder if anyone has fun in doing component development with JSF. It's a pain and i see no chance that JSF2 can reduce this much. I do not know any UI-Framework which makes this more painfull.

> Making reusable components libraries for your organization is a big step in speeding up development.

Then anybody should use a framework which has its strength in this particular aspect.Why should anybody use JSF? I don't get it.

> I must concede though that JSF architecture favors ease of use over ease of development so new components are a bit complex to develop.

There are ways out of this.. wicket, gwt, vaadin.

Bill Bryson replied on Sat, 2009/10/10 - 11:12am

A good well written article but also a reminder why I'm never going to willingly go near a JSF project again.

Jose Maria Arranz replied on Mon, 2009/10/12 - 4:53am

@nfrankel: Besides, you'll get the same problem in developing from scratch for whatever framework you're using (Swing, .Net, what have you) if you don't have the component you want when needed, which is a common occurence.

Really?

 

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.