JSF 2.0 Cookbook

4 (1 reviews total)
By Anghel Leonard
    Advance your knowledge in tech with a Packt subscription

  • Instant online access to over 7,500+ books and videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies
  1. Using Standard and Custom Converters in JSF

About this book

JavaServer Faces is a Java-based Web application framework intended to simplify development of user interfaces for Java EE applications. You may already be aware of the laborious search through reference guides and documentation to develop your JSF applications. With the JSF Cookbook, you can find solutions to the most common JSF problems in a quick and easy way.

This book will cover all the important aspects involved in developing JSF applications. It provides clear instructions for getting the most out of JSF and offers many exercises to build impressive desktop-style interfaces for your web applications. Develop JSF applications starting with simple recipes and gradually moving on to complex recipes.

We discuss all of the fundamental aspects of JSF applications. Once you locate your desired topic, you can try to find a recipe that corresponds to your problem.

We start off with the simple concepts of Converters, validators and file management. We then work our way with various resources such as CSS, JavaScript, and images to improve your web applications. You will learn to build simple and complex custom components to suit your needs. Next, you get to exploit AJAX as well as implement internationalization and localization for your JSF applications. We then look into ensuring security for your applications and perform testing of your applications. You also get to learn all about Facelets and explore the newest JSF 2.0 features. Finally you get learn a few integrations such as JSTL with JSF, Spring with JSF, and Hibernate with JSF. All these concepts are presented in the form of easy-to-follow recipes.

Each chapter discusses separate types of recipes and they are presented with an increasing level of complexity from simple to advanced. All of these recipes can be used with JSF 1.2 as well.

Publication date:
June 2010
Publisher
Packt
Pages
396
ISBN
9781847199522

 

Chapter 1. Using Standard and Custom Converters in JSF

In this chapter, we will cover:

  • Working with implicit and explicit conversions

  • Standard converters for numbers

  • Standard converters for date and time

  • Converters and NULL values

  • Creating and using a custom converter

  • Using converters for h:selectOneMenu

  • Binding converters to backing bean properties

  • RichFaces and standard converters

  • RichFaces and custom converters

  • Instance variables in converters

  • Client-side converters with MyFaces Trinidad

 

Introduction


Data conversion is the process of converting/transforming one data type into another. Before going further and analyzing some aspects of JSF converters, let's see what they actually are and what they are good for.

For this, let's take an example of a web application in which the user has to fill up a simple form with some information, such as name, age, and date of birth. The server component of our application will receive this information as strings, even if we know that they are a string (the name), an integer (the age), and a date (the date of birth). This is the phase when JSF converters enter into the scene and convert the user input according to application requirements. If the submitted information is not successfully converted then the form is redisplayed (this time an attention message is also displayed) and the user can refill the form. The case repeats until the submitted information is successfully converted to the correct type.

