JBoss.orgCommunity Documentation

JBoss Cache Users' Guide

A clustered, transactional cache

Release 3.0.0 Naga

October 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.2.1. Caching objects
1.2.2. Local and clustered modes
1.2.3. Clustered caches and transactions
1.2.4. Thread safety
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.7.1. Synchronous and Asynchronous Notifications
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. Validating Configuration Files
3.2.3. Programmatic Configuration
3.2.4. Using an IOC Framework
3.3. Composition of a Configuration Object
3.4. Dynamic Reconfiguration
3.4.1. Overriding the Configuration via the Option API
4. Batching API
4.1. Introduction
4.2. Configuring batching
4.3. Batching API
5. Deploying JBoss Cache
5.1. Standalone Use/Programatic Deployment
5.2. Via JBoss Microcontainer (JBoss AS 5.x)
5.3. Automatic binding to JNDI in JBoss AS
5.4. Runtime Management Information
5.4.1. JBoss Cache MBeans
5.4.2. Registering the CacheJmxWrapper with the MBeanServer
5.4.3. JBoss Cache Statistics
5.4.4. Receiving JMX Notifications
5.4.5. Accessing Cache MBeans in a Standalone Environment using the jconsole Utility
6. Version Compatibility and Interoperability
6.1. API compatibility
6.2. Wire-level interoperability
6.3. Compatibility Matrix
II. JBoss Cache Architecture
7. Architecture
7.1. Data Structures Within The Cache
7.2. SPI Interfaces
7.3. Method Invocations On Nodes
7.3.1. Interceptors
7.3.2. Commands and Visitors
7.3.3. InvocationContexts
7.4. Managers For Subsystems
7.4.1. RpcManager
7.4.2. BuddyManager
7.4.3. CacheLoaderManager
7.5. Marshalling And Wire Formats
7.5.1. The Marshaller Interface
7.5.2. VersionAwareMarshaller
7.6. Class Loading and Regions
8. Cache Modes and Clustering
8.1. Cache Replication Modes
8.1.1. Local Mode
8.1.2. Replicated Caches
8.2. Invalidation
8.3. State Transfer
8.3.1. State Transfer Types
8.3.2. Byte array and streaming based state transfer
8.3.3. Full and partial state transfer
8.3.4. Transient ("in-memory") and persistent state transfer
8.3.5. Configuring State Transfer
9. Cache Loaders
9.1. The CacheLoader Interface and Lifecycle
9.2. Configuration
9.2.1. Singleton Store Configuration
9.3. Shipped Implementations
9.3.1. File system based cache loaders
9.3.2. Cache loaders that delegate to other caches
9.3.3. JDBCCacheLoader
9.3.4. S3CacheLoader
9.3.5. TcpDelegatingCacheLoader
9.3.6. Transforming Cache Loaders
9.4. Cache Passivation
9.4.1. Cache Loader Behavior with Passivation Disabled vs. Enabled
9.5. Strategies
9.5.1. Local Cache With Store
9.5.2. Replicated Caches With All Caches Sharing The Same Store
9.5.3. Replicated Caches With Only One Cache Having A Store
9.5.4. Replicated Caches With Each Cache Having Its Own Store
9.5.5. Hierarchical Caches
9.5.6. Multiple Cache Loaders
10. Eviction
10.1. Design
10.1.1. Collecting Statistics
10.1.2. Determining Which Nodes to Evict
10.1.3. How Nodes are Evicted
10.1.4. Eviction threads
10.2. Eviction Regions
10.2.1. Resident Nodes
10.3. Configuring Eviction
10.3.1. Basic Configuration
10.3.2. Programmatic Configuration
10.4. Shipped Eviction Policies
10.4.1. LRUAlgorithm - Least Recently Used
10.4.2. FIFOAlgorithm - First In, First Out
10.4.3. MRUAlgorithm - Most Recently Used
10.4.4. LFUAlgorithm - Least Frequently Used
10.4.5. ExpirationAlgorithm
10.4.6. ElementSizeAlgorithm - Eviction based on number of key/value pairs in a node
11. Transactions and Concurrency
11.1. Concurrent Access
11.1.1. Multi-Version Concurrency Control (MVCC)
11.1.2. Pessimistic and Optimistic Locking Schemes
11.2. JTA Support
III. JBoss Cache Configuration References
12. Configuration References
12.1. Sample XML Configuration File
12.1.1. XML validation
12.2. Configuration File Quick Reference
13. JMX References
13.1. JBoss Cache Statistics
13.2. JMX MBean Notifications

This is the official JBoss Cache Users' Guide. Along with its accompanying documents (an FAQ, a tutorial and a whole set of documents on POJO Cache), this is freely available on the JBoss Cache documentation website.

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 developers wishing to use JBoss Cache as either a standalone in-memory cache, a distributed or replicated cache, a clustering library, or an in-memory database. It is targeted at application developers who wish to use JBoss Cache in their code base, as well as "OEM" developers who wish to build on and extend JBoss Cache 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 programming is necessary. No prior knowledge of JBoss Application Server is expected or required.

For further discussion, use the user forum available 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 JBoss Cache 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.

I always appreciate feedback, suggestions and corrections, and these should be directed to the developer mailing list rather than direct emails to any of the authors. We hope you find this book useful, and wish you happy reading!

Manik Surtani, October 2008

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.

Table of Contents

1. Overview
1.1. What is JBoss Cache?
1.1.1. And what is POJO Cache?
1.2. Summary of Features
1.2.1. Caching objects
1.2.2. Local and clustered modes
1.2.3. Clustered caches and transactions
1.2.4. Thread safety
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.7.1. Synchronous and Asynchronous Notifications
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. Validating Configuration Files
3.2.3. Programmatic Configuration
3.2.4. Using an IOC Framework
3.3. Composition of a Configuration Object
3.4. Dynamic Reconfiguration
3.4.1. Overriding the Configuration via the Option API
4. Batching API
4.1. Introduction
4.2. Configuring batching
4.3. Batching API
5. Deploying JBoss Cache
5.1. Standalone Use/Programatic Deployment
5.2. Via JBoss Microcontainer (JBoss AS 5.x)
5.3. Automatic binding to JNDI in JBoss AS
5.4. Runtime Management Information
5.4.1. JBoss Cache MBeans
5.4.2. Registering the CacheJmxWrapper with the MBeanServer
5.4.3. JBoss Cache Statistics
5.4.4. Receiving JMX Notifications
5.4.5. Accessing Cache MBeans in a Standalone Environment using the jconsole Utility
6. Version Compatibility and Interoperability
6.1. API compatibility
6.2. Wire-level interoperability
6.3. Compatibility Matrix

JBoss Cache is a tree-structured, clustered, transactional cache. It can be used in a standalone, non-clustered environment, to cache frequently accessed data in memory thereby removing data retrieval or calculation bottlenecks while providing "enterprise" features such as JTA compatibility, eviction and persistence.

JBoss Cache is also a clustered cache, and can be used in a cluster to replicate state providing a high degree of failover. A variety of replication modes are supported, including invalidation and buddy replication, and network communications can either be synchronous or asynchronous.

When used in a clustered mode, the cache is an effective mechanism of building high availability, fault tolerance and even load balancing into custom applications and frameworks. For example, the JBoss Application Server and Red Hat's Enterprise Application Platform make extensive use of JBoss Cache to cluster services such as HTTP and EJB sessions, as well as providing a distributed entity cache for JPA.

JBoss Cache can - and often is - used outside of JBoss AS, in other Java EE environments such as Spring, Tomcat, Glassfish, BEA WebLogic, IBM WebSphere, and even in standalone Java programs thanks to its minimal dependency set.

JBoss Cache requires a Java 5.0 (or newer) compatible virtual machine and set of libraries, and is developed and tested on Sun's JDK 5.0 and JDK 6.

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, as well as several optional jars for optional features.

JBoss Cache is an open source project, 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.

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.

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

Here is 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();
      

In this example, 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");
      

