Monthly Archives: September 2009

Supporting reloading in your Grails plugins

I just recently added a new artifact type to the GWT plugin for Grails and tried to add support for automatic reloading of those artifacts, just like controllers and services. My first attempt ended in failure, even though I had successfully implemented reloading in previous plugins. What was wrong?

I initially thought there was a problem in the Spring bean registering code, but after some discussions with Graeme, a little debugging, and comparing my code with the services plugin, I finally discovered the problem. What follows is the technique I use to handle artifact reloading in a plugin. You should be able to apply this without any problems to your own artifact types.

Spring beans

If you configure your artifacts as Spring beans and want changes to them to take effect (almost) immediately, you need to re-register them. That means registering the beans in both the doWithSpring closure and onChange. Since the bean configuration is typically the same in both cases, re-using the bean definition code is often essential. The question is, how do we do that?

Let’s look at the code for the GWT plugin as an example:

class GwtGrailsPlugin {
    ...
    def doWithSpring = {
        // Create Spring beans for all the actions defined by the user.
        final c = configureActionHandler.clone()
        c.delegate = delegate

        application.actionHandlerClasses.each { handlerClass ->
            log.info "Registering action handler: ${handlerClass.fullName}"
            c.call(handlerClass)
        }
    }

    def onChange = { event ->
        if (application.isActionHandlerClass(event.source)) {
            def grailsClass = application.addArtefact(ActionHandlerArtefactHandler.TYPE, event.source)

            // Re-register the action handler bean.
            final c = configureActionHandler.clone()
            def beans = beans {
                c.delegate = delegate
                c.call(grailsClass)
            }

            beans.registerBeans(event.ctx)
        }
    }

    def configureActionHandler = { grailsClass ->
        "gwt${grailsClass.shortName}"(grailsClass.clazz) { bean ->
            bean.autowire = "byName"
        }
    }
    ...
}

What I have done here is create a closure field, configureActionHandler, that contains the bean definition code. Of course, that bean definition code won’t work unless the closure’s delegate is an instance of Grails’ BeanBuilder. So, both doWithSpring and onChange set the closure delegate (see highlighted lines) before invoking the closure. The GrailsClass instance for the artifact is passed in as an argument because it’s needed by the bean definition, but you may not need it in your own code.

Did you notice that I clone the closure and then call the clone? This is fairly standard procedure when you modify a property of a closure, otherwise any other code using that closure may start behaving oddly because the delegate or something else has changed!

That’s the bean definition shared between the two plugin hooks (doWithSpring and onChange). You could also use a method instead of a closure for configureActionHandler, but then you would need to pass the bean builder in as an argument, as well as the logger if you wanted to use that. Regardless of which approach you use, you still need to do a bit more to support automatic reloading of Spring beans. Let’s take a closer look at a part of the code above:

    def onChange = { event ->
        if (application.isActionHandlerClass(event.source)) {
            def grailsClass = application.addArtefact(ActionHandlerArtefactHandler.TYPE, event.source)

The highlighted line is the fix I mentioned at the beginning of the article. When you re-register your Spring bean, the old class will be used unless you first call addArtefact. It’s just one of those things you need to do. The event source is the Class instance of the reloaded artifact.

Once that’s done, you can use the beans() method to define the Spring beans again and finally call registerBeans() on the result to make the changes visible to the rest of the application.

Dynamic methods

I’m not going to say much about adding dynamic methods to reloaded classes, because there aren’t any real gotchas. If your plugin adds dynamic properties or methods to artifacts that can be reloaded, then you should re-add them in onChange, otherwise they will no longer be available on that artifact. But the code that does the actual work of adding dynamic methods and properties can be factored out into a separate method and called from the two plugin hooks. Simple.

Anyway, I hope this helps budding plugin authors to implement reloading support, because it’s something that users expect and demand – even in volunteer-effort projects!

PS The call to addArtefact() may be unnecessary in future versions of Grails. Keep an eye on the related JIRA issue so you know if and when you no longer need to use it.