In addition, you should know that JSF provides a set of standard converters (used for the most common conversions) and the possibility to define your own converters, known as custom converters (this kind of converters are very useful when the standard converters can't accomplish the desired conversions). Speaking of standard converters, the following are the most used converters:

Converter IDs

Converter class

javax.faces.Byte

javax.faces.convert.ByteConverter

javax.faces.Float

javax.faces.convert.FloatConverter

javax.faces.BigInteger

javax.faces.convert.BigIntegerConverter

javax.faces.BigDecimal

javax.faces.convert.BigDecimalConverter

javax.faces.Character

javax.faces.convert.CharacterConverter

javax.faces.DateTime

javax.faces.convert.DateTimeConverter

javax.faces.Boolean

javax.faces.convert.BooleanConverter

javax.faces.Double

javax.faces.convert.DoubleConverter

javax.faces.Long

javax.faces.convert.LongConverter

javax.faces.Short

javax.faces.convert.ShortConverter

javax.faces.Integer

javax.faces.convert.IntegerConverter

Some JSF tags that support converters are as follows:

  • <h:outputText>

  • <h:outputLink>

  • <h:selectManyListbox>

  • <h:selectMaynyMenu>

  • <h:inputTextarea>

  • <h:inputHidden>

  • <h:outputLabel>

  • <h:inputText>

  • <h:inputSecret>

  • <h:selectBooleanCheckbox>

  • <h:selectOneRadio>

  • <h:selectOneListbox>

  • <h:outputFormat>

  • <h:selectOneMenu>

Speaking about a converter lifecycle, you should focus on two main phases named: Apply Request Values Phase and Render Response Phase . For example, if we assume a form that is submitted with a set of values, a converter for those values, a corresponding backing bean, and a render page, then the application lifecycle will be like this (notice when and where the converter is involved!):

  • Restore View Phase: The backing bean is created and the components are stored into the UIViewRoot.

  • Apply Request Values Phase: The submitted values are decoded and set in the corresponding components in UIViewRoot.

  • Process Validations Phase: The converter getAsObject method receives the submitted values (eventually a potential validator is also called).

  • Update Model Values Phase: The converted (validated) values are set in the backing bean.

  • Invoke Application Phase: The phase responsible for form processing.

  • Render Response Phase: The values that should be displayed are extracted from a backing bean. The getAsString method of the converter receives these values before rendering. The conversion results are redirected to the result page.

Using the proper converter is the developer's choice. The developer is also responsible for customizing the error messages displayed when the conversion fails. When the standard converters don't satisfy the application needs, the developer can write a custom converter as you will see in our recipes.

Notice that our recipes make use of JSF 2.0 features, such as annotation, new navigation style, and no faces-config.xml file. Especially you must notice the new @FacesConverter annotation for indicating to a normal class that it is a JSF 2.0 converter.

Let's start with a simple recipe about working with implicit and explicit conversions.

 

Working with implicit and explicit conversions


By implicit conversions, we understand all the conversions that JSF will accomplish automatically, without the presence of an explicit converter (in other words, if you don't specify a converter, JSF will pick one for you). Actually, JSF uses implicit conversion when you map a component's value to a managed bean property of a Java primitive type or of BigInteger and BigDecimal objects.

In this recipe, we will see an example of an implicit and an explicit conversion. Anyway, don't forget that explicit conversion provides greater control over the conversion.

Getting ready

We developed this recipe 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.

How to do it...

Our recipe is based on an imaginary situation where the user should insert their age into a simple JSF form consisting of a text field and a submit button. The submitted age will be implicitly converted and displayed on another simple JSF page. The following is the JSF form (the highlighted code maps the text field's value to the userAge managed bean property of a Java integer type):

<h:form id="AgeForm">
  <h:inputText id="userAgeID" required="true"      
               value="#{userBean.userAge}">
  </h:inputText>
  <h:message showSummary="true" 
             showDetail="false" for="userAgeID"
             style="color: red; text-decoration:overline"/>
  <br />
  <h:commandButton id="submit" action="response?faces-
                  redirect=true" value="Submit Age"/>
</h:form>

Note

The preceding code snippet makes uses of the new JSF 2 implicit navigation style. The {page_name}?faces-redirect=true request parameter indicates to JSF to navigate to the {page_name}. There is more about JSF 2 navigation in Chapter 11, JSF 2.0 Features.

The userAge is mapped into a managed bean as shown next:

package users;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
@ManagedBean
@SessionScoped
public class UserBean {
    private int userAge;
    public int getUserAge(){
        return this.userAge;
    }
    public void setUserAge(int userAge){
        this.userAge=userAge;
    }
}

As the userAge is a Java integer, JSF will automatically convert the inserted age to this type (notice that we did not indicate any conversion in the previous code). This is called an implicit conversion. In the case that the inserted age is not an integer, this will be reflected by an error message exposed by the h:message component.

Now, speaking of explicit conversion we can enforce the previous situation by using the UIComponent converter attribute or f:converter tag nested within a UIComponent. The modifications are reflected in the next two lines:

<!-- explicit conversion using the UIComponent converter attribute -->
<h:inputText id="userAgeID" required="true" 
             value="#{userBean.userAge}" 
             converter="javax.faces.Integer">
</h:inputText>
<!-- converter tag nested within a UIComponent -->
<h:inputText id="userAgeID" required="true" 
             value="#{userBean.userAge}">
  <f:converter converterId="javax.faces.Integer"/>
</h:inputText>

How it works...

There is no trick here! In the case of implicit conversion, JSF tries to identify which is the appropriate converter to be applied. Obviously, for explicit conversion, JSF tries to apply the indicated converter. When conversion fails, the form is redisplayed and an exception message is fired. Otherwise, the application follows its normal flow.

There's more...

You can mix explicit and implicit conversion over the same managed bean property, but, in this case, you should keep in mind the Java cast rules. For example, if you try to explicitly force an integer to a Byte type you will get an error, as java.lang.Integer type can't be cast to java.lang.Byte type, while a java.lang.Integer can be cast to java.lang.Double.

See also

The code bundled with this book contains a complete example of this recipe. The project can be opened with NetBeans 6.8 and is named: Working_with_implicit_and_explicit_conversion.

 

Standard converters for numbers


Numbers are a generic notion used to quantify many things, such as age, salary, percent, currency, custom pattern, and so on. Also, we know that numbers can be integers, floats, doubles, and so on. Depending on what we represent, we know what kind of number to use and how to write it in the correct format and with the correct symbols attached. In this recipe you will see how to accomplish this task using JSF standard capabilities. For this we will take a generic double number and we will output it to represent different things.

Getting ready

We developed this recipe 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.

How to do it...

Converting numbers and applying basic formats to them are tasks that can be accomplished by the f:convertNumber JSF converter. This converter can be customized using a set of attributes, listed next:

Attribute name

Description

type

Represents the type of number. By default this type is set to number, but you can set it to currency or percent.

pattern

Represents the decimal format pattern used to convert this number.

locale

Represents the locale to be used for displaying this number. The user's current locale is overridden.

maxIntegerDigits

Represents the maximum number of integer digits to display

minIntegerDigits

Represents the minimum number of integer digits to display.

maxFractionDigits

Represents the maximum number of fractional digits to display.

minFractionDigits

Represents the minimum number of fractional digits to display.

currencyCode

Represents a three-digit international currency code when the attribute type is currency.

currenySymbol

Represents a symbol, like $, to be used when the attribute type is currency.

integerOnly

Set the value of this attribute to true, if you want to ignore the fractional part of a number. By default it is set to false.

groupingUsed

Set the value of this attribute to true, if you want to use a grouping symbol like comma or space. By default it is set to true.

Now, let's suppose that we have the number 12345.12345 (five integer digits and five fraction digits). The following code will output this number using the f:convertNumber converter and the previously listed attributes:

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">
<h:head>
  <title>Standard converters for numbers - format numbers</title>
</h:head>
<h:body>
  <b><h:outputText value="-Formatting the double value 12345.12345-"/></b><br />
        <!-- Format as 00000.00000 -->
        <h:outputText value="Format as 00000.00000: "/>
        <h:outputText value="#{numbersBean.doubleNumber}">
            <f:convertNumber type="number" maxIntegerDigits="5" 
                             maxFractionDigits="5"
                             groupingUsed="false"/>
        </h:outputText>
        <br />
        <!-- Format as 00000 -->
        <h:outputText value="Format as 00000: "/>
        <h:outputText value="#{numbersBean.doubleNumber}">
            <f:convertNumber type="number" maxIntegerDigits="5" 
                             maxFractionDigits="0"/>
        </h:outputText>
        <br />
        <!-- Format as currency -->
        <h:outputText value="Format as currency: "/>
        <h:outputText value="#{numbersBean.doubleNumber}">
            <f:convertNumber type="currency" currencySymbol="$" 
                             maxIntegerDigits="5" 
                             maxFractionDigits="2"/>
        </h:outputText>
        <br />
        <!-- Format as percent -->
        <h:outputText value="Format as percent: "/>
        <h:outputText value="#{numbersBean.doubleNumber}">
            <f:convertNumber type="percent" maxIntegerDigits="5" 
                             maxFractionDigits="5"/>
        </h:outputText>
        <br />
        <!-- Format as pattern #####,00% -->
        <h:outputText value="Format as pattern #####,00%: "/>
        <h:outputText value="#{numbersBean.doubleNumber}">
             <f:convertNumber pattern="#####,00%"/>
        </h:outputText>
    </h:body>
</html>

The NumbersBean is the managed bean, as shown next:

package numbers;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
@ManagedBean
@SessionScoped
public class NumbersBean {
    private double doubleNumber = 12345.12345;
    public double getDoubleNumber(){
        return this.doubleNumber;
    }
    public void setDoubleNumber(double doubleNumber){
        this.doubleNumber=doubleNumber;
    }
}

The output will be as follows:

-Formatting the double value 12345.12345
Format as 00000.00000: 12345.12345 
Format as 00000: 12,345 
Format as currency: $12,345.12 
Format as percent: 34,512.345% 
Format as pattern #####,00%: 1,23,45,12%

How it works...

The number is displayed corresponding to the formatting attributes. The parts of the number that don't correspond to the conversion's restrictions are ignored or an error message is generated.

There's more...

Notice that we have used the f:convertNumber with the h:outputText component, but you can follow the same logic to use with the h:inputText component. These two components are the most used in conjunction with the f:convertNumber converter.

See also

The code bundled with this book contains a complete example of this recipe. The project can be opened with NetBeans 6.8 and is named: Standard_converters_for_numbers.

 

Standard converters for date and time


Measuring, representing, formatting, and localizing date and time was always an important issue for developers. In this recipe, you will see how to get different formats for date and time using JSF standard converters. We will display a date/time in different formats and for different locales.

Getting ready

We developed this recipe 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.

How to do it...

JSF provides a dedicated converter to accomplish tasks related to date and time, named converterDateTime. This converter can be customized through a set of attributes listed in the following table:

Attribute name

Description

type

Specifies whether to display the date, time, or both.

dateStyle

Specifies the formatting style for the date portion of the string. Supported values are medium (this is the default), short, long, and full. Only valid if attribute type is set.

timeStyle

Specifies the formatting style for the time portion of the string. Valid options are medium (this is the default), short, long, and full. Only valid if attribute type is set.

timeZone

Specifies the time zone for the date (For example, EST). By default GMT will be used.

locale

Specifies the locale to use for displaying the date (For example, Romania - "ro", Germany - "de", England - "en". Overrides the user's current locale.

pattern

Represents a date format pattern used to convert a number.

Now, let's suppose that we have the current date (provided by a java.util.Date instance). The next code will output this date using the f:converterDateTime converter and the previously listed attributes:

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">
    <h:head>
        <title>Standard converters for date and time</title>
    </h:head>
    <h:body>
      <b><h:outputText value="-Formatting the current dateand time-"/></b><br />
      <h:outputText value="#{datetimeBean.currentdate}">
        <f:convertDateTime type="date" dateStyle="medium"/>
      </h:outputText>
      <br />
      <h:outputText value="#{datetimeBean.currentdate}">
        <f:convertDateTime type="date" dateStyle="full"/>
      </h:outputText>
      <br />
      <h:outputText value="#{datetimeBean.currentdate}">
        <f:convertDateTime type="time" timeStyle="full"/>
      </h:outputText>
      <br />
      <h:outputText value="#{datetimeBean.currentdate}">
        <f:convertDateTime type="date" pattern="dd/mm/yyyy"/>
      </h:outputText>
      <br />
      <h:outputText value="#{datetimeBean.currentdate}">
        <f:convertDateTime dateStyle="full" pattern="yyyy-mm-dd"/>
      </h:outputText>
      <br />
      <h:outputText value="#{datetimeBean.currentdate}">
        <f:convertDateTime dateStyle="full" 
                          pattern="yyyy.MM.dd 'at' HH:mm:ss z"/>
      </h:outputText>
      <br />
      <h:outputText value="#{datetimeBean.currentdate}">
        <f:convertDateTime dateStyle="full" pattern="h:mm a"/>
      </h:outputText>
      <br />
      <h:outputText value="#{datetimeBean.currentdate}">
        <f:convertDateTime dateStyle="long" 
                          timeZone="EST" type="both" />
      </h:outputText>
      <br />
      <h:outputText value="#{datetimeBean.currentdate}">
        <f:convertDateTime locale="ro" 
                          timeStyle="long" type="both" 
                          dateStyle="full" />
      </h:outputText>
      <br />
      <h:outputText value="#{datetimeBean.currentdate}">
        <f:convertDateTime locale="de" 
                          timeStyle="short" type="both" 
                          dateStyle="full" />
      </h:outputText>
    </h:body>
</html>

The datetimeBean is listed next:

package datetime;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import java.util.Date;
@ManagedBean
@SessionScoped
public class DatetimeBean {
    private Date currentdate = new Date();
    public Date getCurrentdate(){
        return this.currentdate;
    }
    public void setCurrentdate(Date currentdate){
        this.currentdate=currentdate;
    }
}

The output will be as follows:

-Formatting the current date and time-
Jun 15, 2009 
Monday, June 15, 2009 
11:14:53 AM GMT 
15/14/2009 
2009-14-15 
2009.06.15 at 11:14:53 GMT 
11:14 AM 
June 15, 2009 6:14:53 AM 
15 iunie 2009 11:14:53 GMT 
Montag, 15. Juni 2009 11:14

See also

The code bundled with this book contains a complete example of this recipe. The project can be opened with NetBeans 6.8 and is named: Standard_converters_for_date_and_time.

 

Converters and NULL values


The idea of this recipe originates in the following JSF concept: a converter with NULL values is bypassed.

The problem occurs when we want to render a special message for a NULL property, instead of returning an empty String or a NULL value. At first view, a custom converter should fix the problem in an elegant manner, but at second view we notice that the NULL values never get called in the converter, which means that we can't control it before the render phase. This recipe proposes a solution to this problem.

Getting ready

We have developed this recipe 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.

How to do it...

The idea is to have a placebo object—an object that it is not NULL and which is passed to the converter instead of every NULL object. The converter can identify this object by a fixed property, for example its hash code, and every time it gets this object, it will return a custom message to be rendered. For example, if our objects are instances of java.util.Date, then we can write a placebo class like the following one:

//placebo class for java.util.Date
class Placebo extends java.util.Date {
    @Override
    public int hashCode() {
        return 0011001100;
    }
    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Placebo other = (Placebo) obj;
        return true;
    }   
}

Notice that we have arbitrarily chosen a fixed hash code as 0011001100. This hash code will mark the NULL values in the converter's getAsString method. However before that, we need to modify the getter method for our property as shown next (this is the entire bean, but we are focused on the getCurrentdate method):

package nullconv;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import java.util.Date;
@ManagedBean
@SessionScoped
public class NullBean {
    //valid date
    //private Date currentdate = new Date();
    //null date
    private Date currentdate = null;
    //placebo date
    private Date nulldate = new Placebo();
    public Date getCurrentdate() {
        if (currentdate == null) {
            return nulldate;
        }
        return this.currentdate;
    }
    public void setCurrentdate(Date currentdate) {
        this.currentdate = currentdate;
    }
}

Now, the converter gets a real date, when the currentdate property is not NULL, and it gets the placebo nulldate , when the currentdate property is NULL. Now, we know that the converter gets all the values, including the NULL ones. Next, the converter (getAsString method) will check the hash code of the objects, to see which one is NULL and which one is not. The following is the source code for this converter:

package nullconv;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.ConverterException;
import javax.faces.convert.DateTimeConverter;
import javax.faces.convert.FacesConverter;
@FacesConverter(value = "nullConverter")
public class NullConverter extends DateTimeConverter {
    @Override
    public String getAsString(FacesContext arg0, 
                  UIComponent arg1, Object arg2) {
        if (arg0 == null) 
           {throw new NullPointerException("context");}
        if (arg1 == null) 
           {throw new NullPointerException("component");}
        if (arg2 != null && !(arg2 instanceof java.util.Date)) {
            throw new ConverterException("Not valid date");
        }
        if (arg2.hashCode() == 0011001100) {
            return ("Not available!");
        }
        return super.getAsString(arg0, arg1, arg2);
    }
    @Override
    public Object getAsObject(FacesContext arg0, 
                  UIComponent arg1, String arg2) {
       if (arg0 == null) 
          {throw new NullPointerException("context");}
       if (arg1 == null) 
          {throw new NullPointerException("component");}
       return super.getAsObject(arg0, arg1, arg2);
    }
}

Now, the NULL values will be rendered with a "Not available!" message!

How it works...

Every time a NULL date is loaded into the bean it is replaced by the placebo date. This date has the particularity of having a well known hash code. When the placebo object gets into the converter, the getAsString method checks for this hash code. When it finds a match it returns a custom message instead of the String representation of the date, because it knows that the received value is actually a NULL one, which should not be rendered verbatim.

See also

The code bundled with this book contains a complete example of this recipe. The project can be opened with NetBeans 6.8 and is named: Converters_and_NULL_values.

 

Creating and using a custom converter


JSF custom converters run on the server/client side and can accomplish many specific business needs. Basically, JSF custom converters are created by extending the javax.faces.convert.Converter interface or by extending a standard converter class. In this recipe, you will see both cases.

Getting ready

We developed this recipe 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.

How to do it...

First, let's talk about the converters that implement the Converters interface. In this case, a converter should implement two methods, as follows:

The getAsObject method takes the FacesContext instance, the UI component, and the String to be converted to a specified object. According to the official documentation, this method:

Converts the specified string value, which is associated with the specified UIComponent, into a model data object that is appropriate for being stored during the Apply Request Values phase of the request processing lifecycle.

public Object getAsObject(FacesContext context, 
                          UIComponent component, 
                          java.lang.String value){
…
}

The getAsString method takes the FacesContext instance, the UI component, and the object to be converted to a String. According to the official documentation, this method:

Converts the specified model object value, which is associated with the specified UIComponent, into a String that is suitable for being included in the response generated during the Render Response phase of the request processing lifecycle.

public String getAsString(FacesContext context, 
                          UIComponent component, 
                          Object value){
…
}

This converter logic should use javax.faces.converter.ConverterException to throw the appropriate exceptions and javax.faces.application.FacesMessage to generate the corresponding error messages.

For example, the following custom converter will convert a java.util.Date into a format of type yyyy-MM-dd. This implementation will extend the Converter interface, as shown next:

package datetime;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
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 = "customDateConverterImpl")
public class CustomDateConverterImpl 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");
        }
        final Date date = (Date) arg2;
        String DATE_FORMAT = "yyyy-MM-dd";
        SimpleDateFormat sdf =
                new SimpleDateFormat(DATE_FORMAT);
        Calendar c1 = Calendar.getInstance(); // today
        c1.setTime(date);
        return sdf.format(c1.getTime());
    }
    public Object getAsObject(FacesContext arg0, UIComponent arg1, String arg2) {
        if (arg0 == null) {
            throw new NullPointerException("context");
        }
        if (arg1 == null) {
            throw new NullPointerException("component");
        }
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
        try {
            Date today = df.parse(arg2);
            return today;
        } catch (ParseException e) {
            FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR,
                    "Parser error!", "Cannot parse this date!");
            throw new ConverterException(message);
        }
    }
}