In this example, 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("/opt/configurations/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, lets 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 a hold 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("favoriteDrink", 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("favoriteDrink");
   assertEquals(keys, peterGriffin.getKeys());
   // let's remove some data from the node
   peterGriffin.remove("favoriteDrink");
   assertNull(peterGriffin.get("favoriteDrink");
   // 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, for convenience:



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

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 cache mode affects behavior. See the chapter on Configuration for info on how to configure things like cache 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 CACHE_STARTED:
               System.out.println("Cache has started");
               break;
            case 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 cache loaders, along with advanced aspects and tuning issues, are discussed in the chapter dedicated to cache loaders.

Eviction policies are the counterpart to cache loaders. They are necessary to make sure the cache does not run out of memory and when the cache starts to fill, an eviction algorithm running in a separate thread evicts in-memory state and frees up memory. If configured with a cache loader, the state can then be retrieved from the cache loader if needed.

Eviction policies can be configured on a per-region basis, so different subtrees in the cache could have different eviction preferences. JBoss Cache ships with several eviction policies:

Detailed configuration and implementing custom eviction policies are discussed in the chapter dedicated to eviction policies.

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 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 such as Spring, Google Guice, JBoss Microcontainer, etc. 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:
  • EvictionAlgorithmConfig : implementation-specific configuration object for the EvictionAlgorithm implementation being used. What configuration elements are exposed depends on the needs of the EvictionAlgorithm 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 .

Dynamically changing the configuration of some options while the cache is running is supported, by programmatically obtaining the Configuration object from the running cache and changing values. E.g.,



   Configuration liveConfig = cache.getConfiguration();
   liveConfig.setLockAcquisitionTimeout(2000);
         

A complete listing of which options may be changed dynamically is in the configuration reference section. An org.jboss.cache.config.ConfigurationException will be thrown if you attempt to change a setting that is not dynamic.

When used in a standalone Java program, all that needs to be done is to instantiate the cache using the CacheFactory and a Configuration instance or an XML file, as discussed in the User API and Configuration chapters.

The same techniques can be used when an application running in an application server wishes to programatically deploy a cache rather than relying on an application server's deployment features. An example of this would be a webapp deploying a cache via a javax.servlet.ServletContextListener.

After creation, you could share your cache instance among different application components either by using an IOC container such as Spring, JBoss Microcontainer, etc., or by binding it to JNDI, or simply holding a static reference to the cache.

If, after deploying your cache you wish to expose a management interface to it in JMX, see the section on Programatic Registration in JMX.

Beginning with AS 5, JBoss AS supports deployment of POJO services via deployment of a file whose name ends with -beans.xml. A POJO service is one whose implementation is via a "Plain Old Java Object", meaning a simple java bean that isn't required to implement any special interfaces or extend any particular superclass. A Cache is a POJO service, and all the components in a Configuration are also POJOs, so deploying a cache in this way is a natural step.

Deployment of the cache is done using the JBoss Microcontainer that forms the core of JBoss AS. JBoss Microcontainer is a sophisticated IOC framework similar to Spring. A -beans.xml file is basically a descriptor that tells the IOC framework how to assemble the various beans that make up a POJO service.

For each configurable option exposed by the Configuration components, a getter/setter must be defined in the configuration class. This is required so that JBoss Microcontainer can, in typical IOC way, call these methods when the corresponding properties have been configured.

You need to ensure that the jbosscache-core.jar and jgroups.jar libraries are in your server's lib directory. This is usually the case when you use JBoss AS in its all configuration. Note that you will have to bring in any optional jars you require, such as jdbm.jar based on your cache configuration.

The following is an example -beans.xml file. If you look in the server/all/deploy directory of a JBoss AS 5 installation, you can find several more examples.



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

<deployment xmlns="urn:jboss:bean-deployer:2.0">

   <!-- First we create a Configuration object for the cache -->
   <bean name="ExampleCacheConfig"
         class="org.jboss.cache.config.Configuration">
      
      <!-- Externally injected services -->  
      <property name="runtimeConfig">
         <bean name="ExampleCacheRuntimeConfig" class="org.jboss.cache.config.RuntimeConfig">
            <property name="transactionManager">
               <inject bean="jboss:service=TransactionManager" 
                       property="TransactionManager"/>
            </property>
            <property name="muxChannelFactory"><inject bean="JChannelFactory"/></property>
         </bean>
      </property>
      
      <property name="multiplexerStack">udp</property>

      <property name="clusterName">Example-EntityCache</property>
        
      <property name="isolationLevel">REPEATABLE_READ</property>

      <property name="cacheMode">REPL_SYNC</property>

      <property name="initialStateRetrievalTimeout">15000</property>

      <property name="syncReplTimeout">20000</property>

      <property name="lockAcquisitionTimeout">15000</property>
        
      <property name="exposeManagementStatistics">true</property>
   </bean>
   
   <!-- Factory to build the Cache. -->
   <bean name="DefaultCacheFactory" class="org.jboss.cache.DefaultCacheFactory">      
      <constructor factoryClass="org.jboss.cache.DefaultCacheFactory" />
   </bean>
   
   <!-- The cache itself -->
   <bean name="ExampleCache" class="org.jboss.cache.Cache">
      
      <constructor factoryMethod="createCache">
          <factory bean="DefaultCacheFactory"/>
          <parameter class="org.jboss.cache.config.Configuration"><inject bean="ExampleCacheConfig"/></parameter>
          <parameter class="boolean">false</false>
      </constructor>
          
   </bean>

</deployment>      

See the JBoss Microcontainer documentation for details on the above syntax. Basically, each bean element represents an object and is used to create a Configuration and its constituent parts.

An interesting thing to note in the above example is the use of the RuntimeConfig object. External resources like a TransactionManager and a JGroups ChannelFactory that are visible to the microcontainer are dependency injected into the RuntimeConfig. The assumption here is that in some other deployment descriptor in the AS, the referenced beans have already been described.

JBoss Cache includes JMX MBeans to expose cache functionality and provide statistics that can be used to analyze cache operations. JBoss Cache can also broadcast cache events as MBean notifications for handling via JMX monitoring tools.

The best way to ensure the CacheJmxWrapper is registered in JMX depends on how you are deploying your cache.

CacheJmxWrapper is a POJO, so the microcontainer has no problem creating one. The trick is getting it to register your bean in JMX. This can be done by specifying the org.jboss.aop.microcontainer.aspects.jmx.JMX annotation on the CacheJmxWrapper bean:



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

<deployment xmlns="urn:jboss:bean-deployer:2.0">

   <!-- First we create a Configuration object for the cache -->
   <bean name="ExampleCacheConfig"
         class="org.jboss.cache.config.Configuration">
      
      ... build up the Configuration
      
   </bean>
   
   <!-- Factory to build the Cache. -->
   <bean name="DefaultCacheFactory" class="org.jboss.cache.DefaultCacheFactory">      
      <constructor factoryClass="org.jboss.cache.DefaultCacheFactory" />
   </bean>
   
   <!-- The cache itself -->
   <bean name="ExampleCache" class="org.jboss.cache.CacheImpl">
      
      <constructor factoryMethod="createnewInstance">
          <factory bean="DefaultCacheFactory"/>
          <parameter><inject bean="ExampleCacheConfig"/></parameter>
          <parameter>false</false>
      </constructor>
          
   </bean>
   
   <!-- JMX Management -->
   <bean name="ExampleCacheJmxWrapper" class="org.jboss.cache.jmx.CacheJmxWrapper">
      
      <annotation>@org.jboss.aop.microcontainer.aspects.jmx.JMX(name="jboss.cache:service=ExampleTreeCache", 
                         exposedInterface=org.jboss.cache.jmx.CacheJmxWrapperMBean.class, 
                         registerDirectly=true)</annotation>
      
      <constructor>
          <parameter><inject bean="ExampleCache"/></parameter>
      </constructor>
          
   </bean>

</deployment>      

As discussed in the Programatic Registration section, CacheJmxWrapper can do the work of building, creating and starting the Cache if it is provided with a Configuration. With the microcontainer, this is the preferred approach, as it saves the boilerplate XML needed to create the CacheFactory.



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

<deployment xmlns="urn:jboss:bean-deployer:2.0">

   <!-- First we create a Configuration object for the cache -->
   <bean name="ExampleCacheConfig"
         class="org.jboss.cache.config.Configuration">
      
      ... build up the Configuration
      
   </bean>
    
   <bean name="ExampleCache" class="org.jboss.cache.jmx.CacheJmxWrapper">
      
      <annotation>@org.jboss.aop.microcontainer.aspects.jmx.JMX(name="jboss.cache:service=ExampleTreeCache", 
                         exposedInterface=org.jboss.cache.jmx.CacheJmxWrapperMBean.class, 
                         registerDirectly=true)</annotation>
      
      <constructor>
          <parameter><inject bean="ExampleCacheConfig"/></parameter>
      </constructor>
          
   </bean>

</deployment>      

JBoss Cache users can register a listener to receive cache events described earlier in the User API chapter. Users can alternatively utilize the cache's management information infrastructure to receive these events via JMX notifications. Cache events are accessible as notifications by registering a NotificationListener for the CacheJmxWrapper.

See the section in the JMX Reference chapter pertaining to JMX notifications for a list of notifications that can be received through the CacheJmxWrapper.

The following is an example of how to programmatically receive cache notifications when running in a JBoss AS environment. In this example, the client uses a filter to specify which events are of interest.



   MyListener listener = new MyListener();
   NotificationFilterSupport filter = null;
   // get reference to MBean server
   Context ic = new InitialContext();
   MBeanServerConnection server = (MBeanServerConnection)ic.lookup("jmx/invoker/RMIAdaptor");
   // get reference to CacheMgmtInterceptor MBean
   String cache_service = "jboss.cache:service=TomcatClusteringCache";
   ObjectName mgmt_name = new ObjectName(cache_service);
   // configure a filter to only receive node created and removed events
   filter = new NotificationFilterSupport();
   filter.disableAllTypes();
   filter.enableType(CacheNotificationBroadcaster.NOTIF_NODE_CREATED);
   filter.enableType(CacheNotificationBroadcaster.NOTIF_NODE_REMOVED);
   // register the listener with a filter
   // leave the filter null to receive all cache events
   server.addNotificationListener(mgmt_name, listener, filter, null);
   // ...
   // on completion of processing, unregister the listener
   server.removeNotificationListener(mgmt_name, listener, filter, null);
         

The following is the simple notification listener implementation used in the previous example.



   private class MyListener implements NotificationListener, Serializable
   {
      public void handleNotification(Notification notification, Object handback)
      {
         String message = notification.getMessage();
         String type = notification.getType();
         Object userData = notification.getUserData();
         System.out.println(type + ": " + message);
         if (userData == null)
         {
            System.out.println("notification data is null");
         }
         else if (userData instanceof String)
         {
            System.out.println("notification data: " + (String) userData);
         }
         else if (userData instanceof Object[])
         {
            Object[] ud = (Object[]) userData;
            for (Object data : ud)
            {
               System.out.println("notification data: " + data.toString());
            }
         }
         else
         {
            System.out.println("notification data class: " + userData.getClass().getName());
         }
      }
   }
         

Note that the JBoss Cache management implementation only listens to cache events after a client registers to receive MBean notifications. As soon as no clients are registered for notifications, the MBean will remove itself as a cache listener.



[1] Note that if the CacheJmxWrapper is not registered in JMX, the interceptor MBeans will not be registered either. The JBoss Cache 1.4 releases included code that would try to "discover" an MBeanServer and automatically register the interceptor MBeans with it. For JBoss Cache 2.x we decided that this sort of "discovery" of the JMX environment was beyond the proper scope of a caching library, so we removed this functionality.

A compatibility matrix is maintained on the JBoss Cache website, which contains information on different versions of JBoss Cache, JGroups and JBoss Application Server.

This section digs deeper into the JBoss Cache architecture, and is meant for developers wishing to use the more advanced cache features,extend or enhance the cache, write plugins, or are just looking for detailed knowledge of how things work under the hood.

Table of Contents

7. Architecture
7.1. Data Structures Within The Cache
7.2. SPI Interfaces
7.3. Method Invocations On Nodes
7.3.1. Interceptors
7.3.2. Commands and Visitors
7.3.3. InvocationContexts
7.4. Managers For Subsystems
7.4.1. RpcManager
7.4.2. BuddyManager
7.4.3. CacheLoaderManager
7.5. Marshalling And Wire Formats
7.5.1. The Marshaller Interface
7.5.2. VersionAwareMarshaller
7.6. Class Loading and Regions
8. Cache Modes and Clustering
8.1. Cache Replication Modes
8.1.1. Local Mode
8.1.2. Replicated Caches
8.2. Invalidation
8.3. State Transfer
8.3.1. State Transfer Types
8.3.2. Byte array and streaming based state transfer
8.3.3. Full and partial state transfer
8.3.4. Transient ("in-memory") and persistent state transfer
8.3.5. Configuring State Transfer
9. Cache Loaders
9.1. The CacheLoader Interface and Lifecycle
9.2. Configuration
9.2.1. Singleton Store Configuration
9.3. Shipped Implementations
9.3.1. File system based cache loaders
9.3.2. Cache loaders that delegate to other caches
9.3.3. JDBCCacheLoader
9.3.4. S3CacheLoader
9.3.5. TcpDelegatingCacheLoader
9.3.6. Transforming Cache Loaders
9.4. Cache Passivation
9.4.1. Cache Loader Behavior with Passivation Disabled vs. Enabled
9.5. Strategies
9.5.1. Local Cache With Store
9.5.2. Replicated Caches With All Caches Sharing The Same Store
9.5.3. Replicated Caches With Only One Cache Having A Store
9.5.4. Replicated Caches With Each Cache Having Its Own Store
9.5.5. Hierarchical Caches
9.5.6. Multiple Cache Loaders
10. Eviction
10.1. Design
10.1.1. Collecting Statistics
10.1.2. Determining Which Nodes to Evict
10.1.3. How Nodes are Evicted
10.1.4. Eviction threads
10.2. Eviction Regions
10.2.1. Resident Nodes
10.3. Configuring Eviction
10.3.1. Basic Configuration
10.3.2. Programmatic Configuration
10.4. Shipped Eviction Policies
10.4.1. LRUAlgorithm - Least Recently Used
10.4.2. FIFOAlgorithm - First In, First Out
10.4.3. MRUAlgorithm - Most Recently Used
10.4.4. LFUAlgorithm - Least Frequently Used
10.4.5. ExpirationAlgorithm
10.4.6. ElementSizeAlgorithm - Eviction based on number of key/value pairs in a node
11. Transactions and Concurrency
11.1. Concurrent Access
11.1.1. Multi-Version Concurrency Control (MVCC)
11.1.2. Pessimistic and Optimistic Locking Schemes
11.2. JTA Support

Since the cache is essentially a collection of nodes, aspects such as clustering, persistence, eviction, etc. need to be applied to these nodes when operations are invoked on the cache as a whole or on individual nodes. To achieve this in a clean, modular and extensible manner, an interceptor chain is used. The chain is built up of a series of interceptors, each one adding an aspect or particular functionality. The chain is built when the cache is created, based on the configuration used.

It is important to note that the NodeSPI offers some methods (such as the xxxDirect() method family) that operate on a node directly without passing through the interceptor stack. Plugin authors should note that using such methods will affect the aspects of the cache that may need to be applied, such as locking, replication, etc. To put it simply, don't use such methods unless you really know what you're doing!

JBoss Cache essentially is a core data structure - an implementation of DataContainer - and aspects and features are implemented using interceptors in front of this data structure. A CommandInterceptor is an abstract class, interceptor implementations extend this.

CommandInterceptor implements the Visitor interface so it is able to alter commands in a strongly typed manner as the command makes its way to the data structure. More on visitors and commands in the next section.

Interceptor implementations are chained together in the InterceptorChain class, which dispatches a command across the chain of interceptors. A special interceptor, the CallInterceptor, always sits at the end of this chain to invoke the command being passed up the chain by calling the command's process() method.

JBoss Cache ships with several interceptors, representing different behavioral aspects, some of which are:

The interceptor chain configured for your cache instance can be obtained and inspected by calling CacheSPI.getInterceptorChain(), which returns an ordered List of interceptors in the order in which they would be encountered by a command.

Early versions of JBoss Cache simply wrote cached data to the network by writing to an ObjectOutputStream during replication. Over various releases in the JBoss Cache 1.x.x series this approach was gradually deprecated in favor of a more mature marshalling framework. In the JBoss Cache 2.x.x series, this is the only officially supported and recommended mechanism for writing objects to datastreams.


This chapter talks about aspects around clustering JBoss Cache.

JBoss Cache can be configured to be either local (standalone) or clustered. If in a cluster, the cache can be configured to replicate changes, or to invalidate changes. A detailed discussion on this follows.

Replicated caches replicate all changes to some or all of the other cache instances in the cluster. Replication can either happen after each modification (no transactions or batches), or at the end of a transaction or batch.

Replication can be synchronous or asynchronous. Use of either one of the options is application dependent. Synchronous replication blocks the caller (e.g. on a put() ) until the modifications have been replicated successfully to all nodes in a cluster. Asynchronous replication performs replication in the background (the put() returns immediately). JBoss Cache also offers a replication queue, where modifications are replicated periodically (i.e. interval-based), or when the queue size exceeds a number of elements, or a combination thereof. A replication queue can therefore offer much higher performance as the actual replication is performed by a background thread.

Asynchronous replication is faster (no caller blocking), because synchronous replication requires acknowledgments from all nodes in a cluster that they received and applied the modification successfully (round-trip time). However, when a synchronous replication returns successfully, the caller knows for sure that all modifications have been applied to all cache instances, whereas this is not be the case with asynchronous replication. With asynchronous replication, errors are simply written to a log. Even when using transactions, a transaction may succeed but replication may not succeed on all cache instances.

When using transactions, replication only occurs at the transaction boundary - i.e., when a transaction commits. This results in minimizing replication traffic since a single modification is broadcast rather than a series of individual modifications, and can be a lot more efficient than not using transactions. Another effect of this is that if a transaction were to roll back, nothing is broadcast across a cluster.

Depending on whether you are running your cluster in asynchronous or synchronous mode, JBoss Cache will use either a single phase or two phase commit protocol, respectively.

Buddy Replication allows you to suppress replicating your data to all instances in a cluster. Instead, each instance picks one or more 'buddies' in the cluster, and only replicates to these specific buddies. This greatly helps scalability as there is no longer a memory and network traffic impact every time another instance is added to a cluster.

One of the most common use cases of Buddy Replication is when a replicated cache is used by a servlet container to store HTTP session data. One of the pre-requisites to buddy replication working well and being a real benefit is the use of session affinity , more casually known as sticky sessions in HTTP session replication speak. What this means is that if certain data is frequently accessed, it is desirable that this is always accessed on one instance rather than in a round-robin fashion as this helps the cache cluster optimize how it chooses buddies, where it stores data, and minimizes replication traffic.

If this is not possible, Buddy Replication may prove to be more of an overhead than a benefit.

In the unfortunate event of an instance crashing, it is assumed that the client connecting to the cache (directly or indirectly, via some other service such as HTTP session replication) is able to redirect the request to any other random cache instance in the cluster. This is where a concept of Data Gravitation comes in.

Data Gravitation is a concept where if a request is made on a cache in the cluster and the cache does not contain this information, it asks other instances in the cluster for the data. In other words, data is lazily transferred, migrating only when other nodes ask for it. This strategy prevents a network storm effect where lots of data is pushed around healthy nodes because only one (or a few) of them die.

If the data is not found in the primary section of some node, it would (optionally) ask other instances to check in the backup data they store for other caches. This means that even if a cache containing your session dies, other instances will still be able to access this data by asking the cluster to search through their backups for this data.

Once located, this data is transferred to the instance which requested it and is added to this instance's data tree. The data is then (optionally) removed from all other instances (and backups) so that if session affinity is used, the affinity should now be to this new cache instance which has just taken ownership of this data.

Data Gravitation is implemented as an interceptor. The following (all optional) configuration properties pertain to data gravitation.

See the configuration reference section for details on configuring buddy replication.

If a cache is configured for invalidation rather than replication, every time data is changed in a cache other caches in the cluster receive a message informing them that their data is now stale and should be evicted from memory. Invalidation, when used with a shared cache loader (see chapter on cache loaders) would cause remote caches to refer to the shared cache loader to retrieve modified data. The benefit of this is twofold: network traffic is minimized as invalidation messages are very small compared to replicating updated data, and also that other caches in the cluster look up modified data in a lazy manner, only when needed.

Invalidation messages are sent after each modification (no transactions or batches), or at the end of a transaction or batch, upon successful commit. This is usually more efficient as invalidation messages can be optimized for the transaction as a whole rather than on a per-modification basis.

Invalidation too can be synchronous or asynchronous, and just as in the case of replication, synchronous invalidation blocks until all caches in the cluster receive invalidation messages and have evicted stale data while asynchronous invalidation works in a 'fire-and-forget' mode, where invalidation messages are broadcast but doesn't block and wait for responses.

State Transfer refers to the process by which a JBoss Cache instance prepares itself to begin providing a service by acquiring the current state from another cache instance and integrating that state into its own state.

If either in-memory or persistent state transfer is enabled, a full or partial state transfer will be done at various times, depending on how the cache is used. "Full" state transfer refers to the transfer of the state related to the entire tree -- i.e. the root node and all nodes below it. A "partial" state transfer is one where just a portion of the tree is transferred -- i.e. a node at a given Fqn and all nodes below it.

If either in-memory or persistent state transfer is enabled, state transfer will occur at the following times:

  1. Initial state transfer. This occurs when the cache is first started (as part of the processing of the start() method). This is a full state transfer. The state is retrieved from the cache instance that has been operational the longest. [2] If there is any problem receiving or integrating the state, the cache will not start.

    Initial state transfer will occur unless:

    1. The cache's InactiveOnStartup property is true . This property is used in conjunction with region-based marshalling.

    2. Buddy replication is used. See below for more on state transfer with buddy replication.

  2. Partial state transfer following region activation. When region-based marshalling is used, the application needs to register a specific class loader with the cache. This class loader is used to unmarshall the state for a specific region (subtree) of the cache.

    After registration, the application calls cache.getRegion(fqn, true).activate() , which initiates a partial state transfer of the relevant subtree's state. The request is first made to the oldest cache instance in the cluster. However, if that instance responds with no state, it is then requested from each instance in turn until one either provides state or all instances have been queried.

    Typically when region-based marshalling is used, the cache's InactiveOnStartup property is set to true . This suppresses initial state transfer, which would fail due to the inability to deserialize the transferred state.

  3. Buddy replication. When buddy replication is used, initial state transfer is disabled. Instead, when a cache instance joins the cluster, it becomes the buddy of one or more other instances, and one or more other instances become its buddy. Each time an instance determines it has a new buddy providing backup for it, it pushes its current state to the new buddy. This "pushing" of state to the new buddy is slightly different from other forms of state transfer, which are based on a "pull" approach (i.e. recipient asks for and receives state). However, the process of preparing and integrating the state is the same.

    This "push" of state upon buddy group formation only occurs if the InactiveOnStartup property is set to false . If it is true , state transfer amongst the buddies only occurs when the application activates the region on the various members of the group.

    Partial state transfer following a region activation call is slightly different in the buddy replication case as well. Instead of requesting the partial state from one cache instance, and trying all instances until one responds, with buddy replication the instance that is activating a region will request partial state from each instance for which it is serving as a backup.

The state that is acquired and integrated can consist of two basic types:

Which of these types of state transfer is appropriate depends on the usage of the cache.



[2] The longest operating cache instance is always, in JGroups terms, the coordinator.

JBoss Cache can use a CacheLoader to back up the in-memory cache to a backend datastore. If JBoss Cache is configured with a cache loader, then the following features are provided:

  • Whenever a cache element is accessed, and that element is not in the cache (e.g. due to eviction or due to server restart), then the cache loader transparently loads the element into the cache if found in the backend store.
  • Whenever an element is modified, added or removed, then that modification is persisted in the backend store via the cache loader. If transactions are used, all modifications created within a transaction are persisted. To this end, the CacheLoader takes part in the two phase commit protocol run by the transaction manager, although it does not do so explicitly.


The interaction between JBoss Cache and a CacheLoader implementation is as follows. When CacheLoaderConfiguration (see below) is non-null, an instance of each configured CacheLoader is created when the cache is created, and started when the cache is started.

CacheLoader.create() and CacheLoader.start() are called when the cache is started. Correspondingly, stop() and destroy() are called when the cache is stopped.

Next, setConfig() and setCache() are called. The latter can be used to store a reference to the cache, the former is used to configure this instance of the CacheLoader . For example, here a database cache loader could establish a connection to the database.

The CacheLoader interface has a set of methods that are called when no transactions are used: get() , put() , remove() and removeData() : they get/set/remove the value immediately. These methods are described as javadoc comments in the interface.

Then there are three methods that are used with transactions: prepare() , commit() and rollback() . The prepare() method is called when a transaction is to be committed. It has a transaction object and a list of modfications as argument. The transaction object can be used as a key into a hashmap of transactions, where the values are the lists of modifications. Each modification list has a number of Modification elements, which represent the changes made to a cache for a given transaction. When prepare() returns successfully, then the cache loader must be able to commit (or rollback) the transaction successfully.

JBoss Cache takes care of calling prepare(), commit() and rollback() on the cache loaders at the right time.

The commit() method tells the cache loader to commit the transaction, and the rollback() method tells the cache loader to discard the changes associated with that transaction.

See the javadocs on this interface for a detailed explanation on each method and the contract implementations would need to fulfill.

Cache loaders are configured as follows in the JBoss Cache XML file. Note that you can define several cache loaders, in a chain. The impact is that the cache will look at all of the cache loaders in the order they've been configured, until it finds a valid, non-null element of data. When performing writes, all cache loaders are written to (except if the ignoreModifications element has been set to true for a specific cache loader. See the configuration section below for details.



...

<!-- Cache loader config block -->
<!-- if passivation is true, only the first cache loader is used; the rest are ignored -->
<loaders passivation="false" shared="false">
      <preload>
         <!-- Fqns to preload -->
         <node fqn="/some/stuff"/>
      </preload>
      <!-- if passivation is true, only the first cache loader is used; the rest are ignored -->
      <loader class="org.jboss.cache.loader.JDBCCacheLoader" async="false" fetchPersistentState="true"
              ignoreModifications="false" purgeOnStartup="false">
         <properties>
            cache.jdbc.driver=com.mysql.jdbc.Driver
            cache.jdbc.url=jdbc:mysql://localhost:3306/jbossdb
            cache.jdbc.user=root
            cache.jdbc.password=
            cache.jdbc.sql-concat=concat(1,2)
         </properties>
      </loader>
  </loaders>

The class element defines the class of the cache loader implementation. (Note that, because of a bug in the properties editor in JBoss AS, backslashes in variables for Windows filenames might not get expanded correctly, so replace="false" may be necessary). Note that an implementation of cache loader has to have an empty constructor.

The properties element defines a configuration specific to the given implementation. The filesystem-based implementation for example defines the root directory to be used, whereas a database implementation might define the database URL, name and password to establish a database connection. This configuration is passed to the cache loader implementation via CacheLoader.setConfig(Properties) . Note that backspaces may have to be escaped.

preload allows us to define a list of nodes, or even entire subtrees, that are visited by the cache on startup, in order to preload the data associated with those nodes. The default ("/") loads the entire data available in the backend store into the cache, which is probably not a good idea given that the data in the backend store might be large. As an example, /a, /product/catalogue loads the subtrees /a and /product/catalogue into the cache, but nothing else. Anything else is loaded lazily when accessed. Preloading makes sense when one anticipates using elements under a given subtree frequently. .

fetchPersistentState determines whether or not to fetch the persistent state of a cache when joining a cluster. Only one configured cache loader may set this property to true; if more than one cache loader does so, a configuration exception will be thrown when starting your cache service.

async determines whether writes to the cache loader block until completed, or are run on a separate thread so writes return immediately. If this is set to true, an instance of org.jboss.cache.loader.AsyncCacheLoader is constructed with an instance of the actual cache loader to be used. The AsyncCacheLoader then delegates all requests to the underlying cache loader, using a separate thread if necessary. See the Javadocs on AsyncCacheLoader for more details. If unspecified, the async element defaults to false .

Note on using the async element: there is always the possibility of dirty reads since all writes are performed asynchronously, and it is thus impossible to guarantee when (and even if) a write succeeds. This needs to be kept in mind when setting the async element to true.

ignoreModifications determines whether write methods are pushed down to the specific cache loader. Situations may arise where transient application data should only reside in a file based cache loader on the same server as the in-memory cache, for example, with a further shared JDBCCacheLoader used by all servers in the network. This feature allows you to write to the 'local' file cache loader but not the shared JDBCCacheLoader . This property defaults to false , so writes are propagated to all cache loaders configured.

purgeOnStatup empties the specified cache loader (if ignoreModifications is false ) when the cache loader starts up.

shared indicates that the cache loader is shared among different cache instances, for example where all instances in a cluster use the same JDBC settings t talk to the same remote, shared database. Setting this to true prevents repeated and unnecessary writes of the same data to the cache loader by different cache instances. Default value is false .



 <loaders passivation="false" shared="true">
    <preload>
       <node fqn="/a/b/c"/>
       <node fqn="/f/r/s"/>
    </preload>

    <!-- we can now have multiple cache loaders, which get chained -->
    <loader class="org.jboss.cache.loader.JDBCCacheLoader" async="false" fetchPersistentState="false"
         ignoreModifications="false" purgeOnStartup="false">
       <properties>
         cache.jdbc.datasource=java:/DefaultDS
       </properties>
       <singletonStore enabled="true" class="org.jboss.cache.loader.SingletonStoreCacheLoader">
          <properties>
             pushStateWhenCoordinator=true
             pushStateWhenCoordinatorTimeout=20000
          </properties>
       </singletonStore>
    </loader>
 </loaders>
 

singletonStore element enables modifications to be stored by only one node in the cluster, the coordinator. Essentially, whenever any data comes in to some node it is always replicated so as to keep the caches' in-memory states in sync; the coordinator, though, has the sole responsibility of pushing that state to disk. This functionality can be activated setting the enabled subelement to true in all nodes, but again only the coordinator of the cluster will store the modifications in the underlying cache loader as defined in loader element. You cannot define a cache loader as shared and with singletonStore enabled at the same time. Default value for enabled is false .

Optionally, within the singletonStore element, you can define a class element that specifies the implementation class that provides the singleton store functionality. This class must extend org.jboss.cache.loader.AbstractDelegatingCacheLoader , and if absent, it defaults to org.jboss.cache.loader.SingletonStoreCacheLoader .

The properties subelement defines properties that allow changing the behavior of the class providing the singleton store functionality. By default, pushStateWhenCoordinator and pushStateWhenCoordinatorTimeout properties have been defined, but more could be added as required by the user-defined class providing singleton store functionality.

pushStateWhenCoordinator allows the in-memory state to be pushed to the cache store when a node becomes the coordinator, as a result of the new election of coordinator due to a cluster topology change. This can be very useful in situations where the coordinator crashes and there's a gap in time until the new coordinator is elected. During this time, if this property was set to false and the cache was updated, these changes would never be persisted. Setting this property to true would ensure that any changes during this process also get stored in the cache loader. You would also want to set this property to true if each node's cache loader is configured with a different location. Default value is true .

pushStateWhenCoordinatorTimeout is only relevant if pushStateWhenCoordinator is true in which case, sets the maximum number of milliseconds that the process of pushing the in-memory state to the underlying cache loader should take, reporting a PushStateException if exceeded. Default value is 20000.

Note on using the singletonStore element: setting up a cache loader as a singleton and using cache passivation (via evictions) can lead to undesired effects. If a node is to be passivated as a result of an eviction, while the cluster is in the process of electing a new coordinator, the data will be lost. This is because no coordinator is active at that time and therefore, none of the nodes in the cluster will store the passivated node. A new coordinator is elected in the cluster when either, the coordinator leaves the cluster, the coordinator crashes or stops responding.

The currently available implementations shipped with JBoss Cache are as follows.

JBoss Cache ships with several cache loaders that utilize the file system as a data store. They all require that the <loader><properties> configuration element contains a location property, which maps to a directory to be used as a persistent store. (e.g., location=/tmp/myDataStore ). Used mainly for testing and not recommended for production use.

Note that the BerkeleyDB implementation is much more efficient than the filesystem-based implementation, and provides transactional guarantees, but requires a commercial license if distributed with an application (see http://www.oracle.com/database/berkeley-db/index.html for details).

JBossCache is distributed with a JDBC-based cache loader implementation that stores/loads nodes' state into a relational database. The implementing class is org.jboss.cache.loader.JDBCCacheLoader .

The current implementation uses just one table. Each row in the table represents one node and contains three columns:

Fqns are stored as strings. Node content is stored as a BLOB. WARNING: JBoss Cache does not impose any limitations on the types of objects used in Fqn but this implementation of cache loader requires Fqn to contain only objects of type java.lang.String . Another limitation for Fqn is its length. Since Fqn is a primary key, its default column type is VARCHAR which can store text values up to some maximum length determined by the database in use.

See this wiki page for configuration tips with specific database systems.

Below is an example of a JDBCCacheLoader using Oracle as database. The CacheLoaderConfiguration XML element contains an arbitrary set of properties which define the database-related configuration.



<loaders passivation="false" shared="false">
      <preload>
         <node fqn="/some/stuff"/>
      </preload>
      <!-- if passivation is true, only the first cache loader is used; the rest are ignored -->
      <loader class="org.jboss.cache.loader.JDBCCacheLoader" async="false" fetchPersistentState="true"
              ignoreModifications="false" purgeOnStartup="false">
         <properties>
            cache.jdbc.table.name=jbosscache
            cache.jdbc.table.create=true
            cache.jdbc.table.drop=true
            cache.jdbc.table.primarykey=jbosscache_pk
            cache.jdbc.fqn.column=fqn
            cache.jdbc.fqn.type=varchar(255)
            cache.jdbc.node.column=node
            cache.jdbc.node.type=blob
            cache.jdbc.parent.column=parent
            cache.jdbc.driver=oracle.jdbc.OracleDriver
            cache.jdbc.url=jdbc:oracle:thin:@localhost:1521:JBOSSDB
            cache.jdbc.user=SCOTT
            cache.jdbc.password=TIGER
            cache.jdbc.sql-concat=concat(1,2)
          </properties>
      </loader>
  </loaders>

As an alternative to configuring the entire JDBC connection, the name of an existing data source can be given:



 <loaders passivation="false" shared="false">
      <preload>
         <node fqn="/some/stuff"/>
      </preload>
      <!-- if passivation is true, only the first cache loader is used; the rest are ignored -->
      <loader class="org.jboss.cache.loader.JDBCCacheLoader" async="false" fetchPersistentState="true"
              ignoreModifications="false" purgeOnStartup="false">
         <properties>
            cache.jdbc.datasource=java:/DefaultDS
         </properties>
      </loader>
  </loaders>

Cconfiguration example for a cache loader using c3p0 JDBC connection pooling:



 <loaders passivation="false" shared="false">
      <preload>
         <node fqn="/some/stuff"/>
      </preload>
      <!-- if passivation is true, only the first cache loader is used; the rest are ignored -->
      <loader class="org.jboss.cache.loader.JDBCCacheLoader" async="false" fetchPersistentState="true"
              ignoreModifications="false" purgeOnStartup="false">
         <properties>
            cache.jdbc.table.name=jbosscache
            cache.jdbc.table.create=true
            cache.jdbc.table.drop=true
            cache.jdbc.table.primarykey=jbosscache_pk
            cache.jdbc.fqn.column=fqn
            cache.jdbc.fqn.type=varchar(255)
            cache.jdbc.node.column=node
            cache.jdbc.node.type=blob
            cache.jdbc.parent.column=parent
            cache.jdbc.driver=oracle.jdbc.OracleDriver
            cache.jdbc.url=jdbc:oracle:thin:@localhost:1521:JBOSSDB
            cache.jdbc.user=SCOTT
            cache.jdbc.password=TIGER
            cache.jdbc.sql-concat=concat(1,2)
            cache.jdbc.connection.factory=org.jboss.cache.loader.C3p0ConnectionFactory
            c3p0.maxPoolSize=20
            c3p0.checkoutTimeout=5000
         </properties>
      </loader>
   </loaders>

The S3CacheLoader uses the Amazon S3 (Simple Storage Solution) for storing cache data. Since Amazon S3 is remote network storage and has fairly high latency, it is really best for caches that store large pieces of data, such as media or files. But consider this cache loader over the JDBC or file system based cache loaders if you want remotely managed, highly reliable storage. Or, use it for applications running on Amazon's EC2 (Elastic Compute Cloud).

If you're planning to use Amazon S3 for storage, consider using it with JBoss Cache. JBoss Cache itself provides in-memory caching for your data to minimize the amount of remote access calls, thus reducing the latency and cost of fetching your Amazon S3 data. With cache replication, you are also able to load data from your local cluster without having to remotely access it every time.

Note that Amazon S3 does not support transactions. If transactions are used in your application then there is some possibility of state inconsistency when using this cache loader. However, writes are atomic, in that if a write fails nothing is considered written and data is never corrupted.

Data is stored in keys based on the Fqn of the Node and Node data is serialized as a java.util.Map using the CacheSPI.getMarshaller() instance. Read the javadoc on how data is structured and stored. Data is stored using Java serialization. Be aware this means data is not readily accessible over HTTP to non-JBoss Cache clients. Your feedback and help would be appreciated to extend this cache loader for that purpose.

With this cache loader, single-key operations such as Node.remove(Object) and Node.put(Object, Object) are the slowest as data is stored in a single Map instance. Use bulk operations such as Node.replaceAll(Map) and Node.clearData() for more efficiency. Try the cache.s3.optimize option as well.

At a minimum, you must configure your Amazon S3 access key and secret access key. The following configuration keys are listed in general order of utility.

This cache loader allows to delegate loads and stores to another instance of JBoss Cache, which could reside (a) in the same address space, (b) in a different process on the same host, or (c) in a different process on a different host.

A TcpDelegatingCacheLoader talks to a remote org.jboss.cache.loader.tcp.TcpCacheServer , which can be a standalone process started on the command line, or embedded as an MBean inside JBoss AS. The TcpCacheServer has a reference to another JBoss Cache instance, which it can create itself, or which is given to it (e.g. by JBoss, using dependency injection).

As of JBoss Cache 2.1.0, the TcpDelegatingCacheLoader transparently handles reconnects if the connection to the TcpCacheServer is lost.

The TcpDelegatingCacheLoader is configured with the host and port of the remote TcpCacheServer, and uses this to communicate to it. In addition, 2 new optional parameters are used to control transparent reconnecting to the TcpCacheServer. The timeout property (defaults to 5000) specifies the length of time the cache loader must continue retrying to connect to the TcpCacheServer before giving up and throwing an exception. The reconnectWaitTime (defaults to 500) is how long the cache loader should wait before attempting a reconnect if it detects a communication failure. The last two parameters can be used to add a level of fault tolerance to the cache loader, do deal with TcpCacheServer restarts.

The configuration looks as follows:



 <loaders passivation="false" shared="false">
      <preload>
         <node fqn="/"/>
      </preload>
      <!-- if passivation is true, only the first cache loader is used; the rest are ignored -->
      <loader class="org.jboss.cache.loader.TcpDelegatingCacheLoader">
         <properties>
            host=myRemoteServer
            port=7500
            timeout=10000
            reconnectWaitTime=250
         </properties>
      </loader>
   </loaders>

This means this instance of JBoss Cache will delegate all load and store requests to the remote TcpCacheServer running on myRemoteServer:7500 .

A typical use case could be multiple replicated instances of JBoss Cache in the same cluster, all delegating to the same TcpCacheServer instance. The TcpCacheServer might itself delegate to a database via JDBCCacheLoader, but the point here is that - if we have 5 nodes all accessing the same dataset - they will load the data from the TcpCacheServer, which has do execute one SQL statement per unloaded data set. If the nodes went directly to the database, then we'd have the same SQL executed multiple times. So TcpCacheServer serves as a natural cache in front of the DB (assuming that a network round trip is faster than a DB access (which usually also include a network round trip)).

To alleviate single point of failure, we could configure several cache loaders. The first cache loader is a ClusteredCacheLoader, the second a TcpDelegatingCacheLoader, and the last a JDBCacheLoader, effectively defining our cost of access to a cache in increasing order.

The way cached data is written to FileCacheLoader and JDBCCacheLoader based cache stores has changed in JBoss Cache 2.0 in such way that these cache loaders now write and read data using the same marhalling framework used to replicate data across the network. Such change is trivial for replication purposes as it just requires the rest of the nodes to understand this format. However, changing the format of the data in cache stores brings up a new problem: how do users, which have their data stored in JBoss Cache 1.x.x format, migrate their stores to JBoss Cache 2.0 format?

With this in mind, JBoss Cache 2.0 comes with two cache loader implementations called org.jboss.cache.loader.TransformingFileCacheLoader and org.jboss.cache.loader.TransformingJDBCCacheLoader located within the optional jbosscache-cacheloader-migration.jar file. These are one-off cache loaders that read data from the cache store in JBoss Cache 1.x.x format and write data to cache stores in JBoss Cache 2.0 format.

The idea is for users to modify their existing cache configuration file(s) momentarily to use these cache loaders and for them to create a small Java application that creates an instance of this cache, recursively reads the entire cache and writes the data read back into the cache. Once the data is transformed, users can revert back to their original cache configuration file(s). In order to help the users with this task, a cache loader migration example has been constructed which can be located under the examples/cacheloader-migration directory within the JBoss Cache distribution. This example, called examples.TransformStore , is independent of the actual data stored in the cache as it writes back whatever it was read recursively. It is highly recommended that anyone interested in porting their data run this example first, which contains a readme.txt file with detailed information about the example itself, and also use it as base for their own application.

A cache loader can be used to enforce node passivation and activation on eviction in a cache.

Cache Passivation is the process of removing an object from in-memory cache and writing it to a secondary data store (e.g., file system, database) on eviction. Cache Activation is the process of restoring an object from the data store into the in-memory cache when it's needed to be used. In both cases, the configured cache loader will be used to read from the data store and write to the data store.

When an eviction policy in effect evicts a node from the cache, if passivation is enabled, a notification that the node is being passivated will be emitted to the cache listeners and the node and its children will be stored in the cache loader store. When a user attempts to retrieve a node that was evicted earlier, the node is loaded (lazy loaded) from the cache loader store into memory. When the node and its children have been loaded, they're removed from the cache loader and a notification is emitted to the cache listeners that the node has been activated.

To enable cache passivation/activation, you can set passivation to true. The default is false . When passivation is used, only the first cache loader configured is used and all others are ignored.

When passivation is disabled, whenever an element is modified, added or removed, then that modification is persisted in the backend store via the cache loader. There is no direct relationship between eviction and cache loading. If you don't use eviction, what's in the persistent store is basically a copy of what's in memory. If you do use eviction, what's in the persistent store is basically a superset of what's in memory (i.e. it includes nodes that have been evicted from memory).

When passivation is enabled, there is a direct relationship between eviction and the cache loader. Writes to the persistent store via the cache loader only occur as part of the eviction process. Data is deleted from the persistent store when the application reads it back into memory. In this case, what's in memory and what's in the persistent store are two subsets of the total information set, with no intersection between the subsets.

Following is a simple example, showing what state is in RAM and in the persistent store after each step of a 6 step process:

When passivation is disabled:

            1) Memory: /A Disk: /A
            2) Memory: /A, /B Disk: /A, /B
            3) Memory: /B Disk: /A, /B
            4) Memory: /A, /B Disk: /A, /B
            5) Memory: /A Disk: /A, /B
            6) Memory: /A Disk: /A
         

