1. Commons Validator
|
Important |
In version 0.4, the commons validator support was moved from the commons module to the validation module. As a
consequence, all related classes in the commons module were deprecated and will be removed in version 0.5.
Please note that due to this change, the packages were renamed. Apart from that, the functionality stayed more
or less the same with the exception of some bug fixes and small improvements. Also note that all bug fixes and
maintenance will be done on the validation module solely.
|
The Commons Validator is a library that allows you to perform
validation based on rules specified in XML configuration files.
TODO: Describe the concepts of Commons Validator in more details.
1.1. Configure an Validator Factory
Firstly you need to configure the Validator Factory which is the
factory to get Validator instances. To do so, the support provides the
class DefaultValidatorFactory in the package org.springmodules.validation.commons
You need to specify with the property validationConfigLocations the
file containing the Commons Validator rules and the file containing the
validation rules specific to the application.
The following code shows how to configure this factory.
<bean id="validatorFactory"
class="org.springmodules.validation.commons.DefaultValidatorFactory">
<property name="validationConfigLocations">
<list>
<value>/WEB-INF/validator-rules.xml</value>
<value>/WEB-INF/validation.xml</value>
</list>
</property>
</bean>
1.2. Use a dedicated validation-rules.xml
The file validation-rules.xml must contain
Commons Validator elements based on classes provided by the support of
this framework in Spring Modules.
For example, the configuration of the entities "required" and
"requiredif" must be now in the validation-rules.xml
file.
<validator name="required"
classname="org.springmodules.validation.commons.FieldChecks"
method="validateRequired"
methodParams="java.lang.Object,
org.apache.commons.validator.ValidatorAction,
org.apache.commons.validator.Field,
org.springframework.validation.Errors"
msg="errors.required">
<javascript><![CDATA[
(...)
]]></javascript>
</validator>
<validator name="requiredif"
classname="org.springmodules.validation.commons.FieldChecks"
method="validateRequiredIf"
methodParams="java.lang.Object,
org.apache.commons.validator.ValidatorAction,
org.apache.commons.validator.Field,
org.springframework.validation.Errors,
org.apache.commons.validator.Validator"
msg="errors.required">
</validator>
The validation sample of the distribution provides a complete
validation-rules.xml based on the classes of the
support.
You must note that the support of validwhen is
not provided at the moment in the support. However, some codes are
provides in JIRA. For more informations, see the issues
MOD-38 and
MOD-49.
1.3. Configure a Commons Validator
Then you need to configure the Validator itself basing the previous
Validator Factory. It corresponds to an adapter in order to hide Commons
Validator behind a Spring Validator.
The following code shows how to configure this validator.
<bean id="beanValidator" class="org.springmodules.validation.commons.DefaultBeanValidator">
<property name="validatorFactory" ref="validatorFactory"/>
</bean>
1.4. Server side validation
Spring MVC provides the implementation SimpleFormController
of the interface Controller in order to process HTML forms.
It allows a validation of informations processing by the controller by using the
property validator of the controller. In the case of
Commons Validator, this property must be set with the bean beanValidator
previously configured.
The following code shows how to configure a controller which validates a form on
the server side using the support of Commons Validator.
<bean id="myFormController" class="org.springmodules.sample.MyFormController">
(...)
<property name="validator" ref="beanValidator"/>
<property name="commandName" value="myForm"/>
<property name="commandClass" value="org.springmodules.sample.MyForm"/>
(...)
</bean>
The beanValidator bean uses the value of the property
commandClass of the controller to select the name of
the form tag in the validation.xml file. The configuration
is not based on the commandName property. For example, with
the class name org.springmodules.sample.MyForm,
Commons Validator must contain a form tag with myForm
as value of the name property. The following code shows the contents of this file.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE form-validation PUBLIC
"-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.1//EN"
"http://jakarta.apache.org/commons/dtds/validator_1_1.dtd">
<form-validation>
<formset>
<form name="myForm">
<field property="field1" depends="required">
<arg0 key="error.field1" />
</field>
<field property="field2" depends="email">
<arg0 key="error.field2" />
</field>
</form>
</formset>
</form-validation>
1.5. Partial Bean Validation Support
Partial validation support enables partial validation of beans where not all properties are validated
but only selected ones.
Commons validator enables partial validation by specifying the page attribute
for each field in the form configuration:
<form name="personDataWizard">
<field property="firstName" depends="required" page="0">
<arg0 key="person.first.name" />
</field>
<field property="lastName" depends="required" page="0">
<arg0 key="person.last.name" />
</field>
<field property="email" depends="required,email" page="0">
<arg0 key="person.email" />
</field>
<field property="password" depends="required" page="1">
<arg0 key="person.password" />
</field>
<field property="verifyPassword" depends="validwhen" page="1">
<arg0 key="person.password.not.matching" />
<var>
<var-name>test</var-name>
<var-value>(*this* == password)</var-value>
</var>
</field>
</form>
The org.springmodules.validation.commons.ConfigurablePageBeanValidator and
org.springmodules.validation.commons.DefaultPageBeanValidator classes support
partial validation by setting their page property. The value of this property will
be matched with the page attribute in the form configuration, and only the fields with the appropriate
page configured will be validated.
The following is an example of a partial validation support usage within a wizard controller:
<bean id="personWizardController" class="PersonWizardController">
<property name="pages">
<list>
<value>personPage0</value>
<value>personPage1</value>
</list>
</property>
<property name="allowDirtyForward" value="false"/>
<property name="validators">
<list>
<ref bean="pageValidator0"/>
<ref bean="pageValidator1"/>
</list>
</property>
<property name="commandName" value="person"/>
<property name="commandClass" value="PersonData"/>
</bean>
<bean id="pageValidator0" class="ConfigurablePageBeanValidator" parent="pageValidator">
<property name="page" value="0"/>
</bean>
<bean id="pageValidator1" class="ConfigurablePageBeanValidator" parent="pageValidator">
<property name="page" value="1"/>
</bean>
<bean id="pageValidator" abstract="true">
<property name="formName" value="personDataWizard"/>
<property name="validatorFactory" ref="validatorFactory"/>
</bean>
...
The controller will look like this:
public class PersonWizardController extends AbstractWizardFormController {
...
protected void validatePage(Object command, Errors errors, int page) {
Validator[] validators = getValidators();
for (int i=0; i<validators.length; i++) {
Validator validator = validators[i];
if (validator instanceof PageAware) {
if (((PageAware)validator).getPage() == page) {
validator.validate(command, errors);
}
}
}
}
}
1.6. Client side validation
The support of Commons Validator in Spring Modules provides too the
possibility to use a client side validation. It provides a dedicated
taglib to generate the validation javascript code. To use this taglib, we
firstly need to declare it at the beginnig of JSP files as following.
<%@ tglib uri="http://www.springmodules.org/tags/commons-validator" prefix="validator" %>
You need then to include the generated javascript code in the JSP file as
following by using the javascript tag.
<validator:javascript formName="account"
staticJavascript="false" xhtml="true" cdata="false"/>
At last, you need to set the onSubmit attribute
on the form tag in order to trigger the validation on
the submission of the form.
<form method="post" action="(...)" onsubmit="return validateMyForm(this)">
Valang (Va-lidation Lang-uage), provides a simple and
intuitive way for creating spring validators. It was initially create with three goals in mind:
-
Enables writing validation rules quickly, without the need of writing classes or even any java code.
-
Ease the use of Spring validation tools.
-
Make validation rules compact, readable and easily maintainable.
Valang is built upon two major constructs - The valang expression
language and valang validators. The former is a generic boolean
expression language that enables expressing boolean rule in a "natural
language"-like fashion. The later is a concrete implementation of the
Spring Validator interface that is built around the expression
language.
Before going into details, lets first have a look at a small example, just to have
an idea of what valang is and how it can be used. For this example, we'll assume a Person class
with two properties - firstName and lastName. In addition, there are two main validation rules that
need to be applied:
One way of applying these validation rules (and currently the most common one) is to implement the Validator
interface specifically for the Person class:
public class PersonValidator implements Validator {
public boolean supports(Class aClass) {
return Person.class.equals(aClass);
}
public void validate(Object person, Errors errors) {
String firstName = ((Person)person).getFirstNam();
String lastName = ((Person)person).getLastName();
if (firstName == null || firstName.length() >= 30) {
errors.reject("first_name_length", new Object[] { new Integer(30) },
"First name must be shorter than 30");
}
if (lastName == null || lastName.length() >= 50) {
errors.reject("last_name_length", new Object[] { new Integer(50) },
"Last name must be shorter than 50");
}
}
}
While this is a perfectly valid approach, it has its downsides. First,
it is quite verbose and time consuming - quite a lot of code to write
just for two very simple validation rules. Second, it required an
additional class which clutters the code (in case it is an inner-class)
or the design - just imagine having a validator class for each of the
domain model objects in the application.
The following code snippet shows how to create a valang validator to apply the same rules as above:
<bean id="personValidator" class="org.springmodules.validation.valang.ValangValidator">
<property name="valang">
<value>
<![CDATA[
{ firstName : length(?) < 30 : 'First name too long' : 'first_name_length' : 30}
{ lastName : length(?) < 50 : 'Last name too long' : 'last_name_length' : 50 }
]]>
</value>
</property>
</bean>
There are a few things to notice here. First, no new class is created -
with valang, one can reuse a predefined validator class (as shown
here). Second, This validator is not part of the java code, but put in
the application context instead - In the above case, the ValangValidator
is instantiated and can be injected to other objects in the system.
Last but not least, The validation rules are defined using the valang
expression language which is very simple and quick to define.
The following two sections will elaborate on the expression language and the use of the Valang validator in greater details.
The valang syntax is based on the valang expression language and the
valang validation rule configuration. As mentioned above, the former is
a boolean expression language by which the validation rules predicates
(conditions) are expressed. The later binds the rule predicates to a
key (usually a bean property), error message, and optionally error code
and arguments.
2.1.1. Rule Configuration
Here is the basic structure of the valang rule configuration:
{ <key> : <predicate_expression> : <message> [: <error_code> [: <args> ]] }
-
<key> - The key to which the validation error will be bound to. (mandatory)
-
<predicate_expression> - A valang expression that defines the predicate (condition) of the validation rule. (mandatory)
-
<message> - The error message of the validation rule. The message
is mandatory but can be an empty string if not used. This message is
also used as the default message in case the error code could not be
resolved. (mandatory)
-
<error_code> - An error code that represents the validation error. Used to support i18n. (optional)
-
<args> - A comma separated list of arguments to associate with
the error code. When error codes are resolved, this arguments may be
used in the resolved message. (optional)
2.1.2. Expression Language
As mentioned, the valang expression language is used to define the
predicate to be associated with the validation rule. The expression is
always evaluated against a context bean. The expression can be defined
as follows:
<expression> ::= <expression> ( ( "AND" | "OR" ) <expression> )+ | <predicate>
The <predicate> in an evaluation that is composed of operators,
literals, bean properties, functions, and mathematical expressions.
The following are the supported operators:
-
Binary Operators:
-
String, boolean, date and number operators:
-
Number and date operators:
-
> | GREATER THAN | IS GREATER THAN
-
< | LESS THAN | IS LESS THAN
-
>= | => | GREATER THAN OR EQUALS | IS GREATER THAN OR EQUALS
-
<= | =< | LESS THAN OR EQUALS | IS LESS THAN OR EQUALS
-
Unary Operators:
-
Object operators:
-
NULL | IS NULL
-
NOT NULL | IS NOT NULL
-
String operators:
-
HAS TEXT
-
HAS NO TEXT
-
HAS LENGTH
-
HAS NO LENGTH
-
IS BLANK
-
IS NOT BLANK
-
IS UPPERCASE | IS UPPER CASE | IS UPPER
-
IS NOT UPPERCASE | IS NOT UPPER CASE | IS NOT UPPER
-
IS LOWERCASE | IS LOWER CASE | IS LOWER
-
IS NOT LOWERCASE | IS NOT LOWER CASE | IS NOT LOWER
-
IS WORD
-
IS NOT WORD
-
Special Operators:
-
BETWEEN
-
NOT BETWEEN
-
IN
-
NOT IN
-
NOT
These operators are case insensitive. Binary operators have a left and a right side.
Unary operators only have a left side.
Value types on both sides of the binary operators must always match. The following expressions will
throw an exception:
name > 0
age == 'some string'
BETWEEN / NOT BETWEEN Operators
The BETWEEN and NOT BETWEEN operators have the following special syntax:
<between_operator> ::= <left_side> BETWEEN <value> AND <value>
<not_between_operator> ::= <left_side> NOT BETWEEN <value> AND <value>
Both the left side and the values can be any valid combination of literals, bean properties,
functions and mathematical operations.
Examples:
width between 10 and 90
length(name) between minLength and maxLength
The IN and NOT IN operators have the following special syntax:
<in_operator> ::= <left_side> IN <value> ( "," <value> )*
<not_in_operator> ::= <left_side> NOT IN <value> ( "," <value> )*
Both the left side and the values can be any valid combination of literals, bean properties,
functions and mathematical operations.
There's another special syntax where a java.util.Collection, java.util.Enumeration,
java.util.Iterator
or object array instance can be retrieved from a bean property. These
values are then used as right side of the operator. This feature
enables to create dynamic sets of values based on other properties of
the bean.
<special_in_operator> ::= <left_side> IN "@"<bean_property>
<special_not_in_operator> ::= <left_side> NOT IN "@"<bean_property>
Examples:
size in 'S', 'M', 'L', 'XL'
size in @sizes
The not operator has the following special syntax:
<not_operator> ::= "NOT" <expression>
This operator inverses the result of one or a set of predicates.
Four type of literals are supported by valang: string,
number, date, and boolean.
Strings are quoted with single quotes:
'Bill', 'George', 'Junior'
Number literals are unquoted and are parsed by java.math.BigDecimal:
0.70, 1, 2000, -3.14
Date literals are delimited with square brackets and are parsed upon each evaluation by a special date parser.
[TODO: write documentation for date parser]
[T<d], [2005-05-28]
Boolean literals are not quoted and have the following form:
<boolean> ::= ( "TRUE" | "YES" | "FALSE" | "NO" )
As mentioned above, the valang always evaluates the expressions against a context bean. Once can
access this bean's properties directly within the expression. To better understand how this works
lets assume a Person class with the following properties:
The Address class has the following properties:
-
street (String)
-
city (String)
-
Country (String)
The context bean properties can be accessed directly by using their names:
name, address, attributes
Accessing nested properties is also supported by using a dot-separated expression. For example,
accessing the street of the person can be done as follows:
address.street
List and/or array elements can be access by their index number as follows:
friends[1].name
enemies[0].address.city
Map entries can also be accessed by their keys:
specialFriends[bestFriend].name
Valang expressions can contain functions. A function is basically an
operation which accepts arguments and returns a result. Functions can
accept one or more arguments where each may be either a literal, bean
property, or a function as described in the following definition:
function ::= <function_name> "(" <arg> [ "," <arg> ]* ")"
<arg> ::= <literal> | <bean_property> | <function>
Valang ships with the following predefined functions:
Table 11.1. Functions
Name | Description |
length |
Returns the size of the passed in collection or array. If the passed in argument
is neither, the length of the string returned from the toString() call on
the passed in argument.
|
len |
See length above |
size |
See length above |
count |
See length above |
match |
Matches the given regular expression (first argument) to the
string returned from the toString() call on the passed
in value (second argument).
|
matches |
See match above. |
email |
Returns true if the string returned from the
toString() call on the passed in argument represents
a valid email
|
upper |
Converts the string returned from the toString() call on the argument
to upper case.
|
lower |
Converts the string returned from the toString() call on the argument
to lower case.
|
! |
Not operation on a boolean value. |
resolve |
Wrap string in org.springframework.context.support.DefaultMessageSourceResolvable. |
inRole |
Accepts a role name as an argument and returns true if the current user has this role.
This function uses Acegi to fetch the current user.
|
Examples:
length(?)
size(upper('test'))
upper(address.city)
One of the more powerful features in Valang expression language is that it is extensible with custom
functions. To add a custom function one first needs to implement the
org.springmodules.validation.valang.functions.Function interface or extend the
org.springmodules.validation.valang.functions.AbstractFunction. Then, when using
the ValangValidatorFactoryBean or ValangValidator,
register the new function with the customFunctions property using the
function name as the key.
[TODO: show an example of a custom function]
The following mathematical operators are supported:
Parentheses are supported and expression are parsed left to right so that
2 - 3 + 5 = 4
Values in the mathematical expression can be literals, bean properties, and functions.
Examples:
(2 * (15 - 3) + ( 20 / 5 ) ) * -1
(22 / 7) - (22 div 7)
10 % 3
length(?) mod 4
2.2. Valang Validator Support
As we saw in the previous chapter, Valang offers quite a reach and powerful expression language to
represent the validation rules. Language that for most cases relieves the user from creating custom
Validator classes.
The only missing piece of the puzzle now is to see how this expression language and the validation rule
configuration integrate with Spring validation support.
The 2 most important constructs of Spring validation are the
org.springframework.validation.Validator and org.springframework.validation.Errors
classes. The Errors class serves as a registry for validation errors that are associated with
an object (a.k.a the target object). The Validator interface provides a mechanism to validate objects
and register the various validation error within the passed in Errors.
Valang ships with some support classes that leverage the power of the Valang expression language and validation
rule configuration, and integrates nicely with Spring validation. The most important of them all is the
org.springmodules.validation.valang.ValangValidator class.
The org.springmodules.validation.valang.ValangValidator class is a concrete
implementation of Spring's Validator interface. The most important property of
this validator is the valang property.
The valang property is of type java.lang.String and holds a
textual representation of the validation rules that are applied by the validator. We saw in the previous
section that a single validation rule is represented in valang using the following format:
{ <key> : <predicate_expression> : <message> [: <error_code> [: <args> ]] }
Since, a validator may apply more then just one rule, the valang property accepts
a set of such rule definitions.
Example:
{ firstName : length(?) < 30 : 'First name too long' : 'first_name_length' : 30}
{ lastName : length(?) < 50 : 'Last name too long' : 'last_name_length' : 50 }
There are two ways to use the valang validator. It can be explicitly instantiated and initialized with
the rule definitions by calling the setValang(String) method on it. But the recommended
way is actually to let the Spring IoC container do this job for you. The valang validator was design
as a POJO specifically for that reason - to easily define it within Spring application context and inject
it to all other dependent objects in the application.
Here is an example of how to define a simple valang validator within the application context:
<bean id="personValidator" class="org.springmodules.validation.valang.ValangValidator">
<property name="valang">
<value>
<![CDATA[
{ firstName : length(?) < 30 : 'First name too long' : 'first_name_length' : 30}
{ lastName : length(?) < 50 : 'Last name too long' : 'last_name_length' : 50 }
]]>
</value>
</property>
</bean>
This validator defines two validation rules - one for the maximum size of the first name of the person and
the other for the maximum size of the last name of the person.
Also notice that the above validator is unaware of the object type it validates. The valag validator is
not restricted to a specific class to be validated. It will always apply the defined validation rules as
long as the validated object has the validated properties (firstName and lastName in this case).
This configuration should be enough for most cases. But there are some
cases in which you need to apply extra configuration. With ValangValidator it is possible to register custom
function (thus, extend the valang expression language). This can be done by registering the functions within
the customFunctions property, where the function name serves as the registration key.
Here is an example of a valang validator configuration with a custom function:
<bean id="personValidator" class="org.springmodules.validation.valang.ValangValidator">
<property name="customFunctions">
<map>
<entry key="doIt">
<value>org.springmodules.validation.valang.functions.DoItFunction</value>
</entry>
</map>
</property>
<property name="valang">
<value>
<![CDATA[
{ firstName : doIt(?) and length(?) < 30 : 'First name too long' : 'first_name_length' : 30}
{ lastName : length(?) < 50 : 'Last name too long' : 'last_name_length' : 50 }
]]>
</value>
</property>
</bean>
It is also possible to register extra property editors and custom date parsers for valang to use. For
more details about valang validator configuration options, please refer to the class javadoc.
NOTE: Until version 0.3 the org.springmodules.validation.valang.ValangValidatorFactoryBean
class served the same purpose as the ValangValidator. In version 0.4, this class was deprecated
and is planned to be removed in version 0.5.