Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

A plugin that makes annotation-driven caching available to Grails artefacts

branch: master
Octocat-spinner-32 grails-app licence headers March 03, 2010
Octocat-spinner-32 scripts Initial commit June 15, 2009
Octocat-spinner-32 src licence headers March 03, 2010
Octocat-spinner-32 test git forcing me to commit intellij junk before I can switch branches March 04, 2010
Octocat-spinner-32 .classpath renamed spring bean properties on aspects November 29, 2009
Octocat-spinner-32 .gitignore ignore zip files March 03, 2010
Octocat-spinner-32 .project renamed spring bean properties on aspects November 29, 2009
Octocat-spinner-32 LICENSE.txt licence and todo files November 29, 2009
Octocat-spinner-32 README.markdown add docs for class level annotations on controllers, fix indentation … March 04, 2010
Octocat-spinner-32 SpringcacheGrailsPlugin.groovy bump version number ready for release March 03, 2010
Octocat-spinner-32 TODO.txt removed some done stuff from todo list March 02, 2010
Octocat-spinner-32 application.properties fix for GRAILSPLUGINS-1960 February 13, 2010
Octocat-spinner-32 build.gradle gradle build can convert markdown to grails wiki March 04, 2010
Octocat-spinner-32 grails-springcache-grailsPlugins.iml git forcing me to commit intellij junk before I can switch branches March 04, 2010
Octocat-spinner-32 grails-springcache.iml git forcing me to commit intellij junk before I can switch branches March 04, 2010
Octocat-spinner-32 grails-springcache.ipr git forcing me to commit intellij junk before I can switch branches March 04, 2010
Octocat-spinner-32 plugin.xml remove snapshot zip March 03, 2010
Octocat-spinner-32 settings.gradle Overhauled annotations and removed cache providers and models. This w… February 27, 2010
Octocat-spinner-32 springcache.tmproj reorganised ignore file November 28, 2009
README.markdown

Springcache Plugin

The Springcache plugin allows you to easily add the following functionality to your Grails project:

  • Caching of Spring bean methods (typically Grails service methods).
  • Caching of page fragments generated by Grails controllers.
  • Cache flushing when Spring bean methods or controller actions are invoked.

The plugin depends on the EhCache and EhCache-Web libraries.

Upgrading from earlier plugin versions

Successive versions of the plugin have introduced some non-backwards compatible changes. If you are upgrading from an earlier version you will need to consider the following:

Upgrading from 1.1.* to 1.2:

  • The plugin no longer uses caching and flushing models. Instead cache names are referenced directly by the annotations. This means you will need to 'inline' your model definitions from Config.groovy to your annotations.
  • The plugin no longer supports alternate cache libraries, it's EhCache or nothing.

From pre 1.1:

  • You will need to change the import statements for your @Cacheable and @CacheFlush annotations to point to the grails.plugin.springcache.annotations package.

Basics

The Springcache plugin provides two annotations that are the basis of how you can apply caching and flushing behaviour to both Spring bean methods and page fragments. Both annotations are in the grails.plugin.springcache.annotations package.

The @Cachable annotation

The @Cacheable annotation is applied to methods on Spring managed beans such as Grails services to cache method results or to controller actions to cache page fragments. The annotation requires a single argument which is the name of the cache that will be used.

The @CacheFlush annotation

The @CacheFlush annotation can be applied in the same places as the @Cacheable annotation but instead of caching results it will cause a cache or set of caches to be flushed. The @CacheFlush annotation can take a single argument or a String array. Either way the arguments can simply be literal cache names or regular expression patterns that may match multiple cache names. For example:

@CacheFlush("myCache")
@CacheFlush(/\w+ControllerCache/)
@CacheFlush(["cacheA", "cacheB", "cacheC"])
@CacheFlush([/cache[A-Z]/, "myCache"])

Applying caching and flushing behaviour to Spring bean methods

The typical use case for method caching is when you have Grails service methods that invoke expensive operations such as HTTP gets, web service calls, filesystem IO, etc. Although you can use the Springcache plugin to cache service methods that query or update GORM domain objects you should consider whether it's more appropriate to use the Hibernate 2nd level cache (see the relevant sections in the Grails documentation).

