- Better Domain Driven Design, by putting the shallow domain validation in the POJO where it belongs.
- Hopefully we can finally get rid of Value Objects Pattern which is a dark heritage from the EJB 2.1 age and instead use domain POJO beans through out our architecture. There is of course exception to this rule, e.g. in service layer where the exposed model is totally different than the domain model or in a UI layer with heavy usage of line charts, there an OO model does not fit into a point orientation representation.
Here follows a simple example of the usage of Bean Validation.
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>se.msc.examples</groupId>
<artifactId>validation-domain</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>Validation :: Domain</name>
<url>http://www.msc.se/examples/validation</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<repositories>
<repository>
<id>repository.jboss.org</id>
<url>http://repository.jboss.org/maven2</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<dependencies>
<!-- common logging library -->
<!-- version is depended on hibernate-annotations v3.4.0.GA -->
<!--
http://repo1.maven.org/maven2/org/hibernate/hibernate-annotations/3.4.0.GA/hibernate-annotations-3.4.0.GA.pom
-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.4.2</version>
</dependency>
<!-- sun bean validation api -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.0.0.GA</version>
</dependency>
<!-- hibernate bean validation impl -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>4.0.0.GA</version>
</dependency>
<!-- test support -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<defaultGoal>install</defaultGoal>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
package se.msc.examples.validation.domain;
import java.io.Serializable;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
private Long personId;
private String firstName;
@NotNull
@Size(min=1)
private String surname;
@NotNull
@Pattern(regexp=".+@.+\\.[a-z]+")
private String mail;
public Long getPersonId() {
return personId;
}
protected void setPersonId(Long personId) {
this.personId = personId;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public String getMail() {
return mail;
}
public void setMail(String mail) {
this.mail = mail;
}
}
package se.msc.examples.validation.domain;
import static org.junit.Assert.*;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
public class PersonTest {
private Validator validator;
private Person person;
@BeforeClass
public static void oneTimeSetUp() throws Exception {
}
@AfterClass
public static void oneTimeTearDown() throws Exception {
}
@Before
public void setUp() throws Exception {
validator = Validation.buildDefaultValidatorFactory().getValidator();
person = new Person();
person.setFirstName("Magnus K");
person.setSurname("Karlsson");
person.setMail("magnus.k.karlsson@domain.se");
}
@After
public void tearDown() throws Exception {
}
private <T> void debugPrint(Set<ConstraintViolation<T>> violations) {
for (ConstraintViolation<T> violation : violations) {
String propertyPath = violation.getPropertyPath().toString();
String message = violation.getMessage();
System.out.println("invalid value for: '" + propertyPath + "': " + message);
}
}
@Test
public void testValidate_OK() throws Exception {
Set<ConstraintViolation<Person>> violations = validator.validate(person);
assertTrue(violations.size() == 0);
debugPrint(violations);
}
@Test
public void testValidate_FAIL_MAIL() throws Exception {
person.setMail("magnus.k.karlsson@domain");
Set<ConstraintViolation<Person>> violations = validator.validate(person);
assertTrue(violations.size() == 1);
debugPrint(violations);
}
@Test
public void testValidate_FAIL_SURNAME() throws Exception {
person.setSurname(null);
Set<ConstraintViolation<Person>> violations1 = validator.validate(person);
assertTrue(violations1.size() == 1);
debugPrint(violations1);
person.setSurname("");
Set<ConstraintViolation<Person>> violations2 = validator.validate(person);
assertTrue(violations2.size() == 1);
debugPrint(violations2);
}
}
The resource bundle message ValidationMessages.properties
javax.validation.constraints.Null.message=must be null
javax.validation.constraints.NotNull.message=must not be null
javax.validation.constraints.AssertTrue.message=must be true
javax.validation.constraints.AssertFalse.message=must be false
javax.validation.constraints.Min.message=must be greater than or equal to {value}
javax.validation.constraints.Max.message=must be less than or equal to {value}
javax.validation.constraints.Size.message=size must be between {min} and {max}
javax.validation.constraints.Digits.message=numeric value out of bounds (<{integer} digits>.<{fraction} digits> expected)
javax.validation.constraints.Past.message=must be a past date
javax.validation.constraints.Future.message=must be a future date
javax.validation.constraints.Pattern.message=must match the following regular expression: {regexp}
References:
JSR 303: Bean Validation
http://jcp.org/en/jsr/detail?id=303
Article comparing JSR 303 Reference Implementation And Spring 2.5 Validation
http://blog.jteam.nl/2009/08/04/bean-validation-integrating-jsr-303-with-spring/
JSR 303 Reference Material
http://people.redhat.com/~ebernard/validation/
JSR 303 Specification Leads Emmanuel Bernard Blog
http://in.relation.to/Bloggers/Emmanuel
No comments:
Post a Comment