I want to perform the validation of my entities in two steps. While I use a defaultValidatorFactory to validate all the fields of my entities before persisting to the database, I would like to perform a partial validation of my entities at a earlier step. But I cannot find a way to configure my validator (or validatorFactory).
Let's say I have the following class:
public class Car {
#NotNull
private String manufacturer;
#AssertTrue
private boolean isRegistered;
public Car(String manufacturer, boolean isRegistered) {
super();
this.manufacturer = manufacturer;
this.isRegistered = isRegistered;
}
}
When I do the full validation of my entity, I use the given code:
Validator validator = validation.buildDefaultValidatorFactory().getValidator();
Set<ConstraintViolation<Car>> errors = validator.validate(car);
This works fine and validate both annotations NotNull and AssertTrue.
Now, I want to perform an partial validation. I mean by partial validation, I want to only validate, for example, NotNull constraints and ignore other annotations.
Is there a way to get a Validator or ValidatorFactory which uses a custom restricted list of validators?
You can find a lot of things to create your own constraint/constraint validator. In my case, I want to validate only some constraints.
Maybe I can create a custom ConstraintValidatorFactory and inject it in the Validation context? I found that we can reconfigure the context of the factory with the following code, but I don't know how to deal with it.
ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
validatorFactory.usingContext().constraintValidatorFactory(myCustomFactory);
For the moment, I'm lost. Someone has already done something like that? Do you have any idea how I can do this? Thanks for your time.
I'm using Java 8 and Hibernate Validator 6.0.14.
As Slaw write - use groups.
An Example
package jpatest.jpatest;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.constraints.NotNull;
public class TestApp {
/** A validation group marker */
public interface ValidationGroup1 {};
/** The bean */
public static class Bean {
// Validate for group ValidationGroup1
#NotNull(groups = ValidationGroup1.class)
private String s;
}
public static void main(String[] args) {
Bean b = new Bean();
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
// Validation without the validation group => No ConstraintViolation
Set<ConstraintViolation<Bean>> errors1 = validator.validate(b);
assert errors1.isEmpty() : "No ConstraintViolation expected";
// Validation with the validation group => 1 ConstraintViolation
Set<ConstraintViolation<Bean>> errors2 = validator.validate(b, ValidationGroup1.class);
assert errors2.size() == 1 : "1 ConstraintViolation expected";
}
}
I have implemented a custom annotation #Password to perform validation on an argument of my method setPassword(). The annotation is defined like this:
// Password.java
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
import javax.validation.*;
#Target({METHOD, FIELD, PARAMETER, ANNOTATION_TYPE})
#Retention(RUNTIME)
#Constraint(validatedBy = PasswordValidator.class)
#Documented
public #interface Password {
String message() default PasswordValidator.message;
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
And the current implementation of the validator is this:
// PasswordValidator.java
package utils;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class PasswordValidator implements ConstraintValidator<Password, String> {
/* Default error message */
final static public String message = "error.invalid.password";
/**
* Validator init
* Can be used to initialize the validation based on parameters
* passed to the annotation.
*/
public void initialize(Password constraintAnnotation) {
System.out.println("in initialize()");
}
public boolean isValid(String string, ConstraintValidatorContext constraintValidatorContext) {
System.out.println("in isValid()");
return false;
}
}
Note that in the current implementation isValid() always returns false. The reason will be apparent shortly.
My usage of the validator is in a class User. For brevity I won't post the whole source here, but the relevant parts are:
package models;
import utils.Password;
// other imports omitted
#Entity
#Table(name = "users", schema="public")
public class User {
#Required
private String password;
...
public void setPassword(#Password String clearPassword) {
try {
this.password = HashHelper.createPassword(clearPassword);
} catch (AppException e) {
e.printStackTrace();
}
}
...
}
The basic idea is that I use the User class to store a hashed password for a user, but before setting the hashed password, I (would like to) run validation on the unhashed password (i.e. clearPassword).
The problem I am having is that this validation is not taking place. In the current implementation, it should (according to my understanding) always throw a ConstraintViolationException because isValid() always returns false, but this is not the case.
I have checked that the annotation is being attached to the method argument by calling (in another part of the application) something along the lines of:
Method method = user.getClass().getMethod("setPassword", new Class[] { String.class });
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
System.out.println(method.getName() + ": " + parameterAnnotations[0][0]);
which produces the following output:
setPassword:#utils.Password(message=error.invalid.password, payload=[], groups=[])
So this tells me the annotation is being applied to the method argument. But I can't understand why I'm not getting the ConstraintViolationException when I actually call the method. I also never see the output "in initialize()" or "in isValid()" that I added to these methods as a check to see if they're being fired.
As another test, I also added the #Password annotation to the member variable password in User.class. This causes the ConstraintViolationException to be thrown as expected, e.g. when I try to persist a User object.
Can anyone shed light as to why the annotation on the method argument is not working properly? Thanks in advance!
I think you are missing this declaration in your Password annotation:
ConstraintTarget validationAppliesTo() default ConstraintTarget.IMPLICIT;
From the docs:
validationAppliesTo is used at constraint declaration time to clarify what the constraint targets (i.e. the annotated element, the method return value or the method parameters).
The element validationAppliesTo must only be present for constraints that are both generic and cross-parameter, it is mandatory in this situation. A ConstraintDefinitionException is raised if these rules are violated.
The type of the validationAppliesTo parameter is ConstraintTarget. The default value must be ConstraintTarget.IMPLICIT.
It won't get validated because hibernate validator validates on user object not on the method setPassword. In case you want to throw that exception, you have to get the ExecutableValidator instance, call validateParameters method and then throw the ConstraintViolationException by yourself.
I am writing code that has explicit call to Bean Validation (JSR-303) something like this:
public class Example {
#DecimalMin(value = "0")
private static final String ANNOTATED = "";
public void isPossitiveNumber(String str){
ValidatorFactory factory =
Validation.buildDefaultValidatorFactory();
ConstraintValidator<DecimalMin, String>
validator =
factory.getConstraintValidatorFactory().getInstance(
DecimalMinValidatorForString.class);
validator.initialize(
ReflectionUtils.findField(getClass(), "ANNOTATED")
.getAnnotation(
DecimalMin.class));
boolean isValid = validator.isValid(str, null);
return isValid;
}
}
Note the line boolean isValid = validator.isValid(str, null);
I transfer null for ConstraintValidatorContext because I found no way to obtain/construct it. In this particular case, this if fine, because there is no use of the ConstraintValidatorContext internally, but it is obvious a hack. How should I get ConstraintValidatorContext?
ADDED
I was asked to provide use-cases. So, for example, I am writting custom validator and I want to reuse exisiting validations. Or I am writting plane Java code as desribed above and I want to reuse exisiting validation.
I recently had exactly the same issue as the OP. However contrary to the accepted answer it is possible to write Unit tests that include the ConstraintValidationContext. This excellent link explains how to do it, http://farenda.com/java/bean-validation-unit-testing/
Basically you need to use the ValidatorFactory to obtain a Validator interface, then call validate(c) on that interface, where the parameter c is an instance of the class containing the bean validation annotations. A code example is clearer, code sample taken from the above link.
public class Player {
// name have to be 3 chars:
#Size(min = 3, max = 3)
private String name;
// possible score in game:
#Min(0) #Max(100)
private int score;
public Player(String name, int score) {
this.name = name;
this.score = score;
}
// just for logs
#Override
public String toString() {
return "Player{name='" + name + '\'' + ", score=" + score + '}';
}
}
public class PlayerValidationTest {
private static ValidatorFactory validatorFactory;
private static Validator validator;
#BeforeClass
public static void createValidator() {
validatorFactory = Validation.buildDefaultValidatorFactory();
validator = validatorFactory.getValidator();
}
#AfterClass
public static void close() {
validatorFactory.close();
}
#Test
public void shouldDetectInvalidName() {
//given too short name:
Player player = new Player("a", 44);
//when:
Set<ConstraintViolation<Player>> violations
= validator.validate(player);
//then:
assertEquals(violations.size(), 1);
}
}
The simple answer is you cannot. ConstraintValidatorContext is an interface and there is no Bean Validation API to get an instance like this. You could write your own implementation, but to implement it properly you would have to re-implement a lot of functionality of a Bean Validation provider. Look for example at the Hibernate Validator specific implementation - https://github.com/hibernate/hibernate-validator/blob/master/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ConstraintValidatorContextImpl.java
That said, I believe your attempt of reuse is misguided. This is not in the indent of Bean Validation and you are ending up with non portable and hard to maintain code. If you want to reuse existing constraints have a look at constraint composition, for example #NotEmpty reusing #NotNull and #Size
#Documented
#Constraint(validatedBy = { })
#Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
#Retention(RUNTIME)
#ReportAsSingleViolation
#NotNull
#Size(min = 1)
public #interface NotEmpty {
String message() default "{org.hibernate.validator.constraints.NotEmpty.message}";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
/**
* Defines several {#code #NotEmpty} annotations on the same element.
*/
#Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
#Retention(RUNTIME)
#Documented
public #interface List {
NotEmpty[] value();
}
}
You should declare a group for the constraints you validate in that case. Then you can call the normal validation for that group. See sections 2.1.1.2 and section 3.4 of the spec for group definitions and their semantics. For validating the group, you then just need to call Validator.validate(T Object, Class<?>... groups). There is no need to mess around with the ConstraintValidatorContext in this case.
I would like to know what is the cleanest and best way to perform form validation of user inputs. I have seen some developers implement org.springframework.validation.Validator. A question about that: I saw it validates a class. Does the class have to be filled manually with the values from the user input, and then passed to the validator?
I am confused about the cleanest and best way to validate the user input. I know about the traditional method of using request.getParameter() and then manually checking for nulls, but I don't want to do all the validation in my Controller. Some good advice on this area will be greatly appreciated. I am not using Hibernate in this application.
With Spring MVC, there are 3 different ways to perform validation : using annotation, manually, or a mix of both. There is not a unique "cleanest and best way" to validate, but there is probably one that fits your project/problem/context better.
Let's have a User :
public class User {
private String name;
...
}
Method 1 : If you have Spring 3.x+ and simple validation to do, use javax.validation.constraints annotations (also known as JSR-303 annotations).
public class User {
#NotNull
private String name;
...
}
You will need a JSR-303 provider in your libraries, like Hibernate Validator who is the reference implementation (this library has nothing to do with databases and relational mapping, it just does validation :-).
Then in your controller you would have something like :
#RequestMapping(value="/user", method=RequestMethod.POST)
public createUser(Model model, #Valid #ModelAttribute("user") User user, BindingResult result){
if (result.hasErrors()){
// do something
}
else {
// do something else
}
}
Notice the #Valid : if the user happens to have a null name, result.hasErrors() will be true.
Method 2 : If you have complex validation (like big business validation logic, conditional validation across multiple fields, etc.), or for some reason you cannot use method 1, use manual validation. It is a good practice to separate the controller’s code from the validation logic. Don't create your validation class(es) from scratch, Spring provides a handy org.springframework.validation.Validator interface (since Spring 2).
So let's say you have
public class User {
private String name;
private Integer birthYear;
private User responsibleUser;
...
}
and you want to do some "complex" validation like : if the user's age is under 18, responsibleUser must not be null and responsibleUser's age must be over 21.
You will do something like this
public class UserValidator implements Validator {
#Override
public boolean supports(Class clazz) {
return User.class.equals(clazz);
}
#Override
public void validate(Object target, Errors errors) {
User user = (User) target;
if(user.getName() == null) {
errors.rejectValue("name", "your_error_code");
}
// do "complex" validation here
}
}
Then in your controller you would have :
#RequestMapping(value="/user", method=RequestMethod.POST)
public createUser(Model model, #ModelAttribute("user") User user, BindingResult result){
UserValidator userValidator = new UserValidator();
userValidator.validate(user, result);
if (result.hasErrors()){
// do something
}
else {
// do something else
}
}
If there are validation errors, result.hasErrors() will be true.
Note : You can also set the validator in a #InitBinder method of the controller, with "binder.setValidator(...)" (in which case a mix use of method 1 and 2 would not be possible, because you replace the default validator). Or you could instantiate it in the default constructor of the controller. Or have a #Component/#Service UserValidator that you inject (#Autowired) in your controller : very useful, because most validators are singletons + unit test mocking becomes easier + your validator could call other Spring components.
Method 3 :
Why not using a combination of both methods? Validate the simple stuff, like the "name" attribute, with annotations (it is quick to do, concise and more readable). Keep the heavy validations for validators (when it would take hours to code custom complex validation annotations, or just when it is not possible to use annotations). I did this on a former project, it worked like a charm, quick & easy.
Warning : you must not mistake validation handling for exception handling. Read this post to know when to use them.
References :
A very interesting blog post about bean validation (Original link is dead)
Another good blog post about validation (Original link is dead)
Latest Spring documentation about validation
There are two ways to validate user input: annotations and by inheriting Spring's Validator class. For simple cases, the annotations are nice. If you need complex validations (like cross-field validation, eg. "verify email address" field), or if your model is validated in multiple places in your application with different rules, or if you don't have the ability to modify your model object by placing annotations on it, Spring's inheritance-based Validator is the way to go. I'll show examples of both.
The actual validation part is the same regardless of which type of validation you're using:
RequestMapping(value="fooPage", method = RequestMethod.POST)
public String processSubmit(#Valid #ModelAttribute("foo") Foo foo, BindingResult result, ModelMap m) {
if(result.hasErrors()) {
return "fooPage";
}
...
return "successPage";
}
If you are using annotations, your Foo class might look like:
public class Foo {
#NotNull
#Size(min = 1, max = 20)
private String name;
#NotNull
#Min(1)
#Max(110)
private Integer age;
// getters, setters
}
Annotations above are javax.validation.constraints annotations. You can also use Hibernate's
org.hibernate.validator.constraints, but it doesn't look like you are using Hibernate.
Alternatively, if you implement Spring's Validator, you would create a class as follows:
public class FooValidator implements Validator {
#Override
public boolean supports(Class<?> clazz) {
return Foo.class.equals(clazz);
}
#Override
public void validate(Object target, Errors errors) {
Foo foo = (Foo) target;
if(foo.getName() == null) {
errors.rejectValue("name", "name[emptyMessage]");
}
else if(foo.getName().length() < 1 || foo.getName().length() > 20){
errors.rejectValue("name", "name[invalidLength]");
}
if(foo.getAge() == null) {
errors.rejectValue("age", "age[emptyMessage]");
}
else if(foo.getAge() < 1 || foo.getAge() > 110){
errors.rejectValue("age", "age[invalidAge]");
}
}
}
If using the above validator, you also have to bind the validator to the Spring controller (not necessary if using annotations):
#InitBinder("foo")
protected void initBinder(WebDataBinder binder) {
binder.setValidator(new FooValidator());
}
Also see Spring docs.
Hope that helps.
I would like to extend nice answer of Jerome Dalbert. I found very easy to write your own annotation validators in JSR-303 way. You are not limited to have "one field" validation. You can create your own annotation on type level and have complex validation (see examples below). I prefer this way because I don't need mix different types of validation (Spring and JSR-303) like Jerome do. Also this validators are "Spring aware" so you can use #Inject/#Autowire out of box.
Example of custom object validation:
#Target({ TYPE, ANNOTATION_TYPE })
#Retention(RUNTIME)
#Constraint(validatedBy = { YourCustomObjectValidator.class })
public #interface YourCustomObjectValid {
String message() default "{YourCustomObjectValid.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class YourCustomObjectValidator implements ConstraintValidator<YourCustomObjectValid, YourCustomObject> {
#Override
public void initialize(YourCustomObjectValid constraintAnnotation) { }
#Override
public boolean isValid(YourCustomObject value, ConstraintValidatorContext context) {
// Validate your complex logic
// Mark field with error
ConstraintViolationBuilder cvb = context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate());
cvb.addNode(someField).addConstraintViolation();
return true;
}
}
#YourCustomObjectValid
public YourCustomObject {
}
Example of generic fields equality:
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
#Target({ TYPE, ANNOTATION_TYPE })
#Retention(RUNTIME)
#Constraint(validatedBy = { FieldsEqualityValidator.class })
public #interface FieldsEquality {
String message() default "{FieldsEquality.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
/**
* Name of the first field that will be compared.
*
* #return name
*/
String firstFieldName();
/**
* Name of the second field that will be compared.
*
* #return name
*/
String secondFieldName();
#Target({ TYPE, ANNOTATION_TYPE })
#Retention(RUNTIME)
public #interface List {
FieldsEquality[] value();
}
}
import java.lang.reflect.Field;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ReflectionUtils;
public class FieldsEqualityValidator implements ConstraintValidator<FieldsEquality, Object> {
private static final Logger log = LoggerFactory.getLogger(FieldsEqualityValidator.class);
private String firstFieldName;
private String secondFieldName;
#Override
public void initialize(FieldsEquality constraintAnnotation) {
firstFieldName = constraintAnnotation.firstFieldName();
secondFieldName = constraintAnnotation.secondFieldName();
}
#Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
if (value == null)
return true;
try {
Class<?> clazz = value.getClass();
Field firstField = ReflectionUtils.findField(clazz, firstFieldName);
firstField.setAccessible(true);
Object first = firstField.get(value);
Field secondField = ReflectionUtils.findField(clazz, secondFieldName);
secondField.setAccessible(true);
Object second = secondField.get(value);
if (first != null && second != null && !first.equals(second)) {
ConstraintViolationBuilder cvb = context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate());
cvb.addNode(firstFieldName).addConstraintViolation();
ConstraintViolationBuilder cvb = context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate());
cvb.addNode(someField).addConstraintViolation(secondFieldName);
return false;
}
} catch (Exception e) {
log.error("Cannot validate fileds equality in '" + value + "'!", e);
return false;
}
return true;
}
}
#FieldsEquality(firstFieldName = "password", secondFieldName = "confirmPassword")
public class NewUserForm {
private String password;
private String confirmPassword;
}
If you have same error handling logic for different method handlers, then you would end up with lots of handlers with following code pattern:
if (validation.hasErrors()) {
// do error handling
}
else {
// do the actual business logic
}
Suppose you're creating RESTful services and want to return 400 Bad Request along with error messages for every validation error case. Then, the error handling part would be same for every single REST endpoint that requires validation. Repeating that very same logic in every single handler is not so DRYish!
One way to solve this problem is to drop the immediate BindingResult after each To-Be-Validated bean. Now, your handler would be like this:
#RequestMapping(...)
public Something doStuff(#Valid Somebean bean) {
// do the actual business logic
// Just the else part!
}
This way, if the bound bean was not valid, a MethodArgumentNotValidException will be thrown by Spring. You can define a ControllerAdvice that handles this exception with that same error handling logic:
#ControllerAdvice
public class ErrorHandlingControllerAdvice {
#ExceptionHandler(MethodArgumentNotValidException.class)
public SomeErrorBean handleValidationError(MethodArgumentNotValidException ex) {
// do error handling
// Just the if part!
}
}
You still can examine the underlying BindingResult using getBindingResult method of MethodArgumentNotValidException.
Find complete example of Spring Mvc Validation
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
import com.technicalkeeda.bean.Login;
public class LoginValidator implements Validator {
public boolean supports(Class aClass) {
return Login.class.equals(aClass);
}
public void validate(Object obj, Errors errors) {
Login login = (Login) obj;
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "userName",
"username.required", "Required field");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "userPassword",
"userpassword.required", "Required field");
}
}
public class LoginController extends SimpleFormController {
private LoginService loginService;
public LoginController() {
setCommandClass(Login.class);
setCommandName("login");
}
public void setLoginService(LoginService loginService) {
this.loginService = loginService;
}
#Override
protected ModelAndView onSubmit(Object command) throws Exception {
Login login = (Login) command;
loginService.add(login);
return new ModelAndView("loginsucess", "login", login);
}
}
Put this bean in your configuration class.
#Bean
public Validator localValidatorFactoryBean() {
return new LocalValidatorFactoryBean();
}
and then You can use
<T> BindingResult validate(T t) {
DataBinder binder = new DataBinder(t);
binder.setValidator(validator);
binder.validate();
return binder.getBindingResult();
}
for validating a bean manually. Then You will get all result in BindingResult and you can retrieve from there.
Validation groups
Also it is worth to mention validation for some more complex cases, when you have some "multi steps" within your business logic. In such cases we need "validation groups".
#Validated annotation was added to support "validation groups" in validated bean. This can be used in multi step forms where in the first step you need, for example, validate name and email, and in the second step you need to validate, for example, phone number.
With #Validated you first need to declare groups. Groups are declared with your custom marker interfaces.
#Validated example
Let's say we have a scenario when we have a form for user sign up. On this form we want user to provide a name and email. And after user is signed up we have another form where we suggest the user to add his some extra information, for example, email. We don't want email be provided on the first step. But it is required to provide it on the second step.
For this case, we'll declare two groups. First group would be OnCreate, and the second group would be OnUpdate :
OnCreate:
public interface OnCreate {}
OnUpdate:
public interface OnUpdate {}
Our user UserAccount class:
public class UserAccount {
// we will return this field after User is created
// and we want this field to be provided only on update
// so we can determine which user needs to be updated
#NotBlank(groups = OnUpdate.class)
private String id;
#NotBlank(groups = OnCreate.class)
private String name;
#NotBlank(groups = OnCreate.class)
private String email;
#NotBlank(groups = OnUpdate.class)
private String phone;
// standard constructors / setters / getters / toString
}
We mark the validation annotations with our groups interfaces depending on which group those validations are supposed to be related.
And finally our Controller methods:
#PostMapping(value = "/create")
public UserAccount createAccount(#Validated(OnCreate.class) #RequestBody UserAccount userAccount) {
...
}
#PatchMapping(value = "/update")
public UserAccount updateAccount(#Validated(OnUpdate.class) #RequestBody UserAccount userAccount) {
...
}
Here we specify #Validated(...) instead of #Valid and specify the validation group which should be used in different cases.
Now depending on validation group we'll perform the validations for the particular fields within different steps.
Play 1.0 comes with a full featured validation framework base on http://oval.sourceforge.net/.
With the release of 2.0, my custom validators do not work anymore.
How does one create custom validator using Play Framework 2.0 ?
In Play 2.0, the validation framework extends beyond the actual validation of the data as it reaches to:
Annotations - to easily declare validation contraints using the '#' sign
Validators - which actually implements to logic behind the validation
Messages - to display parametrized error messages (i18 compliant)
Finally, HTML helpers - that glue all the previous together
The HTML Helpers are something new to Play 2.0. In 1.x, Play was already pretty good at enforcing a well defined validation framework. It was powerful and easy to use. Yet we still had to wire the HTML form and the validation framework together. This could be a little confusing to the beginner.
With Play 2.0, this is now done automatically.
But let's focus on the answer and provide some guidance: We will create an AllUpperCase validator, that generates an error either when:
the input is not a String
the input is empty
one of the characters is lower-case.
The validator
package myvalidators;
import javax.validation.*;
public class AllUpperCaseValidator
extends play.data.validation.Constraints.Validator<Object>
implements ConstraintValidator<AllUpperCase, Object> {
/* Default error message */
final static public String message = "error.alluppercase";
/**
* Validator init
* Can be used to initialize the validation based on parameters
* passed to the annotation.
*/
public void initialize(AllUpperCase constraintAnnotation) {}
/**
* The validation itself
*/
public boolean isValid(Object object) {
if(object == null)
return false;
if(!(object instanceof String))
return false;
String s = object.toString();
for(char c : s.toCharArray()) {
if(Character.isLetter(c) && Character.isLowerCase(c))
return false;
}
return true;
}
/**
* Constructs a validator instance.
*/
public static play.data.validation.Constraints.Validator<Object> alluppercase() {
return new AllUpperCaseValidator();
}
}
The first thing you may notice is the import: Play 2.0 indeed complies with JSR 303 - Bean Validation Framework. In this context, the validator needs to implement ConstraintValidator. Which in our case translates into the annotation as class AllUpperCase (which we will introduce in a minute) and T as a generic Object.
The validator is straighforward:
We defined the method public boolean isValid(Object object) that returns a boolean, if true the validation passed. There is also an message id and a method that instanciates the validator.
The Annotation
The class below defines an annotation named #AllUpperCase which takes no parameters but enforces the validation defined previously. Providing details related to the annotation framework is outside the scope of this post.
package myvalidators;
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.*;
import javax.validation.*;
#Target({FIELD})
#Retention(RUNTIME)
#Constraint(validatedBy = AllUpperCaseValidator.class)
#play.data.Form.Display(name="constraint.alluppercase")
public #interface AllUpperCase {
String message() default AllUpperCaseValidator.message;
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Note how the anotation glues to the other pieces of the puzzle.
#Constraint, a JSR 303 annotation, links to the validator
#play.data.Form.Display, links the annotation to the play html helpers. Note that the name is important: we are defining a constraint named alluppercase. Play uses this information to call the method public static play.data.validation.Constraints.Validator<Object> alluppercase() on the Validator.
Finally note that the default message is set within the anotation interface.
Usage
We now have our custom validator and annotation
import myvalidators.*;
public static class MyData {
#AllUpperCase
public String name;
}
Describing the usage is outside the scope of this post, please find a working sample at this URL