The previous converter can be called from an XHTML page as shown next (notice that we pass to the converter attribute the value from the @FacesConverter annotation; this annotation defines a name for a converter and it is specific to JSF 2.0):

<h:form id="customDateTimeID">
 <h:inputText id="dateID" value="#{datetimeBean.currentdate}" 
              converter="customDateConverterImpl">
 </h:inputText>
 <h:message showSummary="true" showDetail="false" for="dateID"
            style="color: red; text-decoration:overline"/>
 <br />
 <h:commandButton value="Submit" 
                  action="selected?faces-redirect=true"/>
</h:form>

Now, let's discuss converters that extend existing converters. In this case, we override the getAsString and getAsObject methods (mark them with the @Override annotation) or we can call setter methods from the extended converter. For example, we can extend the DateTimeConverter and call the setPattern to obtain the same effect as the previous converter.

package datetime;
import java.util.TimeZone;
import javax.faces.convert.DateTimeConverter;
import javax.faces.convert.FacesConverter;
@FacesConverter(value = "customDateConverterExtend")
public class CustomDateConverterExtend extends DateTimeConverter {
  public CustomDateConverterExtend() {
    super();
    setTimeZone(TimeZone.getDefault());
    setPattern("yyyy-MM-dd");
  }
}

How it works...

