The Grails Infinispan Plugin plugin provides the developer with the ability to quickly configure Infinispan and register cache managers and caches as Spring beans. Additionally this plugin contains a tag library which developers can use to interact with caches directly on Groovy Server Pages.

Note that the 0.4.3 release is the last release that will focus on the Infinispan 4.x series – subsequent releases will all target Infinispan 5.x releases. Note also that Infinispan 5.x requires a newer version of Hibernate core than Grails 1.3.x series ships with, so your options include working with the Grails Infinispan Plugin version 0.4.3 until Grails 1.4 is available or manually patch Grails and see what happens.

Commercial Support & Update Notifications (via Twitter)

If you have a question or would you like a new feature we would be happy to hear from you so send us an email and let us know!

giplugin is the official Grails Infinispan Plugin Twitter feed which is used to notify users of new releases and other information — for updates, follow us using the button below.


Contact Red Hat for commercial Infinispan support. The article Red Hat Introduces JBoss Enterprise Data Grid on the Wall Street Journal web site has additional information about this service.

Plugin Documentation, Test Reports, and Source Code

Plugin Test / Demo Applications

For Grails pre 2.0.0 and Infinispan pre 5.0.0 click here. Instructions for how to run this application and interesting parts of the application worth reviewing are included in the *readme.txt* file available in the application’s root directory.

For Grails 2.0.0 and Infinispan 5.0.0 click here. Instructions for how to run this application and interesting parts of the application worth reviewing are included in the readme.txt file available in the application’s root directory.

Installing the Grails Infinispan Plugin

Usage:

C:/…/workspace/InfinispanTestApp>grails install-plugin infinispan

Note that Grails does not allow plugins to modify some files – of particular importance here is the DataSource.groovy file which, by default, configures EhCache as the second level cache.

If you install this plugin you will need to manually modify the settings in the DataSource.groovy file as follows:

Remove the following:

hibernate {
   cache.use_second_level_cache = true
   cache.use_query_cache = true
   cache.provider_class = 'net.sf.ehcache.hibernate.EhCacheProvider'
}

And replace with:

hibernate {
   cache.use_second_level_cache=true
   cache.use_query_cache=true
   cache.region.factory_class='org.hibernate.cache.infinispan.InfinispanRegionFactory'
}

For more information, refer to this article.

Configuring the Grails Infinispan Plugin

In the example below we see a development environment and a production environment. The development environment reads the configuration from a file whereas the production environment uses the globalConfiguration and configuration variables.

def globalConfiguration = new GlobalConfiguration ()
def configuration = new Configuration ()

globalConfiguration.asyncTransportExecutorFactoryClass =
    "org.infinispan.executors.DefaultExecutorFactory"

globalConfiguration.asyncTransportExecutorProperties =
    [maxThreads:1] as Properties

configuration.indexingEnabled = true
configuration.indexLocalOnly  = true

def queryHelperProperties = ["hibernate.search.default.directory_provider" :
    "org.hibernate.search.store.RAMDirectoryProvider"] as Properties

environments {
    development {
        infinispan = {
            register {
                cacheManager {
                    named "matrixCacheManager"
                    configured {
                        externally {
                            using "grails-app/conf/infinispan/workingQuery.xml"
                        }
                    }
                    caches "agentCache", "illinoisCitiesCache"
                }
                queryHelper {
                    named "agentQueryHelper"
                    referencing "agentCache"
                    properties queryHelperProperties
                    classes infinispantestapp.Agent, infinispantestapp.SuperHero
                }
            }
        }
    }
    production {
        infinispan = {
            register {
                cacheManager {
                    named "matrixCacheManager"
                    configured {
                        declaratively {
                            using globalConfiguration
                            using configuration
                        }
                    }
                    caches "agentCache", "illinoisCitiesCache"
                }
                queryHelper {
                    named "agentQueryHelper"
                    referencing "agentCache"
                    properties queryHelperProperties
                    classes infinispantestapp.Agent, infinispantestapp.SuperHero
                }
            }
        }
    }
}

The following example demonstrates how the cacheManager can be configured from an xml file. Note that the path to the file in the development environment is slightly different than the path in the production environment. For example, in development the path would look like file “grails-app/conf/infinispan/minimal.xml”, whereas in production it would be file “infinispan/minimal.xml”.

cacheManager {
    named "fooCacheManager"
    configured {
        externally {
            using "grails-app/conf/infinispan/minimal.xml"
        }
    }
    caches "foo"
}

