Introduction to Spring Validation

In this article, we will see the usage of Spring Validators. Spring provides a simplified set of APIs and supporting classes for validating domain objects. As always, the framework is extensible and it is possible to hook-in custom validator implementations into the framework. This article starts in explaining the basics of Spring validator by guiding to write a custom validators for simple and complex objects. Later on, it explores the theory behind ‘message codes resolver’ and illustrates the usage of writing customized version of the same. Finally, the article concludes in the usage of validator in the Web tier. This article assumes that readers has the sufficient knowledge on Spring Framework. If you are beginner looking for basic concepts on Spring Framework, please read Introduction to Spring Framework.

also read:

Validating Simple objects

In this section, we will see how to make use of Spring’s Validation services with the help of an example. At the core, Spring provides an interface called ‘Validator’ which can be implemented by the clients for validating custom objects. We will illustrate the usage of validation with the help of an example.

package net.javabeat.articles.validator.simple;

public class Book {

	private String name;
	private String author;
	private int noOfPages;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getAuthor() {
		return author;
	}

	public void setAuthor(String author) {
		this.author = author;
	}

	public int getNoOfPages() {
		return noOfPages;
	}

	public void setNoOfPages(int noOfPages) {
		this.noOfPages = noOfPages;
	}
}

The above code defines a model class called ‘Book’ which we want to validate. The class defines basic property like ‘name’, ‘author’ and the ‘noOfPages’. We wanted to ensure that the caller creating a book should set non-null values to the properties ‘name’ and ‘author’. For the property ‘noOfPages’, a value of zero or a negative value cannot be defined.

package net.javabeat.articles.validator.simple;

import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;

public class BookValidator implements Validator{

	@Override
	public boolean supports(Class<?> classObject) {
		return classObject == Book.class;
	}

	@Override
	public void validate(Object object, Errors errors) {

		Book book = (Book)object;
		if (book.getName() == null || book.getName().trim().length() == 0){
			errors.rejectValue("name", "name.required", "Name field is missing");
		}

		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "author", "author.required", "Author field is mandatory");

		int pages = book.getNoOfPages();
		if (pages <= 0){
			errors.rejectValue("noOfPages", "noOfPages.incorrect", "No of pages has an incorrect value (zero or -ve)");
		}
	}
}

Have a look at the above class which validates the book object. Note that the custom validator implements the interface ‘Validator’. The method ‘validate()’ can be invoked directly or indirectly by the framework/caller to invoke the validation. Note that the first argument passed to this method is the object to be validated and the second object is the ‘Errors’ object. This object is expected to be populated within the method in case of any errors.
For validating the ‘name’ property, we check if the value is null or empty and if so, we call the method ‘rejectValue()’ that is defined on the ‘errors’ object. The argument to this method is the name of the field that violated the validation, the error code and the error message. Note that there is a utility method ‘rejectIfEmptyOrWhitespace’ which is available in the class ‘ValidationUtils’ which does the job of ‘Errors.rejectValue’. We have used this method for validating the property ‘author’. We have also checked if the value for the property ‘noOfPages’ in case if the value is zero or negative.

package net.javabeat.articles.validator.simple;

import java.util.Arrays;
import java.util.List;

import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.validation.ValidationUtils;

public class BookClient {

	public static void main(String[] args) {

		testNameEmpty();
		testNoOfPages();
	}

	private static void testNameEmpty(){

		BookValidator bookValidator = new BookValidator();
		Book bookObject = new Book();
		bookObject.setNoOfPages(10);

		BeanPropertyBindingResult result = new BeanPropertyBindingResult(bookObject, "book");
		ValidationUtils.invokeValidator(bookValidator, bookObject, result);

		System.out.println("Total error count is " + result.getErrorCount());
		System.out.println("-------------------------------------------------");

		List<ObjectError> allObjectErrors = result.getAllErrors();
		for (ObjectError objectError : allObjectErrors){

			if (objectError instanceof FieldError){

				FieldError fieldError = (FieldError)objectError;
				System.out.println("Field name is " + fieldError.getField());
			}

			System.out.println("Codes " + Arrays.asList(objectError.getCodes()).toString());
			System.out.println("Error Code is " + objectError.getCode());
			System.out.println("Default message is " + objectError.getDefaultMessage());
			System.out.println();
		}
		System.out.println("-------------------------------------------------");
	}

