Archive for the Category 'gorm'

Grails Database Reverse Engineering Plugin

Tuesday, November 09th, 2010

Support for database migrations and reverse engineering are two related features that we’ve scheduled for Grails 1.4/2.0 (see the roadmap wiki page for the others). The migration support will be based on Liquibase and there’s already a plugin for that so I started looking at reverse engineering first.

Work progressed faster than I expected (thanks to the features of the Hibernate Tools library and all of the time I spent digging into its internals for the App Info plugin) and it didn’t depend on any new features in 1.4 (not yet anyway) so I released the plugin yesterday so users can started using it now. Install it the usual way:

grails install-plugin db-reverse-engineer

and refer to the documentation for configuration options.

I tested this with MySQL and Oracle, and other databases that Hibernate supports should work too. There’s a tutorial in the documentation that uses MySQL, and you can use the Chinook database to test with Oracle. I used these settings (in grails-app/conf/Config.groovy) for the Chinook database:

grails.plugin.reveng.packageName = 'com.codeplex.chinookdatabase'
grails.plugin.reveng.defaultSchema = 'CHINOOK'
grails.plugin.reveng.manyToManyBelongsTos = [PLAYLISTTRACK: 'PLAYLIST']

and these datasource settings (in grails-app/conf/DataSource.groovy)

