Creating a JSF Component Library

Tutorial on how to create JSF components using the MyFaces JSF Components Archetype 

Overview

Setting up your project

Creating a component

Testing the component

Foreword


Overview

Have you ever tried to create a library that contains your custom JSF components? Never dared to create a component because you don't know where to begin? This is the tutorial for you. This tutorial requires some Maven knowledge as well as some understanding of JSF basic concepts.

This tutorial focuses the creation of a JSF component library from scratch, but to help with the setup of the project a Maven archetype for Myfaces is used. This archetype is called the MyFaces JSF Components Archetype and its objective is to create the structure for a JSF Component Library able to run in any JSF implementation (not only MyFaces!). 

The tutorial begins with the creation of the project layout, using the archetype and then discusses JSF component creation by following the existing example created by the archetype. Finally, it explains how to test the custom component.

Setting up your project

To begin, we are going to create the project structure using the archetype. As we will we using Maven as the build tool (an archetype is a thing for Maven), we should have Maven 2.0.4 or higher installed in our system.

Installing the archetype 

The MyFaces JSF Components Archetype does not exist in a public maven repository yet (this will happen sometime soon), so in the meanwhile we have to get the sources of the archetype using Subversion and install the archetype locally on our system. To do this, you need to have a subversion client installed and then, go to the directory of your choice and check out the sources with the command:

Then, go to the directory created by subversion and install the archetype with maven:

cd maven-archetype-jsfcomponents 
mvn install

At this point, you will have the archetype installed locally in your system and you don't have to repeat these steps again. You can use the archetype now.

Using the archetype

The current version of the archetype is the 1.0-SNAPSHOT. If this is the first time you are using a maven archetype, I recommend you to read this page from the maven documentation.

To create the structure for our project using the archetype you just need to execute this command in the parent directory where you want to create the project. The archertype will create a folder with the name of the project:

mvn archetype:create -DarchetypeGroupId=org.apache.myfaces.maven \
                     -DarchetypeArtifactId=maven-archetype-jsfcomponents \
                     -DarchetypeVersion=1.0-SNAPSHOT \
                     -DgroupId=myAppId \
                     -DartifactId=myJsfComponents

Feel free to modify the groupId and the artifactId variables to the names of your choice. In this tutorial we will be using the testApp as the group Id and myJsfComponents as the artifact Id (the name for the new JSF custom library).

The new project structure

After using the archetype to create our new project, we will have the following structure:

-+ project base (parent pom)
|
+--- core (component library)
|
+--- examples (examples for the components)

A maven multi-module project is created, so we will have a module for the JSF library itself and a module for an web application to test and show your custom components.

So, now we have these folders:

  • myJsfComponents: parent folder, that contains the pom to build the the JSF components library and the examples.
  • myJsfComponents/core: the module that contains the sources of your components. The components wil be developed here.
  • myJsfComponents/examples: the module with the sources for the examples. The web application generated will contain your new library as well as the needed dependencies, everything already defined in the pom.xml file.

