The Kaptain on … stuff

17 Apr, 2010

Groovy and Hibernate Validator for Dynamic Constraints

Posted by: TheKaptain In: Development

I was reading this article about hibernate validator today and it inspired me to apply a little Groovy to the problem of validating a bean. More specifically, finding out how hard it would be to apply different validation rules to the same classes at runtime. Turns out it’s really pretty simple.
Hibernate validator, if you didn’t already know, is the reference implementation of JSR-303 and it provides the ability to specify by xml or annotation configuration validation rules for pojos.

Where Hibernate Validator Shines

Annotations on domain classes allow for easily validating object state at the time of persistence. Excellent integration with frameworks like JBoss Seam allow this same ability to be utilized for validating web forms on the client-side with little more than an <s:validateAll/> tag. Seam practically hides the entire interaction with validation components from the developer. Since validation rules are defined directly in the domain class, you can (almost) guarantee that no objects with inconsistent state will ever end up being saved in your database. There are certain validations that aren’t possible to verify without actually looking in the database, unique constraints for example, but generally in my experience hibernate validator is extremely easy to configure and work with. Implementing CRUD functionality is pretty trivial, and UIs can achieve consistency since all validations are applied equally.
Alternatively, if you can’t or don’t want to use annotations for some reason, you can specify your validation rules in an xml file. Usually a singleton validation.xml file is made available on the classpath and picked up automagically when a ValidationFactory is created. A simple xml configuration looks like this:

[xml]
<constraint-mappings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation="http://jboss.org/xml/ns/javax/validation/mapping validation-mapping-1.0.xsd" xmlns="http://jboss.org/xml/ns/javax/validation/mapping">
<default-package>org.kar.test</default-package>
<bean class="ValidateTestableClass">
<field name="name">
<constraint annotation="javax.validation.constraints.NotNull">
</constraint>
</field>
</bean>
[/xml]

and is meant to be applied to this simple class:

[groovy]
class ValidateTestableClass
{
int id
String name
String description
boolean enabled
}
[/groovy]

Comparing with Grails Validation

Grails automatically provides validation capabilities for domain classes and command objects, and enables adding the same behavior to any pogo through a combination of the @Validateable annotation and a static constraints closure. Adding validation support to arbitrary classes also requires specifying which packages to scan for the annotation.
Plugin support from projects like bean-fields simplifies the handling of client-side validation and rendering error markers in the UI, an ability which the Grails framework provides natively by adding an ‘errors’ field directly onto the domain or command object class instances bound to a web form.

Dynamic Constraints

Both the hibernate validator and the Grails strategies for applying validation described here have the same limitation: both are universally applied to all instances of a class. There’s no easy apparent way to override those constraints at runtime, although I suspect that some fancy MOP’ing or configuration could probably be used to accomplish overrides at runtime.
Hibernate validator also supports creation of ad hoc validators by seeding with one or more xml documents. Or if you’re like me and hate hand editing xml, you can leverage Groovy to take a bit of the pain away. Here’s the same xml snippet from above in a Groovy Closure, generated simply by turning the DomToGroovy class loose on the raw xml:

[groovy]
looseConstraint = {
mkp.declareNamespace(xsi: ‘http://www.w3.org/2001/XMLSchema-instance’)
‘constraint-mappings'(xmlns: ‘http://jboss.org/xml/ns/javax/validation/mapping’,
‘xsi:schemaLocation’: ‘http://jboss.org/xml/ns/javax/validation/mapping validation-mapping-1.0.xsd’) {
‘default-package'(‘org.kar.test.objects’)
bean(‘class’: ‘ValidateTestableClass’) {
field(name: ‘name’) {
constraint(annotation: ‘javax.validation.constraints.NotNull’)
}
}
}
}
[/groovy]

Losing all the angle brackets is a good start, but we really haven’t saved a lot of typing. Until you start taking advantage of the ability to define more complicated structures. Note the use of a list structure here to apply NotNull constraints to multiple fields.

[groovy]
strictConstraint = {
mkp.declareNamespace(xsi: ‘http://www.w3.org/2001/XMLSchema-instance’)
‘constraint-mappings'(xmlns: ‘http://jboss.org/xml/ns/javax/validation/mapping’,
‘xsi:schemaLocation’: ‘http://jboss.org/xml/ns/javax/validation/mapping validation-mapping-1.0.xsd’) {
‘default-package'(‘org.kar.test.objects’)
bean(‘class’: ‘ValidateTestableClass’) {
[‘name’, ‘description’].each {
field(name: it) {
constraint(annotation: ‘javax.validation.constraints.NotNull’)
}
}
field(name: ‘id’) {
constraint(annotation: ‘javax.validation.constraints.DecimalMin’) {
element(name: ‘value’, ‘2’)
}
}
field(name: ‘enabled’) {
constraint(annotation: ‘javax.validation.constraints.AssertTrue’)
}
}
}
}
[/groovy]

Applying Dynamic Constraints

Applying constraints is as simple as converting Closures to xml and mapping them to a Configuration object, which then supplies a Validator to use. StreamingMarkupBuilder is utilized to create the xml behind the scenes.

[groovy]
/**
* Create a configuration object passing closures as validation mapping documents.
* @param closures closures to render into validation mapping documents
* @return config
*/
public Configuration createConfig(Closure… closures)
{
Configuration config = Validation.byDefaultProvider().configure()
closures.each {
config.addMapping(new ByteArrayInputStream(GroovyXmlConversionUtil.convertToXml(it).bytes))
}
config
}
[/groovy]

I haven’t tested the use of multiple mappings extensively, but minimally each class you’re configuring must be confined to a single mapping – you can’t extend the validations by layering configurations on top of one another. You should however be able to map constraints for different classes in separate Closures.

Crying out for a Builder!

Going from Closures to xml is a quick and dirty way to test out this functionality, but what would really be nice is a Builder that could create an appropriate validation environment more directly. At the least it would allow for removing the namespace declarations and explicit package naming that make up the bulk of the content.

So what do you get?

  1. 1. Ability to declare validations against any existing Java or Groovy class without changing the source code
  2. 2. Programmatic ability to create the configuration of validations
  3. 3. A choice of which validations to apply at runtime
  4. 4. Consistency with the behavior of domain class validation

And what’d I get? A fun bit of quick coding on a Saturday afternoon. Nice! Source code is available on git-hub if you want to check it out.

Reblog this post [with Zemanta]

4 Responses to "Groovy and Hibernate Validator for Dynamic Constraints"

1 | Tweets that mention Groovy and Hibernate Validator for Dynamic Constraints | The Kaptain on ... stuff -- Topsy.com

April 17th, 2010 at 9:27 pm

Avatar

[…] This post was mentioned on Twitter by kellyrob99, groovyblogs.org. groovyblogs.org said: Groovy and Hibernate Validator for Dynamic Constraints — http://bit.ly/dkSoXY — The Kaptain on … stuff […]

2 | Blog bookmarks 04/19/2010 « My Diigo bookmarks

April 18th, 2010 at 7:30 pm

Avatar

[…] Groovy and Hibernate Validator for Dynamic Constraints | The Kaptain on … stuff […]

3 | uberVU - social comments

April 22nd, 2010 at 8:50 pm

Avatar

Social comments and analytics for this post…

This post was mentioned on Twitter by kellyrob99: New blog post: Groovy and Hibernate Validator for Dynamic Constraints http://bit.ly/dkSoXY

4 | w3cvalidation

May 10th, 2010 at 2:53 am

Avatar

Nice information, I really appreciate the way you presented.Thanks for sharing..

Comment Form