JBoss.orgCommunity Documentation

Chapter 4. Deploying JBoss Cache

4.1. Standalone Use / Programatic Deployment
4.2. JMX-Based Deployment in JBoss AS (JBoss AS 5.x and 4.x)
4.3. Via JBoss Microcontainer (JBoss AS 5.x)
4.4. Binding to JNDI in JBoss AS
4.5. Runtime Management Information
4.5.1. JBoss Cache MBeans
4.5.2. Registering the CacheJmxWrapper with the MBeanServer
4.5.3. JBoss Cache Statistics
4.5.4. Receiving JMX Notifications
4.5.5. Accessing Cache MBeans in a Standalone Environment

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 .

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

If JBoss Cache is run in JBoss AS then the cache can be deployed as an MBean simply by copying a standard cache configuration file to the server's deploy directory. The standard format of JBoss Cache's standard XML configuration file (as shown in the Configuration Reference ) is the same as a JBoss AS MBean deployment descriptor, so the AS's SAR Deployer has no trouble handling it. Also, you don't have to place the configuration file directly in deploy ; you can package it along with other services or JEE components in a SAR or EAR.

In AS 5, if you're using a server config based on the standard all config, then that's all you need to do; all required jars will be on the classpath. Otherwise, you will need to ensure jbosscache.jar and jgroups-all.jar are on the classpath. You may need to add other jars if you're using things like JdbmCacheLoader . The simplest way to do this is to copy the jars from the JBoss Cache distribution's lib directory to the server config's lib directory. You could also package the jars with the configuration file in Service Archive (.sar) file or an EAR.

