Faces Data Validation

Faces supports several different kinds of data validation resources and capabilities.  They range from simple checks to insure that a field is non-blank to sophisticated, independent classes and methods that can incorporate data from the domain model to validate input data.  A good reference for validation is Richard Hightower's JSF for Non-Believers: JSF Conversion and Validation.

Validation is related to conversion of data values, whereby values are mapped from form representation, typically String values, to their appropriate types as properties in a Faces' model/ManagedBean.  Validation occurs in the third step in the lifecycle just before those model values are actually updated.  It can also occur in the sixth step, during rendering, particularly for validation methods that function at the application level.

In the discussion that follows, four types of validation resources will be discussed:

We note that Faces' application-level validation is different from the other types listed above in that it requires business or persistent storage logic.  Thus, this type of validation could be used to determine whether a propose loginname was taken or whether a client's credit limit had been exceeded.  Since the emphasis here is on validation based on the format of data, application-level validation will not be discussed further.  The Hightower reference, cited above, includes a discussion of it.



Built-in Validation

Faces uses the JSP core and html taglibs, which include several standard or built-in validation tags or attributes that can be used with form input fields.  These include the following:

Required is used as an attribute with several h: tags, such as h:inputText.  As implied by the name, this attribute will result in an error message and interruption of processing if a nonblank value is not present.

The three validation tags, listed above, all accept maximum and minimum attributes.  For example, a f:validateLength might indicate that a password must include a minimum of 6 characters and a maximum of 12.



Custom Validation Error Messages

Validation errors are normally associated with form data, especially those pertaining to input fields.  Such fields are frequently included within an h:panelGrid (table).  A common practice is to include an additional column in the table so that potential error messages can be displayed immediately beside the offending field.  to implement this approach for a given field, one might include a tag such as the following: <h:message for="email"/>, where email is the id for the associated input field that also includes a required attribute.  If the email field is left blank, a standard error message will be generated by Faces. 

You can also generate a custom message for such an error, if you wish.  Doing so requires three steps:

  1. You must create a properties file, such as Bundle.properties, to include your custom messages and place this file within your application classpath (e.g., include the file in a conventional Java package, along with your class files, within your development/deployment environment).  Within that file, include individual messages as attribute=values pairs; for example:

    emptyField=Invalid data: empty field.
     
  2. Include a reference to this file in your faces-config.xml file as an application resource bundle:

    <application>
        <resource-bundle>
            <base-name>jbs.message.Bundle</base-name>
            <var>customMessage</var>
        </resource-bundle>
    </application>
     
  3. Include a reference to the bundle, using its symbolic name -- customMessage, in this case -- as an attribute of the inputText field in your form, such as:

    <h:inputText id="email" value="#{managedBeanUser.email}" required="true" requiredMessage="#{customMessage['emptyField']}" />


Custom Validation

Custom validation involves creating a validator class that implements the Faces Validator interface.  Whereas this may seem like a lot of work, the class can be reused in future applications.  The class must also override the validate method and it must be registered with the faces-config.xml file.  Finally, the Faces JSP must include an f:validator tag that refers to the class.  These steps are summarized in the example code, below:

  1. Create custom validation class, implementing Validator interface and overriding validate method:
    package jbs.eb2c.validate;
    
    import javax.faces.application.FacesMessage;
    import javax.faces.component.UIComponent;
    import javax.faces.component.UIInput;
    import javax.faces.context.FacesContext;
    import javax.faces.validator.Validator;
    import javax.faces.validator.ValidatorException;
    
    public class ValidateEmail implements Validator  {
    	
    	public ValidateEmail() {
    		super();
    	}
    
    	public void validate (FacesContext context, UIComponent component, Object value) 
    		throws ValidatorException  { 
    	
    		int numberErrors = 0;
    		String messageString = "Invalid data:";
    		String atErrorString = " missing at (@) symbol";
    		String dotErrorString = " missing dot (.) symbol";
    		
    		String emailTemp = (String) value;
    		
    	if ( ! (emailTemp.indexOf( "@" ) > 0) )  {
    		numberErrors += 1;
    		messageString += atErrorString;
    	}
    
    	if ( ! (emailTemp.indexOf( "." ) > 0 ) )  {
    		numberErrors += 1;
    		if (numberErrors > 1 )
    			messageString += ";";
    		messageString += dotErrorString;
    	}
    
    	if (numberErrors > 0 )  {
    		messageString += ".";
    	
    		((UIInput)component).setValid(false);
    		
    		FacesMessage message = new FacesMessage();
    		message.setSeverity(FacesMessage.SEVERITY_ERROR);
    		message.setSummary( messageString );
    		message.setDetail( messageString );
    
    		throw new ValidatorException(message);
    	}
    	
    	}
    
    }
    
  2. Include a reference to this class in your faces-config.xml file as a validator:

    <validator>
        <validator-id>custom.validateEmail</validator-id>
        <validator-class>jbs.eb2c.validate.ValidateEmail</validator-class>
    </validator>
     
  3. Include an f:validator tag within the input field tag, such as:

    <h:inputText id="email" value="#{managedBeanUser.email}" required="true" requiredMessage="#{customMessage['emptyField']}" >
        <f:validator validatorId="custom.validateEmail" />
    </h:inputText>


Validation in Backing Bean

Whereas custom validation messages can be placed in a custom component class, they may also be placed in individual ManagedBeans.  The advantage of this is a little simpler implementation; the disadvantage is less practical reuse.  Since the ManagedBean is already registered in the faces-config.xml file, the process includes only two steps, as follows:

  1. Implement the validation in the relevant ManagedBean:
    public void validatePasswordComplexity (
    	FacesContext context, 
    	UIComponent component, 
    	Object value) 
    	throws ValidatorException  { 
    		
    	String loginPasswordTemp = (String) value;
    	//  tests minimum length, not complexity
    	if ( loginPasswordTemp.length() < 3 )  {
    						
    		FacesMessage message = new FacesMessage();
    		message.setSeverity(FacesMessage.SEVERITY_ERROR);
    		message.setSummary("Invalid data:  password not sufficiently complex.");
    		message.setDetail("Invalid data:  password not sufficiently complex.");
    			
    		((UIInput)component).setValid(false);
    		
    		context.addMessage( "registerForm:password", message );
    			
    		throw new ValidatorException(message);
    		
    	}
    }	
  2. Include a reference to the method as a parameter of the input field:

    validator="#{managedBeanUser.validatePasswordComplexity}"

Note the literal references in the form tag to the particular ManagedBean and method and, conversely, in the method to the particular JSP form and field.