When passivation is enabled:

            1) Memory: /A Disk:
            2) Memory: /A, /B Disk:
            3) Memory: /B Disk: /A
            4) Memory: /A, /B Disk:
            5) Memory: /A Disk: /B
            6) Memory: /A Disk:
         

This section discusses different patterns of combining different cache loader types and configuration options to achieve specific outcomes.

The following figure shows 2 JBoss Cache instances sharing the same backend store:


Both nodes have a cache loader that accesses a common shared backend store. This could for example be a shared filesystem (using the FileCacheLoader), or a shared database. Because both nodes access the same store, they don't necessarily need state transfer on startup. [3] Rather, the FetchInMemoryState attribute could be set to false, resulting in a 'cold' cache, that gradually warms up as elements are accessed and loaded for the first time. This would mean that individual caches in a cluster might have different in-memory state at any given time (largely depending on their preloading and eviction strategies).

When storing a value, the writer takes care of storing the change in the backend store. For example, if node1 made change C1 and node2 C2, then node1 would tell its cache loader to store C1, and node2 would tell its cache loader to store C2.


This is a similar case to the previous one, but here only one node in the cluster interacts with a backend store via its cache loader. All other nodes perform in-memory replication. The idea here is all application state is kept in memory in each node, with the existence of multiple caches making the data highly available. (This assumes that a client that needs the data is able to somehow fail over from one cache to another.) The single persistent backend store then provides a backup copy of the data in case all caches in the cluster fail or need to be restarted.