	private static void testNoOfPages(){

		BookValidator bookValidator = new BookValidator();
		Book bookObject = new Book();
		bookObject.setName("Programming in Java");
		bookObject.setAuthor("Some Author");

		BeanPropertyBindingResult result = new BeanPropertyBindingResult(bookObject, "book");
		ValidationUtils.invokeValidator(bookValidator, bookObject, result);

		System.out.println("Total error count is " + result.getErrorCount());
		System.out.println("-------------------------------------------------");

		List<ObjectError> allObjectErrors = result.getAllErrors();
		for (ObjectError objectError : allObjectErrors){

			if (objectError instanceof FieldError){

				FieldError fieldError = (FieldError)objectError;
				System.out.println("Field name is " + fieldError.getField());
			}

			System.out.println("Error Code is " + objectError.getCode());
			System.out.println("Default message is " + objectError.getDefaultMessage());
			System.out.println();
		}
		System.out.println("-------------------------------------------------");
	}
}

Here is the client program that makes use of validation. In the first test method, we have created an instance of Book object and haven’t set the properties ‘name’ and ‘author’. For invoking the validator object on the book object we have used the utility method ‘invokeValidator’ defined on the class ‘ValidationUtils’ by passing the validator object, the object to be validated and the ‘errors’ object. In case of any validation errors, the ‘Errors’ object can be queried to obtain information on the same. In this case, we have used the method ‘getErrorCount()’ to get the number of validation errors. After that, an iteration is done to obtain the list of errors.

Validating Complex objects

In this section, we will see how to validate a complex object, i.e. an object containing custom type or a set of custom types as its properties. Let us take the definition of a model object ‘Product’ whose code listing is given below.

package net.javabeat.articles.validator.complex;

import java.util.HashSet;
import java.util.Set;

public class Product {

	private String name;
	private String category;
	private String version;
	private LicenseType licenseType;
	private Set<Platform> supportedPlatforms;

	public Product(){
		supportedPlatforms = new HashSet<Platform>();
	}

	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getCategory() {
		return category;
	}
	public void setCategory(String category) {
		this.category = category;
	}
	public String getVersion() {
		return version;
	}
	public void setVersion(String version) {
		this.version = version;
	}
	public Set<Platform> getSupportedPlatforms() {
		return supportedPlatforms;
	}
	public void setSupportedPlatforms(Set<Platform> supportedPlatforms) {
		this.supportedPlatforms = supportedPlatforms;
	}

	public LicenseType getLicenseType() {
		return licenseType;
	}

	public void setLicenseType(LicenseType licenseType) {
		this.licenseType = licenseType;
	}
}

This model class defines the simple properties such as ‘name’, ‘category’ and ‘version’. It also defines a complex property ‘licenseType’ which is of type ‘LicenseType’. Since a software can be supported in a list of platforms, this model defines a property called ‘supportedPlatforms’ which is a set where each element in the set is of type ‘Platform’.

package net.javabeat.articles.validator.complex;

public enum LicenseType {

	FREEWARE,

	SHAREWARE
}

Have a look at the definition for the enum type ‘LicenseType’. For simplicity we have defined two enumeration types which are ‘FREEWARE’ and ‘SHAREWARE’. The complete code listing for the type ‘Platform’ is given below.

package net.javabeat.articles.validator.complex;

public class Platform {

	private String name;

