JBoss.orgCommunity Documentation

JBoss Cache User Guide

A clustered, transactional cache

Bela Ban

Brian Stansberry

Galder Zamarreño

Daniel Huang

Release 2.2.0 Poblano

July 2008


Preface
I. Introduction to JBoss Cache
1. Overview
1.1. What is JBoss Cache?
1.1.1. And what is Pojo Cache?
1.2. Summary of Features
1.3. Requirements
1.4. License
2. User API
2.1. API Classes
2.2. Instantiating and Starting the Cache
2.3. Caching and Retrieving Data
2.4. The Fqn Class
2.5. Stopping and Destroying the Cache
2.6. Cache Modes
2.7. Adding a Cache Listener - registering for cache events
2.8. Using Cache Loaders
2.9. Using Eviction Policies
3. Configuration
3.1. Configuration Overview
3.2. Creating a Configuration
3.2.1. Parsing an XML-based Configuration File
3.2.2. Programmatic Configuration
3.2.3. Using an IOC Framework
3.3. Composition of a Configuration Object
3.4. Dynamic Reconfiguration
3.5. Overriding the Configuration Via the Option API
4. Deploying JBoss Cache
4.1. Standalone Use / Programatic Deployment
4.2. JMX-Based Deployment in JBoss AS (JBoss AS 5.x and 4.x)
4.3. Via JBoss Microcontainer (JBoss AS 5.x)
4.4. Binding to JNDI in JBoss AS
4.5. Runtime Management Information
4.5.1. JBoss Cache MBeans
4.5.2. Registering the CacheJmxWrapper with the MBeanServer
4.5.3. JBoss Cache Statistics
4.5.4. Receiving JMX Notifications
4.5.5. Accessing Cache MBeans in a Standalone Environment
5. Version Compatibility and Interoperability
5.1. Compatibility Matrix
II. JBoss Cache Architecture
6. Architecture
6.1. Data Structures Within The Cache
6.2. SPI Interfaces
6.3. Method Invocations On Nodes
6.3.1. Interceptors
6.3.2. MethodCalls
6.3.3. InvocationContexts
6.4. Managers For Subsystems
6.4.1. RpcManager
6.4.2. BuddyManager
6.4.3. CacheLoaderManager
6.5. Marshalling And Wire Formats
6.5.1. The Marshaller Interface
6.5.2. VersionAwareMarshaller
6.5.3. CacheMarshaller200
6.6. Class Loading and Regions
7. Clustering
7.1. Cache Replication Modes
7.1.1. Local Mode
7.1.2. Replicated Caches
7.2. Invalidation
7.3. State Transfer
7.3.1. State Transfer Types
7.3.2. Byte array and streaming based state transfer
7.3.3. Full and partial state transfer
7.3.4. Transient ("in-memory") and persistent state transfer
7.3.5. Configuring State Transfer
8. Cache Loaders
8.1. The CacheLoader Interface and Lifecycle
8.2. Configuration
8.2.1. Singleton Store Configuration
8.3. Shipped Implementations
8.3.1. File system based cache loaders
8.3.2. Cache loaders that delegate to other caches
8.3.3. JDBCCacheLoader
8.3.4. S3CacheLoader
8.3.5. TcpDelegatingCacheLoader
8.3.6. Transforming Cache Loaders
8.4. Cache Passivation
8.4.1. Cache Loader Behavior with Passivation Disabled vs. Enabled
8.5. Strategies
8.5.1. Local Cache With Store
8.5.2. Replicated Caches With All Caches Sharing The Same Store
8.5.3. Replicated Caches With Only One Cache Having A Store
8.5.4. Replicated Caches With Each Cache Having Its Own Store
8.5.5. Hierarchical Caches
8.5.6. Multiple Cache Loaders
9. Eviction Policies
9.1. Configuring Eviction Policies
9.1.1. Basic Configuration
9.1.2. Eviction Regions
9.1.3. Resident Nodes
9.1.4. Programmatic Configuration
9.2. Shipped Eviction Policies
9.2.1. LRUPolicy - Least Recently Used
9.2.2. FIFOPolicy - First In, First Out
9.2.3. MRUPolicy - Most Recently Used
9.2.4. LFUPolicy - Least Frequently Used
9.2.5. ExpirationPolicy
9.2.6. ElementSizePolicy - Eviction based on number of key/value pairs in a node
9.3. Writing Your Own Eviction Policies
9.3.1. Eviction Policy Plugin Design
9.3.2. Interfaces to implement
10. Transactions and Concurrency
10.1. Concurrent Access
10.1.1. Locks
10.1.2. Pessimistic locking
10.1.3. Optimistic Locking
10.2. Transactional Support
III. JBoss Cache Configuration References
11. Configuration References
11.1. Sample XML Configuration File
11.2. Reference table of XML attributes
12. JMX References
12.1. JBoss Cache Statistics
12.2. JMX MBean Notifications

