In Bean Validation, constraints can inherit from one another. Of course, this is not the same thing as one class inheriting from another because you cannot extend annotations. However, per convention, constraint annotations usually include a target of ElementType.ANNOTATION_TYPE.
When a constraint annotation is located, the Validator determines if the annotation definition is annotated with any other constraints. If so, it combines all the additional constraints with the logic defined by the original constraint (if any) into a single, composite constraint. In this sense, the constraint inherits all the constraints with which it is annotated. If for some reason you need to create a constraint that cannot be inherited, you simply omit ElementType.ANNOTATION_TYPE from the definition. With all this in mind, take a look at the @Email definition.
@Target(
{ ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = {})
@Pattern(regexp = "^[a-z0-9`!#$%^&*'{}?/+=|_~-]+(\\.[a-z0-9`!#$%^&*'{}?/+=|"
+ "_~-]+)*@([a-z0-9]([a-z0-9-]*[a-z0-9])?)+(\\.[a-z0-9]" + "([a-z0-9-]*[a-z0-9])?)*$", flags =
{ Pattern.Flag.CASE_INSENSITIVE })
@ReportAsSingleViolation
public @interface Email
{
String message() default "{ml.javasopenblog.site.validation.Email.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
@Target(
{ ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR,
ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
@Documented
static @interface List
{
Email[] value();
}
}
There’s a lot going on here, so take a look at it line by line, starting with the annotations:
@Target : This annotation indicates which language features this annotation can be placed on. The values listed are pretty standard and should be used for most constraints.
@Retention : Indicates that the annotation must be retained at run time. If not, Bean Validation will not detect it.
@Documented : This means that the Javadoc of targets marked with this annotation should indicate the annotation’s presence. This is especially useful when programming in an IDE because it makes the contract more visible.
@Constraint : This is a must: It’s what indicates that this annotation represents a Bean Validation constraint, so all constraint definitions have to be annotated with this. Without this, your constraint is ignored. @Constraint also indicates which ConstraintValidator implementation or implementations are responsible for validating your constraint. However, in this case no ConstraintValidator is necessary.
@Pattern : This is another constraint, indicating that this constraint inherits the constraint declared with @Pattern. This is the same regular expression seen earlier, but now you won’t have to duplicate the regular expression every time you use it. You can just use the @Email annotation, instead.
@ReportAsSingleViolation : Indicates that the composite constraint should be considered one constraint and use @Email’s message instead of @Pattern’s message. It is very rare that you should ever create a constraint that inherits other constraints without using @ReportAsSingleViolation.
Within the annotation are three attributes: message, groups, and payload. These are the standard attributes that must be present in all constraints. Without one or more of these, use of @Email would result in a ConstraintDefinitionException. The @Email.List inner annotation, like all the bean validation list annotations, defines a way to specify multiple @Email constraints on a target.
The @NotBlank constraint looks nearly identical to @Email. For the most part, it has the same annotations, attributes, and features. Instead of being annotated with @Pattern, it’s annotated with @NotNull. In this case @NotBlank should imply non-null, so you inherit the @NotNull constraint to accomplish this. (If you anticipate needing to define targets that can be null but cannot be blank strings, you would simply remove @NotNull from this annotation.)
@Target(
{ ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy =
{ NotBlankValidator.class })
@NotNull
@ReportAsSingleViolation
public @interface NotBlank
{
String message() default "{ml.javasopenblog.site.validation.NotBlank.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
@Target(
{ ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR,
ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
@Documented
static @interface List
{
NotBlank[] value();
}
}
However, unlike @Email, it can’t inherit all its functionality. It needs a ConstraintValidator to test whether the value is blank. The following NotBlankValidator class, declared in the @Constraint annotation on the @NotBlank annotation, accomplishes this.
public class NotBlankValidator implements ConstraintValidator<NotBlank, CharSequence>
{
@Override
public void initialize(NotBlank annotation)
{
}
@Override
public boolean isValid(CharSequence value, ConstraintValidatorContext context)
{
if (value instanceof String)
return ((String) value).trim().length() > 0;
return value.toString().trim().length() > 0;
}
}
Labels
- OCPJP
- OCPJP 8
- Path
- Comparator
- Eclipse
- Archetypes
- Arrays
- BufferedReader
- BufferedWriter
- Comparable
- DataInputStream
- DataOutputStream
- DateTimeFormatter
- FileInputStream
- FileOutputStream
- FileReader
- FileWriter
- Files
- Logger
- Maven
- Paths
- Singleton
- StandardCopyOption
- Static nested class
- Threading
- ZonedDateTime
- enum
Showing posts with label Validation. Show all posts
Showing posts with label Validation. Show all posts
Friday, December 9, 2016
Thursday, December 8, 2016
Adding Constraint Validation Annotations to Your Beans
Although you can create your own constraint annotations any time you like, the Bean Validation API comes with several built-in annotations that satisfy the most common validation requirements.
These are all very simple constraints, but in many cases they are all you need to use. All these
constraints are in the package javax.validation.constraints.
@Null : You can apply this to any type, and it ensures that the annotated target is null.
@NotNull : You can also apply this to any type. It ensures that the target is not null.
@AssertTrue and @AssertFalse : These ensure that their annotated targets are true and false, respectively. As such, the field, parameter, or method (return value) that they annotate must be of the type boolean or Boolean. A null Boolean is considered valid for either constraint, so combine these with @NotNull if you do not accept null values.
@DecimalMax : This defines an upper limit for a numeric type, specified with the value attribute. It may annotate fields, parameters, and methods (return values) of type BigDecimal, BigInteger, CharSequence (String), byte, Byte, short, Short, int, Integer, long, and Long. The primitives double, Double, float, and Float are not supported due to precision concerns. CharSequences are converted to a decimal before validation, and null values are considered valid. The optional inclusive attribute specifies whether the test should be inclusive (less than or equal to) or exclusive (less than), and defaults to inclusive (true).
@DecimalMin : This is the counterpart to @DecimalMax. It applies to all the same types with the same rules. It also contains an inclusive attribute.
@Digits : You can use this to ensure that the annotated target is a parseable number (if it’s a CharSequence) and then tests the limits of that number’s parts (whether it’s a CharSequence, BigDecimal, BigInteger, byte, Byte, short, Short, int, Int, long, or Long). The mandatory integer attribute specifies the maximum number of integral digits (before the decimal point) allowed, whereas the required fraction attribute specifies the maximum number of fractional digits (after the decimal point) allowed. As always, null values are considered valid.
@Future : Ensures that the Date or Calendar field, parameter, or method (return value) is at some point in the future, however near or distant. As of Bean Validation 1.1, there is no support for Java 8 Date and Time API types. null values are considered valid.
@Past : Ensures that Date and Calendar targets are at some point in the past.
@Max and @Min : These are similar to @DecimalMax and @DecimalMin, but they do not support CharSequence targets, and they do not host an inclusive attribute; they are always inclusive. Targets that are null are considered valid.
@Pattern : This defines a regular expression regexp that the target CharSequence (String) must match, and it considers null values to be valid. It hosts an optional flags attribute that supports an array of any of Pattern.Flag enum values. Supported flags are:
CANON_EQ : Enables canonical equivalence
CASE_INSENSITIVE : Enables case-insensitive matching
COMMENTS : Enables white space and comments in the pattern
DOTALL : Turns dotall mode on
MULTILINE : Turns multiline mode on
UNICODE_CASE : Enables Unicode case folding
UNIX_LINES : Turns Unix lines mode on
@Size : This defines inclusive max and min limits for the length of a CharSequence (String), the number of values in a Collection, the number of entries in a Map, or the number of elements in an array of any type.
These are all very simple constraints, but in many cases they are all you need to use. All these
constraints are in the package javax.validation.constraints.
@Null : You can apply this to any type, and it ensures that the annotated target is null.
@NotNull : You can also apply this to any type. It ensures that the target is not null.
@AssertTrue and @AssertFalse : These ensure that their annotated targets are true and false, respectively. As such, the field, parameter, or method (return value) that they annotate must be of the type boolean or Boolean. A null Boolean is considered valid for either constraint, so combine these with @NotNull if you do not accept null values.
@DecimalMax : This defines an upper limit for a numeric type, specified with the value attribute. It may annotate fields, parameters, and methods (return values) of type BigDecimal, BigInteger, CharSequence (String), byte, Byte, short, Short, int, Integer, long, and Long. The primitives double, Double, float, and Float are not supported due to precision concerns. CharSequences are converted to a decimal before validation, and null values are considered valid. The optional inclusive attribute specifies whether the test should be inclusive (less than or equal to) or exclusive (less than), and defaults to inclusive (true).
@DecimalMin : This is the counterpart to @DecimalMax. It applies to all the same types with the same rules. It also contains an inclusive attribute.
@Digits : You can use this to ensure that the annotated target is a parseable number (if it’s a CharSequence) and then tests the limits of that number’s parts (whether it’s a CharSequence, BigDecimal, BigInteger, byte, Byte, short, Short, int, Int, long, or Long). The mandatory integer attribute specifies the maximum number of integral digits (before the decimal point) allowed, whereas the required fraction attribute specifies the maximum number of fractional digits (after the decimal point) allowed. As always, null values are considered valid.
@Future : Ensures that the Date or Calendar field, parameter, or method (return value) is at some point in the future, however near or distant. As of Bean Validation 1.1, there is no support for Java 8 Date and Time API types. null values are considered valid.
@Past : Ensures that Date and Calendar targets are at some point in the past.
@Max and @Min : These are similar to @DecimalMax and @DecimalMin, but they do not support CharSequence targets, and they do not host an inclusive attribute; they are always inclusive. Targets that are null are considered valid.
@Pattern : This defines a regular expression regexp that the target CharSequence (String) must match, and it considers null values to be valid. It hosts an optional flags attribute that supports an array of any of Pattern.Flag enum values. Supported flags are:
CANON_EQ : Enables canonical equivalence
CASE_INSENSITIVE : Enables case-insensitive matching
COMMENTS : Enables white space and comments in the pattern
DOTALL : Turns dotall mode on
MULTILINE : Turns multiline mode on
UNICODE_CASE : Enables Unicode case folding
UNIX_LINES : Turns Unix lines mode on
@Size : This defines inclusive max and min limits for the length of a CharSequence (String), the number of values in a Collection, the number of entries in a Map, or the number of elements in an array of any type.
Wednesday, December 7, 2016
Configuring Validation in the Spring Framework Container
At its simplest, configuring Spring Framework’s LocalValidatorFactoryBean is as simple as instantiating it and returning it in a @Bean method in the RootContextConfiguration class:
@Bean
public LocalValidatorFactoryBean localValidatorFactoryBean()
{
return new LocalValidatorFactoryBean();
}
The LocalValidatorFactoryBean automatically detects the Bean Validation implementation on the classpath, whether that’s Hibernate Validator or some other implementation, and uses its default javax.validation.ValidatorFactory as the backing factory. There’s no need to set up the META-INF/validation.xml file usually required to take advantage of Bean Validation in your application. However, sometimes there is more than one Bean Validation Provider on the classpath (for example, when running within a full Java EE application server such as GlassFish or WebSphere).
In these cases, which provider Spring selects is unpredictable (it might even change each time!), so you should set the provider class manually if you prefer the provider to be predictable.
@Bean
public LocalValidatorFactoryBean localValidatorFactoryBean()
{
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.setProviderClass(HibernateValidator.class);
return validator;
}
The only downside to doing this is that it requires Hibernate Validator to be a compile-time dependency instead of a runtime dependency. This pollutes your compile time classpath, meaning your IDE will sometimes make code suggestions that you don’t want. You can avoid this by loading the class dynamically, which of course has its own downside in that any mistakes in the name will not be caught at compile time.
@Bean
public LocalValidatorFactoryBean localValidatorFactoryBean()
throws ClassNotFoundException
{
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.setProviderClass(Class.forName(
"org.hibernate.validator.HibernateValidator"
));
return validator;
}
N.B. Setting the provider class manually is not necessary when using Tomcat.
@Bean
public LocalValidatorFactoryBean localValidatorFactoryBean()
{
return new LocalValidatorFactoryBean();
}
The LocalValidatorFactoryBean automatically detects the Bean Validation implementation on the classpath, whether that’s Hibernate Validator or some other implementation, and uses its default javax.validation.ValidatorFactory as the backing factory. There’s no need to set up the META-INF/validation.xml file usually required to take advantage of Bean Validation in your application. However, sometimes there is more than one Bean Validation Provider on the classpath (for example, when running within a full Java EE application server such as GlassFish or WebSphere).
In these cases, which provider Spring selects is unpredictable (it might even change each time!), so you should set the provider class manually if you prefer the provider to be predictable.
@Bean
public LocalValidatorFactoryBean localValidatorFactoryBean()
{
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.setProviderClass(HibernateValidator.class);
return validator;
}
The only downside to doing this is that it requires Hibernate Validator to be a compile-time dependency instead of a runtime dependency. This pollutes your compile time classpath, meaning your IDE will sometimes make code suggestions that you don’t want. You can avoid this by loading the class dynamically, which of course has its own downside in that any mistakes in the name will not be caught at compile time.
@Bean
public LocalValidatorFactoryBean localValidatorFactoryBean()
throws ClassNotFoundException
{
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.setProviderClass(Class.forName(
"org.hibernate.validator.HibernateValidator"
));
return validator;
}
N.B. Setting the provider class manually is not necessary when using Tomcat.
Subscribe to:
Posts (Atom)