Project

General

Profile

Common Data Model Validation

The CDM Validation infrastructure is a set of components that let you check the validity (correctness) of a CDM Java Bean. It works by applying rules or constraints to the properties of the object. Because it is possible to use the CDM Java Library to build a variety of applications, some rules or constraints are only applicable to certain categories of application and will therefore be ignored by other applications. Conversely, some rules are universal - violating these constraints will cause any application to behave in an undefined (or defined but bad) way.

The validation component (or Validator) is a simple spring managed bean that can be used throughout the application to check a data object for correctness and allow the application to react accordingly. It is a standard component from the javax.validation or JSR 303 - Bean Valiation specification. The CDM Library uses hibernate validator, which is the reference implementation of the specification, as the standard validation service, and uses the spring framework's LocalValidatorFactoryBean to manage the validator and to hook the validation infrastructure into spring's data binding and validation components.

It is configured thus:

<bean id="validatorFactory" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
  <property name="mappingLocations">
    <set>              
      <value>classpath:/eu/etaxonomy/cdm/validation/name/TaxonNameBase-Constraints.xml</value>
    </set>
  </property>
</bean>

Most of the constraints are standard constraints that are part of the specification or provided as part of the cdmlib-model package. Some (those which depend upon services in the cdmlib-service package) require a bit of extra magic in the form of an xml file that specifies the validator at runtime.

Using the validator

All possible uses of the bean validation specification or spring's data binding and validation infrastructure are beyond the scope of this page, so please refer to the hibernate validator website (http://validator.hibernate.org - note: make sure you're looking at the latest version of this project as the older version is not used by the CDM and is significantly different); Also check out the spring website (http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/ch05.html).

Fundamenally, the validation of an object works like this:

  T t = // Object is of some CDM Class
  Set<ConstraintViolation<T>> constraintViolations  = validator.validate(t);

  if(constraintViolations.isEmpty()) {
    // Object is valid, no problems here
  } else {
    // inspect the list of constraint violations and do something
  }

The ConstraintViolation object contains information about the constraint which has been violated, and a localized message suitable for display to the user.

Validation Groups

As mentioned previously, some constraints are universal, wheras others may be ignored. We distinguish between constraints by putting them into groups. The validate method allows you to pass one or more extra arguments that specify which groups of constraints you want to check against. Calling validate without specifying a group validates at the default level (which all applications should conform to). As an example:

  // Validate at Level2 and Level3
  Set<ConstraintViolation<T>> constraintViolations  = validator.validate(t, Level2.class, Level3.class);

Currently, the available validation levels are Default which is simple constraints such as String lengths etc, Level2 which is context (database)-independent rules such as having correct epithets for a name of a given rank, and Level3 which are context dependent rules that involve a database query (e.g. to check if an identical name exists).

Validation Using Spring MVC

Spring MVC has a comprehensive data binding and validation infrastructure and it is quite straightforward to hook the validation annotations into this, by registering the validator factory as the valiator that should be used by the default web binding initializer (this can be customized on a per-controller basis if neccessary).

<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
    <property name="webBindingInitializer">
        <bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
            <property name="validator" ref="validatorFactory" />
        </bean>
    </property>
</bean>

Then annotating command objects as being @Valid (which is a JSR 303 annotation, not something provided by spring):

@Controller
public class NameController {

    @Autowired
    INameService service;

    @RequestMapping("/name", method=RequestMethod.POST)
    public void doPost(@Valid @ModelAttribute TaxonNameBase name, BindingResult result) {
      if(result.hasErrors()) {
        return new ModelAndView("name/edit");
      } else {
        service.save(name);
        ModelAndView mav = new ModelAndView("name/created");
        mav.addObject(name);
        return mav;
      }
    }
}

This will cause spring to validate the object just after binding the request parameters to it. Any constraint violations are expressed as either FieldErrors (if the error is related to a particular field) or GlobalErrors if the error is related to the object as a whole. These errors are found in the BindingResult object. A common pattern upon failing a model constraint is to return the user to the original form view and display the error messages alongside the relevant form fields so that the user can correct the values and resubmit. Depending on the view technology you're using (e.g. jsp, velocity, freemarker) you might want to use the spring tag / macro libraries thus:

<form:form commandName="name" >
      <table>
          <tr>
              <td>Genus or Uninomial:</td>
              <td><form:input path="genusOrUninomial"/></td>
              <td><form:errors path="genusOrUninomial"/></td>
          </tr>
          <tr>
              <td>Generic Epithet:</td>
              <td><form:input path="genericEpithet"/></td>
              <td><form:errors path="genericEpithet"/></td>
          </tr>
  . . .

An example of a global error:

and an example of a field error:

Object_Error.png View (13 KB) Ben Clark, 10/19/2009 04:02 PM

Field_Error.png View (22.1 KB) Ben Clark, 10/19/2009 04:02 PM

Add picture from clipboard (Maximum size: 40 MB)