Simply add an @Cacheable annotation to methods that should cache their results and a @CacheFlush annotation to methods that should flush caches.

Be aware that the annotations will only have any effect on Spring-managed beans. If you create instances of your class directly rather than getting them from the application context they will not be decorated with caching/flushing behaviour.

A simple example might be:

PiracyService.groovy

@Cacheable("pirateCache")
def getPirates() {
    // return a list of pirates
}

@Cacheable("pirateCache")
def findPirates(name) {
    // return a particular pirate
}

@Cacheable("shipCache")
def getShips() {
    // return a list of ships
}

@CacheFlush("pirateCache")
void registerNewPirate(Pirate sailor) {
    // store a new pirate
}

@CacheFlush("shipCache")
void registerNewShip(Ship ship) {
    // store a new ship
}

@CacheFlush(["pirateCache", "shipCache"])
void registerNewShipWithCrew(Ship ship, Collection<Sailor> crew) {
    // store a new ship and associated pirates
}

This ties the flushes on the register methods to the particular caches they affect, so after calling registerNewPirate the methods getPirates and findPirates will re-populate their cached results but getShips would still use any cached results from previous calls. Calling registerNewShipWithCrew will flush both caches.

It is fine for multiple methods to share the same caches. Both getPirates and findPirates in the example above share the same cache. Cache entries are keyed on target object (the service instance in this case), method name and call parameters so there should be no confusion when using the same caches on multiple methods.

There are various strategies you can adopt in naming and grouping caches, this example shouldn't be seen as definitive.

Page fragment caching

The same annotations can be applied to controller actions and the plugin will then cache the page fragment generated by the controller whether this is done by rendering a GSP, using a MarkupBuilder closure, rendering text directly or whatever. Only successful page renders are cached, so redirects, 404s, errors and so on will not be.

Composing pages so that they can be optimally cached requires some thought. The plugin uses a servlet filter that runs 'inside' the SiteMesh filter provided by Grails. This means that cached output is decorated by SiteMesh and the resulting page can therefore contain uncached content from the SiteMesh template. In addition you can use caching at a modular level to cache the output of controller actions invoked using the g:include tag. Combining these techniques leads to powerful modular page caching. For example, you can cache the output of the 'main' controller then use g:include tags in the SiteMesh layout to include content on the page that is cached separately - and can be flushed separately - from the main body of the page.

Example: caching Grails CRUD pages

Grails' standard scaffolded CRUD pages provide a good example of how caching and flushing can be applied. For example, let's take an Album domain class. The scaffolded controller could be annotated like this:

AlbumController.groovy

class AlbumController {
    // the index action is uncached as it just performs a redirect to list
    def index = {
        redirect(action: "list", params: params)
    }

    @Cacheable("albumControllerCache")
    def list = {
        // standard Grails scaffolding code omitted
    }

    @Cacheable("albumControllerCache")
    def create = {
        // standard Grails scaffolding code omitted
    }

    @CacheFlush(["albumControllerCache", "artistControllerCache", "latestControllerCache", "popularControllerCache"])
    def save = {
        // standard Grails scaffolding code omitted
    }

    @Cacheable("albumControllerCache")
    def show = {
        // standard Grails scaffolding code omitted
    }

    @Cacheable("albumControllerCache")
    def edit = {
        // standard Grails scaffolding code omitted
    }

    @CacheFlush(["albumControllerCache", "latestControllerCache", "popularControllerCache"])
    def update = {
        // standard Grails scaffolding code omitted
    }

    @CacheFlush(["albumControllerCache", "artistControllerCache", "latestControllerCache", "popularControllerCache"])
    def delete = {
        // standard Grails scaffolding code omitted
    }
}

The list, show, create and edit pages are all cached. The show and edit rely on an domain object id parameter and this will be included in the cache key so that /album/show/1 and /album/show/2 are cached separately. The save, update and delete actions will flush caches. Note that in addition to flushing the cache used by the list, show, create and edit actions they are flushing other caches which are content caches for controllers whose output should be refreshed if Album data changes.

Example: decorating a cached page with dynamic content using SiteMesh

