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.
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.
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 theApply 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 theRender 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"); } }
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.
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>