	public Platform(String name){
		this.name = name;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

The definition of the model ‘Platform’ is pretty straightforward. It has a property called ‘name’ which can take values such as ‘Windows XP’, ‘Windows Vista’, ‘Unix’ etc. The subsequent sections show the corresponding validator classes for the model classes ‘LicenseType’, ‘Platform’ and ‘Product’.

package net.javabeat.articles.validator.complex;

import org.springframework.validation.Errors;
import org.springframework.validation.Validator;

public class LicenseTypeValidator implements Validator{

	@Override
	public boolean supports(Class<?> classObject) {
		return LicenseType.class == classObject;
	}

	@Override
	public void validate(Object target, Errors errors) {

		LicenseType licenseType = (LicenseType)target;
		if (licenseType == null){
			errors.reject("licenseType.null", "License type is null");
		}
	}
}

As we can see in the above section, the validation logic for the ‘licenseType’ property is simple as we have ensured that we cannot pass a ‘null’ licenseType. For validating the platform object whose listing is given below, we have asserted that the ‘platform name’ cannot be null or empty. If so, we have used the ‘reject()’ method to populate the error condition on the ‘name’ field.

package net.javabeat.articles.validator.complex;

import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;

public class PlatformValidator implements Validator{

	@Override
	public boolean supports(Class<?> classObject) {
		return Platform.class == classObject;
	}

	@Override
	public void validate(Object target, Errors errors) {

		Platform platform = (Platform)target;
		if (platform == null){
			errors.reject("platform.null", "Platform object is null");
		}

		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name", "name.required");
	}

}

Have a close look at the validator class for the Platform model. For validating the platform object, we have to do validation on the primitive properties of the platform object as well as the custom or the complex properties we have defined. Hence we have defined the ‘licenset-type-validator’ and the ‘platform-validator’ as part of ‘product-validator’.

package net.javabeat.articles.validator.complex;

import java.util.Set;

import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;

public class ProductValidator implements Validator{

	private LicenseTypeValidator licenseTypeValidator;
	private PlatformValidator platformValidator;

	public void setLicenseTypeValidator(LicenseTypeValidator licenseTypeValidator){
		this.licenseTypeValidator = licenseTypeValidator;
	}

	public void setPlatformValidator(PlatformValidator platformValidator){
		this.platformValidator = platformValidator;
	}

	@Override
	public boolean supports(Class<?> classObject) {
		return classObject == Product.class;
	}

	@Override
	public void validate(Object object, Errors errors) {

		Product product = (Product)object;
		if (product == null){
			errors.reject("product.null", "Product is null");
		}

		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name", "name.required", "Name is required");
		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "category", "category.required", "Category is required");
		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "version", "version.required", "Version is required");

		// For License type
		try{
			ValidationUtils.invokeValidator(licenseTypeValidator, product.getLicenseType(), errors);
		}catch (Exception e){
			e.printStackTrace();
		}

		// For platforms
		Set<Platform> allPlatforms = product.getSupportedPlatforms();
		if (allPlatforms == null || allPlatforms.size() == 0){
			ValidationUtils.rejectIfEmptyOrWhitespace(errors, "supportedPlatforms", "supportedPlatforms.required", "Platforms is required");
		}else{
			try{
				for (Platform platform : allPlatforms){
					ValidationUtils.invokeValidator(platformValidator, platform, errors);
				}
			}catch (Exception exception){
				exception.printStackTrace();
			}
		}
	}
}

In the above code, after validating the primitive properties of the product object such as its ‘name’, ‘category’ and ‘version’, we have delegated the control to ‘license-validator’ for validating the ‘licenseType’ property by making use of ValidationUtils. Then, we have iterated over the list of platforms and for each platform object we have invoked the ‘platform-validator’ object for performing validation.

package net.javabeat.articles.validator.complex;

import java.util.Arrays;
import java.util.List;

import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.validation.ValidationUtils;

public class ProductClient {

	public static void main(String[] args) {

		testSuccess();
		testLicenseTypeNull();
		testPlatforms();
	}

