Server-Side JavaServer Faces Validators

Jennifer Ball, Greg Murray

Problem Description

HTTP clients such as browsers send form data to web applications following an event such as a form submission. HTTP does not permit any types other than strings for the key/value pairs representing form data. The resulting form submissions are seen by a server-side application as a set of string objects. This can be a problem for web applications that have more specific requirements on the data. Validation is the process of confirming that form data received from a client conforms to the data format or is within a specific range required by the application.

Examples of validation include the following:

Validation can be performed by clients, such as browsers that provide client-side scripting validation capabilities like the use of JavaScript[TM] technology. Regardless of whether client-side scripting is performed, server-side validation should always be performed.

Validation allows a web application to be more secure. Validation ensures that the input data received directly from a client is never used in database commands, in the outgoing HTML response, or for running shell commands. These techniques are commonly used by malicious individuals to gain control of the systems on which web applications run.

JavaServer[TM] Faces technology provides several ways for a developer as well as a page author to define server side validation using built-in validation as well as customized solutions. This document presents strategies for server-side validation using  JavaServer Faces technology.

Solution

The solution is to use facilities built into JavaServer Faces technology to provide a server-side form data validation. This document will discuss several strategies that may be used with JavaServer Faces technology which are listed below.

Each strategy is discussed in more detail in this document. The table below summarizes the benefits and drawbacks of each strategy.

Strategy Benefits Drawbacks
Built-in validators Easy for page author to configure. Limited number of validators provided.
Custom validators Easy to add to a UI component. Validation code defined in a single class. Page author can not configure validation parameters. Java[TM] source must be changed to change validation.
Custom validators with customizable validation parameters Easy for page author to configure. Provides fine-grained configuration of validation parameters used by the custom validator using a class that extends javax.faces.webapp.ValidatorTag. Validation code is essentially turned into components that may be reused. Complex for a developer to create (requires knowledge of custom actions, low-level details of JavaServer Faces technology). Java code required to validate input parameters which makes it less customizable.
Validation using managed beans Easy for page author to configure. Validation code can be easily defined in a managed bean. Good for coarse grained (application-wide) validation configurable as managed beans. Page author cannot configure the validation for each UI component. Multiple methods are needed to customize validation code.

As a developer, choose the best solution based on your usecases and the drawbacks that are detailed in the table above.

To summarize:

JavaServer Faces technology provides a model by which a JavaServer Faces developer or third party could provide an extensive set of validators beyond those provided with the JavaServer Faces implementation. These validators could be provided as a reusable library. Another consideration is to utilize such a library if it fits the needs of your application.

The remainder of this document describes each strategy in more detail.

Built-In Validators Strategy

JavaServer Faces technology provides some built-in validators for doing basic things such as bounds checking on numbers which might be floats, longs, or integer values. These validators are initialized and configured as managed beans.

You should be aware of a subtle side effect of using the built-in validators. When a user posts a form that contains at least one validation error, the view is re-rendered with the error messages displayed. However, the "Update Model Values" and "Invoke Application" phases of the JavaServer Faces life-cycle are bypassed, and the response is rendered immediately. Of course this is for a good reason; it would not be good if the backing beans were set with values that were determined to be invalid. However, this also means that if your backing bean is in the request scope and a validation error occurs, any value bindings in your page are skipped and your backing bean is not updated accordingly. For example, if a hidden text field is bound to a String property x in the backing bean, then x is not updated if there is a validation error. If other getters depend on x being set, the rendered response might be working from an incomplete data set.

The following validators are provided with JavaServer Faces technology version 1.1:

Tag Name Class Name Description
<f:validateDoubleRange> javax.faces.validator.DoubleRangeValidator Validates ranges with doubles. Good for working with currencies.
<f:validateLongRange> javax.faces.validator.LongRangeValidator Validates ranges that use longs.
<f:validateLength> javax.faces.validator.LengthValidator Validates the length of an form variable.

There are two ways that a validator can be used. You can define and configure it as a managed bean, or use the corresponding tags to define it.

Following is an example on how a page author would define and configure one of the built-in validation tags:

  <h:form id="validatorForm" onsubmit="return client_validation();">
<h:inputText id="bigNumber" value="#{ValidatorBean.bigNumber}">
<f:validateLongRange maximum="11" minimum="5"/>
</h:inputText>
<p>
<h:commandButton id="submit" action="success" value="Submit" />
</p>
<p>
<h:message style="color: orange" id="error3" for="bigNumber"/>
</p>
</h:form>

