Hibernate SVN: r16226 - in validator/trunk/hibernate-validator: src/main/java/org/hibernate/validation and 4 other directories.
by hibernate-commits@lists.jboss.org
Author: hardy.ferentschik
Date: 2009-03-27 13:12:01 -0400 (Fri, 27 Mar 2009)
New Revision: 16226
Added:
validator/trunk/hibernate-validator/src/test/resources/META-INF/
validator/trunk/hibernate-validator/src/test/resources/META-INF/validation.xml
Modified:
validator/trunk/hibernate-validator/pom.xml
validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/HibernateValidationProvider.java
validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ConfigurationImpl.java
validator/trunk/hibernate-validator/src/main/xsd/validation-configuration-1.0.xsd
Log:
HV-111 work in progress
Modified: validator/trunk/hibernate-validator/pom.xml
===================================================================
--- validator/trunk/hibernate-validator/pom.xml 2009-03-27 15:40:12 UTC (rev 16225)
+++ validator/trunk/hibernate-validator/pom.xml 2009-03-27 17:12:01 UTC (rev 16226)
@@ -65,6 +65,9 @@
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
+ <resource>
+ <directory>src/main/xsd</directory>
+ </resource>
</resources>
<plugins>
<plugin>
Modified: validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/HibernateValidationProvider.java
===================================================================
--- validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/HibernateValidationProvider.java 2009-03-27 15:40:12 UTC (rev 16225)
+++ validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/HibernateValidationProvider.java 2009-03-27 17:12:01 UTC (rev 16226)
@@ -49,7 +49,7 @@
"Illegal call to createSpecializedConfiguration() for a non suitable provider"
);
}
- //cast protected by isSuitable call
+ //cast protected by isSuitable call
return configurationClass.cast( new ConfigurationImpl( this ) );
}
Modified: validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ConfigurationImpl.java
===================================================================
--- validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ConfigurationImpl.java 2009-03-27 15:40:12 UTC (rev 16225)
+++ validator/trunk/hibernate-validator/src/main/java/org/hibernate/validation/engine/ConfigurationImpl.java 2009-03-27 17:12:01 UTC (rev 16226)
@@ -18,9 +18,12 @@
package org.hibernate.validation.engine;
import java.io.InputStream;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
-import java.util.Map;
import javax.validation.Configuration;
import javax.validation.ConstraintValidatorFactory;
import javax.validation.MessageInterpolator;
@@ -31,12 +34,28 @@
import javax.validation.spi.BootstrapState;
import javax.validation.spi.ConfigurationState;
import javax.validation.spi.ValidationProvider;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+import org.slf4j.Logger;
+import org.xml.sax.SAXException;
+
+import org.hibernate.validation.engine.resolver.DefaultTraversableResolver;
+import org.hibernate.validation.util.LoggerFactory;
+import org.hibernate.validation.util.ReflectionHelper;
import org.hibernate.validation.util.Version;
-import org.hibernate.validation.engine.resolver.DefaultTraversableResolver;
+import org.hibernate.validation.xml.ValidationConfigType;
/**
+ * Hibernate specific <code>Configuration</code> implementation.
+ *
* @author Emmanuel Bernard
+ * @author Hardy Ferentschik
*/
public class ConfigurationImpl implements HibernateValidatorConfiguration, ConfigurationState {
@@ -44,16 +63,22 @@
Version.touch();
}
+ private static final Logger log = LoggerFactory.make();
+ private static final String VALIDATION_XML_FILE = "/META-INF/validation.xml";
+ private static final String VALIDATION_CONFIGURATION_XSD = "validation-configuration-1.0.xsd";
private static final MessageInterpolator defaultMessageInterpolator = new ResourceBundleMessageInterpolator();
private static final TraversableResolver defaultTraversableResolver = new DefaultTraversableResolver();
+ private final ValidationProvider provider;
+ private final ValidationProviderResolver providerResolver;
+ private final Map<String, String> configProperties = new HashMap<String, String>();
+ private final Set<InputStream> mappings = new HashSet<InputStream>();
+
private MessageInterpolator messageInterpolator;
private ConstraintValidatorFactory constraintValidatorFactory = new ConstraintValidatorFactoryImpl();
- private String configurationFile = "META-INF/validation.xml";
- private final ValidationProvider provider;
- private final ValidationProviderResolver providerResolver;
private TraversableResolver traversableResolver;
- private boolean ignoreXmlConfiguration;
+ private boolean ignoreXmlConfiguration = true; // false;
+ private Class<? extends Configuration<?>> providerClass = null;
public ConfigurationImpl(BootstrapState state) {
if ( state.getValidationProviderResolver() == null ) {
@@ -78,7 +103,8 @@
}
public HibernateValidatorConfiguration ignoreXmlConfiguration() {
- return null; //To change body of implemented methods use File | Settings | File Templates.
+ ignoreXmlConfiguration = true;
+ return this;
}
public ConfigurationImpl messageInterpolator(MessageInterpolator interpolator) {
@@ -97,21 +123,21 @@
}
public HibernateValidatorConfiguration addMapping(InputStream stream) {
- return null; //To change body of implemented methods use File | Settings | File Templates.
+ mappings.add( stream );
+ return this;
}
public HibernateValidatorConfiguration addProperty(String name, String value) {
- return null; //To change body of implemented methods use File | Settings | File Templates.
+ configProperties.put( name, value );
+ return this;
}
public ValidatorFactory buildValidatorFactory() {
+ parseValidationXml();
if ( isSpecificProvider() ) {
return provider.buildValidatorFactory( this );
}
else {
- //read provider name from configuration
- Class<? extends Configuration<?>> providerClass = null;
-
if ( providerClass != null ) {
for ( ValidationProvider provider : providerResolver.getValidationProviders() ) {
if ( provider.isSuitable( providerClass ) ) {
@@ -122,16 +148,12 @@
}
else {
List<ValidationProvider> providers = providerResolver.getValidationProviders();
- assert providers.size() != 0; //I run therefore I am
+ assert providers.size() != 0; // I run therefore I am
return providers.get( 0 ).buildValidatorFactory( this );
}
}
}
- private boolean isSpecificProvider() {
- return provider != null;
- }
-
public boolean isIgnoreXmlConfiguration() {
return ignoreXmlConfiguration;
}
@@ -141,7 +163,7 @@
}
public Set<InputStream> getMappingStreams() {
- throw new UnsupportedOperationException( "TODO" );
+ return mappings;
}
public ConstraintValidatorFactory getConstraintValidatorFactory() {
@@ -153,11 +175,119 @@
}
public Map<String, String> getProperties() {
- throw new UnsupportedOperationException( "TODO" );
+ return configProperties;
}
public MessageInterpolator getDefaultMessageInterpolator() {
return defaultMessageInterpolator;
}
+ private boolean isSpecificProvider() {
+ return provider != null;
+ }
+
+ /**
+ * Tries to check whether a validation.xml file exists and parses it using JAXB
+ */
+ private void parseValidationXml() {
+ if ( ignoreXmlConfiguration ) {
+ log.info( "Ignoring XML configuration." );
+ return;
+ }
+
+ ValidationConfigType config = getValidationConfig();
+ if ( config == null ) {
+ return;
+ }
+
+ setMessageInterpolatorFromXml( config );
+ setTraversableResolverFromXml( config );
+ setProviderClassFromXml( config );
+ }
+
+ private void setMessageInterpolatorFromXml(ValidationConfigType config) {
+ String messageInterpolatorClass = config.getMessageInterpolator();
+ if ( messageInterpolatorClass != null ) {
+ try {
+ @SuppressWarnings("unchecked")
+ Class<MessageInterpolator> clazz = ( Class<MessageInterpolator> ) ReflectionHelper.classForName(
+ messageInterpolatorClass, this.getClass()
+ );
+ messageInterpolator = clazz.newInstance();
+ log.info( "Using {} as message interpolator.", messageInterpolatorClass );
+ }
+ catch ( Exception e ) {
+ throw new ValidationException( "Unable to instantiate message interpolator class " + messageInterpolatorClass + "." );
+ }
+ }
+ }
+
+ private void setTraversableResolverFromXml(ValidationConfigType config) {
+ String traversableResolverClass = config.getTraversableResolver();
+ if ( traversableResolverClass != null ) {
+ try {
+ @SuppressWarnings("unchecked")
+ Class<TraversableResolver> clazz = ( Class<TraversableResolver> ) ReflectionHelper.classForName(
+ traversableResolverClass, this.getClass()
+ );
+ traversableResolver = clazz.newInstance();
+ log.info( "Using {} as traversable resolver", traversableResolverClass );
+ }
+ catch ( Exception e ) {
+ throw new ValidationException( "Unable to instantiate message interpolator class " + traversableResolverClass + "." );
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private void setProviderClassFromXml(ValidationConfigType config) {
+ String providerClassName = config.getDefaultProvider();
+ if ( providerClassName != null ) {
+ try {
+ providerClass = ( Class<? extends Configuration<?>> ) ReflectionHelper.classForName(
+ providerClassName, this.getClass()
+ );
+ log.info( "Using {} as validation provider.", providerClassName );
+ }
+ catch ( Exception e ) {
+ throw new ValidationException( "Unable to instantiate validation provider class " + providerClassName + "." );
+ }
+ }
+ }
+
+ private ValidationConfigType getValidationConfig() {
+ InputStream inputStream = this.getClass().getResourceAsStream( VALIDATION_XML_FILE );
+ if ( inputStream == null ) {
+ log.info( "No {} found. Using defaults.", VALIDATION_XML_FILE );
+ return null;
+ }
+
+ ValidationConfigType validationConfig = null;
+ Schema schema = getValidationConfigurationSchema();
+ try {
+ JAXBContext jc = JAXBContext.newInstance( ValidationConfigType.class );
+ Unmarshaller unmarshaller = jc.createUnmarshaller();
+ unmarshaller.setSchema( schema );
+ StreamSource stream = new StreamSource( inputStream );
+ JAXBElement<ValidationConfigType> root = unmarshaller.unmarshal( stream, ValidationConfigType.class );
+ validationConfig = root.getValue();
+ }
+ catch ( JAXBException e ) {
+ log.error( "Error parsing validation.xml: {}", e.getMessage() );
+ }
+ return validationConfig;
+ }
+
+ private Schema getValidationConfigurationSchema() {
+ URL schemaUrl = this.getClass().getClassLoader().getResource( VALIDATION_CONFIGURATION_XSD );
+ SchemaFactory sf = SchemaFactory.newInstance( javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI );
+ Schema schema = null;
+ try {
+ schema = sf.newSchema( schemaUrl );
+ }
+ catch ( SAXException e ) {
+ log.warn( "Unable to create schema for {}: {}", VALIDATION_XML_FILE, e.getMessage() );
+ }
+ return schema;
+ }
}
Modified: validator/trunk/hibernate-validator/src/main/xsd/validation-configuration-1.0.xsd
===================================================================
--- validator/trunk/hibernate-validator/src/main/xsd/validation-configuration-1.0.xsd 2009-03-27 15:40:12 UTC (rev 16225)
+++ validator/trunk/hibernate-validator/src/main/xsd/validation-configuration-1.0.xsd 2009-03-27 17:12:01 UTC (rev 16226)
@@ -6,7 +6,7 @@
xmlns="http://jboss.org/xml/ns/javax/validation/configuration"
version="1.0">
<xs:element name="validation-config" type="validation-configType"/>
- <xs:complexType name="validation-configType">
+ <xs:complexType name="validation-configType" >
<xs:sequence>
<xs:element type="xs:string" name="default-provider" minOccurs="0"/>
<xs:element type="xs:string" name="message-interpolator" minOccurs="0"/>
Added: validator/trunk/hibernate-validator/src/test/resources/META-INF/validation.xml
===================================================================
--- validator/trunk/hibernate-validator/src/test/resources/META-INF/validation.xml (rev 0)
+++ validator/trunk/hibernate-validator/src/test/resources/META-INF/validation.xml 2009-03-27 17:12:01 UTC (rev 16226)
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<validation-config xmlns="http://jboss.org/xml/ns/javax/validation/configuration"
+ xsi:schemaLocation="http://jboss.org/xml/ns/javax/validation/configuration validation-configuration-1.0.xsd"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <default-provider>org.hibernate.validation.engine.HibernateValidatorConfiguration</default-provider>
+ <message-interpolator>org.hibernate.validation.engine.ResourceBundleMessageInterpolator</message-interpolator>
+ <constraint-mapping>META-INF/validation/order-constraints.xml</constraint-mapping>
+</validation-config>
\ No newline at end of file
15 years, 9 months
Hibernate SVN: r16225 - core.
by hibernate-commits@lists.jboss.org
Author: pete.muir(a)jboss.org
Date: 2009-03-27 11:40:12 -0400 (Fri, 27 Mar 2009)
New Revision: 16225
Removed:
core/se-module/
core/webbeans-xsd-generator/
Log:
remove bad commits
15 years, 9 months
Hibernate SVN: r16224 - validator/trunk/hibernate-validator/src/main/docbook/en-US/modules.
by hibernate-commits@lists.jboss.org
Author: hardy.ferentschik
Date: 2009-03-27 06:11:19 -0400 (Fri, 27 Mar 2009)
New Revision: 16224
Modified:
validator/trunk/hibernate-validator/src/main/docbook/en-US/modules/customconstraints.xml
Log:
Applied Gunnar's last doc updates
Modified: validator/trunk/hibernate-validator/src/main/docbook/en-US/modules/customconstraints.xml
===================================================================
--- validator/trunk/hibernate-validator/src/main/docbook/en-US/modules/customconstraints.xml 2009-03-26 22:27:12 UTC (rev 16223)
+++ validator/trunk/hibernate-validator/src/main/docbook/en-US/modules/customconstraints.xml 2009-03-27 10:11:19 UTC (rev 16224)
@@ -30,44 +30,47 @@
<para>Though the Bean Validation API defines a whole bunch of standard
constraint annotations such as @NotNull, @Size, @Min or @AssertTrue, one can
easily think of situations, for which these standard annotations won't
- suffice.</para>
+ suffice. But as the specification was designed with extensibility in mind,
+ you are able to create custom constraints tailored to your specific
+ validation requirements in a simple and timely manner.</para>
- <para>But as the specification was designed with extensibility in mind, you
- are able to create custom constraints tailored to your specific validation
- requirements in a simple and timely manner. To create a custom constraint,
- the following three steps are required, which will be explained in the
- following:</para>
+ <section id="validator-customconstraints-simple" revision="1">
+ <title>Creating a simple constraint</title>
- <itemizedlist>
- <listitem>
- <para>Create a constraint annotation</para>
- </listitem>
+ <para>To create a custom constraint, the following three steps are
+ required, which will be explained in the following:</para>
- <listitem>
- <para>Implement a validator, that's able to evaluate that
- annotation</para>
- </listitem>
+ <itemizedlist>
+ <listitem>
+ <para>Create a constraint annotation</para>
+ </listitem>
- <listitem>
- <para>Define a default error message</para>
- </listitem>
- </itemizedlist>
+ <listitem>
+ <para>Implement a validator, that's able to evaluate that
+ annotation</para>
+ </listitem>
- <section id="validator-customconstraints-constraintannotation" revision="1">
- <title>Creating a constraint annotation</title>
+ <listitem>
+ <para>Define a default error message</para>
+ </listitem>
+ </itemizedlist>
- <para>Let's write a constraint annotation, that can be used to express
- that a given String shall either be upper case or lower case. We'll apply
- it later on to ensure, that the licensePlate field of the Car class from
- the <link linkend="validator-gettingstarted">Getting started</link>
- chapter is always an upper-case String.</para>
+ <section id="validator-customconstraints-constraintannotation"
+ revision="1">
+ <title>Creating a constraint annotation</title>
- <para>First we need a way to express the two case modes. We might use
- String constants, but a better way to go is to use a Java 5 enum for that
- purpose:</para>
+ <para>Let's write a constraint annotation, that can be used to express
+ that a given String shall either be upper case or lower case. We'll
+ apply it later on to ensure, that the licensePlate field of the Car
+ class from the <link linkend="validator-gettingstarted">Getting
+ started</link> chapter is always an upper-case String.</para>
- <programlisting>package com.mycompany;
+ <para>First we need a way to express the two case modes. We might use
+ String constants, but a better way to go is to use a Java 5 enum for
+ that purpose:</para>
+ <programlisting>package com.mycompany;
+
public enum CaseMode {
UPPER,
@@ -76,11 +79,11 @@
}</programlisting>
- <para>Now we can define the actual constraint annotation. If you've never
- designed an annotation before, this may look a bit scary, but actually
- it's not that hard:</para>
+ <para>Now we can define the actual constraint annotation. If you've
+ never designed an annotation before, this may look a bit scary, but
+ actually it's not that hard:</para>
- <programlisting>package com.mycompany;
+ <programlisting>package com.mycompany;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
@@ -105,66 +108,67 @@
}</programlisting>
- <para>An annotation type is defined using the @interface keyword. All
- attributes of an annotation type are declared in a method-like manner. The
- specification of the Bean Validation API demands, that any constraint
- annotation defines</para>
+ <para>An annotation type is defined using the @interface keyword. All
+ attributes of an annotation type are declared in a method-like manner.
+ The specification of the Bean Validation API demands, that any
+ constraint annotation defines</para>
- <itemizedlist>
- <listitem>
- <para>an attribute "message" that returns the default key for creating
- error messages in case the constraint is violated</para>
- </listitem>
+ <itemizedlist>
+ <listitem>
+ <para>an attribute "message" that returns the default key for
+ creating error messages in case the constraint is violated</para>
+ </listitem>
- <listitem>
- <para>an attribute "groups" that allows the specification of
- validation groups, to which this constraint belongs (see section
- "Using validation groups" for further details, TODO GM: link). This
- must default to an empty array of type Class<?>.</para>
- </listitem>
- </itemizedlist>
+ <listitem>
+ <para>an attribute "groups" that allows the specification of
+ validation groups, to which this constraint belongs (see section
+ "Using validation groups" for further details, TODO GM: link). This
+ must default to an empty array of type Class<?>.</para>
+ </listitem>
+ </itemizedlist>
- <para>Besides those two mandatory attributes we add another one allowing
- for the required case mode to be specified. The name "value" is a special
- one, which can be omitted upon using the annotation, if it is the only
- attribute specified, as e.g. in @CheckCase(CaseMode.UPPER).</para>
+ <para>Besides those two mandatory attributes we add another one allowing
+ for the required case mode to be specified. The name "value" is a
+ special one, which can be omitted upon using the annotation, if it is
+ the only attribute specified, as e.g. in
+ @CheckCase(CaseMode.UPPER).</para>
- <para>In addition we annotate the annotation type with a couple of
- so-called meta annotations:</para>
+ <para>In addition we annotate the annotation type with a couple of
+ so-called meta annotations:</para>
- <itemizedlist>
- <listitem>
- <para>@Target({ METHOD, FIELD, ANNOTATION_TYPE }): Says, that methods,
- fields and annotation declarations may be annotated with @CheckCase
- (but not type declarations e.g.)</para>
- </listitem>
+ <itemizedlist>
+ <listitem>
+ <para>@Target({ METHOD, FIELD, ANNOTATION_TYPE }): Says, that
+ methods, fields and annotation declarations may be annotated with
+ @CheckCase (but not type declarations e.g.)</para>
+ </listitem>
- <listitem>
- <para>@Retention(RUNTIME): Specifies, that annotations of this type
- will be available at runtime by the means of reflection</para>
- </listitem>
+ <listitem>
+ <para>@Retention(RUNTIME): Specifies, that annotations of this type
+ will be available at runtime by the means of reflection</para>
+ </listitem>
- <listitem>
- <para>@Constraint(validatedBy = CheckCaseValidator.class): Specifies
- the validator to be used to validate elements annotated with
- @CheckCase</para>
- </listitem>
+ <listitem>
+ <para>@Constraint(validatedBy = CheckCaseValidator.class): Specifies
+ the validator to be used to validate elements annotated with
+ @CheckCase</para>
+ </listitem>
- <listitem>
- <para>@Documented: Says, that the use of @CheckCase will be contained
- in the JavaDoc of elements annotated with it</para>
- </listitem>
- </itemizedlist>
- </section>
+ <listitem>
+ <para>@Documented: Says, that the use of @CheckCase will be
+ contained in the JavaDoc of elements annotated with it</para>
+ </listitem>
+ </itemizedlist>
+ </section>
- <section id="validator-customconstraints-validator" revision="1">
- <title>Implementing the constraint validator</title>
+ <section id="validator-customconstraints-validator" revision="1">
+ <title>Implementing the constraint validator</title>
- <para>Next, we need to implement a constraint validator, that's able to
- validate elements with a @CheckCase annotation. To do so, we implement the
- interface ConstraintValidator as shown below:</para>
+ <para>Next, we need to implement a constraint validator, that's able to
+ validate elements with a @CheckCase annotation. To do so, we implement
+ the interface ConstraintValidator as shown below:</para>
- <programlisting>package com.mycompany;
+ <programlisting>package com.mycompany;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
@@ -190,53 +194,61 @@
}</programlisting>
- <para>The ConstraintValidator interface defines two type parameters, which
- we set in our implementation. The first one specifies the annotation type
- to be validated (in our example CheckCase), the second one the type of
- elements, which the validator can handle (here String).</para>
+ <para>The ConstraintValidator interface defines two type parameters,
+ which we set in our implementation. The first one specifies the
+ annotation type to be validated (in our example CheckCase), the second
+ one the type of elements, which the validator can handle (here
+ String).</para>
- <para>In case a constraint annotation is allowed at elements of different
- types, a ConstraintValidator for each allowed type has to be implemented
- and registered at the constraint annotation as shown above.</para>
+ <para>In case a constraint annotation is allowed at elements of
+ different types, a ConstraintValidator for each allowed type has to be
+ implemented and registered at the constraint annotation as shown
+ above.</para>
- <para>The implementation of the validator is straightforward. The
- initialize() method gives us access to the attribute values of the
- annotation to be validated. In the example we store the CaseMode in a
- field of the validator for further usage.</para>
+ <para>The implementation of the validator is straightforward. The
+ initialize() method gives us access to the attribute values of the
+ annotation to be validated. In the example we store the CaseMode in a
+ field of the validator for further usage.</para>
- <para>In the isValid() method we implement the logic, that determines,
- whether a String is valid according to a given @CheckCase annotation or
- not. This decision depends on the case mode retrieved in initialize(). As
- the Bean Validation specification recommends, we consider null values as
- being valid. If null is not a valid value for an element, it should be
- annotated with @NotNull explicitely.</para>
+ <para>In the isValid() method we implement the logic, that determines,
+ whether a String is valid according to a given @CheckCase annotation or
+ not. This decision depends on the case mode retrieved in initialize().
+ As the Bean Validation specification recommends, we consider null values
+ as being valid. If null is not a valid value for an element, it should
+ be annotated with @NotNull explicitely.</para>
- <para>The passed-in ConstraintValidatorContext could be used to raise any
- custom validation errors, but as we are fine with the default behavior, we
- can ignore that parameter for now (TODO GM: example for usage).</para>
- </section>
+ <para>The passed-in ConstraintValidatorContext could be used to raise
+ any custom validation errors, but as we are fine with the default
+ behavior, we can ignore that parameter for now (TODO GM: example for
+ usage).</para>
+ </section>
- <section id="validator-customconstraints-errormessage" revision="1">
- <title>Defining the error message</title>
+ <section id="validator-customconstraints-errormessage" revision="1">
+ <title>Defining the error message</title>
- <para>Finally we need to specify the error message, that shall be used, in
- case a @CheckCase constraint is violated. To do so, we create a file named
- ValidationMessages.properties under src/main/resources with the following
- content:</para>
+ <para>Finally we need to specify the error message, that shall be used,
+ in case a @CheckCase constraint is violated. To do so, we create a file
+ named ValidationMessages.properties under src/main/resources with the
+ following content:</para>
- <programlisting>validator.checkcase=Case mode must be {value}.</programlisting>
+ <programlisting>validator.checkcase=Case mode must be {value}.</programlisting>
- <para>If a validation error occurs, the validation runtime will use the
- default value, that we specified for the message attribute of the
- @CheckCase annotation to look up the error message in this file.</para>
+ <para>If a validation error occurs, the validation runtime will use the
+ default value, that we specified for the message attribute of the
+ @CheckCase annotation to look up the error message in this file.</para>
+ </section>
- <para>Now that our first custom constraint is completed, we can use it in
- the Car class from the <link linkend="validator-gettingstarted">Getting
- started</link> chapter to specify that the licensePlate field shall only
- contain upper-case Strings:</para>
+ <section id="validator-customconstraints-using" revision="1">
+ <title>Using the constraint</title>
- <programlisting>package com.mycompany;
+ <para>Now that our first custom constraint is completed, we can use it
+ in the Car class from the <link
+ linkend="validator-gettingstarted">Getting started</link> chapter to
+ specify that the licensePlate field shall only contain upper-case
+ Strings:</para>
+ <programlisting>package com.mycompany;
+
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
@@ -265,10 +277,10 @@
}</programlisting>
- <para>Finally let's demonstrate in a little test that the @CheckCase
- constraint is properly validated:</para>
+ <para>Finally let's demonstrate in a little test that the @CheckCase
+ constraint is properly validated:</para>
- <programlisting>package com.mycompany;
+ <programlisting>package com.mycompany;
import static org.junit.Assert.*;
@@ -316,6 +328,7 @@
assertEquals(0, constraintViolations.size());
}
}</programlisting>
+ </section>
</section>
<section id="validator-customconstraints-compound" revision="1">
@@ -400,4 +413,158 @@
}</programlisting>
</section>
+
+ <section id="validator-customconstraints-classlevel" revision="1">
+ <title>Class-level constraints</title>
+
+ <para>A common requirement with constraints is that wether the value of a
+ given property is valid or not can not be determined by solely inspecting
+ the property itself but depends on the value of annother property of the
+ object owning the property.</para>
+
+ <para>This is where class-level constraints come into play. They are
+ annotated not at the fields of a class but at the class definition itself,
+ allowing for the accompanying validators having access to all properties
+ of the object to be validated.</para>
+
+ <para>As example let's create a constraint, that ensures, that no more
+ passengers sit in a car than its seat count allows. The constraint
+ annotation looks as follows:</para>
+
+ <programlisting>package com.mycompany;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.*;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import javax.validation.Constraint;
+
+@Target( { TYPE, ANNOTATION_TYPE })
+@Retention(RUNTIME)
+@Constraint(validatedBy = {ValidPassengerCountValidator.class})
+@Documented
+public @interface ValidPassengerCount {
+
+ String message() default "{validator.validpassengercount}";
+
+ Class<?>[] groups() default {};
+
+}</programlisting>
+
+ <para>Note, that within the @Target annotation TYPE instead of METHOD and
+ FIELD is specified as possible target for the annotation (besides
+ ANNOTATION_TYPE, which allows for @ValidPassengerCount to be used in
+ constraint composition). The validator implementation is fairly
+ easy:</para>
+
+ <programlisting>package com.mycompany;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+public class ValidPassengerCountValidator implements ConstraintValidator<ValidPassengerCount, Car> {
+
+ public void initialize(ValidPassengerCount constraintAnnotation) {
+ }
+
+ public boolean isValid(Car object,
+ ConstraintValidatorContext constraintContext) {
+
+ return object.getPassengers().size() <= object.getSeatCount();
+ }
+
+}</programlisting>
+
+ <para>As with the first constraint an error text in
+ ValidationMessages.properties is required:</para>
+
+ <programlisting>validator.validpassengercount=Passenger count must be less than or equal to seat count.</programlisting>
+
+ <para>The @ValidPassengerCount annotation can now be used at the car
+ class:</para>
+
+ <programlisting>package com.mycompany;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+
+@ValidPassengerCount
+public class Car {
+
+ @Min(2)
+ private int seatCount;
+
+ @NotNull
+ List<String> passengers;
+
+ public Car(int seatCount, String... passengers) {
+
+ this.seatCount = seatCount;
+ this.passengers = new ArrayList<String>();
+
+ for (String onePassenger : passengers) {
+ this.passengers.add(onePassenger);
+ }
+ }
+
+ //getters and setters ...
+}</programlisting>
+
+ <para>Finally let's try our new class-level annotation in a test:</para>
+
+ <programlisting>package com.mycompany;
+
+import static org.junit.Assert.*;
+
+import java.util.Set;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.Validation;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class CarTest {
+
+ private static Validator validator;
+
+ @BeforeClass
+ public static void setUp() {
+ ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
+ validator = factory.getValidator();
+ }
+
+ @Test
+ public void tooManyPassengers() {
+
+ Car car = new Car(2, "Jupiter", "Pete", "Bob");
+
+ Set<ConstraintViolation<Car3>> constraintViolations =
+ validator.validate(car);
+ assertEquals(1, constraintViolations.size());
+ assertEquals(
+ "Passenger count must be less than or equal to seat count.",
+ constraintViolations.iterator().next().getInterpolatedMessage());
+ }
+
+ @Test
+ public void carIsValid() {
+
+ Car car = new Car(2, "Jupiter", "Pete");
+
+ Set<ConstraintViolation<Car3>> constraintViolations =
+ validator.validate(car);
+
+ assertEquals(0, constraintViolations.size());
+ }
+}</programlisting>
+ </section>
</chapter>
15 years, 9 months