-
Get a monthly update on best practices for delivering successful software.
For a recent project, I needed to plug some Java code into a Grails app. I wanted to use GORM for persistence, but couldn't introduce the interfaces, proxies, and factories into the java code which is required for existing solutions to this problem.
Basically, my constraint was that the java code itself couldn't be altered to work inside the grails app. The solution I came up with is pretty simple, but works well.
The java code delegated to a DAO for persistence (using an interface). Here is what it looked like:
package org.smurf; import java.util.List; class SmurfingServiceImpl implements SmurfingService { private final SmurfDao smurfDao; public SmurfingServiceImpl(SmurfDao smurfDao) { this.smurfDao= smurfDao; } public List listSmurfs() { ... return smurfDao.findAll(); } public Smurf capture(Smurf smurf) { ... return smurfDao.update(smurf); } }
I packaged my groovy domain class to be org.smurf.Smurf so that the java code would be using the Grails generated class. Depending on the situation, this could involve a little bit of tweaking to the grails domain object to be compatible with any existing java classes. For this example, we're assuming an anemic bean so we don't need to do anything except declare the properties.
package org.smurf class Smurf { String name Boolean captured }
Next, I created an abstract DAO which delegates the sort of CRUD operations you see in a DAO to the GORM object.
package org.smurf; import java.util.List; public abstract class GormDelegatingDao<T extends groovy.lang.GroovyObject> { protected abstract Class<T> getGormClass(); public T create(T entity) { return update(entity); } public T retrieve(String id) { return (T) invokeClassLevelMethod("get", id); } public T update(T entity) { return (T) entity.invokeMethod("save", new Object[0]); } public T delete(T entity) { return (T) invokeClassLevelMethod("delete", entity.getId()); } public List<T> findAll() { return (List<T>) invokeClassLevelMethod("list"); } protected Object invokeClassLevelMethod(String method, Object... args) { T instance; try { instance = getGormClass().newInstance(); } catch (Exception e) { throw new RuntimeException(e); } return instance.invokeMethod(method, args); } }
After this, I simply needed to implement SmurfDao using this abstract class.
package org.smurf; class GormSmurfDao extends GormDelegatingDao<Smurf> implements SmurfDao { protected Class<Smurf> getGormClass() { return Smurf.class; } }
Using spring, we just need to make sure an instance of this class is injected into the SmurfingService. The following is an example using grails-app/conf/spring/resources.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean class="org.smurf.GormSmurfDao" autowire="constructor"/>
<bean id="smurfingService" class="org.smurf.SmurfingServiceImpl" autowire="constructor"/>
</beans>
I can now access this service in a controller (or grails service), and it will properly persist my changes without me needing to set up any additional configuration or changing the existing java code.
It is entirely possible to dynamically generate the DAO implementations for each domain class. You could even use a factory and proxy pattern to create them on the fly if you wanted to be really slick.
For a real world application, there are probably operations outside of CRUD which you would need to deal with (e.g. complex finders). Using the general concept presented here, it wouldn't be difficult to delegate these finders to GORM.
Related posts:
Anthony, I was wondering if I could use this technique to integrate Vaadin with Grails?
Basically, I’m thinking we could modify the Grails Vaadin plugin to autogenerate DAO implementations that implement the Property, Item, and Container interfaces in the Vaadin Data Model.
Any thoughts on this?
Comment by Rodney Schneider, Sunday, September 27, 2009 @ 11:45 pm