	private static void testSuccess(){

		Product javaProduct = new Product();
		javaProduct.setName("Java");
		javaProduct.setCategory("Programming Language");
		javaProduct.setVersion("7.0");
		javaProduct.setLicenseType(LicenseType.SHAREWARE);

		Platform windowsPlatform = new Platform("Windows/XP");
		javaProduct.getSupportedPlatforms().add(windowsPlatform);

		Platform unixPlatform = new Platform("Unix");
		javaProduct.getSupportedPlatforms().add(unixPlatform);

		validate(javaProduct);
	}

	private static void testLicenseTypeNull(){

		Product javaProduct = new Product();
		javaProduct.setName("Java");
		javaProduct.setCategory("Programming Language");
		javaProduct.setVersion("7.0");

		Platform windowsPlatform = new Platform("Windows/XP");
		javaProduct.getSupportedPlatforms().add(windowsPlatform);

		Platform unixPlatform = new Platform("Unix");
		javaProduct.getSupportedPlatforms().add(unixPlatform);

		validate(javaProduct);
	}

	private static void testPlatforms(){

		Product javaProduct = new Product();
		javaProduct.setName("Java");
		javaProduct.setCategory("Programming Language");
		javaProduct.setVersion("7.0");
		javaProduct.setLicenseType(LicenseType.FREEWARE);

		Platform windowsPlatform = new Platform("Windows/XP");
		javaProduct.getSupportedPlatforms().add(windowsPlatform);

		Platform nullPlatform = new Platform(null);
		javaProduct.getSupportedPlatforms().add(nullPlatform);

		validate(javaProduct);
	}

	private static void validate(Product javaProduct){

		ProductValidator productValidator = new ProductValidator();
		productValidator.setLicenseTypeValidator(new LicenseTypeValidator());
		productValidator.setPlatformValidator(new PlatformValidator());

		BeanPropertyBindingResult result = new BeanPropertyBindingResult(javaProduct, "javaProduct");
		ValidationUtils.invokeValidator(productValidator, javaProduct, result);

		System.out.println("Total error count is " + result.getErrorCount());
		System.out.println("-------------------------------------------------");

		List<ObjectError> allObjectErrors = result.getAllErrors();
		for (ObjectError objectError : allObjectErrors){

			if (objectError instanceof FieldError){

				FieldError fieldError = (FieldError)objectError;
				System.out.println("Field name is " + fieldError.getField());
			}

			System.out.println("Codes " + Arrays.asList(objectError.getCodes()).toString());
			System.out.println("Error Code is " + objectError.getCode());
			System.out.println("Default message is " + objectError.getDefaultMessage());
			System.out.println();
		}
		System.out.println("-------------------------------------------------");
	}
}

In the above client code, we have test for a successful scenario by populating the required and the desired values to the product object in the method ‘testSuccess’. In the ‘testLicenseTypeNull’ method, we have set ‘null’ reference to the ‘licenseType’ property and we have checked whether invoking validation on the product object has returned any errors. Similarly in the ‘testPlatforms()’ method, we have set ‘null’ platforms if the validation has actually got fired on the platform objects.

Attaching Custom Message Codes Resolver

In this section, we will see how to attach custom message codes resolver object while performing validation. Even before that, we wanted to see where and how messages codes will actually get resolved. When the caller invokes validation on an object, the validator class is expected to populate the error conditions in case of any errors. These errors are encapsulated through the interfaces ‘ObjectError’ and ‘FieldError’. Object Errors are for encapsulating error conditions when the state of the object itself is violated – e.g. that an object being null. Field errors are used to represent error scenarios wherein a error is at the field level – e.g. name is invalid, age is containing negative values.
The validator class owns the responsibility for attaching an error code for an error condition. For example, when found that a property ‘name’ is null, we may designate an error code ‘name.null’ while populating the error object. This error code will be resolved to a message code by Spring suitable for producing output error message. For example, for a property ‘p1′ defined on an object ‘o1′ that is of type ‘String’ having an error code ‘ec1′, Spring will produce multiple message codes for this scenario. Specifically the ‘DefaultMessageCodesResolver’ implementation will produce four message codes ‘ec1.o1.p1′, ‘ec1.p1′, ‘ec1.String’ and ‘ec1′ for the above example scenario.

