Lately I had to configure the Ehcache [0] CacheManager’s diskStorePath property. That alone wouldn’t be a problem, but I had to come up with a mechanism that allowed to set the diskStorePath based on customer-specific configuration settings. As I couldn’t find proper instructions on the web or on stackoverflow, I thought it would be worth sharing the outcome.
The problem
The Grails plugin collective (GPC) offers the so-called “Spring-Cache Plugin” [1], a great plugin by Robert Fletcher. The plugin comes with a @Cacheable annotation. Whenever a type, method or field is annotated as “cacheable” the result is put into a Ehcache instance and further calls might return a cached result (depending on the cache settings).
Let’s start with the default settings. The default Ehcache cache setting look like this (extracted from ehcache-failsafe.xml):
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
maxElementsOnDisk="10000000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
/>
The attribute overflowToDisk is true, meaning that whenever the maximum number of in-memory objects is reached, objects are swapped to disk. However, diskPersistent is false, meaning that between subsequent server/application restarts, the in-memory cache is lost and all swapped objects will be cleaned on startup.
Our requirement was to activate disk persistency and configure a diskStorePath, based on a given customer configuration. The customer configuration diskStore directory path would be based on the build settings, ie. a custom Config.groovy file.
Specifying a custom diskStorePath would be done in a custom ehcache.xml file like this:
<diskStore path="here comes the customer-specific directory"/>
Setting the diskStorePath
The Springcache plugin works with Spring’s EhCacheManagerFactoryBean and EhCacheFactoryBean. Given the following configuration in Config.groovy …
springcache.enabled = true
springcache {
defaults {
// set default cache properties that will apply to all caches that do not override them
eternal = false
diskPersistent = true
maxElementsInMemory = 1000
maxElementsOnDisk = 10000
// 24 hours
timeToIdle = 86400
timeToLive = 86400
overflowToDisk = true
memoryStoreEvictionPolicy = "LRU"
}
caches {
myCache {
// set any properties unique to this cache
}
// more caches follow ...
}
… the plugin will create one or multiple EhCacheFactoryBean instances, by iterating over springcache.defaults.
springcacheDefaultCache(EhCacheFactoryBean) { bean ->
bean."abstract" = true
cacheManager = ref("springcacheCacheManager")
application.config.springcache.defaults.each {
bean.setPropertyValue it.key, it.value
}
}
application.config.springcache.caches.each {String name, ConfigObject cacheConfig ->
"$name"(EhCacheFactoryBean) {bean ->
bean.parent = springcacheDefaultCache
cacheName = name
cacheConfig.each {
bean.setPropertyValue it.key, it.value
}
}
}
The diskStorePath is actually held by Ehcache’s CacheManager. It is a singleton managing the default cache and all the other cache instances. Bad news is that EhcacheManagerFactoryBean does not provide a way to define a diskStorePath [2]. The only way to configure a custom path is to reference a custom ehcache.xml configuration:
public class EhCacheManagerFactoryBean {
// ...
public void setConfigLocation(org.springframework.core.io.Resource configLocation)
}
As we have multiple customers and therefore potentially multiple diskStore paths and wanted to control the path through the customer-specific Config.groovy, we had to find a way to dynamically generate a temporary ehcache.xml.
As it turns out, with Groovy’s XML support this is a pretty straight-forward process in resources.groovy:
springcacheCacheManager(EhCacheManagerFactoryBean) {
// ... set other props
def writer = new StringWriter()
def xml = new MarkupBuilder(writer)
xml.ehcache() {
diskStore(path: config.app.cache.diskStorePath ?: 'java.io.tmpdir/springcache')
defaultCache(config.springcache.defaults)
}
def ehCacheXmlTempFile = File.createTempFile("ehcache-springcache", ".xml")
ehCacheXmlTempFile.setText(writer.toString())
// set the spring bean property
configLocation = new FileSystemResource(ehCacheXmlTempFile)
}
As you can see from the code snippet above, XmlSlurper is used to slurp a ehcache.xml configuration template, without any predefined settings. As the Springcache plugin has already been configured in Config.groovy, it ‘synchronizes’ those settings and injects all properties into the slurped DOM. The best thing is that diskStorePath can be set accordingly to the current configuration setting in config.app.cache.diskStorePath, defaulting to the default temporary directory. Last, StreamingMarkupBuilder is used to write the resulting XML to a newly created temporary file.
Conclusion
This article shows how using a dynamic programming language for bean definitions is just awesome and how Groovy’s excellent XML support can be used in places XML gurus only can dream of.
[0] Ehcache – http://ehcache.org/
[1] Grails Springcache Plugin
[2] Spring JIRA – [EhCacheFactoryBean] diskStorePath property has no effect
good article..