A JSF converter is called from two directions. It is called once during the Apply Request Values Phase and once during the Render Response Phase. In Apply Request Values Phase the converter is called through getAsObject method, which is responsible to for converting the user inputs, while in the Render Response the converter is called through the getAsString method, which is responsible to for converting outputs before rendering.

There's more...

Keep in mind that in JSF 2.0 we don't need a faces-config.xml descriptor, and converters need not be declared in any XML file. If you are using JSF 1.2 then you have to register converters in the faces-config.xml document following the syntax listed next:

<converter>
  <converter-id>CONVERTER_ID</converter-id>
  <converter-class>CONVERTER_CLASS_NAME</converter-class>    
</converter>

See also

The code bundled with this book contains a complete example of this recipe. The project can be opened with NetBeans 6.8 and it is named: Creating_and_using_a_custom_converter.

 

Using custom converters for h:selectOneMenu


A common issue regarding JSF converters and the h:selectOneMenu component can be recreated in a simple scenario. Let's suppose that we are in the following situation: we have a database table that contains a number of rows that define cars. Each row has an Integer value representing the car number and a string value representing the car name. Obviously this table is wrapped into a managed bean, as shown next:

package cars;
import javax.faces.bean.ManagedBean;
@ManagedBean
public class CarBean {
  private Integer carNumber;
  private String carName;
  public CarBean() {}
  public CarBean(Integer carNumber, String carName){
    this.carNumber=carNumber;
    this.carName=carName;
  }
  public Integer getCarNumber(){
    return this.carNumber;
  }
  public void setCarNumber(Integer carNumber){
    this.carNumber=carNumber;
  }
  public String getCarName(){
    return this.carName;
  }
  public void setCarName(String carName){
    this.carName=carName;
  }
}

Going further, let's have another managed bean that contains a collection of cars (we simulate the table database with a few manual instances), as shown next:

package cars;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.model.SelectItem;
@ManagedBean(name = "carsBean")
@SessionScoped
public class CarsBean {
  private HashMap<Integer, CarBean> myCars = 
                                     new HashMap<Integer, CarBean>();
    private List<SelectItem> carItems = new LinkedList<SelectItem>();
    private CarBean selectedCar;
    public CarsBean() {
        CarBean car_1 = new CarBean(1, "Ferrari");
        CarBean car_2 = new CarBean(2, "Logan");
        CarBean car_3 = new CarBean(3, "Fiat");
        CarBean car_4 = new CarBean(4, "Kia");
        CarBean car_5 = new CarBean(5, "Skoda");
        carItems.add(new SelectItem(car_1, car_1.getCarName()));
        myCars.put(car_1.getCarNumber(), car_1);
        carItems.add(new SelectItem(car_2, car_2.getCarName()));
        myCars.put(car_2.getCarNumber(), car_2);
        carItems.add(new SelectItem(car_3, car_3.getCarName()));
        myCars.put(car_3.getCarNumber(), car_3);
        carItems.add(new SelectItem(car_4, car_4.getCarName()));
        myCars.put(car_4.getCarNumber(), car_4);
        carItems.add(new SelectItem(car_5, car_5.getCarName()));
        myCars.put(car_5.getCarNumber(), car_5);
    }
    public CarBean getCar(Integer number) {
        return (CarBean) myCars.get(number);
    }
    public List<SelectItem> getCarItems() {
        return carItems;
    }
    public void setCarItems(List<SelectItem> carItems) {
        this.carItems = carItems;
    }
    public CarBean getSelectedCar() {
        return this.selectedCar;
    }

    public void setSelectedCar(CarBean selectedCar) {
        this.selectedCar = selectedCar;
    }
} 

Now, we can render our car collection using an h:selectOneMenu component, as shown next:

<h:form id="selectCarFormID">
 <h:selectOneMenu id="carsID" value="#{carsBean.selectedCar}">
  <f:selectItems value="#{carsBean.carItems}"/>
 </h:selectOneMenu>
 <h:commandButton value="Submit" action="selected?faces-
    redirect=true"/>
</h:form>

Well, the car list is rendered ok, as you can see the list and make a selection. However, the problem occurs when we choose a car and we try to populate the selectedCar property with it. As you see, the selectedCar is a CarBean instance, while the submitted information represents an integer (the car number). Therefore, we need to convert this integer to a CarBean, before it gets rendered, as shown next:

<h:outputText value="Selected car number:"/>
<h:outputText value="#{carsBean.selectedCar.carNumber}"/>
  <br />