The subelement of the inputText tag validates a number range. In this case, parameters for the validator are set as attributes (maximum/minimum) of the tag. An invalid number causes the page to be redisplayed with an error message rendered in the location of the <h:message> element.

Built-in validators can provide fine-grained UI component validation. The attributes for the built-in validators can also be set using value bindings. Extending the example, you can also specify the validator as the following:

<f:validateLongRange minimum="#{MyBackingBean.minimum}" maximum="#{MyBackingBean.maximum}" />

In the example above, the range attributes are specified in a backing bean named MyBackingBean.

UI Components can define the required attribute as true that causes validators to be called even if a null or zero-length string value is defined, but the validation code must be capable of accepting such values. In cases where the required attribute is not set to true, validators registered with that UI Component are not called.

A built-in validator can also be defined to provide application-wide validation. In this case, the validators are defined and initialized as managed beans. These managed beans can then use method bindings on the validator attribute to validate a UI component's content in the same way you would do with a custom validator method defined in a manged bean. Following is an example of an application-scoped validator managed bean that uses one of the built-in validators.

  <managed-bean>
<description>
A Pre-defined JavaServer Faces Long Range Validator
</description>
<managed-bean-name>JSFValidator</managed-bean-name>
<managed-bean-class>javax.faces.validator.LongRangeValidator</managed-bean-class>
<managed-bean-scope>application</managed-bean-scope>
<managed-property>
<property-name>minimum</property-name>
<property-class>int</property-class>
<value>0</value>
</managed-property>
<managed-property>
<property-name>maximum</property-name>
<property-class>int</property-class>
<value>5</value>
</managed-property>
</managed-bean>

The managed bean definition for JSFValidator uses a built-in range-checking validator which is defined by specifying one of the validators as a managed bean. The minimum and maximum ranges are set on the JSFValidator bean.

    <h:form id="validatorForm">
<p>Enter a number (between 0-5):</p>
<h:inputText id="number" value="#{ValidatorBean.number}"
validator="#{JSFValidator.validate}"/>
<p>
<h:message style="color: orange" id="errors2" for="number"/>
</p>
<p>
<h:commandButton id="submit" action="success" value="Submit" />
</p>
</h:form>

The code above uses the built-in validator (in this case named JSFValidator) to validate the value of a provided number. If the number is not within the range, the page redisplays with an error message.

Managed Bean Method Validation

Method validation can be performed as a method defined in a managed bean or backing bean. The method bindings are mapped using the validator attribute of the UI component.

    <f:view>
<h:form id="validatorForm" >
<p>
Enter a new name (other than "blueprints").
</p>
<h:inputText id="userName" value="#{ValidatorBean.userName}"
validator="#{ValidatorBean.validate}"/>
<h:commandButton id="submit" action="success" value="Submit" />
<p>
<h:message style="color: red"
id="errors1"
for="userName"/>

</h:form>
</f:view>

The JavaServer Faces page snippet above shows an input text field that uses a method called validate in a managed bean named ValidatorBean. The method to which a validator is mapped must conform to a specific method signature. The JavaServer Faces runtime provides the necessary values to this method. A developer need only provide the code to validate the input:

    public void validate(FacesContext context,
UIComponent component,
Object value) throws ValidatorException {

if ((context == null) || (component == null)) {
throw new NullPointerException();
}
if (value != null) {
if (((String)value).equals("blueprints")) {
throw new ValidatorException(new FacesMessage("blueprints is invalid"));
}
}
}

The code snippet above shows the method signature of a method bound to the validator attribute of a UIComponent. The UIComponent provides the FacesContext, a reference to itself, and the value that is to be validated to the method. This method is required to throw a ValidatorException to signify that a validation error was encountered.

The image above shows what occurs when a custom validation exception occurs. In this case a validation error thrown as a ValidatorException causes the JavaServer Faces page to be redisplayed with the error message provided in the exception.

In general, it is a good practice to include validation methods in the same managed bean that defines the properties for the components referencing these methods. The reason is that the methods might need to access the component's data to determine how to handle the event or to perform the validation associated with the component.

It is good practice to use the ResourceBundle facilities of the Java[TM] 2 SDK (java.util.ResourceBundle) to keep from hard-coding error messages in Java code. This also allows you to localize the error messages. JavaServer Faces technology provides a resource bundle facility, but the developer still needs to ensure that the message format is correct.

