Search code examples
grailsgroovygrails-plugin

Hooking into Grails Domain object save()


I'm writing a grails plugin and I need to hook into the domain save() method to do some logic after the save. I need to do this across multiple domain classes. I'm trying to avoid hibernate events in the cases where a plugin user is not using hibernate with GORM.

I've tried many thing but below is what I think should have had the best chance at working. In all cases grailsSave is null. How can I do this?

def doWithDynamicMethods = { ctx ->
    application.domainClasses.each { dc ->
        def grailsSave = dc.metaClass.pickMethod('save', [Map] as Class[])

        domainClass.metaClass.save = { Map params ->
        grailsSave.invoke(delegate, [params] as Object[])
        println "Saved object, now do my thing"
        //...
        }
    }
}

I have the following set in my *Plugin.groovy class:

def dependsOn = [domainClass: '1.1 > *', hibernate: '1.1 > *']
def loadAfter = ['hibernate']

Solution

  • I was unable to successfully get a reference to the save() methods during plugin/app initialization; I don't know why. Instead, I decided to create a listener for the hibernate events after insert, update, and deletes. This post by Sean Hartsock regarding the Audit Logging plugin was a perfect primer for doing that.

    Here's the gist of the Listener:

    class MyListener implements PostInsertEventListener, PostUpdateEventListener, PostDeleteEventListener, Initializable {
    
            public void onPostInsert(final PostInsertEvent event) {
                // logic after insert
                return
            }
    
            public void onPostUpdate(final PostUpdateEvent event) {
                // logic after update
                return
            }
    
            public void onPostDelete(final PostDeleteEvent event) {
                // logic after delete
                return
            }
    
    
            public void initialize(final Configuration config) {
                return
            }   
        }
    

    Then in the *GrailsPlugin.groovy:

    def doWithApplicationContext = { applicationContext ->
    
        // add the event listeners for reindexing on change
        def listeners = applicationContext.sessionFactory.eventListeners
        def listener = new MyListener()
    
        ['postInsert', 'postUpdate', 'postDelete'].each({
           addEventTypeListener(listeners, listener, it)
        })
    
    }
    
    
    // copied from http://hartsock.blogspot.com/2008/04/inside-hibernate-events-and-audit.html
    private addEventTypeListener(listeners, listener, type) {
        def typeProperty = "${type}EventListeners"
        def typeListeners = listeners."${typeProperty}"
    
        def expandedTypeListeners = new Object[typeListeners.length + 1]
        System.arraycopy(typeListeners, 0, expandedTypeListeners, 0, typeListeners.length)
        expandedTypeListeners[-1] = listener
    
        listeners."${typeProperty}" = expandedTypeListeners
    }
    

    Fairly simple at the end of the day...