RichFaces CDK and custom components

In this recipe, we will explore RichFaces CDK for creating JSF custom components. For those who are not familiar, CDK stands for Component Development Kit—a sub-project of RichFaces that allows you to easily create rich components with built-in Ajax support.

During this recipe, we will develop a custom component that will render a text field for inserting a phone number of type xxxx-xxxxx-x. We will render the phone number as a string, of the form xxxx-xxxxx-x, but we will store it as a string of type xxxxxxxxxx—this task will be accomplished by a custom converter.

The HTML prototype of our custom component will be:

<div title="Phone field:">
<input name="phoneField" value="0000-00000-0" />
<img src="phone.gif" />
</div>

Getting ready

RichFaces CDK requires Maven, therefore we should have Maven 2.2.1 or higher installed in our system. This is available at http://maven.apache.org/download.html or under the libraries in the code bundle for this book in the /JSF_libs/Apache Maven 2.2.1 folder.

After download, you have to set the CLASSPATH to that of the /bin directory of the Maven distribution. For example:

SET PATH = "C:PacktJSFKitapache-maven-2.2.1in"

Going forward, you should configure Maven for RichFaces CDK. For this, open the /conf/settings.xml file for editing and add this code to the profiles section (after the last <profile> tag):

...
<profile>
<id>cdk</id>
<repositories>
<repository>
<id>maven2-repository.dev.java.net</id>
<name>Java.net Repository for Maven</name>
<url>http://download.java.net/maven/1</url>
<layout>legacy</layout>
</repository>
<repository>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
<updatePolicy>never</updatePolicy>
</snapshots>
<id>repository.jboss.com</id>
<name>Jboss Repository for Maven</name>
<url>http://repository.jboss.com/maven2/</url>
<layout>default</layout>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>maven.jboss.org</id>
<name>JBoss Repository for Maven Snapshots</name>
<url>http://snapshots.jboss.org/maven2/</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
<updatePolicy>always</updatePolicy>
</snapshots>
</pluginRepository>
<pluginRepository>
RichFaces CDKusing<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
<updatePolicy>never</updatePolicy>
</snapshots>
<id>repository.jboss.com</id>
<name>Jboss Repository for Maven</name>
<url>http://repository.jboss.com/maven2/ </url>
<layout>default</layout>
</pluginRepository>
</pluginRepositories>
</profile>
...

Now activate the new profile by adding the following after the profiles section:

...
<activeProfiles>
<activeProfile>cdk</activeProfile>
</activeProfiles>
...

Now, everything is set for using RichFaces CDK. Next, you should manually create a folder where the components will be stored (we named it, /JSFComponentFolder_CDK), and a file named pom.xml (in this folder) with the following content:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>jsf.custom.component</groupId>
<artifactId>jsfComponent</artifactId>
<url>http://packt.cdk.org</url>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.faces</groupId>
<artifactId>jsf-api</artifactId>
<version>2.0.0-RC</version>
</dependency>
<dependency>
<groupId>javax.faces</groupId>
<artifactId>jsf-impl</artifactId>
<version>2.0.0-RC</version>
</dependency>
<dependency>
<groupId>javax.el</groupId>
<artifactId>el-api</artifactId>
<version>1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>el-impl</groupId>
<artifactId>el-impl</artifactId>
<version>1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>jsr250-api</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.richfaces.ui</groupId>
<artifactId>richfaces-ui</artifactId>
<version>3.3.3.BETA1</version>
</dependency>
</dependencies>
</project>

Some of the pom.xml elements are:

  • groupId: This is the prefix for the Java package structure of your library
  • url: This is the namespace for your library to be used in the TLD file
  • version: This is the version of your library

We have developed a JSF application to test our custom component with NetBeans 6.8, JSF 2.0, and GlassFish v3. The JSF 2.0 classes were obtained from the NetBeans JSF 2.0 bundled library. The custom component JAR can be found with the book code bundle under the /JSF_libs/phoneNumberComponent—JSF 2.0. In addition, we have used RichFaces 3.3.3.BETA1, which provides support for JSF 2.0. You can download this distribution from http://www.jboss.org/richfaces. The RichFaces libraries (including necessary dependencies) are in the book code bundle, under the /JSF_libs/RichFaces—JSF 2.0 folder.