Note that here it may make sense for the cache loader to store changes asynchronously, that is not on the caller's thread, in order not to slow down the cluster by accessing (for example) a database. This is a non-issue when using asynchronous replication.

A weakness with this architecture is that the cache with access to the cache loader becomes a single point of failure. Furthermore, if the cluster is restarted, the cache with the cache loader must be started first (easy to forget). A solution to the first problem is to configure a cache loader on each node, but set the singletonStore configuration to true. With this kind of setup, one but only one node will always be writing to a persistent store. However, this complicates the restart problem, as before restarting you need to determine which cache was writing before the shutdown/failure and then start that cache first.


Here, each node has its own datastore. Modifications to the cache are (a) replicated across the cluster and (b) persisted using the cache loader. This means that all datastores have exactly the same state. When replicating changes synchronously and in a transaction, the two phase commit protocol takes care that all modifications are replicated and persisted in each datastore, or none is replicated and persisted (atomic updates).

Note that JBoss Cache is not an XA Resource, that means it doesn't implement recovery. When used with a transaction manager that supports recovery, this functionality is not available.

The challenge here is state transfer: when a new node starts it needs to do the following:

  1. Tell the coordinator (oldest node in a cluster) to send it the state. This is always a full state transfer, overwriting any state that may already be present.

  2. The coordinator then needs to wait until all in-flight transactions have completed. During this time, it will not allow for new transactions to be started.

  3. Then the coordinator asks its cache loader for the entire state using loadEntireState() . It then sends back that state to the new node.

  4. The new node then tells its cache loader to store that state in its store, overwriting the old state. This is the CacheLoader.storeEntireState() method

  5. As an option, the transient (in-memory) state can be transferred as well during the state transfer.

  6. The new node now has the same state in its backend store as everyone else in the cluster, and modifications received from other nodes will now be persisted using the local cache loader.