<h:outputText value="Selected car name:"/>
<h:outputText value="#{carsBean.selectedCar.carName}"/>

Getting ready

We developed this recipe 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.

How to do it...

The solution came from a custom converter. In the getAsString object, we extract and return the car number, and in the getAsObject method, the submitted car number is converted into a CarBean instance, as shown in the following code:

package cars;
import javax.el.ValueExpression;
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 = "carConverter")
public class CarConverter 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");}
      return ((CarBean)arg2).getCarNumber().toString();
    }
    public Object getAsObject(FacesContext arg0, UIComponent arg1, String arg2) {
      if (arg0 == null){throw new NullPointerException("context");}
      if (arg1 == null){throw new NullPointerException("component");}
      FacesContext ctx = FacesContext.getCurrentInstance();
      ValueExpression vex = ctx.getApplication().getExpressionFactory().createValueExpression(ctx.getELContext(), "#{carsBean}",CarsBean.class);
      CarsBean cars = (CarsBean)vex.getValue(ctx.getELContext());
      CarBean car;
      try {            
      car = cars.getCar(new Integer (arg2));
      } catch( NumberFormatException e ) {
      FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR,
        "Unknown value", "This is not a car number!" );
         throw new ConverterException( message );
      }
      if( car == null ) {
      FacesMessage message = new FacesMessage( FacesMessage.SEVERITY_ERROR,
        "Unknown value", "The car is unknown!" );
        throw new ConverterException( message );
      }
      return car;
    }
}

How it works...

The mechanism is pretty simple! First, the collection of cars is rendered using a SelectItem object. Every single car will pass through the converter's getAsString method and is added to the list. Notice that the getAsString method extracts and returns the car number for each car.

Second, when a car is selected and submitted, the selected car number arrives into the getAsObject method. There we search for the corresponding car into our myCars map. Once the car is found it is returned into the setSelectedCar method.

There's more...

You can use the same technique for h:selectManyCheckbox or h:selectManyListbox. For example, in the case of h:selectManyCheckbox, you will render the list in the following way:

<h:form id="selectCarFormID">
 <h:selectManyCheckbox id="carsID" 
                       value="#{carsBean.selectedCar}"  
                       converter="carConverter">
   <f:selectItems value="#{carsBean.carItems}"/>
 </h:selectManyCheckbox>
 <h:commandButton value="Submit" 
                  action="selected?faces-redirect=true"/>
</h:form>

And the selections can be rendered, as shown next:

<h:dataTable  value="#{carsBean.selectedCar}" var="item">
  <h:column>
    <f:facet name="header">
      <h:outputText value="Car Name:"/>
    </f:facet>
    <h:outputText value="#{item.carName}"/>
  </h:column>
</h:dataTable>

See also

The code bundled with this book contains a complete example of this recipe. The project can be opened with NetBeans 6.8 and is named: Using_custom_converters_for_selectOneMenu_1 and Using_custom_converters_for_selectOneMenu_2.

 

Binding converters to backing bean properties


JSF standard converter tags allow binding attributes (this is also true for listener and validator tags). This means that developers can bind converter implementations to backing bean properties. The main advantages of using the binding facility are:

  • The developer can allow the backing bean to instantiate the implementation

  • The backing bean can programmatically access the implementation's attributes

Getting ready

We developed this recipe 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.

How to do it...

To successfully accomplish a binding task, you can follow the three simple steps listed next (these steps are true for converter, listener, and validator tags):

  1. Nest the converter (listener, validator) tag in the component tag.

  2. Put in the backing bean a property that takes and returns the converter (listener, validator) implementation class.

  3. Reference the backing bean property using a value expression from the binding attribute of the converter (listener, validator) tag.

For example, let's bind the standard convertNumber converter to a backing bean property. The idea is to let the backing bean set the formatting pattern of the user's input. First, you have to register the converter onto the component by nesting the convertNumber tag within the component tag. Then, you have to reference the property with the binding attribute of the convertNumber tag, as shown next:

<h:form id="numberFormID">
  <h:inputText id="numberID" value="#{numbersBean.numbery}">
    <f:convertNumber binding="#{numbersBean.number}" />
  </h:inputText>
  <h:message showSummary="true" showDetail="false" for="numberID"
             style="color: red; text-decoration:overline"/>
  <br />
  <h:commandButton value="Submit" 
                   action="selected?faces-redirect=true"/>
</h:form>

The number property would be similar to the following code:

package numbers;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.convert.NumberConverter;
@ManagedBean
@SessionScoped
public class NumbersBean {
    private NumberConverter number;
    private float numbery;
public float getNumbery(){
    return this.numbery;
 }
public void setNumbery(float numbery){
    this.numbery=numbery;
 }
public NumberConverter getNumber(){
    return this.number;
 }
public void setNumber(NumberConverter number){
    number.setType("currency");
    number.setCurrencySymbol("$");
    this.number=number;
 }
}

How it works...

In our example, the backing bean sets the formatting pattern within the convertNumber tag, which means that the user's input will be constrained to this pattern. This time the numbers are formatted as currencies, without using specific attributes in the convertNumber tag. Instead of this we use the binding attribute to reference the number property, which is a NumberConverter instance, offering us access to this class's methods.

See also

The code bundled with this book contains a complete example of this recipe. The project can be opened with NetBeans 6.8 and is named: Bind_converters_to_backing_bean_properties.

 

RichFaces and standard converters


This recipe will show you how to use one of the standard converters defined in RichFaces. First you have to know that RichFaces 3.3.3 comes with a set of converters that can be found in the following packages:

  • org.richfaces.convert

  • org.richfaces.convert.rowkey

  • org.richfaces.convert.seamtext

  • org.richfaces.convert.seamtext.tags

  • org.richfaces.convert.selection

  • org.richfaces.converter

In this recipe, we will use the org.richfaces.convert.IntegerColorConverter for converting an RGB color from a RichFaces ColorPicker component into an integer and vice versa.

Getting ready

We developed this recipe 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. 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...

In RichFaces, we can use the converter attribute or f:converter tag nested within a UIComponent. This is pretty similar to the JSF standard utilization of converters. For example, in the following code we have a colorPicker component and we apply the IntegerColorConverter converter to the selected color using the converter attribute. The result of conversion is an integer representation of the color and it is rendered into an outputText component:

<a4j:form>
 <h:outputText value="The integer version of 
   the selected color:"/>
 <h:outputText id="RGBvalue" value="#{colorPickerBean.color}"/>        
 <rich:panel header="RichFaces Color Picker" 
   style="width: 315px">
  <rich:colorPicker value="#{colorPickerBean.color}" 
    colorMode="rgb" converter="org.richfaces.IntegerColor">  
    <a4j:support event="onchange" reRender="RGBvalue"/>
  </rich:colorPicker>			
 </rich:panel>
</a4j:form>

Notice that the IntegerColorConverter ID is org.richfaces.IntegerColor. You can find the converters' IDs in the Javadoc of RichFaces.

The ColorPickerBean can be written in the following way:

package colorpicker;
public class ColorPickerBean {
  private Integer color;
  /**
   * @return ColorPickerBean color
   */
  public Integer getColor() {
    return color;
  }
  /**
   * @param ColorPickerBean color
  */
  public void setColor(Integer color) {
    this.color = color;
  }
}

How it works...