How to do it...

OK, at this point zero, we have everything ready to start developing our first JSF custom component using RichFaces CDK.

First, we need to create a project for the component itself. In the library directory, /JSFComponentFolder_CDK, which you just created, launch the following command in MS-DOS command prompt (see the following screenshot):

How to do it...

You should get a BUILD SUCCESSFUL message, and a folder named /phoneNumberComponent with the following structure:

How to do it...

Next, we have to extend the predefined structure with the following directories:

  • srcmainconfig esources: This will contain the resource-config.xml file for the resources registration
  • srcmainjavajsfcustomcomponent enderkit: This will contain the render class
  • srcmain esourcesjsfcustomcomponent enderkithtmlcss: This will store CSS files
  • srcmain esourcesjsfcustomcomponent enderkithtmlimages: This will store images
  • srcmain emplatesjsfcustomcomponent: This will contain JSP templates
  • -srcmainjavajsfcustomcomponentcomponentconverter: It will contain the custom converter

Assuming that you have accomplished this boring task, let's move forward and add maven-compiler-plugin to the plugins section in the /phoneNumberComponent/pom.xml file:

…
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<inherited>true</inherited>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
</plugin>
…

Next step consists in generating a skeleton for our component. For this, navigate from the Command Prompt into the /phoneNumberComponent folder and execute the command from the screenshot:

How to do it...

After the BUILD SUCCESSFUL message, three files will have been generated:

  • An XML configuration file for the metadata, named phoneNumberComponent.xml
  • A UI class, named UIPhoneNumberComponent.java
  • A JSP-like template, named htmlPhoneNumberComponent.jspx

At this point we start the real development stage. We begin with the component class. By default, this class extends the UIComponentBase class, but we can modify it to extend the UIInput class, since we have a simple input component:

package jsf.custom.component.component;
import javax.faces.component.UIInput;
public abstract class UIPhoneNumberComponent extends UIInput {
public static final String COMPONENT_TYPE =
"jsf.custom.component.PhoneNumberComponent";
public static final String COMPONENT_FAMILY =
"jsf.custom.component.PhoneNumberComponent";
}

Now, we can modify the template for generating the renderer class. Open the JSP-like template, generated earlier, and modify it accordingly to our component:

<?xml version="1.0" encoding="UTF-8"?>
<f:root
xmlns:f="http://ajax4jsf.org/cdk/template"
xmlns:c=" http://java.sun.com/jsf/core"
xmlns:ui=" http://ajax4jsf.org/cdk/ui"
xmlns:h=" http://ajax4jsf.org/cdk/h"
xmlns:u=" http://ajax4jsf.org/cdk/u"
xmlns:x=" http://ajax4jsf.org/cdk/x"
xmlns:jsp=" http://ajax4jsf.org/cdk/jsp"
class="jsf.custom.component.renderkit.html.
PhoneNumberComponentRenderer"
baseclass="jsf.custom.component.renderkit.
PhoneNumberComponentRendererBase"
component="jsf.custom.component.component.UIPhoneNumberComponent">
<f:clientid var="clientId"/>
<f:resource
name="/jsf/custom/component/renderkit/html/images/phone.gif"
var="icon" />
<div id="#{clientId}"
x:passThruWithExclusions="value,name,type,id">
<input id="#{clientId}"
name="#{clientId}"
type="text"
value="#{this:getValueAsString(context, component)}"/>
<jsp:scriptlet>
<![CDATA[if(component.getFacet("icon")!=null &&
component.getFacet("icon").isRendered()) {]]>
</jsp:scriptlet>
<u:insertFacet name="icon" />
<jsp:scriptlet>
<![CDATA[
}else{
]]>
</jsp:scriptlet>
<img src="#{icon}"/>
<jsp:scriptlet>
<![CDATA[
}
]]>
</jsp:scriptlet>
</div>
</f:root>

