Chapter 2. User API

2.1. API Classes

The Cache interface is the primary mechanism for interacting with JBoss Cache. It is constructed and optionally started using the CacheFactory . The CacheFactory allows you to create a Cache either from a Configuration object or an XML file. Once you have a reference to a Cache , you can use it to look up Node objects in the tree structure, and store data in the tree.

Reviewing the javadoc for the above interfaces is the best way to learn the API. Below we cover some of the main points.

2.2. Instantiating and Starting the Cache

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

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

  • Gain access to a Configuration , either by having one passed in as a method parameter, or by parsing XML content and constructing one. The XML content can come from a provided input stream or from a classpath or filesystem location. See the chapter on Configuration for more on obtaining a Configuration .
  • Instantiate the Cache and provide it with a reference to the Configuration .
  • Optionally invoke the cache's create() and start() methods.

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

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

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

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

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

         CacheFactory factory = DefaultCacheFactory.getInstance();
         Cache cache = factory.createCache("cache-configuration.xml", false);
         Configuration config = cache.getConfiguration();
         config.setClusterName(this.getClusterName());

         // Have to create and start cache before using it
         cache.create();
         cache.start();
      

2.3. Caching and Retrieving Data

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

         // Let's get ahold of the root node.
         Node rootNode = cache.getRoot();

         // Remember, JBoss Cache stores data in a tree structure.
         // All nodes in the tree structure are identified by Fqn objects.
         Fqn peterGriffinFqn = Fqn.fromString("/griffin/peter");

         // Create a new Node
         Node peterGriffin = rootNode.addChild(peterGriffinFqn);

         // let's store some data in the node
         peterGriffin.put("isCartoonCharacter", Boolean.TRUE);
         peterGriffin.put("favouriteDrink", new Beer());

         // some tests (just assume this code is in a JUnit test case)
         assertTrue(peterGriffin.get("isCartoonCharacter"));
         assertEquals(peterGriffinFqn, peterGriffin.getFqn());
         assertTrue(rootNode.hasChild(peterGriffinFqn));

         Set keys = new HashSet();
         keys.add("isCartoonCharacter");
         keys.add("favouriteDrink");

         assertEquals(keys, peterGriffin.getKeys());

         // let's remove some data from the node
         peterGriffin.remove("favouriteDrink");

         assertNull(peterGriffin.get("favouriteDrink");

         // let's remove the node altogether
         rootNode.removeChild(peterGriffinFqn);

         assertFalse(rootNode.hasChild(peterGriffinFqn));
      

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

         Fqn peterGriffinFqn = Fqn.fromString("/griffin/peter");

         cache.put(peterGriffinFqn, "isCartoonCharacter", Boolean.TRUE);
         cache.put(peterGriffinFqn, "favouriteDrink", new Beer());

         assertTrue(peterGriffin.get(peterGriffinFqn, "isCartoonCharacter"));
         assertTrue(cache.getRootNode().hasChild(peterGriffinFqn));

         cache.remove(peterGriffinFqn, "favouriteDrink");

         assertNull(cache.get(peterGriffinFqn, "favouriteDrink");

         cache.removeNode(peterGriffinFqn);

         assertFalse(cache.getRootNode().hasChild(peterGriffinFqn));
      

2.4. The Fqn Class

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

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

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

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

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

Note that

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

is not the same as

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

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

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

2.5. Stopping and Destroying the Cache

It is good practice to stop and destroy your cache when you are done using it, particularly if it is a clustered cache and has thus used a JGroups channel. Stopping and destroying a cache ensures resources like the JGroups channel are properly cleaned up.

         cache.stop();
         cache.destroy();
      

Not also that a cache that has had stop() invoked on it can be started again with a new call to start() . Similarly, a cache that has had destroy() invoked on it can be created again with a new call to create() (and then started again with a start() call).

2.6. Cache Modes

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:

  • LOCAL - local, non-clustered cache. Local caches don't join a cluster and don't communicate with other caches in a cluster. Therefore their contents don't need to be Serializable; however, we recommend making them Serializable, allowing you the flexibility to change the cache mode at any time.
  • REPL_SYNC - synchronous replication. Replicated caches replicate all changes to the other caches in the cluster. Synchronous replication means that changes are replicated and the caller blocks until replication acknowledgements are received.
  • REPL_ASYNC - asynchronous replication. Similar to REPL_SYNC above, replicated caches replicate all changes to the other caches in the cluster. Being asynchronous, the caller does not block until replication acknowledgements are received.
  • INVALIDATION_SYNC - 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. This reduces replication overhead while still being able to invalidate stale data on remote caches.
  • INVALIDATION_ASYNC - as above, except this invalidation mode causes invalidation messages to be broadcast asynchronously.

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

2.7.  Adding a CacheListener

The @org.jboss.cache.notifications.annotation.CacheListener annotation is a convenient mechanism for receiving notifications from a cache about events that happen in the cache. Classes annotated with @CacheListener need to be public classes. 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 it's subtypes.

  • @CacheStarted - methods annotated such receive a notification when the cache is started. Methods need to accept a parameter type which is assignable from org.jboss.cache.notifications.event.CacheStartedEvent .

  • @CacheStopped - methods annotated such receive a notification when the cache is stopped. Methods need to accept a parameter type which is assignable from org.jboss.cache.notifications.event.CacheStoppedEvent .

  • @NodeCreated - methods annotated such receive a notification when a node is created. Methods need to accept a parameter type which is assignable from org.jboss.cache.notifications.event.NodeCreatedEvent .

  • @NodeRemoved - methods annotated such receive a notification when a node is removed. Methods need to accept a parameter type which is assignable from org.jboss.cache.notifications.event.NodeRemovedEvent .

  • @NodeModified - methods annotated such receive a notification when a node is modified. Methods need to accept a parameter type which is assignable from org.jboss.cache.notifications.event.NodeModifiedEvent .

  • @NodeMoved - methods annotated such receive a notification when a node is moved. Methods need to accept a parameter type which is assignable from org.jboss.cache.notifications.event.NodeMovedEvent .

  • @NodeVisited - methods annotated such receive a notification when a node is started. Methods need to accept a parameter type which is assignable from org.jboss.cache.notifications.event.NodeVisitedEvent .

  • @NodeLoaded - methods annotated such receive a notification when a node is loaded from a CacheLoader . Methods need to accept a parameter type which is assignable from org.jboss.cache.notifications.event.NodeLoadedEvent .

  • @NodeEvicted - methods annotated such receive a notification when a node is evicted from memory. Methods need to accept a parameter type which is assignable from org.jboss.cache.notifications.event.NodeEvictedEvent .

  • @NodeActivated - methods annotated such receive a notification when a node is activated. Methods need to accept a parameter type which is assignable from org.jboss.cache.notifications.event.NodeActivatedEvent .

  • @NodePassivated - methods annotated such receive a notification when a node is passivated. Methods need to accept a parameter type which is assignable from org.jboss.cache.notifications.event.NodePassivatedEvent .

  • @TransactionRegistered - methods annotated such receive a notification when the cache registers a javax.transaction.Synchronization with a registered transaction manager. Methods need to accept a parameter type which is assignable from org.jboss.cache.notifications.event.TransactionRegisteredEvent .

  • @TransactionCompleted - methods annotated such receive a notification when the cache receives a commit or rollback call from a registered transaction manager. Methods need to accept a parameter type which is assignable from org.jboss.cache.notifications.event.TransactionCompletedEvent .

  • @ViewChanged - methods annotated such receive a notification when the group structure of the cluster changes. Methods need to accept a parameter type which is assignable from org.jboss.cache.notifications.event.ViewChangedEvent .

  • @CacheBlocked - methods annotated such receive a notification when the cluster requests that cache operations are blocked for a state transfer event. Methods need to accept a parameter type which is assignable from org.jboss.cache.notifications.event.CacheBlockedEvent .

  • @CacheUnblocked - methods annotated such receive a notification when the cluster requests that cache operations are unblocked after a state transfer event. Methods need to accept a parameter type which is assignable from org.jboss.cache.notifications.event.CacheUnblockedEvent .

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

Example:


   @CacheListener
   public class MyListener
   {

      @CacheStarted
      @CacheStopped
      public void cacheStartStopEvent(Event e)
      {
         switch (e.getType())
         {
            case Event.Type.CACHE_STARTED:
               System.out.println("Cache has started");
               break;
            case Event.Type.CACHE_STOPPED:
               System.out.println("Cache has stopped");
               break;
         }
      }

      @NodeCreated
      @NodeRemoved
      @NodeVisited
      @NodeModified
      @NodeMoved
      public void logNodeEvent(NodeEvent ne)
      {
         log("An event on node " + ne.getFqn() + " has occured");
      }
   }

         

2.8. Using Cache Loaders

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.

  • org.jboss.cache.loader.FileCacheLoader - a basic, filesystem based cache loader that persists data to disk. Non-transactional and not very performant, but a very simple solution. Used mainly for testing and not recommended for production use.
  • org.jboss.cache.loader.JDBCCacheLoader - uses a JDBC connection to store data. Connections could be created and maintained in an internal pool (uses the c3p0 pooling library) or from a configured DataSource. The database this CacheLoader connects to could be local or remotely located.
  • org.jboss.cache.loader.BdbjeCacheLoader - uses Oracle's BerkeleyDB file-based transactional database to persist data. Transactional and very performant, but potentially restrictive license.
  • org.jboss.cache.loader.JdbmCacheLoader - an upcoming open source alternative to the BerkeleyDB.
  • org.jboss.cache.loader.tcp.TcpCacheLoader - uses a TCP socket to "persist" data to a remote cluster, using a "far cache" pattern. [1]
  • org.jboss.cache.loader.ClusteredCacheLoader - used as a "read-only" CacheLoader, where other nodes in the cluster are queried for state.

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

2.9. Using Eviction Policies

Eviction policies are the counterpart to CacheLoaders. They are necessary to make sure the cache does not run out of memory and when the cache starts to fill, the eviction algorithm running in a separate thread offloads in-memory state to the CacheLoader and frees up memory. 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:

  • org.jboss.cache.eviction.LRUPolicy - an eviction policy that evicts the least recently used nodes when thresholds are hit.
  • org.jboss.cache.eviction.LFUPolicy - an eviction policy that evicts the least frequently used nodes when thresholds are hit.
  • org.jboss.cache.eviction.MRUPolicy - an eviction policy that evicts the most recently used nodes when thresholds are hit.
  • org.jboss.cache.eviction.FIFOPolicy - an eviction policy that creates a first-in-first-out queue and evicts the oldest nodes when thresholds are hit.
  • org.jboss.cache.eviction.ExpirationPolicy - an eviction policy that selects nodes for eviction based on an expiry time each node is configured with.
  • org.jboss.cache.eviction.ElementSizePolicy - an eviction policy that selects nodes for eviction based on the number of key/value pairs held in the node.

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



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