This is the official JBoss Cache user guide. Along with its accompanying documents (an FAQ, a tutorial and a whole set of documents on PojoCache), this is freely available on the JBoss Cache documentation site.

When used, JBoss Cache refers to JBoss Cache Core, a tree-structured, clustered, transactional cache. Pojo Cache, also a part of the JBoss Cache distribution, is documented separately. (Pojo Cache is a cache that deals with Plain Old Java Objects, complete with object relationships, with the ability to cluster such pojos while maintaining their relationships. Please see the Pojo Cache documentation for more information about this.)

This book is targeted at both developers wishing to use JBoss Cache as a clustering and caching library in their codebase, as well as people who wish to "OEM" JBoss Cache by building on and extending its features. As such, this book is split into two major sections - one detailing the "User" API and the other going much deeper into specialist topics and the JBoss Cache architecture.

In general, a good knowledge of the Java programming language along with a strong appreciation and understanding of transactions and concurrent threads is necessary. No prior knowledge of JBoss Application Server is expected or required.

For further discussion, use the user forum linked on the JBoss Cache website. We also provide a mechanism for tracking bug reports and feature requests on the JBoss Cache JIRA issue tracker. If you are interested in the development of JBoss Cache or in translating this documentation into other languages, we'd love to hear from you. Please post a message on the user forum or contact us by using the JBoss Cache developer mailing list.

This book is specifically targeted at the JBoss Cache release of the same version number. It may not apply to older or newer releases of JBoss Cache. It is important that you use the documentation appropriate to the version of JBoss Cache you intend to use.

This section covers what developers would need to quickly start using JBoss Cache in their projects. It covers an overview of the concepts and API, configuration and deployment information.

JBoss Cache offers a simple and straightforward API, where data (simple Java objects) can be placed in the cache and, based on configuration options selected, this data may be one or all of:

In addition, JBoss Cache offers a rich set of enterprise-class features:

A cache is organised as a tree, with a single root. Each node in the tree essentially contains a Map, which acts as a store for key/value pairs. The only requirement placed on objects that are cached is that they implement java.io.Serializable . Note that this requirement does not exist for Pojo Cache.

JBoss Cache can be either local or replicated. Local trees exist only inside the JVM in which they are created, whereas replicated trees propagate any changes to some or all other trees in the same cluster. A cluster may span different hosts on a network or just different JVMs on a single host.

When a change is made to an object in the cache and that change is done in the context of a transaction, the replication of changes is deferred until the transaction commits successfully. All modifications are kept in a list associated with the transaction for the caller. When the transaction commits, we replicate the changes. Otherwise, (on a rollback) we simply undo the changes locally resulting in zero network traffic and overhead. For example, if a caller makes 100 modifications and then rolls back the transaction, we will not replicate anything, resulting in no network traffic.

If a caller has no transaction associated with it (and isolation level is not NONE - more about this later), we will replicate right after each modification, e.g. in the above case we would send 100 messages, plus an additional message for the rollback. In this sense, running without a transaction can be thought of as analogous as running with auto-commit switched on in JDBC terminology, where each operation is committed automatically.

