Archive for November 13th, 2011

Create your own Grails holder class

Sunday, November 13th, 2011

I mentioned in an earlier post that using Grails *Holder classes should be avoided, and showed how to access the GrailsApplication, ApplicationContext, etc. from domain classes. But there are still cases where you need access to these objects and dependency injection and the approach I described aren’t sufficient. When all else fails you can always create your own holder class.

The reason this works in your application but not in Grails is that the static fields in your application are only accessed within your application. By using shared jars, multiple applications share static state held by shared classes, but this isn’t a problem for your code.

Since everything is reachable from the ApplicationContext and it’s easy to access, we’ll create a holder for that and expose the other objects from there.

Create a class called ApplicationContextHolder that implements ApplicationContextAware:

package com.yourcompany.yourapp

import org.springframework.context.ApplicationContext
import org.springframework.context.ApplicationContextAware

@Singleton
class ApplicationContextHolder implements ApplicationContextAware {

   private ApplicationContext ctx

   void setApplicationContext(ApplicationContext applicationContext) {
       ctx = applicationContext
   }

   static ApplicationContext getApplicationContext() {
      getInstance().ctx
   }
}

and register it in resources.groovy so the ApplicationContext is injected (using the factoryMethod attribute since the class is a singleton):

import com.yourcompany.yourapp.ApplicationContextHolder

beans = {
   applicationContextHolder(ApplicationContextHolder) { bean ->
      bean.factoryMethod = 'getInstance'
   }
}

Now you can use the ApplicationContext from anywhere to access Spring beans. We can go further though, adding utility methods to expose other classes held by holders:

static Object getBean(String name) {
   getApplicationContext().getBean(name)
}

static GrailsApplication getGrailsApplication() {
   getBean('grailsApplication')
}

static ConfigObject getConfig() {
   getGrailsApplication().config
}

static ServletContext getServletContext() {
   getBean('servletContext')
}

static GrailsPluginManager getPluginManager() {
   getBean('pluginManager')
}

You can also add the ability to register mock beans for unit tests; here’s the complete class:

package com.yourcompany.yourapp

import org.springframework.context.ApplicationContext
import org.springframework.context.ApplicationContextAware
import javax.servlet.ServletContext

import org.codehaus.groovy.grails.commons.GrailsApplication
import org.codehaus.groovy.grails.plugins.GrailsPluginManager
import org.springframework.context.ApplicationContext
import org.springframework.context.ApplicationContextAware

@Singleton
class ApplicationContextHolder implements ApplicationContextAware {

   private ApplicationContext ctx

   private static final Map <String, Object> TEST_BEANS = [:]

   void setApplicationContext(ApplicationContext applicationContext) {
       ctx = applicationContext
   }

   static ApplicationContext getApplicationContext() {
      getInstance().ctx
   }

   static Object getBean(String name) {
      TEST_BEANS[name] ?: getApplicationContext().getBean(name)
   }

   static GrailsApplication getGrailsApplication() {
      getBean('grailsApplication')
   }

   static ConfigObject getConfig() {
      getGrailsApplication().config
   }

   static ServletContext getServletContext() {
      getBean('servletContext')
   }

   static GrailsPluginManager getPluginManager() {
      getBean('pluginManager')
   }

   // For testing
   static void registerTestBean(String name, bean) {
      TEST_BEANS[name] = bean
   }

   // For testing
   static void unregisterTestBeans() {
      TEST_BEANS.clear()
   }
}

Note that the servletContext and pluginManager beans are available in a running application but not the Grails console.

Overriding Groovy constructors

Sunday, November 13th, 2011

It’s rare to create parameterized constructors in Groovy since the Map constructor that’s added to all classes reduces the need for “real” constructors. There’s still a case for defining constructors to enforce business rules, for example if an instance requires certain fields to be set in order for it to be valid. But in general we tend to just use the Map constructor.

But what if you want to override that constructor, replacing its implementation, or adding code before or after the default implementation? Also, Grails augments domain class constructors to support dependency injection from the Spring ApplicationContext, so is it possible to reimplement or augment that behavior?