public class Mobile {

	private String name;
	private Double cost;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Double getCost() {
		return cost;
	}
	public void setCost(Double cost) {
		this.cost = cost;
	}
}

We have a simple model object called ‘Mobile’ have properties ‘name’ and ‘cost’. The code shown below is the corresponding validator class for the Mobile object. There is nothing new in this validator as it follows the traditional model of validating the properties ‘name’ and ‘cost’.

package net.javabeat.articles.validator.simple;

import org.springframework.validation.Errors;
import org.springframework.validation.Validator;

public class MobileValidator implements Validator{

	@Override
	public boolean supports(Class<?> classObject) {
		return classObject == Mobile.class;
	}

	@Override
	public void validate(Object object, Errors errors) {

		Mobile mobile = (Mobile)object;
		if (mobile.getName() == null || mobile.getName().trim().length() == 0){
			errors.rejectValue("name", "name.required", "Name field is missing");
		}

		Double cost = mobile.getCost();
		if (cost == null || cost.doubleValue() == 0){
			errors.rejectValue("cost", "cost.incorrect", "Cost of the mobile has incorrect value");
		}
	}
}

Now, have a look at the custom message codes resolver implementation. Remember that a ‘Message codes resolver’ provides a pattern for producing ‘message codes’ corresponding to the ‘error codes’ that a validator defines and it is always possible to produce multiple message codes for a single error code.

package net.javabeat.articles.validator.simple;

import org.springframework.validation.MessageCodesResolver;

public class MobileMessageCodesResolver implements MessageCodesResolver {

	@Override
	public String[] resolveMessageCodes(String errorCode, String objectName) {
		System.out.println(errorCode + objectName);

		String customErrorCode = "mobile/" + objectName + "/" + errorCode;
		return new String[]{customErrorCode};
	}

	@SuppressWarnings("rawtypes")
	@Override
	public String[] resolveMessageCodes(String errorCode, String objectName,
		String field, Class fieldType) {

		String customErrorCode = "mobile/" + objectName + "/"
			+ fieldType.getName() + "/" + field + "/" + errorCode;
		return new String[]{customErrorCode};
	}
}

The overloaded methods ‘resolveMessageCodes()’ will be called by the framework for collecting the message codes. Note that the parameters passed on to this method wil be the name of the object, the name of the field, error code and the class type of the field. For illustration purpose we have constructed a customized message code with ‘/’ as a separator and have returned it to the caller.

package net.javabeat.articles.validator.simple;

import java.util.Arrays;
import java.util.List;

import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.validation.ValidationUtils;

public class MobileClient {

	public static void main(String[] args) {

		testMobile();
		testMobileWithCustomMessageCodesResolver();
	}

	private static void testMobile(){

		MobileValidator mobileValidator = new MobileValidator();
		Mobile mobileObject = new Mobile();
		BeanPropertyBindingResult bindingResult = new BeanPropertyBindingResult(mobileObject, "mobileObject");

		validate(mobileObject, mobileValidator, bindingResult);
	}

	private static void testMobileWithCustomMessageCodesResolver(){

		MobileValidator mobileValidator = new MobileValidator();
		Mobile mobileObject = new Mobile();

		BeanPropertyBindingResult bindingResult = new BeanPropertyBindingResult(mobileObject, "mobileObject");
		MobileMessageCodesResolver mobileMessageCodesResolver = new MobileMessageCodesResolver();
		bindingResult.setMessageCodesResolver(mobileMessageCodesResolver);

		validate(mobileObject, mobileValidator, bindingResult);
	}