JBoss Cache works out of the box with most popular transaction managers, and even provides an API where custom transaction manager lookups can be written.

The cache is also completely thread-safe. It uses a pessimistic locking scheme for nodes in the tree by default, with an optimistic locking scheme as a configurable option. With pessimistic locking, the degree of concurrency can be tuned using a number of isolation levels, corresponding to database-style transaction isolation levels, i.e., SERIALIZABLE, REPEATABLE_READ, READ_COMMITTED, READ_UNCOMMITTED and NONE. Concurrency, locking and isolation levels will be discussed later.

JBoss Cache requires Java 5.0 (or newer).

However, there is a way to build JBoss Cache as a Java 1.4.x compatible binary using JBossRetro to retroweave the Java 5.0 binaries. However, Red Hat Inc. does not offer professional support around the retroweaved binary at this time and the Java 1.4.x compatible binary is not in the binary distribution. See this wiki page for details on building the retroweaved binary for yourself.

In addition to Java 5.0, at a minimum, JBoss Cache has dependencies on JGroups , and Apache's commons-logging . JBoss Cache ships with all dependent libraries necessary to run out of the box.

JBoss Cache is an open source product, using the business and OEM-friendly OSI-approved LGPL license. Commercial development support, production support and training for JBoss Cache is available through JBoss, a division of Red Hat Inc. JBoss Cache is a part of JBoss Professional Open Source JEMS (JBoss Enterprise Middleware Suite).

An instance of the Cache interface can only be created via a CacheFactory . (This is unlike JBoss Cache 1.x, where an instance of the old TreeCache class could be directly instantiated.)

CacheFactory provides a number of overloaded methods for creating a Cache , but they all do the same thing:

An example of the simplest mechanism for creating and starting a cache, using the default configuration values:



   CacheFactory factory = new DefaultCacheFactory();
   Cache cache = factory.createCache();
      

Here we tell the CacheFactory to find and parse a configuration file on the classpath:



   CacheFactory factory = new DefaultCacheFactory();
   Cache cache = factory.createCache("cache-configuration.xml");
      

Here we configure the cache from a file, but want to programatically change a configuration element. So, we tell the factory not to start the cache, and instead do it ourselves:



   CacheFactory factory = new DefaultCacheFactory();
   Cache cache = factory.createCache("cache-configuration.xml", false);
   Configuration config = cache.getConfiguration();
   config.setClusterName(this.getClusterName());
   // Have to create and start cache before using it
   cache.create();
   cache.start();
      