If you are using an IDE, you can create the IDE files to open the project with your IDE (e.g.  execute "mvn idea:idea" or "mvn eclipse:eclipse" if you use idea or eclipse. Refer to the maven documentation for details.

Creating a component 

Instead of creating a brand new component from scratch, we will study the demo component already in the core folder. It is a very very simple component called SayHello that, when rendered, it will print "Hello <firstName> <lastName>!" (firstName and lastName are provided to the developer). It is intended to serve as a starting point for more complex components.

To create a JSF component you need to create three different classes that work together:

  • UIComponent subclass: defines the behaviour of the component.
  • Renderer: tells the way how the component will be shown (rendered) to the user. 
  • Tag:  links the UIComponent to the renderer. It defines the attributes that the developer can use.

The SayHello component

It is a very simple component. To use it we would use this code in the jspx page:

<mycomp:sayHello firstName="John" lastName="Smith" /> 

And this would ouput this when using a browser:

Hello John Smith! 

Both attributes (firstName and lastName) can be bind to values in a backing bean using EL (Expression language). For instance:

<mycomp:sayHello firstName="#{myBackingBean.myName}" /> 

The component also allows to use UIComponent base attributes: id, binding and rendered.

Let's see what we have implemented to create this component. In the SayHello component, we have the three classes mentioned before. Click on the class name to see its code. This classes are (in the core/main/java folder)

SayHello: the UIComponent subclass

Contains the behaviour of the component and the methods to maintain its state across requests. As the objective of the component is only to output some text, it extends UIOutput (which is a subclass of UIComponent).

This class contains the attributes for SayHello, which are firstName and lastName, as well as the getters and setters for these fields. To handle with possible value bindings, in the getter methods if the property is null we try to get the value from the ValueBinding map (available through the getValueBinding() method). ValueBindings are stored using an arbitrary name. A good practice is that the value binding name is the same than the property name.

public String getFirstName()
{
if (firstName != null)
{
return firstName;
}

ValueBinding vb = getValueBinding("firstName");
return (vb == null)? null : (String) vb.getValue(FacesContext.getCurrentInstance());
}

An important part in a component is the code to maintain the state across requests. This is handled by the saveState() and restoreState() methods, that save and restore the variable values using an array. We have to save and restore the state of all the component properties. It is a common error to forget the save and restore code...

public void restoreState(FacesContext context, Object state) {
Object values[] = (Object[])state;
super.restoreState(context, values[0]);

this.firstName = (String)values[1];
this.lastName = (String)values[2];
}

public Object saveState(FacesContext context) {
Object values[] = new Object[3];
values[0] = super.saveState(context);
values[1] = firstName;
values[2] = lastName;
return values;
}

Let's now define how our component will be rendered.

SayHelloRenderer: the renderer

The only mission of a renderer is to define the component encoding (output to the user) and decodind (request parsed to fill the component properties) behaviours. The SayHello component extends javax.faces.render.Renderer. A component should override the methods decode(), encodeBegin(), encodeChildren() and encodeEnd(), if necessary. The SayHelloRenderer does not get any values from the request (no decode() method is needed) and will only print a simple line. All the code in our component has been put in the encodeEnd() method, because the component does not have to handle with nested children. More sophisticated components will implement the other methods, as necessary.

In the encodeEnd() method we get the ResponseWriter from the FacesContext and use it to render the HTML output. In this method, we first check if the component should be rendered. Otherwise, we finish the execution of the method by calling return. If it is rendered, we proceed to write the output code, by getting the component and "playing" with its properties:

public void encodeEnd(FacesContext facesContext, UIComponent uiComponent) throws IOException
{
super.encodeEnd(facesContext, uiComponent);

if (!uiComponent.isRendered())
{
return;
}

ResponseWriter writer = facesContext.getResponseWriter();

SayHello sayHello = (SayHello) uiComponent;
 
  String firstName = sayHello.getFirstName();
String lastName = sayHello.getLastName();

writer.write("Hello ");

if (firstName != null)
{
writer.write(firstName);
}

if (lastName != null)
{
writer.write(" "+lastName);
}

writer.write("!");

Finally, we need to create the Tag class, to define the available public properties.

SayHelloTag: the tag class

Our SayHelloTag class extends UIComponentTag. This class links the component with the renderer and define and sets the properties of the component. To link component and renderer we implement these two methods:

public String getComponentType()
{
return SayHello.COMPONENT_TYPE;
}

public String getRendererType()
{
return SayHello.DEFAULT_RENDERER_TYPE;
}

We get the String values from static constants in the SayHello class, as a way to centralise the names, but you could put here the Strings directly if you prefer so...

A tag class needs to declare the component attributes and provide setters to them. Also the methods setProperties and release() must be implemented. In the setProperties() we set the attributes of the component. In this method we check if the value provided is a value binding expression or not and provide the attribute to the component accordingly. This is the code for the lastName property:

// lastName
if (lastName != null)
{
if (UIComponentTag.isValueReference(lastName))
{
component.setValueBinding("lastName", application.createValueBinding(lastName));
}
else
{
component.getAttributes().put("lastName", lastName);
}
}

We also need to implement the release method, to release allocate resources. In this method we set the component attributes to null

Registering the Component: the taglib and the faces-config.xml files

The taglib file 

The *.tld file created by the archetype is situated in the core/src/main/tld folder. This file contains all the tags available in out library. For the SayHello component we have defined the sayHello tag.

<!-- sayHello demo component-->
<tag>
<name>sayHello</name>
<tag-class>org.myorganization.component.sayhello.SayHelloTag</tag-class>
<body-content>JSP</body-content>
<description>Simple component to greet a user.</description>

<!-- import the entities for that component here-->
&ui_component_attributes;
&html_input_suggest_attributes;
</tag>

We put the name of the tag class in the tag-class element. 

In this file, we are using entities that contain the xml for the attributes of the component. This will allow a better reutilization of the attributes, because, for instance, most components will have the UIComponent attributes (id, rendered and binding). Then, if our component uses those attributes, we only have to import the entity in the code (&ui_component_attributes), after declaring the entities at the beginning of the file. The &say_hello_attributes, for instance, points to the entities/say_hello_attributes.xml file, that contains the listing of those attributes:

<!-- This is the list of attributes for the sayHello component -->
<attribute>
<name>firstName</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<description>
The first name of the user
</description>
</attribute>
<attribute>
<name>lastName</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<description>
The last name of the user
</description>
</attribute>

The name of the attributes must be the same than the name of the properties in the tag class.

During the build process, maven will create a *.tld file that contains all the attributes inside automatically. Thus, we ensure a major compatibility across servers.

The faces-config.xml

We need to register the component in the faces-config.xml file, situated in the resources META-INF folder. JSF will "know" about your components by reading this file, that will be included inside the jar file created.

<faces-config xmlns="http://java.sun.com/JSF/Configuration">

<!-- components -->

<component>
<component-type>org.myorganization.SayHello</component-type>
<component-class>org.myorganization.component.sayhello.SayHello</component-class>
</component>


<!-- renderkit -->

<render-kit>

<renderer>
<component-family>javax.faces.Output</component-family>
<renderer-type>org.myorganization.SayHelloRenderer</renderer-type>
<renderer-class>org.myorganization.component.sayhello.SayHelloRenderer</renderer-class>
</renderer>

</render-kit>

</faces-config>

Here we map the actual classes with the component type and renderer type (defined in the component class as static constants; must be the same name).

Building the component library

Now we can build the library if we want. By including the jar generated in the classpath of your applications you will be able to use your custom JSF components. To build the library we just have to execute this in the core folder:

cd core 
mvn clean package

And the jar will be generated in the core/target folder.

Testing the component

Now it is time to test our component. We have two alternatives, not mutually exclusive:

  • Creating a small web application that uses the component.
  • Creating JUnit tests

The examples web application

The archetypes already generates a module to create a web application that uses the component library. This module is the examples folder. In the examples/pom.xml file we can see that one of the dependencies of the project is the component library, so calling 

mvn clean install

from the root folder (myJsfComponents), the library and the examples war will be built.

The examples project is very simple. In the src/main/webapp we have the JSP files, the home.jsp with the listing of examples, and the sayHello.jsp that contains an example for our component.

In the folder src/main/java there is the backing beans for the examples (e.g. The org.myorganization.component.example.SayHelloBean is a backing bean for the SayHello component example page). Backing beans and navigation rules are registered in the webapp/WEB-INF/examples-config.xml file.

To execute the examples we can either create the war file with the command "mvn build" or use jetty6 to run the examples directly: 

cd examples 
mvn -PjettyConfig clean package jetty:run

This will build the examples using the MyFaces implementation. Alternatively, you can build your application with Sun's Reference Implementation passing the jsf=ri variable:

mvn -PjettyConfig -Djsf=ri clean package jetty:run

JUnit testing our component

JUnit testing is more than advisable when developing an application. Inside the core/src/test/java folder there is a demo test for the SayHello component. You should add your tests here when developing new components. To test the component a library called shale-test from the Struts Shale project is being used.

As a demonstration, there is a test for the SayHelloRenderer (org.myorganization.component.example.SayHelloRendererTest) that simulates the rendering of the component so we are able to assert if what is rendered is what we expect or not.

When building the component library, maven will automatically run the examples. 

Foreword

To sum up, the MyFaces JSF Components Archetype can ease the burden to develop a custom components library, as well as provides a demo example that is always useful to follow. Creating a component is not the easiest thing to do, but the archetype can help a lot in this task.

The structure provided by the archetype is almost identical to the MyFaces tomahawk library and it is the result of several years of development. Using the same layout across component libraries help developers to understand other libraries easily.

Enjoy creating your own library!