It works exactly like a JSF standard converter. If the value passes the conversion phase, then the backing bean receives the converted value, otherwise the user gets an error message and the option to try again.

See also

The code bundled with this book contains a complete example of this recipe. The project can be opened with NetBeans 6.8 and it is named: RichFaces_standard_and_custom_converters.

 

RichFaces and custom converters


In this recipe, we will develop and use a custom converter in RichFaces. This will convert an RGB color, extracted from a colorPicker, into an integer similar to the result of the java.awt.Color.getRGB method and vice versa. The result is rendered with an outputText component.

Notice that an RGB color from a colorPicker is a String formatted as rgb(red, green, blue).

Getting ready

We developed this recipe 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. 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...

A RichFaces custom converter follows the same principles as a JSF custom converter. We can implement the Converter interface or extend an existing converter class. For example, in this case we will implement the Converter interface and we will implement the getAsString and getAsObject methods. As the code is self-explanatory there is no need for more details:

package colorpicker;
import java.awt.Color;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import java.util.StringTokenizer;
public class RGBConverter implements Converter {
public static final String CONVERTER_ID = "rgbConverter";
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (context == null) {
    throw new NullPointerException("context");
    }
if (component == null) {
    throw new NullPointerException("component");
    }
String getRGBfromString = value.substring(4, value.length() - 1);
StringTokenizer rgbComponents = new StringTokenizer(getRGBfromString,",");
int r = Integer.valueOf(rgbComponents.nextToken().trim());
int g = Integer.valueOf(rgbComponents.nextToken().trim());
int b = Integer.valueOf(rgbComponents.nextToken().trim());
Color rgbColor = new Color(r, g, b);
int rgbValue = rgbColor.getRGB();
Integer rgbValueInt = new Integer(rgbValue);
return rgbValueInt;
}
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (context == null) {
    throw new NullPointerException("context");
    }
if (component == null) {
    throw new NullPointerException("component");
    }
Color rgbColor = new Color((Integer) value);
String stringRGB = "rgb(" + rgbColor.getRed() + ","
+ rgbColor.getGreen() + "," + rgbColor.getBlue() + ")";
return stringRGB;
    }
}

Calling this converter is a simple task that we have accomplished as shown next:

<a4j:form>
 <h:outputText value="The integer version of the 
  selected color:"/>
<h:outputText id="RGBvalue" value="#{colorPickerBean.color}"/>        
<rich:panel header="RichFaces Color Picker" style="width: 315px">
 <rich:colorPicker value="#{colorPickerBean.color}"    
   colorMode="rgb" converter="rgbConverter">  
  <a4j:support event="onchange" reRender="RGBvalue"/>
 </rich:colorPicker>
</rich:panel>
</a4j:form>

The ColorPickerBean can be written in the following way:

package colorpicker;
public class ColorPickerBean {
private Integer color;
/**
 * @return ColorPickerBean color
 */
public Integer getColor() {
	return color;
	}
/**
 * @param ColorPickerBean color
 */
public void setColor(Integer color) {
	this.color = color;
	}
}

How it works...

It works exactly like a JSF custom converter. See the How it works… section, in the Creating and using a custom converter recipe.

See also

The code bundled with this book contains a complete example of this recipe. The project can be opened with NetBeans 6.8 and is named: RichFaces_standard_and_custom_converters.

 

Instance variables in converters


If you are making a simple attempt to declare an instance variable in a converter, you will notice that you can't store the variable state over time. This may look like a strange behavior, but the truth is that the getAsObject and getAsString are called on different instances. This is the simple explanation of why the instance variable doesn't have persistence over these methods calls.

We can fix this using UIComponent set/getAttribute or using a session variable instead. In this recipe, we will use a session variable to simulate an instance variable of a converter. For this, let's suppose that we have two numbers, one inserted by the user and one is selected by the user from a selectOneMenu component. The inserted value is multiplied with the selected value, inside of a custom converter, in the getAsObject method. In the backing bean we keep the multiplied result. Before the result is rendered, its value is divided by the same value in the getAsString method. If everything works fine, then we will not notice these operations over the inserted value.

Getting ready

We developed this recipe 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.

How to do it...

Storing the selected value in the session is a simple task. First, the backing bean associated to this value is marked with the annotation @SessionScoped , indicating that the instance of this bean should be stored in session. Second, we pass the selected value in the traditional way (this code is from the multiply.xhtml page of the application), as shown next:

<h:form id="MultiplyForm">
 <h:outputText value="Select the multiply factor:" />
 <h:selectOneMenu id="factorID" 
                  value="#{factorBean.selectedFactor}">
  <f:selectItems value="#{factorBean.factors}"/>
 </h:selectOneMenu>          
 <h:commandButton id="submit" action="number?faces-redirect=true" value="Submit"/>
</h:form>

The selectedFactor property belongs to the next backing bean:

package multiply;
import java.util.LinkedList;
import java.util.List;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.model.SelectItem;
@ManagedBean
@SessionScoped
public class FactorBean {
 private List<SelectItem> factors = new LinkedList<SelectItem>();
 private double selectedFactor;
public FactorBean(){        
        factors.add(new SelectItem("1.0", "1.0"));
        factors.add(new SelectItem("2.0", "2.0"));
        factors.add(new SelectItem("3.0", "3.0"));
        factors.add(new SelectItem("4.0", "4.0"));
        factors.add(new SelectItem("5.0", "5.0"));      
    }
public List<SelectItem> getFactors() {
        return factors;
    }
public void setFactors(List<SelectItem> factors) {
        this.factors = factors;
    }
public double getSelectedFactor() {
        return this.selectedFactor;
    }
public void setSelectedFactor(double selectedFactor) {
        this.selectedFactor = selectedFactor;
    }
}

Now, the multiplication factor is on session and we can request the user to insert a value to be multiplied by this factor (number.xhtml), as shown next:

<h:form id="NumberForm">
   <h:outputText value="Insert the value to be multiplied:"/>
   <h:inputText id="valueID" required="true" 
                value="#{multiplyBean.value}" 
                converter="multiplyConverter" />
   <h:message showSummary="true" showDetail="false" for="valueID"
              style="color: red; text-decoration:overline"/>
   <br />
   <h:commandButton id="submit" action="number?faces-
          redirect=true" value="Submit"/>
</h:form>

The value is stored in the MultiplyBean, as shown next:

package multiply;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
@ManagedBean
@SessionScoped
public class MultiplyBean {
    private double value = 0.0d;
    public double getValue() {
        return this.value;
    }
    public void setValue(double value) {
        this.value = value;
    }
}

As you can see the operations are taking place in a converter. Now, the converter has access to the multiplication factor in a very easy approach, as shown next:

public String getAsString(FacesContext arg0, UIComponent arg1, 
                          Object arg2) {
    if (arg0 == null) {
            throw new NullPointerException("context");
        }
    if (arg1 == null) {
            throw new NullPointerException("component");
        }
    FacesContext ctx = FacesContext.getCurrentInstance();
    ValueExpression vex =  
         ctx.getApplication().getExpressionFactory().
         createValueExpression(ctx.getELContext(), 
         "#{factorBean}", FactorBean.class);
    FactorBean c = (FactorBean) vex.getValue(ctx.getELContext());
    try {
       Double dividedVal = (Double) arg2 / c.getSelectedFactor();
       return dividedVal.toString();
       } catch (Exception e) {
       FacesMessage message = new 
        FacesMessage(FacesMessage.SEVERITY_ERROR,
        "Error!", "Cannot accomplish this operation (DIVIDE) !");
         throw new ConverterException(message);
        }
    }