Next, let's use the Cache API to access a Node in the cache and then do some simple reads and writes to that node.



   // Let's get ahold of the root node.
   Node rootNode = cache.getRoot();
   // Remember, JBoss Cache stores data in a tree structure.
   // All nodes in the tree structure are identified by Fqn objects.
   Fqn peterGriffinFqn = Fqn.fromString("/griffin/peter");
   // Create a new Node
   Node peterGriffin = rootNode.addChild(peterGriffinFqn);
   // let's store some data in the node
   peterGriffin.put("isCartoonCharacter", Boolean.TRUE);
   peterGriffin.put("favouriteDrink", new Beer());
   // some tests (just assume this code is in a JUnit test case)
   assertTrue(peterGriffin.get("isCartoonCharacter"));
   assertEquals(peterGriffinFqn, peterGriffin.getFqn());
   assertTrue(rootNode.hasChild(peterGriffinFqn));
   Set keys = new HashSet();
   keys.add("isCartoonCharacter");
   keys.add("favouriteDrink");
   assertEquals(keys, peterGriffin.getKeys());
   // let's remove some data from the node
   peterGriffin.remove("favouriteDrink");
   assertNull(peterGriffin.get("favouriteDrink");
   // let's remove the node altogether
   rootNode.removeChild(peterGriffinFqn);
   assertFalse(rootNode.hasChild(peterGriffinFqn));
      

The Cache interface also exposes put/get/remove operations that take an Fqn as an argument:



   Fqn peterGriffinFqn = Fqn.fromString("/griffin/peter");
   cache.put(peterGriffinFqn, "isCartoonCharacter", Boolean.TRUE);
   cache.put(peterGriffinFqn, "favouriteDrink", new Beer());
   assertTrue(peterGriffin.get(peterGriffinFqn, "isCartoonCharacter"));
   assertTrue(cache.getRootNode().hasChild(peterGriffinFqn));
   cache.remove(peterGriffinFqn, "favouriteDrink");
   assertNull(cache.get(peterGriffinFqn, "favouriteDrink");
   cache.removeNode(peterGriffinFqn);
   assertFalse(cache.getRootNode().hasChild(peterGriffinFqn));
      

The previous section used the Fqn class in its examples; now let's learn a bit more about that class.

A Fully Qualified Name (Fqn) encapsulates a list of names which represent a path to a particular location in the cache's tree structure. The elements in the list are typically String s but can be any Object or a mix of different types.

This path can be absolute (i.e., relative to the root node), or relative to any node in the cache. Reading the documentation on each API call that makes use of Fqn will tell you whether the API expects a relative or absolute Fqn .

The Fqn class provides are variety of constructors; see the javadoc for all the possibilities. The following illustrates the most commonly used approaches to creating an Fqn:



   // Create an Fqn pointing to node 'Joe' under parent node 'Smith'
   // under the 'people' section of the tree
        
   // Parse it from a String
   Fqn<String> abc = Fqn.fromString("/people/Smith/Joe/");
        
   // Build it directly. Marginally more efficient to construct than parsing
   String[] strings = new String[] { "people", "Smith", "Joe" };
   Fqn<String> abc = new Fqn<String>(strings);
        
   // Here we want to use types other than String
   Object[] objs = new Object[]{ "accounts", "NY", new Integer(12345) };
   Fqn<Object> acctFqn = new Fqn<Object>(objs);
     

Note that

Fqn<String> f = new Fqn<String>("/a/b/c");

is not the same as

Fqn<String> f = Fqn.fromString("/a/b/c");

The former will result in an Fqn with a single element, called "/a/b/c" which hangs directly under the cache root. The latter will result in a 3 element Fqn, where "c" idicates a child of "b", which is a child of "a", and "a" hangs off the cache root. Another way to look at it is that the "/" separarator is only parsed when it forms part of a String passed in to Fqn.fromString() and not otherwise.

The JBoss Cache API in the 1.x releases included many overloaded convenience methods that took a string in the /a/b/c format in place of an Fqn . In the interests of API simplicity, no such convenience methods are available in the JBC 2.x API.

Although technically not part of the API, the mode in which the cache is configured to operate affects the cluster-wide behavior of any put or remove operation, so we'll briefly mention the various modes here.

JBoss Cache modes are denoted by the org.jboss.cache.config.Configuration.CacheMode enumeration. They consist of:

See the chapter on Clustering for more details on how the cache's mode affects behavior. See the chapter on Configuration for info on how to configure things like the cache's mode.

JBoss Cache provides a convenient mechanism for registering notifications on cache events.



   Object myListener = new MyCacheListener();
   cache.addCacheListener(myListener);
      

Similar methods exist for removing or querying registered listeners. See the javadocs on the Cache interface for more details.

Basically any public class can be used as a listener, provided it is annotated with the @CacheListener annotation. In addition, the class needs to have one or more methods annotated with one of the method-level annotations (in the org.jboss.cache.notifications.annotation package). Methods annotated as such need to be public, have a void return type, and accept a single parameter of type org.jboss.cache.notifications.event.Event or one of its subtypes.

Refer to the javadocs on the annotations as well as the Event subtypes for details of what is passed in to your method, and when.

Example:



   @CacheListener
   public class MyListener
   {
      @CacheStarted
      @CacheStopped
      public void cacheStartStopEvent(Event e)
      {
         switch (e.getType())
         {
            case Event.Type.CACHE_STARTED:
               System.out.println("Cache has started");
               break;
            case Event.Type.CACHE_STOPPED:
               System.out.println("Cache has stopped");
               break;
         }
      }
      @NodeCreated
      @NodeRemoved
      @NodeVisited
      @NodeModified
      @NodeMoved
      public void logNodeEvent(NodeEvent ne)
      {
         log("An event on node " + ne.getFqn() + " has occured");
      }
   }
         

Cache loaders are an important part of JBoss Cache. They allow persistence of nodes to disk or to remote cache clusters, and allow for passivation when caches run out of memory. In addition, cache loaders allow JBoss Cache to perform 'warm starts', where in-memory state can be preloaded from persistent storage. JBoss Cache ships with a number of cache loader implementations.

These CacheLoaders, along with advanced aspects and tuning issues, are discussed in the chapter dedicated to CacheLoaders .



[1] http://wiki.jboss.org/wiki/Wiki.jsp?page=JBossClusteringPatternFarCache

The org.jboss.cache.config.Configuration class (along with its component parts ) is a Java Bean that encapsulates the configuration of the Cache and all of its architectural elements (cache loaders, evictions policies, etc.)

The Configuration exposes numerous properties which are summarized in the configuration reference section of this book and many of which are discussed in later chapters. Any time you see a configuration option discussed in this book, you can assume that the Configuration class or one of its component parts exposes a simple property setter/getter for that configuration option.

As discussed in the User API section , before a Cache can be created, the CacheFactory must be provided with a Configuration object or with a file name or input stream to use to parse a Configuration from XML. The following sections describe how to accomplish this.

The most convenient way to configure JBoss Cache is via an XML file. The JBoss Cache distribution ships with a number of configuration files for common use cases. It is recommended that these files be used as a starting point, and tweaked to meet specific needs.

Here is a simple example configuration file:



<?xml version="1.0" encoding="UTF-8"?>

<!-- ===================================================================== -->
<!--                                                                       -->
<!--  Sample JBoss Cache Service Configuration                             -->
<!--                                                                       -->
<!-- ===================================================================== -->

<server>
   
   <mbean code="org.jboss.cache.jmx.CacheJmxWrapper" name="jboss.cache:service=Cache">
   
      <!-- Configure the TransactionManager -->
      <attribute name="TransactionManagerLookupClass">
         org.jboss.cache.transaction.GenericTransactionManagerLookup
      </attribute>

      <!-- Node locking level : SERIALIZABLE
                                REPEATABLE_READ (default)
                                READ_COMMITTED
                                READ_UNCOMMITTED
                                NONE             -->
      <attribute name="IsolationLevel">READ_COMMITTED</attribute>

      <!-- Lock parent before doing node additions/removes -->
      <attribute name="LockParentForChildInsertRemove">true</attribute>

      <!-- Valid modes are LOCAL (default)
                           REPL_ASYNC
                           REPL_SYNC
                           INVALIDATION_ASYNC
                           INVALIDATION_SYNC   -->
      <attribute name="CacheMode">LOCAL</attribute>

      <!-- Max number of milliseconds to wait for a lock acquisition -->
      <attribute name="LockAcquisitionTimeout">15000</attribute>


      <!-- Specific eviction policy configurations. This is LRU -->
      <attribute name="EvictionConfig">
         <config>
            <attribute name="wakeUpIntervalSeconds">5</attribute>
            <attribute name="policyClass">org.jboss.cache.eviction.LRUPolicy</attribute>

            <!-- Cache wide default -->
            <region name="/_default_">
               <attribute name="maxNodes">5000</attribute>
               <attribute name="timeToLiveSeconds">1000</attribute>
            </region>
         </config>
      </attribute>
   </mbean>
</server>

Another, more complete, sample XML file is included in the configuration reference section of this book, along with a handy look-up table explaining the various options.

For historical reasons, the format of the JBoss Cache configuraton file follows that of a JBoss AS Service Archive (SAR) deployment descriptor (and still can be used as such inside JBoss AS ). Because of this dual usage, you may see elements in some configuration files (such as depends or classpath ) that are not relevant outside JBoss AS. These can safely be ignored.

Here's how you tell the CacheFactory to create and start a cache by finding and parsing a configuration file on the classpath:



   CacheFactory factory = new DefaultCacheFactory();
   Cache cache = factory.createCache("cache-configuration.xml");
         

In addition to the XML-based configuration above, the Configuration can be built up programatically, using the simple property mutators exposed by Configuration and its components. When constructed, the Configuration object is preset with JBoss Cache defaults and can even be used as-is for a quick start.

Following is an example of programatically creating a Configuration configured to match the one produced by the XML example above, and then using it to create a Cache :



   Configuration config = new Configuration();
   String tmlc = GenericTransactionManagerLookup.class.getName();
   config.setTransactionManagerLookupClass(tmlc);
   config.setIsolationLevel(IsolationLevel.READ_COMMITTED);
   config.setCacheMode(CacheMode.LOCAL);
   config.setLockParentForChildInsertRemove(true);
   config.setLockAcquisitionTimeout(15000);
   EvictionConfig ec = new EvictionConfig();
   ec.setWakeupIntervalSeconds(5);
   ec.setDefaultEvictionPolicyClass(LRUPolicy.class.getName());
   EvictionRegionConfig erc = new EvictionRegionConfig();
   erc.setRegionName("_default_");
   LRUConfiguration lru = new LRUConfiguration();
   lru.setMaxNodes(5000);
   lru.setTimeToLiveSeconds(1000);
   erc.setEvictionPolicyConfig(lru);
   List<EvictionRegionConfig> ercs = new ArrayList<EvictionRegionConfig>();
   ercs.add(erc);
   ec.setEvictionRegionConfigs(erc);
   config.setEvictionConfig(ec);
   CacheFactory factory = new DefaultCacheFactory();
   Cache cache = factory.createCache(config);

Even the above fairly simple configuration is pretty tedious programming; hence the preferred use of XML-based configuration. However, if your application requires it, there is no reason not to use XML-based configuration for most of the attributes, and then access the Configuration object to programatically change a few items from the defaults, add an eviction region, etc.

Note that configuration values may not be changed programmatically when a cache is running, except those annotated as @Dynamic . Dynamic properties are also marked as such in the configuration reference table. Attempting to change a non-dynamic property will result in a ConfigurationException .

The Configuration class and its component parts are all Java Beans that expose all config elements via simple setters and getters. Therefore, any good IOC framework should be able to build up a Configuration from an XML file in the framework's own format. See the deployment via the JBoss micrcontainer section for an example of this.

A Configuration is composed of a number of subobjects:

Following is a brief overview of the components of a Configuration . See the javadoc and the linked chapters in this book for a more complete explanation of the configurations associated with each component.

  • Configuration : top level object in the hierarchy; exposes the configuration properties listed in the configuration reference section of this book.
  • BuddyReplicationConfig : only relevant if buddy replication is used. General buddy replication configuration options. Must include a:
  • BuddyLocatorConfig : implementation-specific configuration object for the BuddyLocator implementation being used. What configuration elements are exposed depends on the needs of the BuddyLocator implementation.
  • EvictionConfig : only relevant if eviction is used. General eviction configuration options. Must include at least one:
  • EvictionRegionConfig : one for each eviction region; names the region, etc. Must include a:
  • EvictionPolicyConfig : implementation-specific configuration object for the EvictionPolicy implementation being used. What configuration elements are exposed depends on the needs of the EvictionPolicy implementation.
  • CacheLoaderConfig : only relevant if a cache loader is used. General cache loader configuration options. Must include at least one:
  • IndividualCacheLoaderConfig : implementation-specific configuration object for the CacheLoader implementation being used. What configuration elements are exposed depends on the needs of the CacheLoader implementation.
  • RuntimeConfig : exposes to cache clients certain information about the cache's runtime environment (e.g. membership in buddy replication groups if buddy replication is used.) Also allows direct injection into the cache of needed external services like a JTA TransactionManager or a JGroups ChannelFactory .