dataSource {
   url = 'jdbc:oracle:thin:@localhost:1521:orcl'
   driverClassName = 'oracle.jdbc.driver.OracleDriver'
   username = 'chinook'
   password = 'p4ssw0rd'
   dialect = org.hibernate.dialect.Oracle10gDialect

Try it out and report any issues on the Grails user mailing list or in JIRA.

One related thing I wanted to point out is that the work to replace HSQLDB with H2 is mostly complete (JIRA issue here). I’m a big fan of H2 and one of its coolest features is its embedded web-based console (which works with any database that has a JDBC driver). This is now enabled by default in the development environment and can be enabled in other environments. Accessing data in your development database will be very convenient in 1.4 – just open http://localhost:8080/appname/dbconsole in a browser (JIRA issue here).

Dynamic GORM Domain Classes

Wednesday, October 13th, 2010

A recent discussion on the Grails Dev mailing list about creating a dynamic form builder involved needing to compile new domain classes at runtime. The consensus seemed to be that it’s not possible and/or not advisable, but I’ve thought a lot about this topic and had done similar work when creating the Dynamic Controller plugin, so I started playing with it.

The solution I came up with isn’t pretty but seems to work. There were a few issues to tackle. One is that Grails does quite a bit of work to convert your relatively simple domain classes into full GORM classes, registered with Hibernate and wired up with validation, convenience MetaClass methods, etc. There’s also the issue of automatically compiling in an id and version field, a default toString() method, and collections corresponding to hasMany declarations. In addition there are four Spring beans created for each domain class. There’s a lot being done under the hood that we tend to take for granted.

But the big hurdle is registering the new entity with Hibernate. It’s expected that this is done at startup and never changed, so the data fields in SessionFactoryImpl are mostly private and in two cases final. So the solution is rather hackish and involves brute force reflection. It just so happens that when using reflection, final fields are only mostly final. So I create a whole new SessionFactoryImpl (it’d be convenient to create a small one with just the new domain class, but then you couldn’t reference other domain classes) and replace the real SessionFactoryImpl‘s data with the data from the new one. I can’t replace the SessionFactoryImpl since other classes will have a reference to the previous one.

Delayed SessionFactory Creation in Grails

Monday, January 25th, 2010

The topic of delaying DataSource and SessionFactory creation until some point after startup has come up a few times on the Grails user mailing list so I thought I’d give it a shot. I got it working, but it’s not pretty.

Grails (and Hibernate) will create up to three connections during initialization so the primary focus is to avoid those. In addition the DataSource will pre-instantiate connections, so we’ll delay those as well.

Fixing the User/Role Many-to-Many in the Grails Spring Security Plugin

Sunday, October 25th, 2009

The User/Role many-to-many relationship in the Grails Spring Security plugin is modeled using the standard GORM mapping approach, i.e. using hasMany and belongsTo. As I pointed out here this is a performance concern when you have a large number of users, since granting a new user a popular role (e.g. ROLE_USER) will cause all other users with that role to be loaded from the database.

To fix this in the current plugin would be a breaking change, but I’m planning on creating a new plugin that will use Spring Security 3 once it’s released, so I thought I’d write up some notes on how to fix the many-to-many mapping for current users. It’s only a few steps.

The first is to map the join table, so you’ll need to create a UserRole domain class (I’m assuming that your person class is named User and your authority class is named Role – translate as appropriate):

import org.apache.commons.lang.builder.HashCodeBuilder

class UserRole implements Serializable {

   User user
   Role role

   boolean equals(other) {
      if (!(other instanceof UserRole)) {
         return false

      return == && ==

   int hashCode() {
      return new HashCodeBuilder().append(

   static UserRole create(User user, Role role, boolean flush = false) {
      new UserRole(user: user, role: role).save(flush: flush, insert: true)

   static boolean remove(User user, Role role, boolean flush = false) {
      UserRole userRole = UserRole.findByUserAndRole(user, role)
      return userRole ? userRole.delete(flush: flush) : false

   static void removeAll(User user) {
      executeUpdate("DELETE FROM UserRole WHERE user=:user", [user: user])

   static mapping = {
      id composite: ['role', 'user']
      version false
      table 'role_people'

Some notes on this class:

  • it has to implement Serializable since it’s a Hibernate composite primary key class
  • the mapping block settings ensure that the table DDL is the same as that for the autogenerated join table, so you won’t need to update your database
  • the hashCode and equals methods are just suggestions; feel free to re-implement

Next remove static hasMany = [people: User] from Role and static hasMany = [authorities: Role] and static belongsTo = Role from User.

While we don’t want to map the Role’s User collection, we still need convenient access to the User’s roles, so next add a utility method to User to mimic what we removed when deleting the hasMany. While we’re here let’s add a hasRole method:

Set<Role> getAuthorities() {
   UserRole.findAllByUser(this).collect { it.role } as Set

boolean hasRole(Role role) {
   UserRole.countByUserAndRole(this, role) > 0

If you’re using the plugin-generated CRUD pages (created via grails generate-manager) you’ll want to remove the User listings from views/role/show.gsp:

<tr class="prop">
   <td valign="top" class="name">People:</td>
   <td valign="top" class="value">${authority.people}</td>

and views/role/edit.gsp:

<tr class="prop">
  <td valign="top" class="name"><label for="people">People:</label></td>
  <td valign="top" class="value ${hasErrors(bean:authority,field:'people','errors')}">
  <g :each var="p" in="${authority.people?}">

Then in RegisterController.groovy change



UserRole.create(person, role)

and finally in UserController.groovy, change (in two places)

Role.findAll().each { it.removeFromPeople(person) }






UserRole.create(person, Role.findByAuthority(key))

And that’s it. You shouldn’t need to make any database changes, since the new code will map to the existing tables just like the old code. If you’ve used the addToPeople and removeFromPeople dynamic many-to-many methods elsewhere in your code you’ll need to convert those to use the UserRole helper methods, but otherwise the impact should be fairly minor.

Boston Grails Meetup GORM Presentation

Thursday, August 13th, 2009

I gave a talk last week at the Boston Grails meetup and wanted to share the slides. There were two code demonstrations in the Grails console so I’ll describe those here.

The general gist of the presentation was that mapped collections in Hibernate (and therefore Grails, since GORM wraps Hibernate) are a potentially risky choice for representing One-to-Many and Many-to-Many relationships.

To see what’s going on I added

loggingSql = true

to DataSource.groovy so we’d see the generated SQL in the console when comparing the standard and modified versions of the code.

First we create a Library and associate 20 (a completely arbitrary number) Visits:

def library = new Library(name: 'the library')
20.times {
   library.addToVisits(new Visit(personName: "person$it")) true)

Clear the output window and add one more Visit:

def library = Library.list()[0]
library.addToVisits(new Visit(personName: "new person")) true)

and you’ll see SQL similar to

select as id3_0_, this_.version as version3_0_, as name3_0_ 
from library this_;

select visits0_.library_id as library3_1_, as id1_, as id2_0_,
   visits0_.version as version2_0_, visits0_.library_id as library3_2_0_,
   visits0_.person_name as person4_2_0_, visits0_.visit_date as visit5_2_0_ 
from visit visits0_  where visits0_.library_id=?;

insert  into visit (id, version, library_id, person_name, visit_date) 
values (null, ?, ?, ?, ?);

call identity();

update library
set version=?, name=?
where id=? and version=?;

which means that it’s first loading the Library, then loading all of its previous Visits (!), then creating the new Visit, and finally bumping the version of the Library since it’s “changed”. Not good – not only do we incur a potentially huge performance hit to add one new instance (once there are a large number already), we also run a high risk of a false optimistic locking failure.

Doing the equivalent work with the updated versions:

def library = new Library(name: 'the library').save()
20.times {
    new Visit(personName: "person$it", library: library).save(flush: true)


def library = Library.list()[0]
new Visit(personName: "new person", library: library).save(flush: true)

we get SQL that makes a lot more sense:

select as id2_0_, this_.version as version2_0_, as name2_0_ 
from library this_;

insert into visit (id, version, library_id, person_name, visit_date) 
values (null, ?, ?, ?, ?);

call identity();

First the Library is loaded, and then the Visit is created. No other Visit instances need to be loaded since we know the new instance is unique, and there’s no need to version the Library.

For the Many-to-Many example, we create a Role and grant it to 20 Users:

Role role = new Role(name: 'ROLE_USER').save()
20.times {
   def user = new User(username: "user$it").save()
   user.addToRoles(role) true)

and clear the console and grant the Role to one more User:

def role = Role.list()[0]
def user = new User(username: "new person").save()
user.addToRoles(role) true)

and you’ll see SQL similar to

select as id0_0_, this_.version as version0_0_, as name0_0_
from role this_;

insert into user (id, version, username) 
values (null, ?, ?);

call identity();

select users0_.role_id as role1_0_, users0_.user_id as user2_0_ 
from user_roles users0_
where users0_.role_id=?;

update role
set version=?, name=?
where id=?  and version=?;

update user 
set version=?, username=? 
where id=? and version=?;

insert into user_roles (user_id, role_id) 
values (?, ?);

So we can see that it first loads the Role, then creates the User, then loads all of the previous Users that have that Role (proxies in this case, but still a lot of instances for no good reason), then it bumps the version for both the Role and the User, and finally adds a row to the join table.

Doing the same work with the updated versions:

Role role = new Role(name: 'ROLE_USER').save()
20.times {
	def user = new User(username: "user$it").save()
	UserRole.create(user, role, true)


def role = Role.list()[0]
def user = new User(username: "new user").save()
UserRole.create(user, role, true)

we get more reasonable SQL:

select as id4_0_, this_.version as version4_0_, as name4_0_ 
from role this_;

insert into user(id, version, username)
values (null, ?, ?);

call identity();

select userrole_.user_id, userrole_.role_id
from user_role userrole_
where userrole_.user_id=? and userrole_.role_id=?;

insert into user_role(user_id, role_id) 
values (?, ?);

where we can see that it loads the Role, creates the User, does a lightweight unique check in the join table, and then inserts the join table row.

Download the presentation here and the sample projects at here and here.

Grails “Lite” on Google AppEngine

Monday, April 13th, 2009

This week Google announced Java support for AppEngine. I had signed up when they originally announced Python support, hoping to use it as an excuse to learn Python, but never did anything with it, mostly because I’ve been spending so much time with Grails. So when they announced Java support I immediately signed up and was approved in about an hour.

Guillaume Laforge blogged about Groovy support – they’d worked with Google to ensure compatibility and released v1.6.1 with the required fixes. Of course with Groovy support, lots of people were asking if Grails would work. Unfortunately it looks like it’ll take some work for that. So far it looks like there are problems with threads, XPathFactory, and SiteMesh, and of course GORM will have to be enhanced to work with the AppEngine datastore.

Lots of work is being done to get frameworks working in AppEngine. Shay Banon updated Compass and posted a screencast demonstrating its usage. Thomas Mueller updated H2 database, so in-memory JDBC is possible, and he’s planning on writing a JDBC driver wrapping the datastore API which would make porting apps a lot more straightforward. In addition work is being done to make Tapestry, Struts, JSF, and other frameworks compatible.

The SDK supports both JPA and JDO, but JDO is currently much better documented. I have no experience with JDO so I started playing with the “jdoexamples” demo project that ships with the SDK. Like most of the examples it uses Servlets and JSPs, which after using SpringMVC and Grails seemed painfully dated. And the database code is very repetitive with lots of boilerplate. So I thought I’d play with using Groovy to add Grails-like functionality to reduce some of the clutter. This also gave me an excuse to use the new MVC features in Spring 2.5, especially the annotation support.

I reworked the jdoexamples project as a Spring MVC application, along with a bunch of Groovy MetaClass magic. There’s no BeanBuilder or resources.groovy/resources.xml; instead the “core” applicationContext.xml includes two other context files, one that’s auto-generated during the Ant “war” target containing services, and one that is application-specific in src/java/applicationContext_user.xml.

The domain classes aren’t changed much except that they take advantage of being written in Groovy, and static inner classes were reworked as top-level classes since Groovy doesn’t support inner classes. The “Utils” classes were reworked as Services. The Servlets were reworked as Controllers, and all of the scriptlets in the JSPs were moved to the controllers or reworked using JSTL tags. I didn’t improve the UI (it’s pretty sparse) since I was more focused on the code and not the look and feel.

The code expects a directory structure similar to a Grails project: domain classes in grails-app/domain, services in grails-app/services, controllers in grails-app/controllers, Java source in src/java, and Groovy helper classes in src/groovy:

In addition there’s the framework classes (in src/framework, there’s no separate library for these) and core applicationContext.xml, some application-specific config files in src/java (which will end up in WEB-INF/classes), application jars in lib, web content in war, web config files in war/WEB-INF, JSPs in war/WEB-INF/jsp, and Groovlets in war/WEB-INF/groovy (these aren’t auto-discovered, they need to be manually registered in web.xml as Servlets).

During the build, a file is generated (WEB-INF/artifacts.config) containing the names of the domain, controller, and service classes; this way the framework code knows which MOP methods to add when the app starts up.

The added domain class MetaClass methods are:

  • save
  • get
  • delete
  • findAll
  • withTransaction
  • withPersistenceManager (to execute code with access to the PersistenceManager)
  • isJdoPersistent, isJdoDeleted, isJdoDetached, isJdoDirty, isJdoNew, isJdoTransactional, getJdoTransactionalObjectId, getJdoVersion, getJdoObjectId, and jdoMakeDirty corresponding to the JDOHelper methods isPersistent, isDeleted, isDetached, isDirty, isNew, isTransactional, getTransactionalObjectId, getVersion, getObjectId, and makeDirty

Controllers have these attributes added to their MetaClass:

  • params (with Request attributes like in Grails)
  • session
  • request
  • response
  • servletContext

These attributes provide controllers access to the standard servlet classes but they aren’t as useful as in Grails since the approach taken is to use the newer MVC features in Spring 2.5 like declaring typed (auto-converted) parameters using annotations in controller methods. But they’re available in case they’re needed.

All artifacts have a Commons Logging “log” (wrapping a java.util.Logger) wired up, although for some reason this doesn’t work in Services. The only thing I can think of is that it’s because they’re Spring beans, but that shouldn’t matter since they’re not proxied.

Controllers are singletons. I appreciate the convenience of having stateful per-instance (prototype scope) Controllers in Grails but I tend not to take advantage of that and prefer the more traditional stateless singleton scope approach.

There are no Spring samples in the downloadable SDK demos, but after I’d written most of the demo app code I saw on the mailing list that the autoshoppe demo had been checked into the trunk (along with a few others). I should have expected that Spring would have a JdoTemplate – I’ll need to rework some of the code to use that since I reinvented a few wheels.

There’s a lot of missing functionality. No taglibs, scripts, converters, or GSPs. Also no SiteMesh, no BeanBuilder, and obviously no plugins 🙂 But it mostly works and definitely makes Spring/AppEngine development easier.

In addition I was thinking that I’d integrate Compass, plus it shouldn’t be much work to add Spring Security – it should only need a JDO-based UserDetailsService implementation.

You can download the sample code here and view it in action here.

Note that to use Ant to build the app, you need to copy groovy-all-1.6.1.jar to $ANT_HOME/lib. You also need a with an “appengine.sdk” property pointing to the root directory of your AppEngine SDK directory (to load the shared Ant targets). And you’ll need to change the app name (“bb-grails-lite”) in build.xml, web.xml, and appengine-web.xml. You’ll also need to change the Controller package name in spring-servlet.xml from com.burtbeckwith.gae.guestbook.controller to whatever your Controller package is.

There are two issues with the online demo. One is that the “friends” demo displays an error page – there are error messages in the console logs complaining about a missing index (this works fine locally). The other is that the “addressbook” demo mostly works, but it doesn’t save the state or phoneNumber fields correctly. This means that you can’t search by state, only by last name, and it displays the state value instead of the phoneNumber value. As you can see from the screenshot of the admin console, it thinks that there’s a metaClass field instead of state, and isn’t aware of phoneNumber at all. This seems highly likely to be due to Groovy, but it’ll have to be investigated. If the domain classes can’t be written in Groovy, there’s a lot of lost functionality without being able to use MOP.

Fixing Grails 1.0 Many-to-Many Mappings in 1.1

Sunday, March 01st, 2009

One of the more frequently asked questions about Grails involves its “backwards” Many-to-Many mapping. Given domain classes Foo and Bar, the join table foo_bar that’s generated for them will have a ‘foo’ column that points to the bar table and a ‘bar’ column that points to the foo table. In the comments of this bug it’s explained that this was intentional. But in 1.1 the Grails team decided to listen to the users and rework the approach.

Unfortunately this is a stealth fix that’s listed in the release notes along with many other changes but not in the breaking changes and it is very much a breaking change – unless you make changes your many-to-many relationships will not work after a 1.0 -> 1.1 upgrade. This is of particular interest to me as a developer on the Spring Security (Acegi) plugin since Role < -> User relationships are modeled as a many-to-many.

The plugin uses three domain classes, User, Role, and Requestmap. You can use whatever names you want but I’ll assume those names here; note that Requestmap isn’t affected by this issue. In Grails 1.0 your database would have two tables, user and role, and a many-to-many join table role_user, with backwards mappings, i.e. people_id refers to role and authorities_id refers to user:

   id BIGINT NOT NULL auto_increment,
   version BIGINT NOT NULL,
   authority VARCHAR(255) NOT NULL UNIQUE,
   description VARCHAR(255) NOT NULL,

   id BIGINT NOT NULL auto_increment,
   version BIGINT NOT NULL,
   description VARCHAR(255) NOT NULL,
   email VARCHAR(255) NOT NULL,
   email_show bit NOT NULL,
   enabled bit NOT NULL,
   passwd VARCHAR(255) NOT NULL,
   user_real_name VARCHAR(255) NOT NULL,
   username VARCHAR(255) NOT NULL unique,

CREATE TABLE role_user (
   people_id BIGINT NOT NULL,
   authorities_id BIGINT NOT NULL,
   PRIMARY KEY (people_id, authorities_id)

ALTER TABLE role_user ADD INDEX FK1407FDF48F01F561 (people_id),

ALTER TABLE role_user ADD INDEX FK1407FDF4CF6CDEE4 (authorities_id),

Here the name of the mapping table is the owning end (‘Role’) followed by the owned end (‘User’). Note that the foreign key and index names (‘FK1407FDF48F01F561’, etc.) will probably be different for your tables.

However in Grails 1.1 the join table looks like this:

CREATE TABLE role_people (
	PRIMARY KEY (role_id, user_id)

ALTER TABLE role_people ADD INDEX FK28B75E7852388A1A (role_id),

ALTER TABLE role_people ADD INDEX FK28B75E78F7634DFA (user_id),

and the mappings are correct – role_id references role and user_id references user. The name is the owning end (‘role’) followed by the collection name of the owned end (‘people’).

This means that your many-to-many relationships are completely broken, and in particular it means that you won’t be able to log in to your application if you’re using the Spring Security plugin. You’ll see errors like this in your logs:

ERROR springsecurity.GrailsDaoImpl  - User [foo] has no GrantedAuthority

So there are two options – migrate your data to the new table, or use the GORM mapping closure to get Grails to work in ‘1.0 mode’. The first option is cleaner, but if you’re accessing your join tables explicitly, e.g. for reporting queries using SQL, then this approach will break those.

For MySQL, the migration command is:

INSERT INTO role_people (role_id, user_id)
SELECT people_id, authorities_id FROM role_user;

and you’ll want to create the missing foreign keys and associated indexes:

ALTER TABLE role_people ADD INDEX FK28B75E7852388A1A (role_id),

ALTER TABLE role_people ADD INDEX FK28B75E78F7634DFA (user_id),

Once you verify that this worked, you should drop role_user and its foreign keys:

DROP TABLE role_user;

Alternatively, to continue to use “1.0 mode”, you just need to add a mapping closure to each of your domain classes:

In Role.groovy:

static mapping = {
   people column: 'people_id', joinTable: 'role_user'

and in User.groovy:

static mapping = {
   authorities column: 'authorities_id', joinTable: 'role_user'

and your app will continue to work – and still be backwards 😉

Using GORM outside of Grails part 2 – Swing

Sunday, September 07th, 2008

In an earlier post I wrote about getting GORM to work outside of Grails. It worked, but wasn’t really usable since it could only execute script files, so its usefulness was pretty limited in a real application. Greg Bond replied on the mailing (here and here) with some great enhancements that allowed it to work without script files. So I fleshed that out some more and now have a working implementation and even a Swing application to demonstrate.

The key was that Greg used grails compile to generate his domain class files. I’d just been using the groovyc Ant task, and when I looked at what the Grails compile script the difference turned out to be that Grails uses a Grails-aware subclass of Groovyc, org.codehaus.groovy.grails.compiler.GrailsCompiler. It allows you to specify a resourcePattern attribute to point at the domain class .groovy files for special treatment.

So now instead of one sample project there’s three. One is the gorm standalone project, which creates a jar (gorm_standalone.jar) containing GormHelper which bootstraps GORM. The other two are the sample application, split into GORM domain classes and the Swing application. The domain class application contains the domain class .groovy files plus DataSource.groovy, and most importantly an Ant script that builds a usable jar (domainclasses.jar). The Swing application uses gorm_standalone.jar and domainclasses.jar as libraries and displays a simple UI showing the results of database queries.

Here’s a quick screen shot:

You can download the sample apps here:
GORM standalone app
Sample app domain class app
Sample Swing app

Using GORM outside of Grails

Saturday, August 23rd, 2008

I’ve seen questions about using GORM outside of Grails in standalone apps but didn’t think much about it – I don’t mind using Hibernate directly. It came up again today on the Grails User mailing list, so I thought I’d take a look.

It turns out that Jeremie Weldin got this working quite a while ago but it no longer works with the latest version of Grails. I’ve been mucking around in the Grails internals trying to get a plugin together that’ll allow multiple datasources in a single app, so I figured it wouldn’t take long to get things going. As usual I was overly optimistic, but several hours later it’s ready to share.

I tried to use the public API and not copy/paste code or call private methods, since I’d like for this to continue working with future versions of Grails. So the main trick here is to call DomainClassGrailsPlugin.doWithDynamicMethods and HibernateGrailsPlugin.doWithDynamicMethods to leverage that functionality. The rest of the app basically just configures the Application, SessionFactory, Configuration, and TransactionManager.

Some notes:

  • It’s set up to support annotated classes via a GrailsAnnotationConfiguration, although I haven’t tested that here. You’d follow the same steps as regular Grails – put the jar in the lib directory and a hibernate.cfg.xml file in grails-app/conf/hibernate.
  • There’s no OpenSessionInView, so you’ll need to manage session flush() yourself. The easiest way to do this is to run your update/insert actions in a transaction – use GormHelper.withTransaction() for this (similar to calling withTransaction on a domain class in Grails)
  • Define data source info just like in Grails, in grails-app/conf/DataSource.groovy. It doesn’t make much sense to use dev or test mode, so just keep everything in the prod section.
  • There’s no Dialect autodetection, so you’ll need to specify that in DataSource.groovy, but you should be doing this in Grails anyway
  • There’s no Spring application context, so to access the Application, SessionFactory, and TransactionManager I’ve stored them in static fields in GormHelper

There’s a sample class that configures things and runs a test script – it’s called Tester in src/groovy. Just run “ant run” from the command line and it’ll compile and run this for you. This executes a script (scripts/Main.groovy) that creates some instances of the domain classes defined in grails-app/domain.

One significant issue that limits the usefulness of this is that as currently configured, you cannot execute anything other than dynamically loaded scripts. This is because Grails artifacts are loaded by a special Groovy/Grails classloader to support reloading and MetaClass magic methods. When I tried adding compiled domain classes to the classpath I was able to reference and import them but the MetaClass methods failed. I’ll have to take a look at how to get this working.

Thanks again to Jeremie Weldin. I did a pretty significant rewrite so I took the liberty of repackaging things under com.burtbeckwith but this wouldn’t have been possible without his original work.

You can download the code here. I’ve omitted the jar files from the zip – check out the README in the lib directory for the list of jars you’ll need to copy from your Grails installation.

Creative Commons License
This work is licensed under a Creative Commons Attribution 3.0 License.