Integrating JSR303 with Spring MVC 3 and ExtJS Forms

Previously in Spring MVC and ExtJS Forms I created a simple data entry form and there was no data validation implemented. Typically data validation occurs throughout application layers, from presentation to persistence layer. Here I will be using JSR-303 Bean Validation, where the data validation is defined in the domain model and can be used thoughout application layers. I will walk through how to integrate data validation into Spring MVC and will be using Hibernate Validator 4.x – which is the reference implementation for JSR-303 – to integrate into my previous example.

What is JSR-303?

“This JSR defines a metadata model and API for JavaBean validation. The default metadata source is annotations,
with the ability to override and extend the meta-data through the use of XML validation descriptors.
The validation API developed by this JSR is not intended for use in any one tier or programming model. It is specifically
not tied to either the web tier or the persistence tier, and is available for both server-side application programming,
as well as rich client Swing application developers.” – JSR-303 Specification

The data validation is achieved by defining constraints in the beans. Constraints are defined by a combination of constraint annotation and a list of constraint validation implementations. The constraint annotation can be applied at field-level, property-level (getter), class-level. Currently there are 22 built-in constraints for example @NotNull to check that the annotated value is not null or @Size(min=, max=) to check whether the annotated value lies between the specified range. You can also create your own custom constraint to meet your specific requirement. The concept here is similar to database constraints where the constraint can be applied to table column.

For now, I will walk through how to use the built-in constraints only. As usual, I will start with the configuration required to make the project work. Open the Maven pom.xml and add the following dependencies and repository:

<!-- JSR 303 with Hibernate Validator -->
<dependency>
	<groupId>javax.validation</groupId>
	<artifactId>validation-api</artifactId>
	<version>1.0.0.GA</version>
</dependency>
<dependency>
	<groupId>org.hibernate</groupId>
	<artifactId>hibernate-validator</artifactId>
	<version>4.1.0.Final</version>
</dependency>
...
<!-- For Hibernate Validator Repository -->
<repository>
	<id>org.jboss.repository.release</id>
	<name>JBoss Maven Release Repository</name>
	<url>https://repository.jboss.org/nexus/content/repositories/releases</url>
	<snapshots><enabled>false</enabled></snapshots>
</repository>

In the example, the form is submitted to HomeController and the form is mapped to PersonalContact bean. I added 3 built-in constraints into the PersonalContact bean, @NotBlank, @Patter (using regular expression) and @Email. When message is not define in the constraint annotation, the validator will assign a default value. The second part of the code below shows the validation process. The HomeController is autowired to the Validator bean and the validation() is executed inside the add().

public class PersonalContact implements Serializable
{
	private static final long serialVersionUID = 1L;
	private Long id;

	@NotBlank(message="Please enter name")
	private String name;
               // using regular expression to define the constraint
	@Pattern(regexp="[0-9]{8}", message="Please enter 8 digit phone number")
	private String phone;

	@Email
	private String email;

	public PersonalContact() {}

	public PersonalContact(Long id, String name, String phone, String email) {
		super();
		this.id = id;
		this.name = name;
		this.phone = phone;
		this.email = email;
	}
	//getter & setter
}
@Controller
public class HomeController
{
	@Autowired
	private Validator validator;

	@RequestMapping(value="/", method=RequestMethod.GET)
	public String home() {
		return "home";
	}

	@RequestMapping(value="/load", method=RequestMethod.POST)
	public @ResponseBody Map<String, ? extends Object> load(PersonalContact input) {
	...
	}

	@RequestMapping(value="/add", method=RequestMethod.POST)
	public @ResponseBody Map<String, ? extends Object> add(PersonalContact input, HttpSession session) {
		Map<String, Object> data = new HashMap<String, Object>();

		Set<ConstraintViolation<PersonalContact>> failures = validator.validate(input);
		if (!failures.isEmpty()) {
			//structure the response for ExtJS Form.
			data.put("success",Boolean.FALSE);
			data.put("errors", validationMessages(failures));
			data.put("errorMessage", "Add Failed!");
		} else {
			session.setAttribute(input.getName(), input);
			data.put("success",Boolean.TRUE);
		}

		return data;
	}
	//iterate to retrieve validation errors and store it in HashMap
	private Map<String, String> validationMessages(Set<ConstraintViolation<PersonalContact>> failures) {
		Map<String, String> failureMessages = new HashMap<String, String>();
		for (ConstraintViolation<PersonalContact> failure : failures) {
			failureMessages.put(failure.getPropertyPath().toString(), failure.getMessage());
		}
		return failureMessages;
	}
}

Since I used ExtJS forms, I must return JSON response in a specific format and the errors are mapped to the field and displayed as quicktips.


The spring-mvc-forms project is updated and available for download at myGit. Hope you find it useful. Happy coding! 🙂

References

{
   success: true,
   errors: { field1: 'error for field 1',
                 field2: 'error for field 2',
   	   ...
   }
}
Advertisements

2 thoughts on “Integrating JSR303 with Spring MVC 3 and ExtJS Forms”

  1. Hi!
    I like your approach a lot! I think it is really useful and really well organized. By the way, I saw that you have some constraints defined in your constraints package (‘org.xaab.springmvc.constraints’) but is not used anywhere. I am right? I don’t know if this was a test that you’re starting to create a new Validation to used inside another field, isn’t?
    Best regards and good work, I think I’ll be using your approach (I like the use of the JSR-303) 😉

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s