How to create a custom JSF Validator – The Greater Than Validator to enforce one component's value being larger than some other's

One of the major benefits of Java Server Faces (JSF) in comparison to other View frameworks is its easy extensibility. UI Components can be incorporated, custom converters and page-life-cycles can be defined and of course Validators can be developed an integrated into JSF applications. Although there is a number of tutorials and articles available on line on creating new custom Validators, they do not seem to discuss the creation of a validator that deals with more than one component(‘s value). For example I would like to have a Validator that enforces that one component can only have a value that is larger than the value of some other specified component.

Basically I want to be able to specify something like:

  <af:inputText label="Old Salary" id="oldSalary"
value="#{SalaryBean.oldSalary}"/>
<af:inputText label="New Salary" value="#{SalaryBean.newSalary}"
id="newSalary">
<AMISJSF:greaterThan greaterThanId="mainform:oldSalary"/>
</af:inputText>
 

And have the validator raise an exception if the value in New Salary is lower than the value in Old Salary:

How to create a custom JSF Validator - The Greater Than Validator to enforce one component's value being larger than some other's customValidator error 

....
 

The steps for implementing this type of Custom Validator are fairly straightforward:

1. Create a class that implements the Validator interface 

2. Create a class for this Validator that extends the ValidatorTag class; this class supports a custom JSP tag for this validator (as opposed to using f:validator with validatorId)

3. Register the validator in the faces-config.xml

4. Create an entry for the validator tag in a tag-library

1. The Validator Class

The Validator Class looks like this:

package nl.amis.jsf.validator;

import javax.faces.application.FacesMessage;
import javax.faces.component.EditableValueHolder;
import javax.faces.context.FacesContext;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;
import javax.faces.component.UIComponent;


public class GreaterThanValidator implements Validator {

String greaterThanId;

public GreaterThanValidator() {
}

public void validate(FacesContext facesContext, UIComponent uiComponent,
Object value) {
if (null == value) {
return;
}
UIComponent greaterThanComponent =
uiComponent.findComponent(greaterThanId);
if (greaterThanComponent != null) {
if (greaterThanComponent instanceof EditableValueHolder) {
Object greaterThanValue =
((EditableValueHolder)greaterThanComponent).getValue();
if (null == greaterThanValue) {
return;
}
if ((value instanceof Comparable) &&
(greaterThanValue instanceof Comparable)) {
if (((Comparable)value).compareTo((Comparable)greaterThanValue) < 1 ) {
// raise exception as clearly value is not greater than greaterThanValue
FacesMessage message = new FacesMessage();
message.setDetail("Value in component "+uiComponent.getClientId(facesContext)+" ("+value.toString()+") should be greater than "+greaterThanValue.toString()+".");
message.setSummary("Value should be greater than "+greaterThanValue.toString());
message.setSeverity(FacesMessage.SEVERITY_ERROR);
throw new ValidatorException(message); }
}
}
}


}
}
 

The most noteworthy part is the part where the value of the ‘other’  component is retrieved.

2. The Validator Tag Class

The Tag Class to support a special custom tag for this validator is implemented as follows:

package nl.amis.jsf.tags;

import javax.faces.validator.Validator;
import javax.faces.webapp.ValidatorTag;

import javax.servlet.jsp.JspException;

import nl.amis.jsf.validator.GreaterThanValidator;

public class GreaterThanValidatorTag extends ValidatorTag{

String greaterThanId;

public GreaterThanValidatorTag() {
setValidatorId("GreaterThan");
}



public Validator createValidator() throws JspException {
GreaterThanValidator validator = (GreaterThanValidator) super.createValidator();
validator.setGreaterThanId(greaterThanId);

return validator;
}

public void release() {
greaterThanId = null;
}

public void setGreaterThanId(String greaterThanId) {
this.greaterThanId = greaterThanId;
}

public String getGreaterThanId() {
return greaterThanId;
}
}
 

3. The entry in the custom tag library

The following entry is needed in the TLD for our Custom JSF Components Tag Library

  <tag>
<description>Validator - Enforce that Component's value is GreaterThan other Component's Value</description>
<display-name>greaterThan</display-name>
<name>greaterThan</name>
<tag-class>nl.amis.jsf.tags.GreaterThanValidatorTag</tag-class>
<attribute>
<name>greaterThanId</name>
</attribute>
<body-content>empty</body-content>
<example>Brief snippet showing how to use this tag.</example>
</tag> 
 

4. Register validator in faces-config.xml

We need a simple entry in the faces-config.xml file to make the validator available to our application:

<validator>
<validator-id>GreaterThan</validator-id>
<validator-class>nl.amis.jsf.validator.GreaterThanValidator</validator-class>
</validator>

Using the Custom Validator 

Using the custom validator in an application requires these steps

1. Add the lag-library (see step 4 above) to the ViewController project

How to create a custom JSF Validator - The Greater Than Validator to enforce one component's value being larger than some other's customValidator taglibInProject 

This makes the validator available in the JSF components palette in the IDE I am working with:

How to create a custom JSF Validator - The Greater Than Validator to enforce one component's value being larger than some other's customValidator palette

2. Add the URI for the taglib to the JSPX page, making the tags available within the JSP JSF page

<?xml version='1.0' encoding='UTF-8'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.0"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:afh="http://xmlns.oracle.com/adf/faces/html"
xmlns:af="http://xmlns.oracle.com/adf/faces"
xmlns:AMISJSF="http://nl.amis.jsf/components">
<jsp:output omit-xml-declaration="true" doctype-root-element="HTML"
  ;

3. Include the validator ta
g within a Input Component, such as InputText

            <af:inputText label="Old Salary" id="oldSalary"
value="#{SalaryBean.oldSalary}"/>
<af:inputText label="New Salary" value="#{SalaryBean.newSalary}"
id="newSalary">
<AMISJSF:greaterThan greaterThanId="mainform:oldSalary"/>
</af:inputText>

Here we see two inputText elements, oldSalary and newSalary, both inside a form called mainform. The newSalary element has been subjected to the custom validator greatherThan through the AMISJSF:greaterThan tag. We have specified the greatherThanId attribute; it refers to mainform:oldSalary, the clientId of the Old Salary element. This will enforce our rule that the New Salary must be greater than the old salary. 

The component-tree for our page looks as follows:

How to create a custom JSF Validator - The Greater Than Validator to enforce one component's value being larger than some other's customValidator pageTree 

 

Now we can run the application. 

How to create a custom JSF Validator - The Greater Than Validator to enforce one component's value being larger than some other's customValidator pageNoError 

Submitting these values will raise no error as the validation rule is not violated. However submitting a form with values 4000 for Old Salary and 1200 for New Salary will raise an exception:

How to create a custom JSF Validator - The Greater Than Validator to enforce one component's value being larger than some other's customValidator error

 

Resources

 

Excellent tutorial on creating a Custom Validator (my main inspiration for this article): Java Server Faces FAQ How to create the custom validator?

 

 

 

4 Comments

  1. Jeff Luo January 30, 2007
  2. Ravi January 22, 2007
  3. Lucas Jellema January 13, 2007
  4. Ravi January 12, 2007