There is a precondition required by Infinispan that demands that an instance of QueryHelper be set prior to executing a query. This comes as part of the configuration DSL and ensures that the QueryHelper(s) are created before any items are put into the cache. This prevents the developer from misconfiguring the QueryHelper, in which case zero results will be returned from the cache whenever it’s queried.

Refer to this page and this page for more information. This requirement should be removed from the Infinispan version 5.0 release.

Warning

Note that the Infinispan cache creation happens through instances of the CacheManager by invoking the getCache method. This means that it is possible to have two caches in an application with the exact same name. This plugin registers the actual cache instance with Spring as a bean with the id given in the cache manager _caches_ set (as illustrated above). If you create more than one cache manager and replicate cache names then the second cache bean will not be registered and an exception will be thrown on application startup. The solution is to always choose unique cache names, OR you will be forced to use the CacheManager to get instances of one of the caches that has a duplicated name.

Using the Cache

In this example we create a controller that loads Agent Smith into the cache 1000 times:

package infinispantestapp

class LoadAgentCacheController {

    def agentCache
	
    def index = {
        for (int ctr in 1..1000) {
            agentCache.put ("person id $ctr", new Agent (name:"Agent Smith", weapon:"gun"))
        }
    }
}

Note that the cache manager is also registered as a bean and can be used as well, for example:

class AnotherController {

    def matrixCacheManager

    def index = {
        // ...
    }
}

Querying the Cache and Iterating over the Results

As of the 0.3.2 plugin release two new methods appear on the Cache via the CacheCapabilities mixin which allows the developer to query the cache directly, for example:

class QueryAgentsController {

    def agentCache
    
    def index = {
        def results = agentCache.query (Agent.class, "weapon", "gun")

        def hits = results.getResultSize()

        log.info ("hits: $hits")

        // Iterate over the cache query using an eager iterator and passing each item to the closure.
        // Note that the fetch size is set to 1 by default (this is the same value that Infinispan 
        // defaults to).
        results.each {
            log.info ("it: $it")
        }

        // Iterate over the cache query using a lazy iterator and passing each item to the closure.
        // Note that the fetch size is also set to 1 by default.
        results.each (true, {
            log.info ("it: $it")
        })

        // Iterate over the cache query using an eager iterator and passing each item to the closure.
        // Note that the fetch size in this example is set to 25.
        results.each (25, {
            log.info ("it: $it")
        })

        // Iterate over the cache query using a lazy iterator and passing each item to the closure.
        // Note that the fetch size in this example is set to 25.
        results.each (true, 25, {
            log.info ("it: $it")
        })
    }
}

In order for querying to work, indexing will need to be enabled in the Infinispan XML configuration file; a simple example appears below.

<?xml version="1.0" encoding="UTF-8"?>
<infinispan 
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="urn:infinispan:config:4.0 http://www.infinispan.org/schemas/infinispan-config-4.0.xsd"
      xmlns="urn:infinispan:config:4.0">
    <default>
        <indexing enabled="true" indexLocalOnly="true" />
    </default>
</infinispan>

For more information refer to the QueryCapabilities mixin as well as the QueryCapabilitiesTest unit test.

Using Closures as Listeners

Available in the 0.3.4 release of this plugin are methods that allow the developer to add closures as listeners to the EmbeddedCacheManager and the Cache.

See here for a list of listeners that are available.

Events dispatched to these listeners are delivered synchronously and asynchronously; refer to the section entitled “Threads and notification dispatching” in this article for more detail about the impact this can have.

Warning
Note that the listeners available to the EmbeddedCacheManager differ from those available to the Cache.

The “on” methods all follow the same format:

  • on (Closure closure) // Defaults to synchronous delivery.
  • on (boolean synchronous, Closure closure)

There are also methods available for removing listeners — these follow the following format:

  • removeListener (Closure closure) // Removes the closure that was registered to receive events synchronously.

  • removeListener (boolean synchronous, Closure closure)

Events that the EmbeddedCacheManager accepts:

  • CacheStartedEvents – Use the onCacheStarted or removeCacheStartedListener methods.
  • CacheStoppedEvents – Use the onCacheStopped or removeCacheStoppedListener methods.
  • ViewChangedEvents – Use the onViewChanged or removeViewChangedListener methods.

