JBoss.orgCommunity Documentation

Chapter 2. User API

2.1. API Classes
2.2. Instantiating and Starting the Cache
2.3. Caching and Retrieving Data
2.3.1. Organizing Your Data and Using the Node Structure
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

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. The cache organizes data into a tree structure, made up of nodes. 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.

Note that the diagram above only depicts some of the more popular API methods. Reviewing the javadoc for the above interfaces is the best way to learn the API. Below, we cover some of the main points.

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));
      

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 Strings 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 factory methods; 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 abc = Fqn.fromString("/people/Smith/Joe/");
        
   // Here we want to use types other than String
   Fqn acctFqn = Fqn.fromElements("accounts", "NY", new Integer(12345));
     

Note that

Fqn f = Fqn.fromElements("a", "b", "c");

is the same as

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

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 network sockets and maintenance threads 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).

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.