[3] Of course they can enable state transfer, if they want to have a warm or hot cache after startup.

Eviction controls JBoss Cache's memory management by restricting how many nodes are allowed to be stored in memory, and for how long. Memory constraints on servers mean caches cannot grow indefinitely, so eviction needs to occur to prevent out of memory errors. Eviction is most often used alongside cache loaders.

Eviction in JBoss Cache is designed around four concepts:

In addition, Regions play a key role in eviction, as eviction is always configured on a per-region basis so that different subtrees in the cache can have different eviction characteristics.

The concept of regions and the Region class were visited earlier when talking about marshalling. Regions are also used to define the eviction behavior for nodes within that region. In addition to using a region-specific configuration, you can also configure default, cache-wide eviction behavior for nodes that do not fall into predefined regions or if you do not wish to define specific regions. It is important to note that when defining regions using the configuration XML file, all elements of the Fqn that defines the region are String objects.

For each region, you can define eviction parameters.

It's possible to define regions that overlap. In other words, one region can be defined for /a/b/c, and another defined for /a/b/c/d (which is just the d subtree of the /a/b/c sub-tree). The algorithm, in order to handle scenarios like this consistently, will always choose the first region it encounters. In this way, if the algorithm needed to decide how to handle node /a/b/c/d/e, it would start from there and work its way up the tree until it hits the first defined region - in this case /a/b/c/d.