One thing that’s important to point out is that the Map constructor relies on the default constructor that’s added to all classes that don’t define any explicit constructors. It calls that constructor, then sets properties from the provided Map (this is defined in MetaClassImpl.invokeConstructor() if you’re curious). But if you declare one or more parameterized constructors the compiler doesn’t generate an empty one for you, and the Map constructor fails.

For example, if you run this in a Groovy console, it will fail with an exception:

class Thing {
   Thing(String name) { this.name = name }
   String name
}

def t = new Thing(name: 'x')
println f.name

The fix is to add an empty constructor:

class Thing {
   Thing() {}
   Thing(String name) { this.name = name }
   String name
}

def t = new Thing(name: 'x')
println f.name

or not define any at all:

class Thing {
   String name
}

So how can we replace the Map constructor? It depends on whether we want to redefine the implementation, or add logic before or after the default implementation. Note – for these examples I assume you have a domain class named “User”.

To add behavior, we need to be able to call the constructor that Groovy adds:

def originalMapConstructor = User.metaClass.retrieveConstructor(Map)

User.metaClass.constructor = { Map m ->

   // do work before creation

   def instance = originalMapConstructor.newInstance(m)

   // do work after creation

   instance
}

If you want to redefine the behavior it’s simpler, just do that work in the Closure that will become the constructor:

User.metaClass.constructor = { Map m ->

   // do work before creation

   def instance = new User()

   // do initialization, e.g. "instance.properties = m"

   // do work after creation

   instance
}

Overriding the default constructor is a bit trickier, and only really applies to Grails, and really just domain class constructors. The problem is that it’s nontrivial to access the real default constructor that’s added by the compiler. In fact you can’t, but there’s a hackish workaround for that.

To just add behavior, we need to be able to access the behavior that Grails adds. DomainClassGrailsPlugin overrides the default constructor in its doWithDynamicMethods callback (doing the work in enhanceDomainClasses()) and takes advantage of the fact that domain classes are registered as prototype Spring beans. This means that requesting the associated bean gives you a new instance each time, and since it’s a bean any public fields that correspond to other Spring beans are populated via dependency injection.

So assuming you have access to the GrailsApplication (most likely via dependency injection with "def grailsApplication"), you can do this:

User.metaClass.constructor = { ->

   // do work before creation

   def instance = grailsApplication.mainContext.getBean(User.name)

   // do work after creation

   instance
}

If you want to redefine the behavior, ideally you could call the compiler-generated default constructor. But that’s not available, having been replaced in the MetaClass. So you can use a trick, unfortunately only available in a Sun or JRockit JDK:

import sun.reflect.ReflectionFactory

def objectConstructor = Object.getConstructor()

User.metaClass.constructor = { ->
   def factory = ReflectionFactory.reflectionFactory
   def cleanConstructor = factory.newConstructorForSerialization(
      User, objectConstructor)
   cleanConstructor.accessible = true

   // do work before creation

   def instance = cleanConstructor.newInstance()

   // do work after creation

   instance
}

You can also go further and trigger dependency injection:

import sun.reflect.ReflectionFactory
import org.springframework.beans.factory.config.AutowireCapableBeanFactory

def objectConstructor = Object.getConstructor()

User.metaClass.constructor = { ->
   def factory = ReflectionFactory.reflectionFactory
   def cleanConstructor = factory.newConstructorForSerialization(
      User, objectConstructor)
   cleanConstructor.accessible = true

   // do work before creation

   def instance = cleanConstructor.newInstance()

   // trigger dependency injection
   def ctx = grailsApplication.mainContext
   def beanFactory = ctx.autowireCapableBeanFactory
   beanFactory.autowireBeanProperties(instance,
         AutowireCapableBeanFactory.AUTOWIRE_BY_NAME, false)
   
   // do work after creation

   instance
}

Note that since the constructor is bypassed, field initializations won’t run. So for example if your class looks like this:

class User {
   String name
   Integer foo = 5
   def something = new Something()
   def userService
}

then the ‘foo’ and ‘something’ fields will be null. ‘userService’ will be populated only if you retain the default dependency injection behavior or call it yourself.

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