Hibernate.orgCommunity Documentation
We already seen in Section 5.1, “Configuration and
ValidatorFactory” the
easiest way to create a Validator
instance -
Validation.buildDefaultValidatorFactory
. In this
chapter we have a look at the other methods in
javax.validation.Validation
and how they allow to
configure several aspects of Bean Validation at bootstrapping time.
The different bootstrapping options allow, amongst other things, to
bootstrap any Bean Validation implementation on the classpath. Generally, an
available provider is discovered by the Java
Service Provider mechanism. A Bean Validation implementation
includes the file
javax.validation.spi.ValidationProvider
in
META-INF/services
. This file contains the fully
qualified classname of the ValidationProvider
of the
implementation. In the case of Hibernate Validator this is
org.hibernate.validator.HibernateValidator
.
If there are more than one Bean Validation implementation providers
in the classpath and
Validation.buildDefaultValidatorFactory()
is
used, there is no guarantee which provider will be chosen. To enforce the
provider Validation.byProvider()
should be
used.
There are three different methods in the Validation class to create a Validator instance. The easiest in shown in Example 5.1, “Validation.buildDefaultValidatorFactory()”.
Example 5.1. Validation.buildDefaultValidatorFactory()
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
You can also use the method
Validation.byDefaultProvider()
which will allow
you to configure several aspects of the created Validator instance:
Example 5.2. Validation.byDefaultProvider()
Configuration<?> config = Validation.byDefaultProvider().configure();
config.messageInterpolator(new MyMessageInterpolator())
.traversableResolver( new MyTraversableResolver())
.constraintValidatorFactory(new MyConstraintValidatorFactory());
ValidatorFactory factory = config.buildValidatorFactory();
Validator validator = factory.getValidator();
We will learn more about MessageInterpolator
,
TraversableResolver
and
ConstraintValidatorFactory
in the following
sections.
Last but not least you can ask for a Configuration object of a
specific Bean Validation provider. This is useful if you have more than
one Bean Validation provider in your classpath. In this situation you can
make an explicit choice about which implementation to use. In the case of
Hibernate Validator the Validator
creation looks
like:
Example 5.3. Validation.byProvider( HibernateValidator.class )
HibernateValidatorConfiguration config = Validation.byProvider( HibernateValidator.class ).configure();
config.messageInterpolator(new MyMessageInterpolator())
.traversableResolver( new MyTraversableResolver())
.constraintValidatorFactory(new MyConstraintValidatorFactory());
ValidatorFactory factory = config.buildValidatorFactory();
Validator validator = factory.getValidator();
The generated Validator
instance is
thread safe and can be cached.
In the case that the Java Service Provider mechanism does not work
in your environment or you have a special classloader setup, you are able
to provide a custom ValidationProviderResolver
. An
example in an OSGi environment you could plug your custom provider
resolver like seen in Example 5.4, “Providing a custom ValidationProviderResolver”.
Example 5.4. Providing a custom ValidationProviderResolver
Configuration<?> config = Validation.byDefaultProvider()
.providerResolver( new OSGiServiceDiscoverer() )
.configure();
ValidatorFactory factory = config.buildValidatorFactory();
Validator validator = factory.getValidator();
Your OSGiServiceDiscoverer
must in this case
implement the interface
ValidationProviderResolver
:
Example 5.5. ValidationProviderResolver interface
public interface ValidationProviderResolver {
/**
* Returns a list of ValidationProviders available in the runtime environment.
*
* @return list of validation providers.
*/
List<ValidationProvider<?>> getValidationProviders();
}
Section 2.2.4, “Message interpolation” already discussed
the default message interpolation algorithm. If you have special
requirements for your message interpolation you can provide a custom
interpolator using
Configuration.messageInterpolator()
. This message
interpolator will be shared by all validators generated by the
ValidatorFactory
created from this
Configuration
. Example 5.6, “Providing a custom MessageInterpolator” shows an interpolator (available
in Hibernate Validator) which can interpolate the value being validated in
the constraint message. To refer to this value in the constraint message
you can use:
${validatedValue}: this will call String.valueOf
on the validated value.
${validatedValue:<format>}: provide your own format
string which will be passed to String.format
together
with the validated value. Refer to the javadoc of
String.format
for more information about the format
options.
Example 5.6. Providing a custom MessageInterpolator
Configuration<?> configuration = Validation.byDefaultProvider().configure();
ValidatorFactory factory = configuration
.messageInterpolator(new ValueFormatterMessageInterpolator(configuration.getDefaultMessageInterpolator()))
.buildValidatorFactory();
Validator validator = factory.getValidator();
It is recommended that MessageInterpolator
implementations delegate final interpolation to the Bean Validation
default MessageInterpolator
to ensure standard
Bean Validation interpolation rules are followed. The default
implementation is accessible through
Configuration.getDefaultMessageInterpolator()
.
A common use case is the ability to specify your own resource
bundles for message interpolation. The default
MessageInterpolator
implementation in Hibernate
Validator is called
ResourceBundleMessageInterpolator
and per default
loads resource bundles via
ResourceBundle.getBundle
. However,
ResourceBundleMessageInterpolator
also allows you
to specify a custom implementation of
ResourceBundleLocator
allowing you to provide
your own resource bundles. Example 5.7, “Providing a custom ResourceBundleLocator” shows an example. In the
example
HibernateValidatorConfiguration.getDefaultResourceBundleLocator
is used to retrieve the default
ResourceBundleLocator
which then can be passed to
the custom implementation in order implement delegation.
Example 5.7. Providing a custom ResourceBundleLocator
HibernateValidatorConfiguration configure = Validation.byProvider(HibernateValidator.class).configure();
ResourceBundleLocator defaultResourceBundleLocator = configure.getDefaultResourceBundleLocator();
ResourceBundleLocator myResourceBundleLocator = new MyCustomResourceBundleLocator(defaultResourceBundleLocator);
configure.messageInterpolator(new ResourceBundleMessageInterpolator(myResourceBundleLocator));
Hibernate Validator provides the following implementation of
ResourceBundleLocator
-
PlatformResourceBundleLocator
(the default) and
AggregateResourceBundleLocator
. The latter can be
used to specify a list of resource bundle names which will get loaded
and merged into a single resource bundle. Refer to the JavaDoc
documentation for more information.
The usage of the TraversableResolver
has so
far not been discussed. The idea is that in some cases, the state of a
property should not be accessed. The most obvious example for that is a
lazy loaded property or association of a Java Persistence provider.
Validating this lazy property or association would mean that its state
would have to be accessed triggering a load from the database. Bean
Validation controls which property can and cannot be accessed via the
TraversableResolver
interface (see Example 5.8, “TraversableResolver interface”). In the example
HibernateValidatorConfiguration.
Example 5.8. TraversableResolver interface
/**
* Contract determining if a property can be accessed by the Bean Validation provider
* This contract is called for each property that is being either validated or cascaded.
*
* A traversable resolver implementation must be thread-safe.
*
*/
public interface TraversableResolver {
/**
* Determine if the Bean Validation provider is allowed to reach the property state
*
* @param traversableObject object hosting <code>traversableProperty</code> or null
* if validateValue is called
* @param traversableProperty the traversable property.
* @param rootBeanType type of the root object passed to the Validator.
* @param pathToTraversableObject path from the root object to
* <code>traversableObject</code>
* (using the path specification defined by Bean Validator).
* @param elementType either <code>FIELD</code> or <code>METHOD</code>.
*
* @return <code>true</code> if the Bean Validation provider is allowed to
* reach the property state, <code>false</code> otherwise.
*/
boolean isReachable(Object traversableObject,
Path.Node traversableProperty,
Class<?> rootBeanType,
Path pathToTraversableObject,
ElementType elementType);
/**
* Determine if the Bean Validation provider is allowed to cascade validation on
* the bean instance returned by the property value
* marked as <code>@Valid</code>.
* Note that this method is called only if isReachable returns true for the same set of
* arguments and if the property is marked as <code>@Valid</code>
*
* @param traversableObject object hosting <code>traversableProperty</code> or null
* if validateValue is called
* @param traversableProperty the traversable property.
* @param rootBeanType type of the root object passed to the Validator.
* @param pathToTraversableObject path from the root object to
* <code>traversableObject</code>
* (using the path specification defined by Bean Validator).
* @param elementType either <code>FIELD</code> or <code>METHOD</code>.
*
* @return <code>true</code> if the Bean Validation provider is allowed to
* cascade validation, <code>false</code> otherwise.
*/
boolean isCascadable(Object traversableObject,
Path.Node traversableProperty,
Class<?> rootBeanType,
Path pathToTraversableObject,
ElementType elementType);
}
Hibernate Validator provides two
TraversableResolver
s out of the box which will be
enabled automatically depending on your environment. The first is the
DefaultTraversableResolver
which will always return
true for isReachable()
and
isTraversable()
. The second is the
JPATraversableResolver
which gets enabled when
Hibernate Validator gets used in combination with JPA 2. In case you have
to provide your own resolver you can do so again using the
Configuration
object as seen in Example 5.9, “Providing a custom TraversableResolver”.
Example 5.9. Providing a custom TraversableResolver
Configuration<?> configuration = Validation.byDefaultProvider().configure();
ValidatorFactory factory = configuration
.traversableResolver(new MyTraversableResolver())
.buildValidatorFactory();
Validator validator = factory.getValidator();
Last but not least, there is one more configuration option to
discuss, the ConstraintValidatorFactory
. The
default ConstraintValidatorFactory
provided by
Hibernate Validator requires a public no-arg constructor to instantiate
ConstraintValidator
instances (see Section 3.1.2, “The constraint
validator”). Using a custom
ConstraintValidatorFactory
offers for example the
possibility to use dependency injection in constraint implementations. The
configuration of the custom factory is once more via the
Configuration
(Example 5.10, “Providing a custom ConstraintValidatorFactory”).
Example 5.10. Providing a custom ConstraintValidatorFactory
Configuration<?> configuration = Validation.byDefaultProvider().configure();
ValidatorFactory factory = configuration
.constraintValidatorFactory(new IOCConstraintValidatorFactory())
.buildValidatorFactory();
Validator validator = factory.getValidator();
The interface you have to implement is:
Example 5.11. ConstraintValidatorFactory interface
public interface ConstraintValidatorFactory {
/**
* @param key The class of the constraint validator to instantiate.
*
* @return A constraint validator instance of the specified class.
*/
<T extends ConstraintValidator<?,?>> T getInstance(Class<T> key);
}
Any constraint implementation relying on
ConstraintValidatorFactory
behaviors specific to
an implementation (dependency injection, no no-arg constructor and so
on) are not considered portable.
ConstraintValidatorFactory should not cache instances as the state of each instance can be altered in the initialize method.
Copyright © 2009 - 2011 Red Hat, Inc. & Gunnar Morling