Nodes marked as resident (using Node.setResident() API) will be ignored by the eviction policies both when checking whether to trigger the eviction and when proceeding with the actual eviction of nodes. E.g. if a region is configured to have a maximum of 10 nodes, resident nodes won't be counted when deciding whether to evict nodes in that region. In addition, resident nodes will not be considered for eviction when the region's eviction threshold is reached.

In order to mark a node as resident the Node.setResident() API should be used. By default, the newly created nodes are not resident. The resident attribute of a node is neither replicated, persisted nor transaction-aware.

A sample use case for resident nodes would be ensuring "path" nodes don't add "noise" to an eviction policy. E.g.,:



...
   Map lotsOfData = generateData();
   cache.put("/a/b/c", lotsOfData);
   cache.getRoot().getChild("/a").setResident(true);
   cache.getRoot().getChild("/a/b").setResident(true);
...
               

In this example, the nodes /a and /a/b are paths which exist solely to support the existence of node /a/b/c and don't hold any data themselves. As such, they are good candidates for being marked as resident. This would lead to better memory management as no eviction events would be generated when accessing /a and/a/b.

N.B. when adding attributes to a resident node, e.g. cache.put("/a", "k", "v") in the above example, it would make sense to mark the nodes as non-resident again and let them be considered for eviction..