Defining validation code as methods in a managed bean is easy for a developer with Java coding knowledge. Validators in this case would be tied to a specific UI component or managed bean. While somewhat customizable through managed bean configuration, the managed bean validators are limited.

Custom Validators

The custom validator approach is very similar to the managed bean approach. The method signature is the same, but in the case of the custom validator, the method name is validate. Developers must do the following:

Following is an XML fragment from a JavaServer Faces configuration file declaring a validator named DateValidator.
  <validator>
<description>
Registers the Validator implementation,
DateValidator.
</description>
<validator-id>DateValidator</validator-id>
<validator-class>com.DateValidator</validator-class>
</validator>

A JavaServer Faces page associates that validator with a UI component implementing the javax.faces.component.EditableValueHolder interface. Below is an example of an <h:inputText> tag that uses the DateValidator.

    <h:inputText id="date1" value="11-12-99">
     <f:validator validatorId="DateValidator"/>
    </h:inputText>

The validator implementing the javax.faces.validator.Validator interface is mapped to a UI component  using an immediate sub element <f:validator validatorId="DateValidator"/>. The validate method on the validator class contains the validation logic as shown below.

    public void validate(FacesContext context, UI component component, 
Object inputValue) {

boolean valid = false;
String value = (String)inputValue;

try {
DateFormat df = new SimpleDateFormat("mm-dd-yy");
Date d = df.parse(value);
if (d != null) valid = true;
} catch (java.text.ParseException px) {
valid = false;
} catch (java.lang.IllegalArgumentException iae) {
valid = false;
}

if (!valid) {
FacesMessage error = new FacesMessage("Date " + value +
" is invalid. Please enter a date conforming to " +
datePattern);
throw new ValidatorException(error);
}
}

Notice that the method signature is the same as the one used for a managed bean validation method.

The main drawback of using the validator class approach is that a page author cannot configure the validation parameters on a per-UI component basis. Although a <c:set> tag can be used to set values on the managed beans, this is not a trivial task because it requires detailed knowledge of the managed bean's mechanism and the JavaServer Faces life-cycle model. Also, validation parameters can be hard-coded (the date pattern "mm-dd-yy"
in the example above) in to the validator class. In such cases, these values could be stored in a locale-specific java.util.ResourceBundle class or properties file.

While this approach is good for providing global validation for a web application, it is not as flexible when it comes to changing the validation parameters.

Custom Validators With Customizable Validation Parameters

A developer can provide customizable validation parameters to the validator implementation class by creating a custom action that extends javax.faces.webapp.ValidatorTag. The parameters used for validation can also be set using property binding expressions in a JavaServer Faces page for each component.

A developer should do the following:

The graphic above shows the artifacts needed to create a JavaServer Faces validator with customizable parameters. This validator uses a regular expression to specify date patterns.

The JavaServer Faces runtime initializes the DateValidatorTag when the page is processed. The DateValidatorTag creates an instance of the the DateValidator and sets the parameters based on what was specified in the page.The DateValidator validate method is shown below.

    // this is called by the DateValidator tag when it is initialized
public void setDatePattern(String datePattern) {
this.datePattern = datePattern;
}

public void validate(FacesContext context, UIComponent component,
Object inputValue) {

boolean valid = false;
String value = (String)inputValue;
try {
valid = Pattern.matches(datePattern, value);
} catch(PatternSyntaxException pse) {
// something wrong with expression
}
if (!valid) {
FacesMessage error = new FacesMessage("Date " + value +
" is invalid. Please enter a date conforming to " +
datePattern);
throw new ValidatorException(error);
}
}

The code above shows the validate method of a validator for which the datePattern was set using a custom action. This would allow a page developer to define datePattern on a per-UI component basis. A ValidationException is thrown to signify an error in the value (inputValue in this example) provided.

Customizable validators require some more work on the part of the developer. A developer  should develop the custom action extending the javax.faces.webapp.Validator as well as provide the correct tag library descriptor files. The benefit is that the validator can be used for fine-grained validation of UI components. While validators that use custom actions are difficult to develop, they are easy for a page author to use.

References

For more information about this topic, refer to the following:


© Sun Microsystems 2005. All of the material in The Java BluePrints Solutions Catalog is copyright-protected and may not be published in other works without express written permission from Sun Microsystems.