Bean validation specifications.
- JSR 349 is the specification for Bean Validation 1.1.
- JSR 303 is the specification for Bean Validation 1.0
Bean validation is an important task irrespective of the architecture (enterprise web application or thick client-server) of the application. JSR 303 specification standardize the bean validation process. JSR 303 is not intended for use in any one tier. It can be used in presentation tier or in persistence tier. JSR 303 defines a meta-data model and API for Java Bean validation. The default meta-data source is annotations, However you can override and extend the meta-data through the use of XML validation descriptors. In this post we will provide an example for complex custom validator. Please refer official documentation if you are interested to know about the standard validators. As I mentioned above JSR 303 and JSR 349 are specifications like JDBC or JNDI.
JSR 349 Implementations.
JSR 303 Implementations.
Example Validator.
In our example we are using Bean Validation 1.0 (JSR 303). We need Java 6 or above to run this example.
Bean Validation 1.0 (JSR 303) Dependencies.
- validation-api-1.0.0.GA.jar
- hibernate-validator-4.2.0.Final.jar
- hibernate-validator-annotation-processor-4.2.0.Final.jar
- slf4j-api-1.6.1.jar
Maven dependencies.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>4.2.0.Final</version> </dependency> <dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <version>1.0.0.GA</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator-annotation-processor</artifactId> <version>4.2.0.Final</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.6.1</version> </dependency> |
Usecase for custom validator.
We are going to define a bean for Developer registration in a technology forum. If the developer claim that he has experience then the Employer field also must punched in. So the requirement shall be defined as..”if the experience filed value is greater than zero then the Employer field is required.”
Steps to write a custom validator.
- Write custom constraint annotation
- Write an implementation for the constraint
Custom constraint annotation.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
package com.ourownjava.validation; 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; /** * Custom Validator Definition. * * @author ourownjava.com * */ @Target({TYPE}) @Retention(RUNTIME) @Constraint(validatedBy = ExperienceValidtor.class) @Documented public @interface ValidteExperience { String message() default "{employer field shall not be null if the experince is greater than zero}"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; } |
Implementation for constraint.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
package com.ourownjava.validation; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import com.ourownjava.bean.Developer; /** * * Custom Validator Implementation. * * @author ourownjava.com * */ public class ExperienceValidtor implements ConstraintValidator<ValidteExperience, Developer>{ @Override public void initialize(ValidteExperience arg0) { } @Override public boolean isValid(Developer developer, ConstraintValidatorContext context) { return developer.getExperience() > 0 ? (developer.getEmployer() != null && developer.getEmployer().length() > 0) : true; } } |
Payload Bean.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
package com.ourownjava.bean; import org.hibernate.validator.constraints.NotBlank; import com.ourownjava.validation.ValidteExperience; /** * * @author ourownjava.com * */ @ValidteExperience public class Developer { @NotBlank private String name; private int experience; private String employer; public String getName() { return name; } public void setName(final String name) { this.name = name; } public int getExperience() { return experience; } public void setExperience(final int experience) { this.experience = experience; } public String getEmployer() { return employer; } public void setEmployer(final String employer) { this.employer = employer; } } |
Testcase for custom validator.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
package com.ourownjava.validation; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.util.Set; import javax.validation.ConstraintViolation; import javax.validation.Validation; import javax.validation.Validator; import javax.validation.ValidatorFactory; import org.junit.Before; import org.junit.Test; import com.ourownjava.bean.Developer; /** * * @author ourownjava.com * */ public class ExperienceValidatorBehavior { private Developer developer; private Validator validator; @Before public void setUp() { ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); validator = factory.getValidator(); developer = new Developer(); } @Test public void shouldValidateNamForEmpty(){ final Set<ConstraintViolation<Developer>> validtionResults = validator.validate(developer); assertTrue(validtionResults.size() > 0); for(ConstraintViolation<Developer> validtionResult : validtionResults){ assertEquals("may not be empty", validtionResult.getMessage()); } } @Test public void shouldValidateNameForEmpty(){ developer.setName("ourownjava.com"); final Set<ConstraintViolation<Developer>> validtionResults = validator.validate(developer); assertTrue(validtionResults.size() == 0); } @Test public void shouldValidateExperienceForEmpty(){ developer.setName("ourownjava.com"); developer.setExperience(2); final Set<ConstraintViolation<Developer>> validtionResults = validator.validate(developer); assertTrue(validtionResults.size() > 0); for(ConstraintViolation<Developer> validtionResult : validtionResults){ assertEquals("{employer field shall not be null if the experince is greater than zero}", validtionResult.getMessage()); } } @Test public void shouldValidateExperienceForNotEmpty(){ developer.setName("ourownjava.com"); developer.setExperience(2); developer.setEmployer("scrumretro.com"); final Set<ConstraintViolation<Developer>> validtionResults = validator.validate(developer); assertTrue(validtionResults.size() == 0); } } |