Configuring eviction using the Configuration object entails the use of the org.jboss.cache.config.EvictionConfig bean, which is passed into Configuration.setEvictionConfig(). See the chapter on Configuration for more on building a Configuration programatically.

The use of simple POJO beans to represent all elements in a cache's configuration also makes it fairly easy to programatically add eviction regions after the cache is started. For example, assume we had an existing cache configured via XML with the EvictionConfig element shown above. Now at runtime we wished to add a new eviction region named "/org/jboss/fifo", using LRUAlgorithm but a different number of maxNodes:



   Fqn fqn = Fqn.fromString("/org/jboss/fifo");
   // Create a configuration for an LRUPolicy
   LRUAlgorithmConfig lruc = new LRUAlgorithmConfig();
   lruc.setMaxNodes(10000);
   // Create an eviction region config
   EvictionRegionConfig erc = new EvictionRegionConfig(fqn, lruc);
   // Create the region and set the config
   Region region = cache.getRegion(fqn, true);
   region.setEvictionRegionConfig(erc);
         
This section details the different algorithms shipped with JBoss Cache, and the various configuration parameters used for each algorithm.

org.jboss.cache.eviction.LFUAlgorithm controls the eviction in based on least frequently used algorithm. The least frequently used nodes will be the first to evict with this policy. Node usage starts at 1 when a node is first added. Each time it is visited, the node usage counter increments by 1. This number is used to determine which nodes are least frequently used. LFU is also a sorted eviction algorithm. The underlying EvictionQueue implementation and algorithm is sorted in ascending order of the node visits counter. This class guarantees a constant order ( O (1) ) for adds, removal and searches. However, when any number of nodes are added/visited to the queue for a given processing pass, a single quasilinear ( O (n * log n) ) operation is used to resort the queue in proper LFU order. Similarly if any nodes are removed or evicted, a single linear ( O (n) ) pruning operation is necessary to clean up the EvictionQueue. LFU has the following configuration parameters:

org.jboss.cache.eviction.ExpirationAlgorithm is a policy that evicts nodes based on an absolute expiration time. The expiration time is indicated using the org.jboss.cache.Node.put() method, using a String key expiration and the absolute time as a java.lang.Long object, with a value indicated as milliseconds past midnight January 1st, 1970 UTC (the same relative time as provided by java.lang.System.currentTimeMillis() ).

This policy guarantees a constant order ( O (1) ) for adds and removals. Internally, a sorted set (TreeSet) containing the expiration time and Fqn of the nodes is stored, which essentially functions as a heap.

This policy has the following configuration parameters:

The following listing shows how the expiration date is indicated and how the policy is applied:



   Cache cache = DefaultCacheFactory.createCache();
   Fqn fqn1 = Fqn.fromString("/node/1");
   Long future = new Long(System.currentTimeMillis() + 2000);
   // sets the expiry time for a node
   cache.getRoot().addChild(fqn1).put(ExpirationConfiguration.EXPIRATION_KEY, future);
   assertTrue(cache.getRoot().hasChild(fqn1));
   Thread.sleep(5000);
   // after 5 seconds, expiration completes
   assertFalse(cache.getRoot().hasChild(fqn1));
   

Note that the expiration time of nodes is only checked when the region manager wakes up every wakeUpIntervalSeconds , so eviction may happen a few seconds later than indicated.

JBoss Cache is a thread safe caching API, and uses its own efficient mechanisms of controlling concurrent access. It uses an innovative implementation of multi-versioned concurrency control (MVCC) as the default locking scheme. Versions of JBoss Cache prior to 3.x offered Optimistic and Pessimistic Locking schemes, both of which are now deprecated in favor of MVCC.

MVCC is a locking scheme commonly used by modern database implementations to control fast, safe concurrent access to shared data.

JBoss Cache's implementation of MVCC is based on a few features:

The extremely high performance of JBoss Cache's MVCC implementation for reading threads is achieved by not requiring any synchronization or locking for readers. For each reader thread, the MVCCLockingInterceptor wraps state in a lightweight container object, which is placed in the thread's InvocationContext (or TransactionContext if running in a transaction). All subsequent operations on the state happens via the container object. This use of Java references allows for repeatable read semantics even if the actual state changes simultaneously.

Writer threads, on the other hand, need to acquire a lock before any writing can commence. Currently, we use lock striping to improve the memory performance of the cache, and the size of the shared lock pool can be tuned using the concurrencyLevel attribute of the locking element. See the configuration reference for details. After acquiring an exclusive lock on an Fqn, the writer thread then wraps the state to be modified in a container as well, just like with reader threads, and then copies this state for writing. When copying, a reference to the original version is still maintained in the container (for rollbacks). Changes are then made to the copy and the copy is finally written to the data structure when the write completes.

This way, subsequent readers see the new version while existing readers still hold a reference to the original version in their context.

If a writer is unable to acquire the write lock after some time, a TimeoutException is thrown. This lock acquisition timeout defaults to 10000 millis and can be configured using the lockAcquisitionTimeout attribute of the locking element. See the configuration reference for details.

JBoss Cache 3.x supports two isolation levels: REPEATABLE_READ and READ_COMMITTED, which correspond in semantic to database-style isolation levels. Previous versions of JBoss Cache supported all 5 database isolation levels, and if an unsupported isolation level is configured, it is either upgraded or downgraded to the closest supported level.

REPEATABLE_READ is the default isolation level, to maintain compatibility with previous versions of JBoss Cache. READ_COMMITTED, while providing a slightly weaker isolation, has a significant performance benefit over REPEATABLE_READ.

From JBoss Cache 3.x onwards, pessimistic and optimistic locking schemes are deprecated in favor of MVCC. It is recommended that existing applications move off these legacy locking schemes as support for them will eventually be dropped altogether in future releases.

Documentation for legacy locking schemes are not included in this user guide, and if necessary, can be referenced in previous versions of this document, which can be found on the JBoss Cache website.

JBoss Cache can be configured to use and participate in JTA compliant transactions. Alternatively, if transaction support is disabled, it is equivalent to using autocommit in JDBC calls, where modifications are potentially replicated after every change (if replication is enabled).

What JBoss Cache does on every incoming call is:

  1. Retrieve the current javax.transaction.Transaction associated with the thread

  2. If not already done, register a javax.transaction.Synchronization with the transaction manager to be notified when a transaction commits or is rolled back.

In order to do this, the cache has to be provided with a reference to environment's javax.transaction.TransactionManager. This is usually done by configuring the cache with the class name of an implementation of the TransactionManagerLookup interface. When the cache starts, it will create an instance of this class and invoke its getTransactionManager() method, which returns a reference to the TransactionManager.

JBoss Cache ships with JBossTransactionManagerLookup and GenericTransactionManagerLookup. The JBossTransactionManagerLookup is able to bind to a running JBoss AS instance and retrieve a TransactionManager while the GenericTransactionManagerLookup is able to bind to most popular Java EE application servers and provide the same functionality. A dummy implementation - DummyTransactionManagerLookup - is also provided for unit tests. Being a dummy, this is not recommended for production use a it has some severe limitations to do with concurrent transactions and recovery.

An alternative to configuring a TransactionManagerLookup is to programatically inject a reference to the TransactionManager into the Configuration object's RuntimeConfig element:



   TransactionManager tm = getTransactionManager(); // magic method
   cache.getConfiguration().getRuntimeConfig().setTransactionManager(tm);
      

Injecting the TransactionManager is the recommended approach when the Configuration is built by some sort of IOC container that already has a reference to the TransactionManager.

When the transaction commits, we initiate either a one- two-phase commit protocol. See replicated caches and transactions for details.

This is what a typical XML configuration file looks like. It is recommended that you use one of the configurations shipped with the JBoss Cache distribution and tweak according to your needs rather than write one from scratch.



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

