Skip to content

Commit

Permalink
updated docs
Browse files Browse the repository at this point in the history
  • Loading branch information
robfletcher committed May 9, 2010
1 parent 0352a68 commit 9c13f64
Show file tree
Hide file tree
Showing 21 changed files with 472 additions and 867 deletions.
430 changes: 0 additions & 430 deletions README.markdown

This file was deleted.

15 changes: 15 additions & 0 deletions README.md
@@ -0,0 +1,15 @@
# 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][2] and [EhCache-Web][3] libraries.

Full documentation can be found [here][1].

[1]:http://robfletcher.github.com/grails-springcache
[2]:http://ehcache.org/
[3]:http://ehcache.org/documentation/web_caching.html
437 changes: 0 additions & 437 deletions README.wiki

This file was deleted.

3 changes: 3 additions & 0 deletions TODO.txt
@@ -1,7 +1,10 @@
BUGS
- check caching of service methods with defaulted params
- HEAD and GET requests seem to generate different keys

NEXT RELEASE
- make content negotiation awareness just a config param
- apply annotations to services at class level
- mime type key generator needs to only work if action uses withFormat (somehow)
- only cache GET requests?
- option to disable content caching filter
Expand Down
15 changes: 15 additions & 0 deletions src/docs/guide/1. Introduction.gdoc
@@ -0,0 +1,15 @@
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":http://ehcache.org/ and "EhCache-Web":http://ehcache.org/documentation/web_caching.html libraries.

h3. Contact

The plugin code is hosted on [GitHub|http://github.com/robfletcher/grails-springcache]. 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|http://jira.codehaus.org/browse/GRAILSPLUGINS/component/14010].

Questions, comments? [rob@energizedwork.com|mailto:rob@energizedwork.com] or better still contact me via the [Grails User mailing list|http://grails.org/Mailing+lists].
44 changes: 44 additions & 0 deletions src/docs/guide/1.1. Release Notes.gdoc
@@ -0,0 +1,44 @@
h4. 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.

h4. 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@

h4. 1.1.2

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

h4. 1.1.1

* Fixes bug where plugin crashes if disabled

h4. 1.1

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

h4. 1.0.1

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

h4. 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

h4. 0.2

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

h4. 0.1

* Initial release
16 changes: 16 additions & 0 deletions src/docs/guide/2. The Cacheable and CacheFlush Annotations.gdoc
@@ -0,0 +1,16 @@
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.

h3. 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.

h3. 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:

{code}
@CacheFlush("myCache")
@CacheFlush(/\w+ControllerCache/)
@CacheFlush(["cacheA", "cacheB", "cacheC"])
@CacheFlush([/cache[A-Z]/, "myCache"])
{code}
53 changes: 53 additions & 0 deletions src/docs/guide/3. Caching Service Methods.gdoc
@@ -0,0 +1,53 @@
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.

{note}
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). In some cases using _Springcache_ does make sense, e.g. a service that aggregates the results of multiple queries.
{note}

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

{note}
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.
{note}

A simple example might be:

h4. PiracyService.groovy

{code}
@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
}
{code}

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.
9 changes: 9 additions & 0 deletions src/docs/guide/3.1. Service Method Cache Keys.gdoc
@@ -0,0 +1,9 @@
When a @\@Cacheable@ annotation is found on a service method the plugin generates a key using:

* The _target_ object, i.e. the service being called.
* The service method name.
* All method parameters.

Since Grails services are typically Spring singletons the target object is not usually an issue. There's no need to implements _equals_ or _hashCode_ on your service classes unless you are using a different Spring bean scope and need to differentiate between calls made to different instances of the service.

It is, however, *vital* to ensure that _equals_ and _hashCode_ is properly implemented on all the types used as parameters to cached methods. If this is not done it is very unlikely that the cache will ever be hit.
3 changes: 3 additions & 0 deletions src/docs/guide/4. Content Caching.gdoc
@@ -0,0 +1,3 @@
The @\@Cacheable@ and @\@CacheFlush@ 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.
@@ -0,0 +1,51 @@
h3. 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:

h4. AlbumController.groovy

{code}
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
}
}
{code}

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.
77 changes: 77 additions & 0 deletions src/docs/guide/4.2. Content Caching and SiteMesh.gdoc
@@ -0,0 +1,77 @@
h3. 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:

h4. grails-app/views/layouts/main.gsp

{code}
<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>
{code}

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.

h3. 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:

h4. grails-app/views/layouts/main.gsp

{code}
<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>
{code}

h4. LatestController.groovy

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

h4. LatestController.groovy

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

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.
30 changes: 30 additions & 0 deletions src/docs/guide/4.3. Using Annotations at Class Level.gdoc
@@ -0,0 +1,30 @@
The @\@Cacheable@ and @\@CacheFlush@ annotations can be applied to controllers 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.

{code}
@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 = {
// ...
}
}
{code}

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.
9 changes: 9 additions & 0 deletions src/docs/guide/4.4. Content Cache Keys.gdoc
@@ -0,0 +1,9 @@
By default page fragment cache entries are keyed on controller name, action name and any request parameters (which can be from a query string, _POST_ body 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:

{code}
beans {
springcacheFilter {
keyGenerator = new MyKeyGenerator()
}
}
{code}
11 changes: 11 additions & 0 deletions src/docs/guide/4.5. Content Negotiation.gdoc
@@ -0,0 +1,11 @@
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@":http://grails.org/doc/latest/ref/Controllers/withFormat.html 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:

{code}
import grails.plugin.springcache.web.key.MimeTypeAwareKeyGenerator

beans {
springcacheFilter {
keyGenerator = new MimeTypeAwareKeyGenerator()
}
}
{code}
1 change: 1 addition & 0 deletions src/docs/guide/4.6. Full Page Caching.gdoc
@@ -0,0 +1 @@
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":http://adhockery.blogspot.com/2010/02/full-page-caching-in-grails-with.html for details.
14 changes: 14 additions & 0 deletions src/docs/guide/5. Programmatic Caching and Flushing.gdoc
@@ -0,0 +1,14 @@
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":http://ehcache.org/apidocs/net/sf/ehcache/constructs/blocking/BlockingCache.html 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.

0 comments on commit 9c13f64

Please sign in to comment.