public Object getAsObject(FacesContext arg0, UIComponent arg1, String arg2) {
    if (arg0 == null) {
            throw new NullPointerException("context");
        }
    if (arg1 == null) {
            throw new NullPointerException("component");
        }
    FacesContext ctx = FacesContext.getCurrentInstance();
    ValueExpression vex =  
         ctx.getApplication().getExpressionFactory().
         createValueExpression(ctx.getELContext(), 
         "#{factorBean}", FactorBean.class);
    FactorBean c = (FactorBean) vex.getValue(ctx.getELContext());
    try {
       Double val = new Double(arg2);
       Double multiplyVal = val * c.getSelectedFactor();
       return multiplyVal;
       } catch (NumberFormatException e) {
       FacesMessage message = new 
        FacesMessage(FacesMessage.SEVERITY_ERROR,
        "Error!","Connot accomplish this operation (MULTIPLY)!");
         throw new ConverterException(message);
         }
}

How it works...

First, we store in the session the value that we need to have access to in the converter's methods. Second, we call this session value from the getAsString and getAsObject methods. Using this technique we have replaced the instance variable of the converter with a session variable.

See also

The code bundled with this book contains a complete example of this recipe. The project can be opened with NetBeans 6.8 and is named: Instance_variables_in_converters.

 

Client-side converters with MyFaces Trinidad


A great facility of Apache MyFaces Trinidad is that it supports client-side versions of JSF converters and validators. This means that errors are detected on the client machine, and the server is not involved. In this recipe, we will create such a converter for converting a number into an IP address. Our restrictions will be as follows:

  • The IP address should have exactly 12 digits

  • The IP will always have a pattern of 000.000.000.000

  • The IP can be supplied like 000000000000 or 000.000.000.000

The idea of Apache Trinidad client conversion is that it works on the client in a very similar way to how it works on the server, but in this case the language on the client is JavaScript instead of Java. By convention, JavaScript objects are prefixed in Trindad with the tr prefix, in order to avoid name collisions. There are JavaScript converter objects that support the methods getAsString and getAsObject. A TrConverter can throw a TrConverterException.

Let's see what are the steps that should be accomplished to create such a converter.

Getting ready

We developed this recipe 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. In addition, we have used Apache Trinidad 2.0.0, which provides support for JSF 2.0. You can download this distribution from http://myfaces.apache.org/trinidad/index.html. The Apache Trinidad libraries (including necessary dependencies) are in the book code bundle, under the |JSF_libs|Apache Trinidad - JSF 2.0 folder.

How to do it...