<jbosscache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:jboss:jbosscache-core:config:3.0">


   <!--
      isolation levels supported: READ_COMMITTED and REPEATABLE_READ
      nodeLockingSchemes: mvcc, pessimistic (deprecated), optimistic (deprecated)
   -->
   <locking
         isolationLevel="REPEATABLE_READ"
         lockParentForChildInsertRemove="false"
         lockAcquisitionTimeout="20000"
         nodeLockingScheme="mvcc"
         writeSkewCheck="false"
         concurrencyLevel="500"/>

   <!--
      Used to register a transaction manager and participate in ongoing transactions.
      -->
   <transaction
         transactionManagerLookupClass="org.jboss.cache.transaction.GenericTransactionManagerLookup"
         syncRollbackPhase="false"
         syncCommitPhase="false"/>

   <!--
      Used to register JMX statistics in any available MBean server
      -->
   <jmxStatistics
         enabled="false"/>

   <!--
      If region based marshalling is used, defines whether new regions are inactive on startup.
   -->
   <startup
         regionsInactiveOnStartup="true"/>

   <!--
      Used to register JVM shutdown hooks.
      hookBehavior: DEFAULT, REGISTER, DONT_REGISTER
   -->
   <shutdown
         hookBehavior="DEFAULT"/>

   <!--
      Used to define async listener notification thread pool size
   -->
   <listeners
         asyncPoolSize="1"/>

   <!--
      Used to enable invocation batching and allow the use of Cache.startBatch()/endBatch() methods.
   -->
   <invocationBatching
         enabled="false"/>

   <!--
      serialization related configuration, used for replication and cache loading
   -->
   <serialization
         objectInputStreamPoolSize="12"
         objectOutputStreamPoolSize="14"
         version="3.0.0"
         marshallerClass="org.jboss.cache.marshall.VersionAwareMarshaller"
         useLazyDeserialization="false"
         useRegionBasedMarshalling="false"/>

   <!--
      This element specifies that the cache is clustered.
      modes supported: replication (r) or invalidation (i).
   -->
   <clustering mode="replication" clusterName="JBossCache-cluster">

      <!--
         Defines whether to retrieve state on startup
      -->
      <stateRetrieval timeout="20000" fetchInMemoryState="false"/>

      <!--
         Network calls are synchronous.
      -->
      <sync replTimeout="20000"/>
      <!--
         Uncomment this for async replication.
      -->
      <!-- <async useReplQueue="true" replQueueInterval="10000" replQueueMaxElements="500" serializationExecutorPoolSize="20" /> -->

      <!-- Uncomment to use Buddy Replication -->
      <!--
      <buddy enabled="true" poolName="myBuddyPoolReplicationGroup" communicationTimeout="2000">
         <dataGravitation auto="true" removeOnFind="true" searchBackupTrees="true"/>
         <locator class="org.jboss.cache.buddyreplication.NextMemberBuddyLocator">
            <properties>
               numBuddies = 1
               ignoreColocatedBuddies = true
            </properties>
         </locator>
      </buddy>
      -->

      <!--
         Configures the JGroups channel.  Looks up a JGroups config file on the classpath or filesystem.  udp.xml
         ships with jgroups.jar and will be picked up by the class loader.
      -->
      <jgroupsConfig configFile="udp.xml">
         <!-- uncomment to define a JGroups stack here

         <PING timeout="2000" num_initial_members="3"/>
         <MERGE2 max_interval="30000" min_interval="10000"/>
         <FD_SOCK/>
         <FD timeout="10000" max_tries="5" shun="true"/>
         <VERIFY_SUSPECT timeout="1500"/>
         <pbcast.NAKACK use_mcast_xmit="false" gc_lag="0"
                        retransmit_timeout="300,600,1200,2400,4800"
                        discard_delivered_msgs="true"/>
         <UNICAST timeout="300,600,1200,2400,3600"/>
         <pbcast.STABLE stability_delay="1000" desired_avg_gossip="50000"
                        max_bytes="400000"/>
         <pbcast.GMS print_local_addr="true" join_timeout="5000" shun="false"
                     view_bundling="true" view_ack_collection_timeout="5000"/>
         <FRAG2 frag_size="60000"/>
         <pbcast.STREAMING_STATE_TRANSFER use_reading_thread="true"/>
         <pbcast.FLUSH timeout="0"/>
          -->
      </jgroupsConfig>
   </clustering>

   <!--
      Eviction configuration.  WakeupInterval defines how often the eviction thread runs, in milliseconds.  0 means
      the eviction thread will never run.
   -->
   <eviction wakeUpInterval="500">
      <default algorithmClass="org.jboss.cache.eviction.LRUAlgorithm" eventQueueSize="200000">
         <property name="maxNodes" value="5000" />
         <property name="timeToLive" value="1000" />
      </default>
      <region name="/org/jboss/data1">
         <property name="timeToLive" value="2000" />
      </region>
      <region name="/org/jboss/data2" algorithmClass="org.jboss.cache.eviction.FIFOAlgorithm" eventQueueSize="100000">
         <property name="maxNodes" value="3000" />
         <property name="minTimeToLive" value="4000" />
      </region>
   </eviction>

   <!--
      Cache loaders.

      If passivation is enabled, state is offloaded to the cache loaders ONLY when evicted.  Similarly, when the state
      is accessed again, it is removed from the cache loader and loaded into memory.

      Otherwise, state is always maintained in the cache loader as well as in memory.

      Set 'shared' to true if all instances in the cluster use the same cache loader instance, e.g., are talking to the
      same database.
   -->
   <loaders passivation="false" shared="false">
      <preload>
         <node fqn="/org/jboss"/>
         <node fqn="/org/tempdata"/>
      </preload>

      <!--
         we can have multiple cache loaders, which get chained
      -->
      <loader class="org.jboss.cache.loader.JDBCCacheLoader" async="true" fetchPersistentState="true"
              ignoreModifications="true" purgeOnStartup="true">
         <properties>
            cache.jdbc.table.name=jbosscache
            cache.jdbc.table.create=true
            cache.jdbc.table.drop=true
         </properties>
         <singletonStore enabled="true" class="org.jboss.cache.loader.SingletonStoreCacheLoader">
            <properties>
               pushStateWhenCoordinator=true
               pushStateWhenCoordinatorTimeout=20000
            </properties>
         </singletonStore>
      </loader>
   </loaders>

   <!--
      Define custom interceptors.  All custom interceptors need to extend org.jboss.cache.interceptors.base.CommandInterceptor
   -->
   <!--
   <customInterceptors>
      <interceptor position="first" class="org.jboss.cache.config.parsing.custominterceptors.AaaCustomInterceptor">
         <property name="attrOne" value="value1" />
         <property name="attrTwo" value="value2" />
      </interceptor>
      <interceptor position="last" class="org.jboss.cache.config.parsing.custominterceptors.BbbCustomInterceptor"/>
      <interceptor index="3" class="org.jboss.cache.config.parsing.custominterceptors.AaaCustomInterceptor"/>
      <interceptor before="org.jboss.cache.interceptors.CallInterceptor"
                   class="org.jboss.cache.config.parsing.custominterceptors.BbbCustomInterceptor"/>
      <interceptor after="org.jboss.cache.interceptors.CallInterceptor"
                   class="org.jboss.cache.config.parsing.custominterceptors.AaaCustomInterceptor"/>
   </customInterceptors>
   -->
</jbosscache>


A list of definitions of each of the XML elements attributes used above, and their bean counterparts for programmatic configuration. If the description of an attribute states that it is dynamic, that means it can be changed after the cache is created and started.
























































There is a whole wealth of information being gathered and exposed on to JMX for monitoring the cache. Some of these are detailed below:

Table 13.1. JBoss Cache JMX MBeans

MBeanAttribute/Operation NameDescription
DataContainerImplgetNumberOfAttributes()Returns the number of attributes in all nodes in the data container
 getNumberOfNodes()Returns the number of nodes in the data container
 printDetails()Prints details of the data container
RPCManagerImpllocalAddressStringString representation of the local address
 membersStringString representation of the cluster view
 statisticsEnabledWhether RPC statistics are being gathered
 replicationCountNumber of successful replications
 replicationFailuresNumber of failed replications
 successRatioRPC call success ratio
RegionManagerImpldumpRegions()Dumps a String representation of all registered regions, including eviction regions depicting their event queue sizes
 numRegionsNumber of registered regions
BuddyManagerbuddyGroupA String representation of the cache's buddy group
 buddyGroupsIParticipateInString representations of all buddy groups the cache participates in
TransactionTablenumberOfRegisteredTransactionsThe number of registered, ongoing transactions
 transactionMapA String representation of all currently registered transactions mapped to internal GlobalTransaction instances
MVCCLockManagerconcurrencyLevelThe configured concurrency level
 numberOfLocksAvailableNumber of locks in the shared lock pool that are not used
 numberOfLocksHeldNumber of locks in the shared lock pool that are in use
 testHashing(String fqn)Tests the spreading of locks across Fqns. For a given (String based) Fqn, this method returns the index in the lock array that it maps to.
ActivationInterceptorActivationsNumber of passivated nodes that have been activated.
CacheLoaderInterceptorCacheLoaderLoadsNumber of nodes loaded through a cache loader.
 CacheLoaderMissesNumber of unsuccessful attempts to load a node through a cache loader.
CacheMgmtInterceptorHitsNumber of successful attribute retrievals.
 MissesNumber of unsuccessful attribute retrievals.
 StoresNumber of attribute store operations.
 EvictionsNumber of node evictions.
 NumberOfAttributesNumber of attributes currently cached.
 NumberOfNodesNumber of nodes currently cached.
 ElapsedTimeNumber of seconds that the cache has been running.
 TimeSinceResetNumber of seconds since the cache statistics have been reset.
 AverageReadTimeAverage time in milliseconds to retrieve a cache attribute, including unsuccessful attribute retrievals.
 AverageWriteTimeAverage time in milliseconds to write a cache attribute.
 HitMissRatioRatio of hits to hits and misses. A hit is a get attribute operation that results in an object being returned to the client. The retrieval may be from a cache loader if the entry isn't in the local cache.
 ReadWriteRatioRatio of read operations to write operations. This is the ratio of cache hits and misses to cache stores.
CacheStoreInterceptorCacheLoaderStoresNumber of nodes written to the cache loader.
InvalidationInterceptorInvalidationsNumber of cached nodes that have been invalidated.
PassivationInterceptorPassivationsNumber of cached nodes that have been passivated.
TxInterceptorPreparesNumber of transaction prepare operations performed by this interceptor.
 CommitsNumber of transaction commit operations performed by this interceptor.
 RollbacksNumber of transaction rollbacks operations performed by this interceptor.
 numberOfSyncsRegisteredNumber of synchronizations registered with the transaction manager pending completion and removal.