	private static void validate(Mobile mobileObject, MobileValidator mobileValidator, BeanPropertyBindingResult bindingResult){

		ValidationUtils.invokeValidator(mobileValidator, mobileObject, bindingResult);

		System.out.println("Total error count is " + bindingResult.getErrorCount());
		System.out.println("-------------------------------------------------");

		List<ObjectError> allObjectErrors = bindingResult.getAllErrors();
		for (ObjectError objectError : allObjectErrors){

			if (objectError instanceof FieldError){

				FieldError fieldError = (FieldError)objectError;
				System.out.println("Field name is " + fieldError.getField());
			}

			System.out.println("Codes " + Arrays.asList(objectError.getCodes()).toString());
			System.out.println("Error Code is " + objectError.getCode());
			System.out.println("Default message is " + objectError.getDefaultMessage());
			System.out.println();
		}
		System.out.println("-------------------------------------------------");
	}
}

The above sample client has two methods, the first test method does validation on the mobile object with the Spring’s default ‘message codes resolver implementation’, whereas the second test method attaches a ‘custom message codes resolver’. Note that a ‘custom messages codes resolver’ implementation can be set on the ‘Errors’ object by calling the method ‘setMessageCodesResolver’. Watch out for the output from both the test methods, more specifically on the call getCodes() defined on the error object.

Spring Validation in the Web Tier

In the final section of the article, we will see how Spring validation can be used on the Web tier. Note that the functionality of the sample application will be kept minimal as the concentration is on validation usage only. We will build a simple Customer details form which we prompt for various customer details for registration. All the fields are mandatory, hence if the user didn’t enter a value for any of the fields, appropriate error messages will be thrown.
Have a look at the web.xml file where we define the Spring’s dispatcher servlet. Note that the presence of this servlet will load the application context present in the file ‘application-Context.xml’ present in the ‘WEB-INF’ directory.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>SpringExample8</display-name>
  <servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>*.htm</url-pattern>
  </servlet-mapping>
  <welcome-file-list>
    <welcome-file>main.jsp</welcome-file>
  </welcome-file-list>
</web-app>

Here is the complete code listing of the file ‘applicationContext.xml’. We have defined JSP as views and they will be searched under the directory ‘/WEB-INF’. We will look into the definition of the validator class and the service class soon.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:p="http://www.springframework.org/schema/p"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean id="viewResolver"
          class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          	p:prefix="/WEB-INF/" p:suffix=".jsp" />

    <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"
    	p:basename="customer_error_messages" />

    <bean id="customerDetailService" class="net.javabeat.articles.spring.validation.cdf.service.CustomerDetailServiceImpl" />
    <bean id="customerDetailValidator" class="net.javabeat.articles.spring.validation.cdf.validator.CustomerDetailValidator" />

	<bean name="/customerDetailForm.htm" class="net.javabeat.articles.spring.validation.cdf.controller.CustomerDetailController"
		p:customerDetailService-ref="customerDetailService" p:formView="customerDetailForm"
			p:successView="customerDetailSuccess" p:validator-ref="customerDetailValidator" />

</beans>

Have a look at the mapping for the entry ‘customerDetailForm’ to the controller ‘CustomerDetailCustomer’. We have also mapped the ‘customer-validator’ to this controller in the bean definition.
This jsp page provides a decoration for prompting the user for inputs. We have made use of Spring form tags, which provides a greater flexibility in mapping the form details directly to a command object. Note that we have used ‘form:input’ tag for mapping the field directly to the command object and ‘form:error’ for populating the error messages in case of any errors.

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%>

<html>
<head>
<title>Customer Detail Form Page</title>

<style>
.customer_error{
color: #ff0000;
font-style: italic;
}</style>
</head>
<body>