We will develop a complete application, including the client-side converter by following the four listed steps:

  1. Develop a JavaScript version of the converter. Before doing this you have to be aware of the Trindad API, which is listed next (this can also be found on the Trinidad website http://myfaces.apache.org/trinidad/index.html):

    /**
     * Converter "interface" similar to javax.faces.convert.Converter,
     * except that all relevant information must be passed to the constructor
     * as the context and component are not passed to the getAsString or getAsObject method 
     *
     */
    function TrConverter()
    {
    }
    /**
     * Convert the specified model object value, into a String for display
     *
     * @param value Model object value to be converted 
     * @param label label to identify the editableValueHolder to the user 
     * 
     * @return the value as a string or undefined in case of no converter mechanism is
     * available (see TrNumberConverter).
     */
    TrConverter.prototype.getAsString = function(value, label){}
    /**
     * Convert the specified string value into a model data object 
     * which can be passed to validators
     *
     * @param value String value to be converted 
     * @param label label to identify the editableValueHolder to the user 
     * 
     * @return the converted value or undefined in case of no converter mechanism is
     * available (see TrNumberConverter).
     */
    TrConverter.prototype.getAsObject = function(value, label){}
    TrConverters can throw a TrConverterException, which should contain a TrFacesMessage. Here is the signature for TrFacesMessage: 
    /**
     * Message similar to javax.faces.application.FacesMessage
     *
     * @param summary - Localized summary message text
     * @param detail - Localized detail message text 
     * @param severity - An optional severity for this message.  Use constants
     *  SEVERITY_INFO, SEVERITY_WARN, SEVERITY_ERROR, and
     *  SEVERITY_FATAL from the FacesMessage class.  Default is
     *  SEVERITY_INFO
     */
    function TrFacesMessage(
      summary,
      detail,
      severity
      )

    The signature for the TrConverterException is as follows:

    /** 
     * TrConverterException is an exception thrown by the getAsObject() or getAsString() 
     * method of a Converter, to indicate that the requested conversion cannot be performed.
     *
     * @param facesMessage the TrFacesMessage associated with this exception
     * @param summary Localized summary message text, used to create only if facesMessage is null
     * @param detail Localized detail message text, used only if facesMessage is null
     */
    function TrConverterException(
      facesMessage, 
      summary,
      detail
      )

    Another useful API that can be used to format messages is shown next:

    /**
     * TrFastMessageFormatUtils is a greatly reduced version
     * of the java.text.MessageFormat class, but delivered as a utility. 
     * <p>
     * The only syntax supported by this class is simple index-based
     * replacement, namely:
     * <pre>
     *     some{1}text{0}here{2}andthere
     * </pre>
     * as well as escaping using single quotes.  Like MessageFormat,
     * a single quote must be represented using two consecutive single
     * quotes, but the contents of any text between single quotes
     * will not be interpreted.  So, the following pattern could
     * be used to include a left bracket:
     * <pre>
     *     some'{'text{0}
     * </pre>
     */
    function TrFastMessageFormatUtils()
     /**
      * Formats the given array of strings based on the initial
      * pattern.  
      * @param {String} String to format
      * @param {any...:undefined} Varargs objects to substitute for positional parameters.
      * Each parameter will be converted to a String and substituted into the format.
      */
    TrFastMessageFormatUtils.format = function(
      formatString, // error format string with embedded indexes to be replaced
      parameters    // {any...:undefined} Varargs objects to substitute for positional parameters.
      )
    

    Based on this API, we have developed the JavaScript version of our IP converter as follows (IPConverter.js):

    function ipGetAsString(value, label)
    {
      return value.substring(0,3) + '.' + value.substring(3,6) + '.' + value.substring(6,9) + '.' + value.substring(9,12);
    }
    function ipGetAsObject(value, label)
    { 
      if (!value)return null;
      var len=value.length;
      var messageKey = IPConverter.NOT;
      if (len < 12 )
        messageKey = IPConverter.SHORT;
      else if (len > 15)
        messageKey = IPConverter.LONG;
      else if ((len == 12)||(len == 15))
      { 
          return value;
      }
      if (messageKey!=null && this._messages!=null)
      { 
        // format the detail error string
        var detail = this._messages[messageKey];
        if (detail != null)
        {
          detail = TrFastMessageFormatUtils.format(detail,
                                                label, value);
        }
        var facesMessage = new TrFacesMessage(
                            this._messages[IPConverter.SUMMARY],
                            detail,
                            TrFacesMessage.SEVERITY_ERROR)
       throw new TrConverterException(facesMessage);
     }
     return null;
    }
    function IPConverter(messages) {
        this._messages = messages;
    }
    IPConverter.prototype = new TrConverter();
    IPConverter.prototype.getAsString = ipGetAsString;
    IPConverter.prototype.getAsObject = ipGetAsObject;
    IPConverter.SUMMARY = 'SUM';
    IPConverter.SHORT = 'S';
    IPConverter.LONG = 'L';
    IPConverter.NOT = 'N';
  2. Next we bind the JavaScript converter with the Java converter. For this we have to implement the org.apache.myfaces.trinidad.converter.ClientConverter interface. The methods of this interface are:

    • getClientLibrarySource(): returns a library that includes an implementation of the JavaScript Converter object.

    • getClientConversion(): returns a JavaScript constructor, which will be used to instantiate an instance of the converter.

    • getClientScript(): can be used to write out inline JavaScript.

    • getClientImportNames(): is used to import the built-in scripts provided by Apache MyFaces Trinidad.

    Now, the Java version of our IPConverter looks like this (notice the constructor used to instantiate the JavaScript version):

    package converterJSF;
    import java.util.Collection;
    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 org.apache.myfaces.trinidad.convert.ClientConverter;
    import org.apache.myfaces.trinidad.util.LabeledFacesMessage;
    public class IPConverter implements Converter, ClientConverter
    {
      private static final String _SHORT_ERROR_TEXT = "The value is to short for an IP of type 000.000.000.000!";
      private static final String _LONG_ERROR_TEXT = "The value is to long for an IP of type 000.000.000.000!";
      private static final String _INVALID_ERROR_TEXT = "The value is not a valid IP number";
      public static final String CONVERTER_ID = "converterJSF.IP";
      //getAsObject
      public Object getAsObject(FacesContext context, UIComponent component, String value)
      {	      
      if ( value == null || value.trim().length() == 0)
          return null;
          String ipValue = value.trim();
          int length = ipValue.length();
          if ( length < 12 )
          {
          throw new ConverterException(_getMessage(component, _SHORT_ERROR_TEXT));
          }
          if ( length > 15 )
          {
          throw new ConverterException(_getMessage(component, _LONG_ERROR_TEXT));
          }
         //12
          if (length == 12)
          {
           try
             {
               return Long.valueOf(ipValue);
             } catch(NumberFormatException e)
             {
               throw new ConverterException(_getMessage(component, _INVALID_ERROR_TEXT));
             }
          }
         //15
          if (length == 15)
          {
           try
             {
             String extractIP = ipValue.substring(0,3) + 
             ipValue.substring(4,7) + ipValue.substring(8,11) + 
             ipValue.substring(12,15);
             return Long.valueOf(extractIP);
             } catch(NumberFormatException e)
              {
                throw new ConverterException(_getMessage(component, _INVALID_ERROR_TEXT));
              }
          }
      throw new ConverterException(_getMessage(component, _INVALID_ERROR_TEXT));
    }
      //getAsString
      public String getAsString(FacesContext context, UIComponent component, Object value)
      {
      if ( value == null || !(value instanceof Long))
         return null;
       Long longValue=(Long)value;
       String valueString = longValue.toString();	      	      
       String ip="000.000.000.000";
       if (valueString.length() == 12) 
         {
         ip = valueString.substring(0,3) + '.' +
              valueString.substring(3,6) + '.' +
              valueString.substring(6,9) + '.' +
              valueString.substring(9,12);
         }
      return ip;
      }
      //implement the ClientConverter's getClientImportNames
      public Collection<String> getClientImportNames()
        {
           return null;
        }
      //implement the ClientConverter's getClientLibrarySource
      public String getClientLibrarySource(
       FacesContext context)
       {
       return context.getExternalContext().getRequestContextPath() + 
                 "/jsLibs/IPConverter.js";    
       }
      //implement the ClientConverter's getClientConversion
      public String getClientConversion(FacesContext context, 
                                        UIComponent component)
       {  
       return ("new IPConverter({"
       + "SUM:'Invalid IP.',"
       + "S:'Value \"{1}\" is too short for an 000.000.000.000 IP.',"
       + "L:'Value \"{1}\" is too long for an 000.000.000.000 IP.',"
       + "N:'Value \"{1}\" is not a valid IP of type 000.000.000.000 .'})"
      );
      }
      //implement the ClientConverter's getClientScript
      public String getClientScript(FacesContext context, 
                                    UIComponent component)
      {
        return null;
      }
      private LabeledFacesMessage _getMessage(UIComponent component, String text)
      {
        // Using the LabeledFacesMessage allows the <tr:messages> component to
        // properly prepend the label as a link.
        LabeledFacesMessage lfm =
          new LabeledFacesMessage(FacesMessage.SEVERITY_ERROR,
                                  "Conversion Error", text);
        if (component != null)
        {
          Object label = null;
          label = component.getAttributes().get("label");
          if (label == null)
            label = component.getValueExpression("label");
          if (label != null)
            lfm.setLabel(label);
        }
        return lfm;
      }
    }
  3. Next we need to create a tag for this converter. For example, let's name this tag converterIP:

    <tag>
      <name>convertIP</name>   
      <tag-class>converterJSF.IPConverterTag</tag-class>
      <body-content>empty</body-content>
      <description>
         The convertIP tag converts a number to/from an IP address.
      </description>
    </tag>

    The IPConverterTag is as follows:

    package converterJSF;
    import javax.faces.application.Application;
    import javax.faces.context.FacesContext;
    import javax.faces.convert.Converter;
    import javax.faces.webapp.ConverterELTag;
    import javax.servlet.jsp.JspException;
    public class IPConverterTag extends ConverterELTag
    {
      public IPConverterTag()
      {
      }
      @Override
      protected Converter createConverter() throws JspException
      {
        Application app = FacesContext.getCurrentInstance().getApplication();
        IPConverter converter = (IPConverter)app.createConverter(IPConverter.CONVERTER_ID);
        return converter;
      }
    }
  4. Call the converter from a JSP page, as shown next:

    <tr:inputText value="#{ipBean.ip}"
      label="Insert a number of type 000000000000/000.000.000.000:">
      <trip:convertIP />
    </tr:inputText>

How it works...

The submitted values are first evaluated by the JavaScript converter. As this converter runs on the client side, it can return potential errors almost immediately. If the submitted values successfully pass the JavaScript converter, then they arrive into the Java converter (on the server side) and after that in the backing bean. Reversing the process, the result values pass first through the Java converter and after that, through the JavaScript converter.

There's more...

Speaking about another release of Apache MyFaces you should know that Apache MyFaces Tomahawk project contains several custom objects that do not implement UIComponent. Some of these include objects that implement the Converter interface.

See also

The code bundled with this book contains a complete example of this recipe. The project can be opened with NetBeans 6.8 and is named: Client_side_converters_with_Apache_Trinidad.

About the Author

  • Anghel Leonard

    Anghel Leonard is a chief technology strategist with more than 20 years’ experience in the Java ecosystem. In his daily work, he is focused on architecting and developing Java-distributed applications that empower robust architectures, clean code, and high performance. He is also passionate about coaching, mentoring, and technical leadership.

    Browse publications by this author

Latest Reviews

(1 reviews total)
Good
Book Title
Unlock this book and the full library for FREE
Start free trial