It is often necessary to have portions of a page be dynamic. A typical example is when something is displayed to logged in users that will be different for each user. Those sorts of page sections are not really candidates for caching. At the same time other parts of the page may well be able to take advantage of caching. For example, if you want to display a "Welcome back $username" type message in page headers while caching the main body of the page you can use SiteMesh templates like this:

grails-app/views/layouts/main.gsp

<html>
    <head>
        <title><g:layoutTitle default="Welcome to My Grails Application"/></title>
        <%-- render the page head from the controller - may be cached --%>
        <g:layoutHead/>
    </head>
    <body>
        <%-- render a "welcome back" header (tags used here are from the Spring Security plugin) --%>
        <g:isLoggedIn>
            <div id="loggedInUser"><g:message code="auth.loggedInAs" args="[loggedInUsername()]" default="Logged in as {0}"/></div>
        </g:isLoggedIn>
        <g:isNotLoggedIn>
            <div id="loginLink"><g:link controller="login"><g:message code="default.login.label" default="Login here"/></g:link></div>
        </g:isNotLoggedIn>

        <%-- render the page body from the controller - may be cached --%>
        <g:layoutBody/>
    </body>
</html>

If the controller action invoked uses @Cacheable everything will work fine because the content of the SiteMesh layout is not cached - only the content generated by the cached action. The SiteMesh template is applied to cached and uncached content alike so the correct username will be displayed to your users even though the main body of the page may have been loaded from a cache.

Example: a modular page using multiple cached sections

One of the most powerful features of page fragment caching is that the generated page can be composed from multiple cached sections. This is accomplished using Grails' g:include tag. For example, in this page the main body of the page is rendered by some controller action and the output of other controllers are included in the SiteMesh layout using the g:include tag:

grails-app/views/layouts/main.gsp

<html>
    <head>
        <title><g:layoutTitle default="Welcome to My Grails Application"/></title>
        <%-- render the page head from the controller - may be cached --%>
        <g:layoutHead/>
    </head>
    <body>
        <%-- render the page body from the controller - may be cached --%>
        <g:layoutBody/>

        <div class="sidebar">
            <%-- each of these controller actions can be cached separately as well --%>
            <g:include controller="latest" action="albums"/>
            <g:include controller="popular" action="albums"/>
        </div>
    </body>
</html>

LatestController.groovy

@Cacheable("latestAlbums")
def albums = {
    def albums = Album.list(sort: "dateCreated", order: "desc", max: 10)
    [albumInstanceList: albums]
}

LatestController.groovy

@Cacheable("popularAlbums")
def albums = {
    def albums = Album.listOrderByAverageRating(max: 10)
    return [albumInstanceList: albums]
}

If all the caches are hit the final rendered page will be composed of 3 separate cached sections. What is more, each individual section can be flushed without affecting the others so with some thought about how to compose your page and apply your caches you can optimise cache usage without delivering stale data to the user.

Example: annotations applied at class level

The @Cacheable and @CacheFlush annotations can be applied at class level. This is more likely useful with @Cacheable but it is certainly possible to apply @CacheFlush at class level so that any action on that controller will flush a set of caches. Any annotation on an individual action will be applied in preference to an annotation at class level, so a class level annotation behaves like a default. An annotation at class level will work with dynamic scaffolded actions so you don't have to generate a concrete action in order to benefit from caching behaviour.

@Cacheable("albumControllerCache")
class AlbumController {

    static scaffold = true // all dynamically scaffolded actions will be cached

    @Cacheable("albumListCache")
    def list = {
        // ...
    }

    @CacheFlush(/album\w+Cache/)
    def save = {
        // ...
    }

    def show = {
        // ...
    }
}

In this example:

  • The show action will use the default class level @Cacheable annotation and its page fragment will be cached in the albumControllerCache cache.
  • The list action will not use the default as it specifies its own @Cacheable annotation and its content will be cached separately.
  • The save action uses a @CacheFlush and will therefore not be cached at all.
  • Dynamically scaffolded actions (e.g. edit, update, etc.) will use the class level annotation and their results will be cached in the albumControllerCache cache.

