Showing posts with label Spring. Show all posts
Showing posts with label Spring. Show all posts

Saturday, 13 November 2010

Rules-based Validation using Drools, JSR303 and Spring

Validation and business rules are closely linked. Indeed validation logic could be thought of as being part of the implicit business rules for an application.

It seems natural that integrating a business rules solution with a validation technology might be a natural step. In this post, I explore the possibility of integrating JSR303 and Drools to provide business rule driven validation within a Spring application. JSR303 is the standard Java method for data validation using annotations and Drools is a business rules engine that is a JBoss project and an implementation of JSR94, the standard Java rule engine API.

When to use (and when not to)

I don't think that this is a natural choice for all data validation needs. For a start, if the validation in your application is simple, the standard JSR303 annotations mixed with a few custom annotations might be enough. Integrating Drools comes at a cost in terms of architectural complexity and additional dependencies for you application. And although Drools is highly performance-optimised, a set of business rules in Drools will probably be slower than a set of custom Java statements.

However, there are significant benefits to complex applications - particularly if you are using Drools already. In that case, the complexity and dependency arguments are not valid.

The big benefit is the ability to externalise the business rules from the application and enable them to be edited without redeploying the application.

Drools overview

In a Drools application, business rules can be written in a powerful expression language called mvel (although XML can be used as well). In the Drools Expert documentation, the following basic example is given.

 
    public class Applicant {

        private String name;

        private int age;

        private boolean valid;

        // getter and setter methods here

    }
 
    package com.company.license;

    rule "Is of valid age" 
        when 
            $a : Applicant( age < 18 ) 
        then 
            $a.setValid( false ); 
        end

The rule consists of conditions (when) and consequences (then). In this rule, when the Applicant age property is less than 18, the valid property is set to false.

Often, business rules will need to access collaborators such as services or DAOs. These can be injected as "globals" in the rule definition. For example, the scenario above could be modified so that the applicant needs to be loaded from a DAO in order to check the age. The applicant identifier is a property in an ApplicationForm, submitted from a GUI or web application.

We'll also introduce a new class to hold the validation result, rather than setting the validity on the model object itself, which is somewhat artificial. This object could be returned to the presentation tier to tell the user what went wrong.

Error object to hold validation result:


    package com.acme.app.validation;

    import java.util.ArrayList;
    import java.util.List;
    import java.util.Collections;

    public class Errors {
 
        private final List<Error> errors = Collections.synchronizedList(new ArrayList<Error>());

        public Collection<Error> getErrors() {
            return Collections.unmodifiableCollection(errors);
        }
 
        public void addError(Object target, String field, String message) {
            this.errors.add(new Error(target, field, message));
        }
 
        public boolean hasErrors() {
            if (this.errors.size() > 0) {
                return true;
            }
            return false;
        }
    }

    class Error {
        private final Object target;
        private final String field;
        private final String message;
 
        public Error(Object target, String field, String message) {
            super();
            this.target = target;
            this.field = field;
            this.message = message;
        }

        public Object getTarget() {
            return target;
        }

        public String getField() {
            return field;
        }

        public String getMessage() {
            return message;
        }
    }

Modified Applicant (with identity)

 
    public class Applicant {

        private int id;

        private String name;

        private int age;

        private boolean valid;

        // getter and setter methods here

    }

ApplicationForm from the GUI/web:

 
    public class ApplicationForm {
 
        private int applicantId;
 
        private Date date;

        // getter and setter methods here

    }

Here is a slightly silly test DAO:

    
    public class ApplicantDaoImpl implements ApplicantDao {
 
        private final Map<Integer, Applicant> applicants = new HashMap<Integer, Applicant>();
 
        public ApplicantDaoImpl() {
            Applicant app1 = new Applicant(1, "Mr John Smith", 16);
            applicants.put(app1.getId(), app1);
            Applicant app2 = new Applicant(2, "Mr Joe Bloggs", 21);
            applicants.put(app2.getId(), app2);
        }
 
        @Override
        public Applicant findApplicant(Integer identifier) {
            return applicants.get(identifier);
        }

    }

Finally, a rule with a global representing the ApplicantDao and using our new Errors object could look like:

 
    package com.acme.app.rules

        import com.acme.app.form.ApplicationForm
        import com.acme.app.model.Applicant
        import com.acme.app.validation.Errors
        import com.acme.app.dao.ApplicantDao

        global ApplicantDao applicantDao

        rule "Check applicant age"
            when
                $a : ApplicationForm()
                $errors : Errors() 
                $applicant:Applicant( age < 18 ) from applicantDao.findApplicant($a.getApplicantId())
            then
                $errors.addError( $a, "applicantId", "Applicant age < 18" );
            end

In this version of the rule, the applicant is loaded from a DAO based on their identity. If the age of the applicant is less than 18, an error is inserted into the Errors object specifying the target object, field and message to return to the GUI. Obviously, this could be internationalized by putting a message code instead of the actual message.

That's an extremely brief overview of rules. Next, I'll quickly discuss the main API that Java programmers use when interacting with Drools. These will be used by the JSR303 validator when validating a bean using business rules.

At the most basic level, Drools stores business rules in a KnowledgeBase, from which can be created "sessions" which enable users to execute the rules based on facts, which are usually model objects inserted into the session.

There are two types of session: StatefulKnowldegeSession and StatelessKnowledgeSession. A StatelessKnowledgeSession is considered appropriate for use cases such as validation, because they are intended as a "one-shot" function call: pass in the facts, execute the rules, get a result. That's therefore the interface I will use for the integration with a JSR303 validation.

Here is a simple example of creating a StatelessKnowledgeSession and executing the rules based on a collection of facts, each of which is inserted in turn before the rules are fired.


    KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
        kbuilder.add( ResourceFactory.newFileSystemResource( fileName ), ResourceType.DRL );
        assertFalse( kbuilder.hasErrors() );     
        if (kbuilder.hasErrors() ) {
            System.out.println( kbuilder.getErrors() );
        }
        KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
        kbase.addKnowledgePackages( kbuilder.getKnowledgePackages() );
 
        StatelessKnowledgeSession ksession = kbase.newStatelessKnowledgeSession();
        ksession.execute( collection );

Spring integration

Conveniently, in Drools 5.1 some additional Spring integration was added that enables you to create KnowledgeBases and sessions declaratively in the application context, using a purpose-built namespace.

In this example, Spring is the "glue" bringing together the Drools and JSR303 parts. Here's the configuration.


    <?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:context="http://www.springframework.org/schema/context"
       xmlns:drools="http://drools.org/schema/drools-spring"       
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
               http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd 
                           http://drools.org/schema/drools-spring http://anonsvn.jboss.org/repos/labs/labs/jbossrules/trunk/drools-container/drools-spring/src/main/resources/org/drools/container/spring/drools-spring-1.0.0.xsd">
  
        <drools:kbase id="kbase">
            <drools:resources>
                <drools:resource type="DRL" source="classpath:testSpring.drl"></drools:resource>
            </drools:resources>
        </drools:kbase>
    
        <drools:ksession id="statelessKSession" type="stateless" name="statelessKSession" kbase="kbase">
        </drools:ksession>
  
    </beans>

I'm keeping things very simple - there is a lot more that the namespace can do in terms of configuring knowledge bases, agents, sessions etc.

JSR 303 Overview

JSR303 is a standard Java mechanism for validating JavaBeans using convenient annotations. Creating custom annotations is a matter of creating the annotation and an accompanying ConstraintValidator implementation, which will be automatically used by the Validator when it encounters the annotation on a bean getting validated.

The annotation itself is simple.


    package com.acme.app.validation;

    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
 
    import javax.validation.Constraint;
    import javax.validation.Payload;
 
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Constraint(validatedBy=BusinessRulesConstraintValidator.class)
 
    public @interface BusinessRulesConstraint {
        String message() default "Business rules validation failed.";
        Class<?>[] groups() default {};
        Class<? extends Payload>[] payload() default {};
    }

The validator is also reasonably simple. A StatelessKnowledgeSession and a simple bean carrying a Map of the needed collaborators (to be set as globals) are injected into the constructor.

When the isValid() method is called, an Errors object and the target of the validation are inserted into the session and the validation rules are fired. If the Errors object comes back with errors, validation has failed and a ConstraintViolation is built using the data in the Errors object.


    package com.acme.app.validation;

    import java.util.Arrays;
    import java.util.Map;

    import javax.validation.ConstraintValidator;    
    import javax.validation.ConstraintValidatorContext;

    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.drools.runtime.StatefulKnowledgeSession;
    import org.drools.runtime.StatelessKnowledgeSession;
    import org.springframework.beans.factory.annotation.Autowired;

    /**
     * Custom JSR303 {@link ConstraintValidator} that
     * uses a Drools {@link StatelessKnowledgeSession} to
     * implement rules-based validation of objects
     * decorated with a @BusinessRulesConstraint annotation
     */
    public class BusinessRulesConstraintValidator implements ConstraintValidator<BusinessRulesConstraint, Object> {

        private final Log logger = LogFactory.getLog(BusinessRulesConstraintValidator.class);
 
        private final StatelessKnowledgeSession session;
 
        @Autowired
        public BusinessRulesConstraintValidator(StatelessKnowledgeSession session, Collaborators collaborators) {
            this.session = session;
            if (collaborators != null) {
                Map<String, Object> map = collaborators.getCollaborators();
                for (String key : map.keySet()) {
                    session.setGlobal(key, map.get(key));
                }
            }
        }

        @Override
        public void initialize(BusinessRulesConstraint constraint) {}

        @Override
        public boolean isValid(Object target, ConstraintValidatorContext context) {
  
            // Create Errors
            Errors errors = new Errors();
  
            try {
   
                // Fire rules
                session.execute(Arrays.asList(new Object[]{errors, target}));
   
                // Check for errors
                if (errors.hasErrors()) {
                    // Build constraint violations
                    context.disableDefaultConstraintViolation();
                    for (Error error : errors.getErrors()) {
    context.buildConstraintViolationWithTemplate(error.getMessage()).addNode(error.getField()).addConstraintViolation();
                    }
                    return false;
                }
            } 
            catch (Exception e) {
                logger.error(e);
                return false;
            }
  
            return true;
        }

    }

The Collaborators object is extremely simple:


package com.acme.app.validation;

import java.util.Collections;
import java.util.Map;

public class Collaborators {
 
    private final Map<String, Object> collaborators;

    public Collaborators(Map<String, Object> collaborators) {
        super();
        this.collaborators = collaborators;
    }

    public Map<String, Object> getCollaborators() {
        return Collections.unmodifiableMap(collaborators);
    }
}

It's simply a container holding a map into which collaborators such as DAO or Service facades could be injected using Spring.

The full Spring configuration gluing the whole thing together is below:


<?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:context="http://www.springframework.org/schema/context"
       xmlns:drools="http://drools.org/schema/drools-spring"       
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
               http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd 
                           http://drools.org/schema/drools-spring http://anonsvn.jboss.org/repos/labs/labs/jbossrules/trunk/drools-container/drools-spring/src/main/resources/org/drools/container/spring/drools-spring-1.0.0.xsd">
  
  <drools:kbase id="kbase">
    <drools:resources>
      <drools:resource type="DRL" source="classpath:testSpring.drl"></drools:resource>
    </drools:resources>
  </drools:kbase>
    
  <drools:ksession id="statelessKSession" type="stateless" name="statelessKSession" kbase="kbase"></drools:ksession>

  <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"></bean>
  
  <bean id="collaborators" class="com.acme.app.validation.Collaborators">
      <constructor-arg><map>
              <entry key="applicantDao" value-ref="applicantDao"></entry>
          </map>
      </constructor-arg></bean>

<bean id="applicantDao" class="com.acme.app.dao.impl.ApplicantDaoImpl"></bean>
</beans>

Using the validator is very simple. First, the ApplicantForm object needs to be decorated with the @BusinessRulesConstraint annotation at class level so that the JSR303 validator will be triggered to use the BusienssRulesConstraintValidator.


package com.acme.app.form;

import java.util.Date;

import com.acme.app.validation.BusinessRulesConstraint;

@BusinessRulesConstraint
public class ApplicationForm {
 
    private Integer applicantId;
 
    private Date date;

    // getters/setters
}

Then it is a matter of invoking the validator with a bean instance like this:


package com.acme.app.validation.tests;

import java.util.Date;
import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.Validator;

import junit.framework.Assert;

import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.acme.app.form.ApplicationForm;

public class ValidationTestCase {
 
    private static ApplicationContext ctx;
 
    private static Validator validator;
 
    @BeforeClass
    public static void beforeClass() {
  
        // Create the Spring application context
        String[] paths = { "application-context.xml" };
        ctx = new ClassPathXmlApplicationContext(paths);
  
    }
 
    @Before
    public void before() {
        validator = (Validator)ctx.getBean("validator");
    }
 
    @Test
    public void testInvalidAge() {
        ApplicationForm applicationForm = new ApplicationForm(1, new Date());
        Set<ConstraintViolation<ApplicationForm>> violations = validator.validate(applicationForm);
        Assert.assertNotNull(violations);
        Assert.assertEquals(Integer.valueOf(2), Integer.valueOf(violations.size()));
    }
 
}

Summary

This post shows you an approach for integrating JSR303 and Drools in a Spring application, something you might want to do if you were using these technologies already in your application and it was sufficiently complex to warrant it. The steps involved are:

  • Create your domain objects and decorate them withe @BusinessRulesConstraint where business rule validation is to be used.
  • Create the .drl file with your validation logic in it.
  • Configure Spring as above with a KnowledgeBase containing your drl file as a resource, a StatelessKnowledgeSession derived from that KnowledgeBase, your collaborators for the rules and the JSR303 validator.

That's it!

Postscript: Maven configuration

For Maven users, this should be useful in getting all the dependencies for this example to work.



<dependencies>
  
    <!-- Spring bits -->
    <dependency>
        <groupid>org.springframework</groupId>
        <artifactid>spring-core</artifactId>
        <version>${org.springframework.version}</version>
    </dependency>

    <dependency>
        <groupid>org.springframework</groupId>
        <artifactid>spring-context</artifactId>
        <version>${org.springframework.version}</version>
    </dependency>    
  
    <dependency>
        <groupid>org.springframework</groupId>
        <artifactid>spring-beans</artifactId>
        <version>${org.springframework.version}</version>
    </dependency>   
    
    <!-- Drools bits -->
    <dependency>
        <groupid>org.drools</groupId>
        <artifactid>drools-core</artifactId>
        <version>5.1.1</version>
    </dependency>
  
    <dependency>
        <groupid>org.drools</groupId>
        <artifactid>drools-spring</artifactId>
        <version>5.1.1</version>
    </dependency>

    <!-- JSR303 bits -->
    <dependency>
        <groupid>org.hibernate</groupId>
        <artifactid>hibernate-validator</artifactId>
        <version>4.1.0.Final</version>
    </dependency>

    <dependency>
        <groupid>org.slf4j</groupId>
        <artifactid>slf4j-api</artifactId>
        <version>1.5.6</version>
    </dependency>
  
    <!-- concrete Log4J Implementation for SLF4J API-->
    <dependency>
        <groupid>org.slf4j</groupId>
        <artifactid>slf4j-log4j12</artifactId>
        <version>1.5.6</version>
    </dependency>
 
</dependencies>

Wednesday, 20 October 2010

Validating service method parameters using JSR 303, Spring AOP/AspectJ and Spring

When building an application with Spring and JSR303 you can easily validate form submissions using the built in JSR303 validators. This helps ensure that parameters passed to service methods are valid in a web application. However, there are certain cases where you might wish to invoke your service methods directly rather than from within an MVC controller, for example if you were using DWR or a REST web service. Here is an example of a service facade that might exist in a Spring application.
public interface UserService {

 public abstract void updateUser(UserForm userForm);

}

@Service
public class UserServiceImpl {

 @Override
 @Secured({"ROLE_EMPLOYEE"})
 @Transactional(readOnly=false, isolation=Isolation.READ_COMMITTED,propagation=Propagation.SUPPORTS)
 public abstract void updateUser(UserForm userForm) {
   User user = extractUserFromUserForm(userForm);
   userDao.updateUser(user);
 }

}
Here is the UserForm parameter that is passed to the updateUser method above. It is a simple Form or Command bean using JSR 303 annotations to validate input.
@FieldMatch.List({
 @FieldMatch(first = "password", second = "confirmPassword"),
})
public class UserForm {
 @NotNull
 private String username;

 @NotNull
 @Pattern(regexp = "(?=.*\\d)(?=.*[a-zA-Z]).{6,12}")
 private String password;

 @NotNull
 @Pattern(regexp = "(?=.*\\d)(?=.*[a-zA-Z]).{6,12}")
 private String confirmPassword;

}
If the only client of the facade is a Spring MVC controller, the JSR303 validator can easily be wired in to validate the UserForm before it gets anywhere near the facade. However, if you are invoking the service in another way, there is a risk that the UserForm wouldn't be valid when it is passsed to the updateUser() method. How could we prevent that? The obvious answer would be to invoke the JSR303 validator in code in the facade implementation. Something like this would do the trick:
@Service
public class UserServiceImpl {

 @Autowired
 private Validator validator;

 @Override
 @Secured({"ROLE_EMPLOYEE"})
 @Transactional(readOnly=false, isolation=Isolation.READ_COMMITTED,propagation=Propagation.SUPPORTS)
 public abstract void updateUser(UserForm userForm) {

   // Validate input
   Set> violations = validator.validate(userForm);
   if (!violations.isEmpty())
     throw new ConstraintViolationException(violations);

   // Perform update
   User user = extractUserFromUserForm(userForm);
   userDao.updateUser(user);
 }

}
Reasonably simple - but if you do that for every service method, the repeated boilerplate code will add up to a fair bit of typing and clutter. Here is my proposed alternative. Use a new annotation to decorate the parameter in the service method, and use AOP to intercept method invocations and perform parameter validation. It is effectively the same solution but with less typing. Here is what the end solution would look like.
@Service
public class UserServiceImpl {

 @Autowired
 private Validator validator;

 @Override
 @Secured({"ROLE_EMPLOYEE"})
 @Transactional(readOnly=false, isolation=Isolation.READ_COMMITTED,propagation=Propagation.SUPPORTS)
 public abstract void updateUser(@Valid UserForm userForm) {

   // Perform update
   User user = extractUserFromUserForm(userForm);
   userDao.updateUser(user);
 }

}
Note the @Valid annnotation on the UserForm parameter. This isn't the standard JSR 303, but our own custom annotation that is used to mark parameters that need validation via AOP. On reflection, I probably should have called it something else to avoid confusion.
package myapp.validators.aspects;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
public @interface Valid {
 Class[] groups() default {};
}

The aspect that performs the validation is here.
package myapp.validators.aspects;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validator;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;

@Aspect
public class ValidateAspect {
 
 @Autowired
 private Validator validator;

 public void setVaidator(Validator validator) {
   this.validator = validator;
 }
 
  
 @Before("execution(* *(@myapp.validators.aspects.Valid (*)))")
 public void valid(JoinPoint jp) throws NoSuchMethodException {

   // ConstraintViolations to return
   Set<ConstraintViolation<?>> violations = new HashSet<ConstraintViolation<?>>();

   // Get the target method
   Method interfaceMethod = ((MethodSignature)jp.getSignature()).getMethod();
   Method implementationMethod = jp.getTarget().getClass().getMethod(interfaceMethod.getName(), interfaceMethod.getParameterTypes());
  
   // Get the annotated parameters and validate those with the @Valid annotation
   Annotation[][] annotationParameters = implementationMethod.getParameterAnnotations();
   for (int i = 0; i < annotationParameters.length; i++) {
     Annotation[] annotations = annotationParameters[i];
     for (Annotation annotation : annotations) {
       if (annotation.annotationType().equals(Valid.class)) {
         Valid valid = (Valid)annotation;
         Object arg = jp.getArgs()[i];
         violations.addAll(validator.validate(arg, valid.groups()));
       }
     }
   }
        
   // Throw an exception if ConstraintViolations are found
   if (!violations.isEmpty()) {
     throw new ConstraintViolationException(violations);
   }
  }
}

The final bit of glue in Spring to make it all work is to declare the custom annotation aspect in the application context.


 

Hope this is useful to someone. I found the following blogs and forum threads helpful in coming up with this post and related code. References http://forum.springsource.org/showthread.php?t=77390 http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/aop.html http://blog.newsplore.com/2010/02/23/spring-mvc-3-0-rest-rebuttal

Tuesday, 25 May 2010

Creating a custom JSR 303 constraint annotation with Spring 3

Spring 3.0 has support for JSR303 annotation-driven validation, which makes it easy to build declarative validation into Java Beans in Spring applications. Out of the box, JSR303 supports annotations such as @NotNull and @Size etc which allow you to perform basic validation checks. However, most applications will need to do more sophisticated "business-logic" validation such as checking whether an email already exists in a database. I recently needed to do just that in a Spring MVC application that needed a "Forgot Password" function to let users receive an email with their password. The Spring MVC command object was very simple:
public class SendPasswordReminderCommand implements Serializable {
 
 private static final long serialVersionUID = 42L;

 private String email;

 public String getEmail() {
  return email;
 }

 public void setEmail(String email) {
  this.email = email;
 }

      // equals, hashcode, toString, constructors omitted
}
Annotating this class with JSR 303 annotations to check the format of the email or ensure that the email is not null is easy enough (using @NotNull, @Length from the core javax.validation.constraints standard package and @Email from the org.hibernate.validator.constraints package). Ideally, you would also be able to check the database and ensure that the email exists in the database and inform the user if not as part of the validation. To do this, having a custom annotation called @EmailExistsConstraint would be ideal - but you need to make your own!
public class SendPasswordReminderCommand implements Serializable {
 
 private static final long serialVersionUID = 42L;
 
 @NotNull
 @Length(min=1)
 @Email
 @EmailExistsConstraint
 private String email;

 public String getEmail() {
  return email;
 }

 public void setEmail(String email) {
  this.email = email;
 }
}
Creating the custom constraint requires you to create two classes - an annotation interface and associated constraint validator. The annotation is straightforward.

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.validation.Constraint;
import javax.validation.Payload;

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy=EmailExistsConstraintValidator.class)

public @interface EmailExistsConstraint {
 String message() default "Email doesn't exists";
 Class[] groups() default {};
 Class[] payload() default {};
}

The constraint validator is where you perform the database check. As this is a Spring and Hibernate application, the constraint validator uses a Hibernate DAO as a "collaborator" to perform the check.
public class EmailExistsConstraintValidator implements ConstraintValidator {

 private Log log = LogFactory.getLog(EmailExistsConstraintValidator.class);
 
 @Autowired
 private EmployeeFinder employeeFinder;
 
 @Override
 public void initialize(EmailExistsConstraint constraint) {
  
 }

 @Override
 public boolean isValid(Object target, ConstraintValidatorContext context) {
  
  try {
   Collection employees = employeeFinder.findEmployeeByEmail((String) target);
   if (employees.size() > 0) {
    return true;
   }
  } catch (Exception e) {
   log.error(e);
  }
  return false;
 }

 public void setEmployeeFinder(EmployeeFinder employeeFinder) {
  this.employeeFinder = employeeFinder;
 }
}

This is a fairly straightforward constraint validator implementation. If the database contains a record matching the email address, the isValid() method returns true and false if not. All that Spring needs to use JSR303 is to register the Spring validator implementation in the application context. It then automatically detects any custom constraints and will validate classes using your annotations without any further configuration.

However, there is still one problem with this. As it stands, the Validator will validate all of the annotations in one pass. This is a problem, because the database check will occur even if the email fails all of the other checks which is wasteful and potentially confusing to the user, who would see multiple validation messages. Thankfully, the JSR303 specification contains the concept of groups, which can be used to lump annotations together and process them in a specified order. The example below illustrates how groups can be used to split the basic format checks from the database check.
@GroupSequence(value={SendPasswordReminderCommand.class, FormatChecks.class,BusinessLogicChecks.class})
public class SendPasswordReminderCommand implements Serializable {
 
 private static final long serialVersionUID = 42L;
 
 @NotNull(groups=FormatChecks.class)
 @Length(min=1, groups=FormatChecks.class)
 @Email(groups=FormatChecks.class)
 @EmailExistsConstraint(groups=BusinessLogicChecks.class)
 private String email;

 public String getEmail() {
  return email;
 }

 public void setEmail(String email) {
  this.email = email;
 }
}
Firstly, each annotation has a group parameter that specifies a class (in this example, FormatChecks.class and BusinessLogicChecks.class). These classes are marker interfaces that are simply used to identify groups. To specify the sequence in which annotation groups are processed, the @GroupSequence annotation is used. An array of classes is passed to the values parameter that starts by convention with the class being validated, followed by the group marker interfaces in the order that you wish to process them. The marker interfaces are very straightforward. One of the interfaces must extend the javax.validation.groups.Default marker interface, which as you may guess is the default group if no group is specified.
public interface FormatChecks extends Default {

}

public interface BusinessLogicChecks {

}
You can define as many groups as you want. As you can see, creating custom constraint annotations is pretty straightforward with Spring 3.0. JSR 303 is well thought out and the ability to group constraints is very useful for ensuring that expensive business logic checks are not performed without reason, when validation has already failed for basic data format reasons.