Events that the Cache accepts:

  • CacheEntryModifiedEvents – Use the onCacheEntryModified or removeCacheEntryModifiedListener methods.
  • CacheEntryCreatedEvents – Use the onCacheEntryCreated or removeCacheEntryCreatedListener methods.
  • CacheEntryRemovedEvents – Use the onCacheEntryRemoved or removeCacheEntryRemovedListener methods.
  • CacheEntryVisitedEvents – Use the onCacheEntryVisited or removeCacheEntryVisitedListener methods.
  • CacheEntryLoadedEvents – Use the onCacheEntryLoaded or removeCacheEntryLoadedListener methods.
  • CacheEntryEvictedEvents – Use the onCacheEntryEvicted or removeCacheEntryEvictedListener methods.
  • CacheEntryActivatedEvents – Use the onCacheEntryActivated or removeCacheEntryActivatedListener methods.
  • CacheEntryPassivatedEvents – Use the onCacheEntryPassivated or removeCacheEntryPassivatedListener methods.
  • TransactionRegisteredEvents – Use the onTransactionRegistered or removeTransactionRegisteredListener methods.
  • TransactionCompletedEvents – Use the onTransactionCompleted or removeTransactionCompletedListener methods.
  • CacheEntryInvalidatedEvents – Use the onCacheEntryInvalidated or removeCacheEntryInvalidatedListener methods.

A closure that has been registered to receive multiple events can be removed by invoking the *removeAllListeners* method. Note that this applies to both the EmbeddedCacheManager as well as the Cache.

For example:

def loggingClosure = {
    log.info ("Cache started: $it")
}

/**
 * The default is synchronous delivery, see below for an example of how to receive events asynchronously.
 */
matrixCacheManager.onCacheStarted (loggingClosure)
matrixCacheManager.onCacheStopped (loggingClosure)

/**
 * Note that we can remove the listener later by passing the closure to a [new] removeAllListeners method.
 */
matrixCacheManager.removeAllListeners (loggingClosure)

/**
 * If the intention is to add the listener permanently, then the closure can be added as follows:
 */
matrixCacheManager.onCacheStopped {
    log.info ("Cache stopped: $it")
}

/**
 * The default is synchronous delivery, if we want delivery to be asynchronous we need to set 
 * the synchronous flag to false as follows:
 */
matrixCacheManager.onCacheStopped (false, { /* Process asynchronous events. */})

Using Transactions

Note that the transaction manager is passed into the closure as the transactionManager parameter.

testCache.transactionally {
    testCache.remove (DEFAULT_KEY)

    if (transactionManager.status == ...) {
        // Do something...
    }
}

See also javax.transaction.TransactionManager.

Using the Cache Tag Library

Here is a simple example of using the Cache tag library:

<cache:size name="agentCache" var="size"/>

There are ${size} agents in the cache.

Clearing the cache of all agents...<cache:clear name="agentCache"/>

At this time tags are provided for the following Cache methods:

  • clear
  • containsKey
  • get
  • isEmpty
  • put
  • remove
  • size
  • replace
  • getStatus
  • getVersion
  • putAll
  • evict
  • getName
  • putForExternalRead
  • getConfiguration
  • startBatch
  • endBatch
  • query
  • compact

Warning

The following methods are not supported:

  • containsValue
  • keySet
  • entrySet
  • values

Logging

Add the following to the application’s grails-app\conf\Config.groovy log4j closure to enable logging on Infinispan and the Grails Infinispan Plugin:

warn       'org.infinispan.profiling',
           'org.infinispan.jmx',
           'org.infinispan.factories'
    
info       'org.infinispan'
    
debug      'com.coherentlogic'

Dependencies

The Grails Infinispan plugin contains the following dependencies:

Infinispan-4.2.1.FINAL (Ursus)

  • infinispan-core.jar
  • jboss-common-core-2.2.14.GA.jar
  • jgroups-2.12.0.CR5.jar
  • marshalling-api-1.2.3.GA.jar
  • river-1.2.3.GA.jar

Infinispan Query Module

  • hibernate-commons-annotations-3.2.0.Final.jar
  • hibernate-search-3.2.1.Final.jar
  • infinispan-query.jar
  • lucene-analyzers-2.9.3.jar
  • lucene-core-2.9.3.jar

Source: Infinispan

Hibernate Infinispan version 3.5.1-Final

  • hibernate-infinispan-3.5.1-Final.jar

Source: Hibernate Infinispan

Download PDF