Customising key generation for page fragment caching

By default page fragment cache entries are keyed on controller name, action name and any request parameters (which can be from a query string or those added by Grails URL mappings, e.g. the id parameter on a standard show or edit action). If you need to use some kind of special key generation you can implement the interface grails.plugin.springcache.web.key.KeyGenerator (or extend grails.plugin.springcache.web.key.AbstractKeyGenerator or one of the existing implementations that the plugin provides) then simply override the Spring bean property on the filter in Config.groovy like this:

beans {
    springcacheFilter.keyGenerator = new MyKeyGenerator()
}

Page fragment caching and content negotiation

By default the key generator used by the page fragment caching filter does not take content negotiation into account. However, if you are caching controller actions that use Grails' withFormat dynamic method to render different content types you will want to cache results separately according to the output format. The plugin provides a key generator implementation that supports this, you just need to override the filter's key generator in Config.groovy like this:

import grails.plugin.springcache.web.key.MimeTypeAwareKeyGenerator

beans {
    springcacheFilter.keyGenerator = new MimeTypeAwareKeyGenerator()
}

Full page caching

The plugin only provides page fragment caching rather than full page caching. Full page caching is very simple to apply using the EhCache-Web library that the Springcache plugin uses. See my blog post here for details.

Programmatic caching and flushing

Both the servlet filter used for content caching and the AOP aspects used for service method caching use a Grails service to handle caching and flushing. Your application can access this service directly if you need to do any programmatic caching or flushing. The service is called springcacheService and can be auto-injected into your Grails artefacts just like any other Spring bean. The service provides the following methods:

  • doWithCache(String, Serializable, Closure) : Parameters are cache name, cache key and closure invoked when there is no cached value. The method returns either the cached value or the return value of the closure. If the closure is invoked the return value is cached.
  • doWithBlockingCache(String, Serializable, Closure) : A variant of doWithCache that ensures a BlockingCache is used and handles exceptions so that the cache's lock is relinquished correctly.
  • flush(patterns) : Flushes all caches matching the specified names/patterns. The parameter can be a String, a regex pattern or a Collection or array of them.
  • flushAll() : Flushes all caches.
  • clearStatistics() : Clears statistics for all caches.
  • getOrCreateCache(name) : Gets the named cache or creates it from defaults if it does not exist.
  • getOrCreateBlockingCache(name) : As getOrCreateCache but will decorate the cache with a BlockingCache if it is non-blocking.

The plugin encourages you to use declarative caching and flushing to maintain a good separation of concerns. Over-using the springcacheService is likely to render your code harder to test and maintain. That said programmatic caching may be necessary in some places but there are some caveats:

  • If you try to perform caching or flushing in interceptors on controller actions bear in mind those actions, and therefore any interceptors, will not be invoked at all if they are annotated with @Cacheable and the cache is hit.
  • Controller actions don't return HTML output so you can't do fine grained content caching by using springcacheService.doWithCache in a controller action.

Configuring caches

Caches referenced by the annotations can be configured, either in an ehcache.xml (usually kept in the grails-app/conf directory) file, using EhCacheFactoryBean definitions in grails-app/conf/spring/resources.groovy or via Config.groovy. If you do not configure caches individually they will be created on demand using defaults.

Configuring caches with resources.groovy

You can configure caches in grails-app/conf/spring/resources.groovy using Spring's EhCacheFactoryBean. For example:

grails-app/conf/spring/resources.groovy

pirateCache(EhCacheFactoryBean) { bean ->
    cacheManager = ref("springcacheCacheManager")
    cacheName = "pirateCache"
    // these are just examples of properties you could set
    eternal = false
    diskPersistent = false
    memoryStoreEvictionPolicy = "LRU"
}

You can inherit default cache properties from those defined in Config.groovy by setting the factory bean's parent to 'springcacheDefaultCache'. For example:

pirateCache(EhCacheFactoryBean) { bean ->
    bean.parent = ref("springcacheDefaultCache")
    cacheName = "pirateCache"
    // set any properties unique to this cache
    memoryStoreEvictionPolicy = "LRU"
}

Configuring caches with Config.groovy