<form:form method="POST" commandName="customerDetail">
	<table align="center" border="2">
		<tr>
			<td>Customer Name:</td>
			<td><form:input path="name" /></td>
			<td><form:errors path="name" cssClass="customer_error" /></td>
		</tr>
		<tr>
			<td>Customer Address:</td>
			<td><form:password path="address" /></td>
			<td><form:errors path="address" cssClass="customer_error" /></td>
		</tr>
		<tr>
			<td>Email Id:</td>
			<td><form:password path="emailId" /></td>
			<td><form:errors path="emailId" cssClass="customer_error" /></td>
		</tr>
		<tr>
			<td>Phone Number:</td>
			<td><form:password path="phoneNumber" /></td>
			<td><form:errors path="phoneNumber" cssClass="customer_error" /></td>
		</tr>
		<tr>
			<td colspan="3"><input type="submit" value="Register Customer"></td>
		</tr>
	</table>
</form:form>

</body>
</html>

Here is the definition of the ‘CustomerDetail’ model object.

package net.javabeat.articles.spring.validation.cdf.model;

public class CustomerDetail {

	private String name;
	private String address;
	private String emailId;
	private String phoneNumber;

	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getAddress() {
		return address;
	}
	public void setAddress(String address) {
		this.address = address;
	}
	public String getEmailId() {
		return emailId;
	}
	public void setEmailId(String emailId) {
		this.emailId = emailId;
	}
	public String getPhoneNumber() {
		return phoneNumber;
	}
	public void setPhoneNumber(String phoneNumber) {
		this.phoneNumber = phoneNumber;
	}
}

The definition is pretty simple – it has properties like ‘name’, ‘address’, ‘emailId’ and ‘phoneNumber’ which needs to be validated.

package net.javabeat.articles.spring.validation.cdf.service;

import net.javabeat.articles.spring.validation.cdf.model.CustomerDetail;

public interface CustomerDetailService {
	void addCustomerDetail(CustomerDetail customerDetail);

}

The definition of the customer service object is given below. The method ‘addCustomerDetail’ is exposed which can be invoked as part of registration. The implementation for the above service object is given below. The implementation stores all the customer details in a set.

package net.javabeat.articles.spring.validation.cdf.service;

import java.util.HashSet;
import java.util.Set;

import net.javabeat.articles.spring.validation.cdf.model.CustomerDetail;

public class CustomerDetailServiceImpl implements CustomerDetailService{

	private Set<CustomerDetail> customerDetails;

	public CustomerDetailServiceImpl(){
		customerDetails = new HashSet<CustomerDetail>();
	}

	public void addCustomerDetail(CustomerDetail customerDetail) {
		customerDetails.add(customerDetail);
	}
}

The validator class that does the validation on the customer object is provided. In case of any error scenarios, the error object is populated and the error information will be mapped to the property file with the base name ‘customer_error_messages’ as that is how we have configured the bean definition in the dispatcher servlet. Rest of the files are omitted, however they are included and available in the download.

package net.javabeat.articles.spring.validation.cdf.validator;

import net.javabeat.articles.spring.validation.cdf.model.CustomerDetail;

import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;

public class CustomerDetailValidator implements Validator{

	public boolean supports(Class<?> classObject) {
		return classObject == CustomerDetail.class;
	}

	public void validate(Object bean, Errors errors) {

		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name", "customer.name.required");
		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "address", "customer.address.required");
		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "emailId", "customer.emailId.required");
		ValidationUtils.rejectIfEmptyOrWhitespace(errors, "phoneNumber", "customer.phoneNumber.required");
	}
}

Run the application and click the ‘Register’ button without providing any data in the form. It can be noticed that the validator would have got invoked and the validation error codes are appropriately mapped to the error messages and they will get displayed in the UI.
Screen with validation errors

also read:

Conclusion

This article provides plenty of code samples to get a better understanding on Spring validators. Hope the readers would have gained information on Spring’s validator API and the supporting classes and can use them at appropriate instances.

Comments

comments

About Krishna Srinivasan

He is Founder and Chief Editor of JavaBeat. He has more than 8+ years of experience on developing Web applications. He writes about Spring, DOJO, JSF, Hibernate and many other emerging technologies in this blog.

Comments

  1. Thank you.

  2. Thank you for explanation :) But I get only negative after using Validator.

Speak Your Mind

*