Create your own Grails holder class
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.
An earlier comment on a post contextualized this by giving a specific use case: accessing a static string for URL mapping. While it is probably unreasonable to ask for an exhaustive set of use cases, posts like this may be improved by early consideration and guidance on “when would I use this?”
Why not let grails create-app create the ApplicationContextHolder?
Burt,
I tried creating ApplicationContextHolder.groovy under src/groovy, but the method setApplicationContext is never called. Its declared in resources.groovy. I am using 2.0.0.RC1, any thoughts what could be going wrong here?
Thanks
Correction – It does get set, however I am trying to read configurations in one my job triggers to establish repeat interval, and at that point, there is no grailsApplication, hence NULL config. It use to work fine, but now ConfigurationHolder gone, what is the suggested way to handle this use-case?
I have the same question. I have a quartz job and need to store the cron expression in my config file. I am trying to set it like this:
static triggers = {
cron name: ‘cronTrigger’, startDelay: 0, cronExpression: ACH.config.batch.cron.expression.validateDelegates
}
I get this error:
Cannot invoke method getBean() on null object
In my ApplicationContextHolder, getApplicationContext() is returning null. Anyone have a suggestion?
Can you use this in the _Events.groovy file? I am having problems getting the configuration.
Asked the grails user mailing list. config is accessible without needing any of the wiring above.
So how do you get at the config in static context without any of the above? Thanks.
Hi Burt, thanks for this post.
I tried to use your solution but I’m getting the following error:
“Cannot invoke method getBean() on null object”.
The error is triggered because the private variable “private ApplicationContext ctx” is null. I can’t understand when and how to set that up for the instance?
I get exactly the same.
What happens is that setApplicationContext is called long after getApplicationContext() is called from the DataSource.groovy class. It’s to get a database name out of message.properties, but that’s not important. So how can one get the setter to be called first? And won’t having the connection to the database ready so late – if it has to wait for the setApplicationContext – cause problems?