The Springcache plugin enables you to define caches in Config.groovy for convenience. For example:

grails-app/conf/Config.groovy

springcache {
    defaults {
        // set default cache properties that will apply to all caches that do not override them
        eternal = false
        diskPersistent = false
    }
    caches {
        pirateCache {
            // set any properties unique to this cache
            memoryStoreEvictionPolicy = "LRU"
        }
    }
}

Under the hood this is simply setting up EhCacheFactoryBean instances in the Spring context, so it is up to you whether you prefer to use resources.groovy or Config.groovy there is not much difference.

The properties shown are just examples, see the EhCacheFactoryBean documentation for full details of all the properties you can set.

Tips

Flushing content caches with service methods and vice-versa

There is nothing special about the different types of cache so it's perfectly fine to flush a content cache with a @CacheFlush annotation on a service method or a service method cache with a @CacheFlush annotation on a controller action.

Tearing down caches in tests

In integration test and some types of functional test (e.g. Selenium RC tests when not running in remote mode) your tests can have Spring beans automatically injected. You can use this facility to tear down caches between tests. For example:

def springcacheService // auto-injected service bean from plugin

void tearDown() {
    super.tearDown()
    springcacheService.flushAll()
    // only need to do this if your tests are making assertions about hit/miss counts, etc.
    springcacheService.clearStatistics()
}

Disabling

Rather than tearing down caches between tests you may prefer to disable the plugin altogether. This is done by setting the config key springcache.disabled = true which can be done on a per-environment basis. For example:

springcache {
    // cache definitions, etc
}

environments {
    test {
        springcache.disabled = true
    }
}

Logging Output

To see logging from the plugin set the logging level on grails.plugin.springcache in your Config.groovy file.

FAQ

My cache config doesn't seem to be working.

Ensure all your config for the Springcache plugin is nested inside a single springcache block in Config.groovy otherwise only the last block will take effect.

Can I programatically disable caching in specific circumstances such as when a user is logged in?

Not right now, although I hope to add this to a future release of the plugin. I'd encourage you to think about structuring your pages using SiteMesh layouts and the g:include tag such that it is possible to separate the dynamic and cacheable sections.

Why isn't there a taglib so I can just wrap parts of my page that need caching?

It's something I may add but from a purist point of view I'm not very keen on the idea. Caching is a separate concern from view rendering and the two really shouldn't be mixed up. So far the plugin has deliberately taken a declarative approach to caching which encourages you to maintain a good separation of concerns.

Can I use @Cacheable on taglib Closures?

Not yet. This is definitely on the roadmap for a future version of the plugin.

Release Notes

1.2

  • Adds page fragment caching via annotations on controllers.
  • Simplifies config by getting rid of caching and flushing models and having annotations refer to cache names directly.
  • Adds configurable cache defaults that apply to configured caches and auto-created caches
  • Removes pluggable cache implementation in favour of using EhCache.

1.1.3

  • Fixes bug where an expired ehcache element whose key is still in the cache can cause the plugin to think the key still maps to a valid value.
  • Allows configuration of ehcache caches directly in Config.groovy

1.1.2

  • Automatically create ehcache caches if they are not explicitly configured in ehcache.xml

1.1.1

  • Fixes bug where plugin crashes if disabled

1.1

  • Complete rewrite to support Grails 1.2 and Spring 3.0.
  • Requires Grails 1.2+

1.0.1

  • Fixes bug where plugin causes crash if disabled when debug logging is switched on.
  • Fixes compatibility with Java 1.5.

1.0

  • Configure alternate caching providers via Config.groovy rather than having to override bean definitions in resources.groovy
  • Removed dependency on joda-time which was only there for testing
  • Better synchronization for getting caches from the mapcache CacheManager

0.2

  • Configure caching and flushing models via Config.groovy
  • Flag to disable plugin entirely for testing environments

0.1

  • Initial release

Contact

The plugin code is hosted on GitHub. Please feel free to fork the plugin and contribute patches.

Please raise defects or enhancements against the Grails Springcache plugin component on the Codehaus JIRA.

Questions, comments? rob@energizedwork.com.

Something went wrong with that request. Please try again.