Next, we develop the renderer class. This class should be stored in srcmainjavajsfcustomcomponent enderkit and it looks as shown next:

package jsf.custom.component.renderkit;
import java.io.IOException;
import java.util.Map;
import javax.faces.component.UIComponent;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;
import org.ajax4jsf.renderkit.HeaderResourcesRendererBase;
import jsf.custom.component.component.UIPhoneNumberComponent;
import jsf.custom.component.component.converter.PhoneConverter;
public abstract class PhoneNumberComponentRendererBase extends HeaderResourcesRendererBase {
public void decode(FacesContext ctx, UIComponent ui_comp){
ExternalContext externalContext = ctx.getExternalContext();
Map paramsOnRequest = externalContext.getRequestParameterMap();
UIPhoneNumberComponent uiPhoneNumberComponent =
(UIPhoneNumberComponent)ui_comp;
String clientId = uiPhoneNumberComponent.getClientId(ctx);
String submittedValue = (String)paramsOnRequest.get(clientId);
if (submittedValue != null) {
uiPhoneNumberComponent.setSubmittedValue(submittedValue);
}
}
public String getValueAsString(FacesContext ctx,
UIComponent ui_comp) throws IOException {
UIPhoneNumberComponent uiPhoneNumberComponent =
(UIPhoneNumberComponent)ui_comp;
String valueString =
(String)uiPhoneNumberComponent.getSubmittedValue();
if (valueString == null) {
Object value = uiPhoneNumberComponent.getValue();
if (value != null) {
Converter converter = getConverter(ctx,
uiPhoneNumberComponent);
valueString = converter.getAsString(ctx, ui_comp, value);
}
}
return valueString;
}
public Object getConvertedValue(FacesContext ctx, UIComponent
ui_comp, Object submittedValue) throws ConverterException{
UIPhoneNumberComponent uiPhoneNumberComponent =
(UIPhoneNumberComponent)ui_comp;
Converter converter = getConverter(ctx, uiPhoneNumberComponent);
String valueString = (String)submittedValue;
return converter.getAsObject(ctx, ui_comp, valueString);
}
private Converter getConverter(FacesContext ctx,
UIPhoneNumberComponent uiPhoneNumberComponent){
Converter converter = uiPhoneNumberComponent.getConverter();
if (converter == null)
{
PhoneConverter phoneConverter = new PhoneConverter();
converter=phoneConverter;
}
return converter;
}
}

This class has several methods that are very important for rendering the component. First, we have the decode method, which is responsible for reading values from request parameters and storing the submitted value locally on the component. Next, we have the getConverter method, responsible for returning an instance of our converter (if none was indicated). The last two methods, getValueAsString and getConvertedValue, are responsible for rendering the value back to the view, and converting the submitted value.

The final class that we will develop is our custom converter. This is a simple converter that transforms the string xxxx-xxxxx-x to xxxxxxxxxx and vice versa:

package jsf.custom.component.component.converter;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;
import javax.faces.convert.FacesConverter;
@FacesConverter(value = "PhoneConverter")
public class PhoneConverter implements Converter {
public String getAsString(FacesContext arg0,
UIComponent arg1, Object arg2) {
if (arg0 == null) {
throw new NullPointerException("context");
}
if (arg1 == null) {
throw new NullPointerException("component");
}
String s = String.valueOf(arg2);
if (s.length() != 10) {
return "0000-00000-0";
}
String phoneNumber = s.substring(0,4) + "-" +
s.substring(4,9) + "-" + s.substring(9,10);
return phoneNumber;
}
public Object getAsObject(FacesContext arg0,
UIComponent arg1, String arg2) {
if (arg0 == null) {
throw new NullPointerException("context");
}
if (arg1 == null) {
throw new NullPointerException("component");
}
try {
String start = arg2.substring(0,4);
String midle = arg2.substring(5,10);
String end = arg2.substring(11,12);
String phoneNumber = start + midle + end;
return phoneNumber;
} catch (Exception e) {
FacesMessage message = new
FacesMessage(FacesMessage.SEVERITY_ERROR,
"Parser error! - Cannot convert this phone number from
xxxx-xxxxx-x to xxxxxxxxxx!",
"Cannot convert this phone number from
xxxx-xxxxx-x to xxxxxxxxxx!");
throw new ConverterException(message);
}
}
}

Now, speaking about resources, we need an image named phone.gif (accordingly in our component and template). This should be placed in the srcmain esourcesjsfcustomcomponent enderkithtmlimages folder, and should be configured in the resource-config.xml file, as shown next (in this file you should configured many other resources, such as CSS files):

…
<resource>
<name>jsf/custom/component/renderkit/html/images/phone.gif</name>
<path>jsf/custom/component/renderkit/html/images/phone.gif</path>
</resource>
…

We must finish one more step before compiling our component. We must specify the component properties in the phoneNumberComponent.xml file as shown next (place this in the <component> tag):

…
<property>
<name>value</name>
<classname>java.lang.Object</classname>
<description>Component value</description>
</property>
<property>
<name>title</name>
<classname>java.lang.String</classname>
<description>Component title</description>
<defaultvalue>&quot;Phone number&quot;</defaultvalue>
</property>
<property>
<name>name</name>
<classname>java.lang.String</classname>
<description>Component name</description>
</property>
…

Now is the big moment! From the /phoneNumberComponent folder, execute the following command:

mvn clean install

If you get a BUILD SUCCESSFUL message, then a new folder target was created under the /phoneNumberComponent folder. In this folder, you should find a JAR file named phoneNumberComponent-1.0-SNAPSHOT.jar. This is our component!

Next, we can develop a simple JSF project and test it (don't forget to add this new component and RichFaces 3.3.3.BETA1 into the project classpath). Since you should have enough experience for this, the following snippet is only a variant of the JSF test page:

<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<!-- RichFaces tag library declaration -->
<%@ taglib uri="http://richfaces.org/a4j" prefix="a4j"%>
<%@ taglib uri="http://richfaces.org/rich" prefix="rich"%>
<!-- Custom component tag library declaration -->
<%@ taglib uri="http://packt.cdk.org/phoneNumberComponent" prefix="e"%>
<html>
<head>
<title>Using the phoneNumberComponent custom component</title>
</head>
<body>
<f:view>
<h:form id="phoneFormID">
<e:phoneNumberComponent id="phoneID"
title="Enter phone number as xxxx-xxxxx-x"
value="#{bean.phone}" />
<h:message showSummary="true" showDetail="false"
for="phoneID" style="color: red;
text-decoration:overline" />
<br />
<h:commandButton id="btnID" value="Submit Phone" />
</h:form>
</f:view>
</body>
</html>

The following is output screenshot:

How to do it...

In addition if you want to add a custom validator (let's name it PhoneValidator.java) you can follow the given steps:

  • Create the folder srcmainjavajsfcustomcomponentcomponentvalidator, and save in it the validator source
  • Modify the custom component class as shown next:
    package jsf.custom.component.component;
    import javax.faces.component.UIInput;
    import jsf.custom.component.component.validator.PhoneValidator;
    public abstract class UIPhoneNumberComponent extends UIInput {
    public static final String COMPONENT_TYPE =
    "jsf.custom.component.PhoneNumberComponent";
    public static final String COMPONENT_FAMILY =
    "jsf.custom.component.PhoneNumberComponent";
    public UIPhoneNumberComponent() {
    PhoneValidator phoneValidator=new PhoneValidator();
    addValidator(phoneValidator);
    }
    }
    

It's done!

There's more...

More details about custom components with RichFaces CDK can be found at the address http://docs.jboss.org/richfaces/latest_3_3_X/en/cdkguide/html/.

See also

The code bundled with this book contains the complete code of our custom component under the recipe: RichFaces_CDK_phoneNumberComponent.

In addition, you have the component itself (out of the box from RichFaces CDK), under the project: JSFComponentFolder_CDK.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset