Build a Custom Validator in Grails with a Plugin

Grails is a really nice MVC framework inspired by Ruby on Rails. The difference is that Grails is built using Groovy and Java and leverages existing, well known frameworks as it’s foundation. It is essentially a fairly thin convention-over-configuration and integration layer on top of Spring and Hibernate. The documentation for common scenarios is pretty good, but once you get outside of the common cases it deteriorates pretty quickly. This is part of my contribution I guess.

Constraints and Validation

Grails has a fairly good set of built in constraints to use to validate your domain objects. This allows you to decorate your Domain with constraints which are validated prior to persisting the objects.

class Address {
String street
String city
String state

static constraints = {
street(nullable:true)
city(blank:false)
state(size:2..2)
}
}

You can find a comprehensive list of the built in validations on the Grails website. It includes the ability to create a custom validator for a domain class if the built in ones do not suffice. So you can write a validator inline to your domain object.


class Student {
String name
Calendar dateOfBirth
Calendar enrollmentDate

static constraints = {
name(blank:false)
dateOfBirth(blank:false, validator: { val, obj ->
// Ensure that the date of birth is before the enrollment date
return ! val.after(obj.enrollmentDate)
}))
}
}

This validator is just a closure, so you could write it external to the class and reuse it among many different domain classes. I was interested to know how to create a “first-class” validator though that looked and acted just like the built in ones.

Create a Plugin

Grails is essentially built as a series of plugins. Everything from Hibernate support to Service Injection is implemented as a Plugin. Because of this it obviously must have a strong plugin system that allows you to do almost anything. So I decided to look at using a plugin to create a shared validation.

So, first: Generate a plugin.
On the command line issue the command grails create-plugin MyApp. This will create a directory containing everything you need to construct a new plugin. The directory really looks like a mini Grails application. In fact you can run it exactly as a grails app which is great for testing.

This will create a src/ directory in the plugin as well. We can create our validator in there. You can create some directories under src/groovy or src/java to create a package. Use groovy or java depending on which language you want to use to implement your validator.

Create your Validator

Grails provides org.codehaus.groovy.grails.validation.AbstractConstraint as a simple base class to inherit from to get the basic functionality. Of course if you want to implement the whole thing yourself you just need to implement the interface
org.codehaus.groovy.grails.validation.Constraint. That would be a lot of work, so just inherit from the AbstractConstraint.

Here’s an example of a constraint used to validate a Postal Code (or zip code). This is a simple implementation that supports validating Canadian and US postal codes.

package net.zorched.validation

import org.codehaus.groovy.grails.validation.AbstractConstraint
import org.springframework.validation.Errors

class PostalCodeConstraint extends AbstractConstraint {

private static final String DEFAULT_NOT_POSTAL_CODE_MESSAGE_CODE = "default.not.postalCode.message";
public static final String POSTAL_CODE_CONSTRAINT = "postalCode";

private boolean postalCode;

private US = { postalCode->
postalCode ==~ /\d{5}/
}

private CA = { postalCode->
postalCode ==~ /[A-Z]\d[A-Z] \d[A-Z]\d/
}

public void setParameter(Object constraintParameter) {
if(!(constraintParameter instanceof Boolean))
throw new IllegalArgumentException("Parameter for constraint ["
+POSTAL_CODE_CONSTRAINT+"] of property ["
+constraintPropertyName+"] of class ["
+constraintOwningClass+"] must be a boolean value");

this.postalCode = ((Boolean)constraintParameter).booleanValue();
super.setParameter(constraintParameter);
}

protected void processValidate(Object target, Object propertyValue, Errors errors) {
if (! validPostalCode(target, propertyValue)) {
def args = (Object[]) [constraintPropertyName, constraintOwningClass,
propertyValue]
super.rejectValue(target, errors, DEFAULT_NOT_POSTAL_CODE_MESSAGE_CODE,
"not." + POSTAL_CODE_CONSTRAINT, args);
}
}

boolean supports(Class type) {
return type != null && String.class.isAssignableFrom(type);
}

String getName() {
return POSTAL_CODE_CONSTRAINT;
}

boolean validPostalCode(target, propertyValue) {
def country = "US"
if (target.metaClass.hasMetaProperty("country")) {
country = target.country
}
println country
this."$country"(propertyValue)
}
}

The name property is the key used to match your validator in your domain class to this Constraint instance.

class Address {
String street
String city
String state
String postalCode
String country = "US"

static constraints = {
street(nullable:true)
city(blank:false)
state(size:2..2)
postalCode(blank:false,postalCode:true)
}
}

Unit Test It

You’ll notice that I wrote the main algorithm for doing the validation as its own method. That will make it easy for me to write Unit Tests to confirm the functionality of my validator before I implement it in a domain class. So, now when someone comes back and wants to support Zip+4 in the US, I’ll can write a test, see that it fails and then fix my Constraint to support that case as well.


class PostalCodeConstraintTests extends GroovyTestCase {

void test_us_postal_code_succeeds_for_valid() {
def postalCodeConstraint = new PostalCodeConstraint()
def address = new Address(country:"US", postalCode:"53212")

assert postalCodeConstraint.validPostalCode(address, address.postalCode)
}

void test_us_postal_code_fails_for_canada() {
def postalCodeConstraint = new PostalCodeConstraint()
def address = new Address(country:"US", postalCode:"A1A 3E3")

assert ! postalCodeConstraint.validPostalCode(address, address.postalCode)
}

void test_ca_postal_code_succeeds_for_valid() {
def postalCodeConstraint = new PostalCodeConstraint()
def address = new Address(country:"CA", postalCode:"A1A 1D3")

assert postalCodeConstraint.validPostalCode(address, address.postalCode)
}
}

class Address {
String country
String postalCode
}

Wire It Into Grails

The only thing left to do is to register your Constraint so that Grails knows about it. When you created the plugin, grails generated a plugin “bootstrap” class for you. You can do that by registering your class with the ConstrainedProperty class provided by the Grails framework.


import net.zorched.validation.PostalCodeConstraint

import org.codehaus.groovy.grails.validation.ConstrainedProperty

class MyAppPlugin {
def version = 0.1
def author = "Geoff Lane"
def description = '''
This plugin adds specific functionality to the application for my app.
'''
def dependsOn = [:]
def loadAfter = ['controllers']

def doWithSpring = {
ConstrainedProperty.registerNewConstraint(
PostalCodeConstraint.POSTAL_CODE_CONSTRAINT,
PostalCodeConstraint.class);
}
}

ConstrainedProperty basically keeps a map of the constraint keys to the class type that implements the constraint.

Conclusion

Grails Plugins basically allow you to do anything that you might want to create an easily packaged piece of reusable functionality. I didn’t find a anything in the documentation about how to build validators like the built in ones, so I read a bunch of source code. Now you don’t have to, except for fun.


UPDATE: Please check out the Grails Custom Constraints plugin that automates all of this at http://grails.org/plugin/constraints

12 thoughts on “Build a Custom Validator in Grails with a Plugin”

  1. Wow Geoff was looking for custom constraints in grails, and found your site through google… was wondering if this is the same Geoff that I was familiar with – and it got confirmed after I saw Milwaukee somewhere and finally Spiderlogic ;) This is a really elegant way, thanks for your documentation I hadn’t found it anywhere on the net.

  2. I created a custom validator for a specific app.
    The plugin seems to be able to register the constraint and is able to initialize the constraints but does not ever call processValidate.
    What could be wrong? >_<

  3. hi,

    great post

    question,

    how could I apply 2 custom constraints to one field in my domain object ?
    Reason I ask is that I want to check two different things on a field and want to display a seperate error message for each that does not pass validation

    any help is great thanks

  4. @we,
    The constraint for a given property is a map of constraints, so you can have multiple, comma-separated values.

    e.g.

    static constraints = {
    postalCode(blank:false, postalCode:true, inWisconsin:true)
    }

  5. I need to add a validator where bid amount should always be $.50 greater than previous bid. Do i need to declare two variables (bid and old bid) in my domain class to do this?

  6. @aditi,
    From what you are doing, it sounds like you should implement it as a persistent constraint. A persistent constraint would let you query the database for the previous bid for a given item and then compare that to the new bid. Take a look at the “UniqueEgConstraint” example on GitHub for an example of how to write a persistent constraint.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>