It is possible to deploy a JBoss Cache 2.0 instance in JBoss AS 4.x (at least in 4.2.0.GA; other AS releases are completely untested). However, the significant API changes between the JBoss Cache 2.x and 1.x releases mean none of the standard AS 4.x clustering services (e.g. http session replication) that rely on JBoss Cache will work with JBoss Cache 2.x. Also, be aware that usage of JBoss Cache 2.x in AS 4.x is not something the JBoss Cache developers are making any significant effort to test, so be sure to test your application well (which of course you're doing anyway.)

Note in the example the value of the mbean element's code attribute: org.jboss.cache.jmx.CacheJmxWrapper . This is the class JBoss Cache uses to handle JMX integration; the Cache itself does not expose an MBean interface. See the JBoss Cache MBeans section for more on the CacheJmxWrapper .

Once your cache is deployed, in order to use it with an in-VM client such as a servlet, a JMX proxy can be used to get a reference to the cache:



   MBeanServer server = MBeanServerLocator.locateJBoss();
   ObjectName on = new ObjectName("jboss.cache:service=Cache");
   CacheJmxWrapperMBean cacheWrapper =
     (CacheJmxWrapperMBean) MBeanServerInvocationHandler.newProxyInstance(server, on,
                                             CacheJmxWrapperMBean.class, false);
   Cache cache = cacheWrapper.getCache();
   Node root = cache.getRoot(); // etc etc
   

The MBeanServerLocator class is a helper to find the (only) JBoss MBean server inside the current JVM. The javax.management.MBeanServerInvocationHandler class' newProxyInstance method creates a dynamic proxy implementing the given interface and uses JMX to dynamically dispatch methods invoked against the generated interface to the MBean. The name used to look up the MBean is the same as defined in the cache's configuration file.

Once the proxy to the CacheJmxWrapper is obtained, the getCache() will return a reference to the Cache itself.

Beginning with AS 5, JBoss AS also 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.

The rules for how to deploy the file, how to package it, how to ensure the required jars are on the classpath, etc. are the same as for a JMX-based deployment .

Following is an example -beans.xml file. If you look in the server/all/deploy directory of an 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>
        
      <!--
              Node locking level : SERIALIZABLE
                                   REPEATABLE_READ (default)
                                   READ_COMMITTED
                                   READ_UNCOMMITTED
                                   NONE
      -->
      <property name="isolationLevel">REPEATABLE_READ</property>

      <!--     Valid modes are LOCAL
                               REPL_ASYNC
                               REPL_SYNC
      -->
      <property name="cacheMode">REPL_SYNC</property>

      <!--  The max amount of time (in milliseconds) we wait until the
            initial state (ie. the contents of the cache) are retrieved from
            existing members in a clustered environment
      -->
      <property name="initialStateRetrievalTimeout">15000</property>

      <!--    Number of milliseconds to wait until all responses for a
              synchronous call have been received.
      -->
      <property name="syncReplTimeout">20000</property>

      <!--  Max number of milliseconds to wait for a lock acquisition -->
      <property name="lockAcquisitionTimeout">15000</property>
        
      <property name="exposeManagementStatistics">true</property>
      
      <!-- Must be true if any entity deployment uses a scoped classloader -->
      <property name="useRegionBasedMarshalling">true</property>
      <!-- Must match the value of "useRegionBasedMarshalling" -->
      <property name="inactiveOnStartup">true</property>

      <!--  Specific eviction policy configurations. This is LRU -->
      <property name="evictionConfig">
         <bean name="ExampleEvictionConfig" 
               class="org.jboss.cache.config.EvictionConfig">
            <property name="defaultEvictionPolicyClass">
               org.jboss.cache.eviction.LRUPolicy
            </property>
            <property name="wakeupIntervalSeconds">5</property>
            <property name="evictionRegionConfigs">
               <list>
                  <bean name="ExampleDefaultEvictionRegionConfig" 
                        class="org.jboss.cache.config.EvictionRegionConfig">
                     <property name="regionName">/_default_</property>
                     <property name="evictionPolicyConfig">
                        <bean name="ExampleDefaultLRUConfig" 
                              class="org.jboss.cache.eviction.LRUConfiguration">
                           <property name="maxNodes">5000</property>
                           <property name="timeToLiveSeconds">1000</property>
                        </bean>
                     </property>
                  </bean>
               </list>
            </property>
         </bean>
      </property>
      
   </bean>
   
   <!-- Factory to build the Cache. -->
   <bean name="DefaultCacheFactory" class="org.jboss.cache.DefaultCacheFactory">      
      <constructor factoryClass="org.jboss.cache.DefaultCacheFactory" 
                   factoryMethod="getInstance"/>
   </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 [2] for details on the above syntax. Basically, each bean element represents an object; most going 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 been described.

With the 1.x JBoss Cache releases, a proxy to the cache could be bound into JBoss AS's JNDI tree using the AS's JRMPProxyFactory service. With JBoss Cache 2.x, this no longer works. An alternative way of doing a similar thing with a POJO (i.e. non-JMX-based) service like a Cache is under development by the JBoss AS team [3] . That feature is not available as of the time of this writing, although it will be completed before AS 5.0.0.GA is released. We will add a wiki page describing how to use it once it becomes available.

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.

JBoss Cache provides an MBean that can be registered with your environments JMX server to allow access to the cache instance via JMX. This MBean is the org.jboss.cache.jmx.CacheJmxWrapper . It is a StandardMBean, so it's MBean interface is org.jboss.cache.jmx.CacheJmxWrapperMBean . This MBean can be used to:

See the CacheJmxWrapperMBean javadoc for more details.

It is important to note a significant architectural difference between JBoss Cache 1.x and 2.x. In 1.x, the old TreeCache class was itself an MBean, and essentially exposed the cache's entire API via JMX. In 2.x, JMX has been returned to it's fundamental role as a management layer. The Cache object itself is completely unaware of JMX; instead JMX functionality is added through a wrapper class designed for that purpose. Furthermore, the interface exposed through JMX has been limited to management functions; the general Cache API is no longer exposed through JMX. For example, it is no longer possible to invoke a cache put or get via the JMX interface.

If a CacheJmxWrapper is registered, JBoss Cache also provides MBeans for each interceptor configured in the cache's interceptor stack. These MBeans are used to capture and expose statistics related to cache operations. They are hierarchically associated with the CacheJmxWrapper MBean and have service names that reflect this relationship. For example, a replication interceptor MBean for the jboss.cache:service=TomcatClusteringCache instance will be accessible through the service named jboss.cache:service=TomcatClusteringCache,cache-interceptor=ReplicationInterceptor .

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

Simplest way to do this is to create your Cache and pass it to the CacheJmxWrapper constructor.



   CacheFactory factory = new DefaultCacheFactory();
   // Build but don't start the cache
   // (although it would work OK if we started it)
   Cache cache = factory.createCache("cache-configuration.xml", false);
   CacheJmxWrapperMBean wrapper = new CacheJmxWrapper(cache);
   MBeanServer server = getMBeanServer(); // however you do it
   ObjectName on = new ObjectName("jboss.cache:service=TreeCache");
   server.registerMBean(wrapper, on);
   // Invoking lifecycle methods on the wrapper results
   // in a call through to the cache
   wrapper.create();
   wrapper.start();
   ... use the cache
   ... on application shutdown
   // Invoking lifecycle methods on the wrapper results
   // in a call through to the cache
   wrapper.stop();
   wrapper.destroy();
            

Alternatively, build a Configuration object and pass it to the CacheJmxWrapper . The wrapper will construct the Cache :



   Configuration config = buildConfiguration(); // whatever it does
   CacheJmxWrapperMBean wrapper = new CacheJmxWrapper(config);
   MBeanServer server = getMBeanServer(); // however you do it
   ObjectName on = new ObjectName("jboss.cache:service=TreeCache");
   server.registerMBean(wrapper, on);
   // Call to wrapper.create() will build the Cache if one wasn't injected
   wrapper.create();
   wrapper.start();
   // Now that it's built, created and started, get the cache from the wrapper
   Cache cache = wrapper.getCache();
   ... use the cache
   ... on application shutdown
   wrapper.stop();
   wrapper.destroy();
            

When you deploy your cache in JBoss AS using a -service.xml file , a CacheJmxWrapper is automatically registered. There is no need to do anything further. The CacheJmxWrapper is accessible from an MBean server through the service name specified in the cache configuration file's mbean element.

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" 
                   factoryMethod="getInstance"/>
   </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 captures statistics in its interceptors and exposes the statistics through interceptor MBeans. Gathering of statistics is enabled by default; this can be disabled for a specific cache instance through the ExposeManagementStatistics configuration attribute. Note that the majority of the statistics are provided by the CacheMgmtInterceptor , so this MBean is the most significant in this regard. If you want to disable all statistics for performance reasons, you set ExposeManagementStatistics to false as this will prevent the CacheMgmtInterceptor from being included in the cache's interceptor stack when the cache is started.

If a CacheJmxWrapper is registered with JMX, the wrapper also ensures that an MBean is registered in JMX for each interceptor that exposes statistics [4] . Management tools can then access those MBeans to examine the statistics. See the section in the JMX Reference chapter pertaining to the statistics that are made available via JMX.

The name under which the interceptor MBeans will be registered is derived by taking the ObjectName under which the CacheJmxWrapper is registered and adding a cache-interceptor attribute key whose value is the non-qualified name of the interceptor class. So, for example, if the CacheJmxWrapper were registered under jboss.cache:service=TreeCache , the name of the CacheMgmtInterceptor MBean would be jboss.cache:service=TreeCache,cache-interceptor=CacheMgmtInterceptor .

Each interceptor's MBean exposes a StatisticsEnabled attribute that can be used to disable maintenance of statistics for that interceptor. In addition, each interceptor MBean provides the following common operations and attributes.

  • dumpStatistics - returns a Map containing the interceptor's attributes and values.
  • resetStatistics - resets all statistics maintained by the interceptor.
  • setStatisticsEnabled(boolean) - allows statistics to be disabled for a specific interceptor.

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.



[2] http://labs.jboss.com/jbossmc/docs

[3] http://jira.jboss.com/jira/browse/JBAS-4456

[4] 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.