Tuesday, December 8, 2009

Newbie's Guide to using JBoss Cache in JBoss AS


The promise of JBoss Cache is that it makes distributed caching really easy. But it can be hard to figure out how to use it, because many of the docs are concerned with installation and configuration. Yet the promise of JBoss AS is that everything is already installed, configured and integrated together, so how do you skip all that and get straight to The Good Stuff?

So, this is the blog I was looking for:

If Today You Are Running JBoss AS And Have...

public class MyBeanOrControllerOrWhatever {

   private final static Map MY_CACHE = new HashMap();

   public Object lookupSomething( Object key ) {

      Object value = MY_CACHE.get( key );

      if ( value == null ) {
         value = lookupUncached();
         MY_CACHE.put( key, value );
      }

      return value;
   }
}
(ie. you are using a static Map for your cache - which is fine but will not distribute across VMs)

...Then Using JBoss Cache You Can Do

public class MyBeanOrControllerOrWhatever {

   public Object lookupSomething( Object key ) {

      CacheManager cacheManager = CacheManagerLocator.getCacheManagerLocator().getCacheManager( null );
      Cache myCache = cacheManager.getCache( "sfsb-cache", true );


      Fqn fqn = Fqn.fromString( MyBeanOrControllerOrWhatever.class.getName() );
      Object value = myCache.get( fqn, key );

      if ( value == null ) {
         value = lookupUncached();
         myCache.put( fqn, key, value );
      }

      return value;
   }
}

Where:
  • fqn is a unique key in the JBoss Cache tree so that you don't conflict with other caches

  • sfsb-cache is a pre-configured JBoss AS cache that replicates asynchronously and never expires

  • CacheManagerLocator is in common/lib/jboss-ha-server-api.jar

  • CacheManager is in server/all/lib/jbosscache-core.jar

You'll also need to edit server/all/deploy/cluster/jboss-cache-manager.sar/META-INF/jboss-cache-manager-jboss-beans.xml to pre-start the cache:

<!-- Start these caches as part of the start of this CacheManager -->
<property name="eagerStartCaches">
   <set>
      <value>sfsb-cache</value>
   </set>
</property>

This should be enough to get you working and get you excited. All this actually is in the JBoss Cache docs, just waaaay at the back in Chapter 11. After a while you should probably look at configuring your own cache rather than hijacking sfsb-cache, as that's not strictly meant for your own stuff. Here's a simple one that's kind of a 'distributed Map with max 300 entries':

   <entry>
      <key>my-cache</key>
      <value>
         <bean name="MyCache" class="org.jboss.cache.config.Configuration">
            <property name="isolationLevel">NONE</property>
            <property name="cacheMode">REPL_ASYNC</property>
            <property name="evictionConfig">
               <bean class="org.jboss.cache.config.EvictionConfig">
                  <property name="wakeupInterval">5000</property>
                  <property name="defaultEvictionRegionConfig">
                     <bean class="org.jboss.cache.config.EvictionRegionConfig">
                        <property name="regionName">/</property>
                        <property name="evictionAlgorithmConfig">
                           <bean class="org.jboss.cache.eviction.LRUAlgorithmConfig">
                              <property name="maxNodes">300</property>
                           </bean>
                        </property>
                     </bean>
                  </property>
               </bean>
            </property>
         </bean>
      </value>
   </entry>


Though note that maxNodes refers to the number of Fqn nodes, rather than the Map entries within one of those nodes. So instead of:

myCache.put( fqn, key, value )


You'll have to start doing:

myCache.getChild( fqn ).addChild( Fqn.fromElements( key )).put( key, value )


(ie. create a full-blown child node based on converting each key to an Fqn)

Improvements welcome!

5 comments:

Galder ZamarreƱo said...

Thanks for the article but it's worth noting that if your bean will be accessed in a multi-threaded way, the put might be overriding values set by another thread. In this case, using cache.getNode(fqn).putIfAbsent might be a better option rather than just calling put.

Also, you might wanna look into using Infinispan (JBoss Cache's evolution) which has an Map-like API which is much more user friendly.

sharl said...

Thanks man you post is very helpful i have been searching the web for 2 days now finding lots of jboss junk docs either too old or words that requires decryption.

straight to the point.

Is there a way to use injection for CacheManager?

Richard said...

Sharl,

No worries, glad to help.

For injection, see my forum post.

Richard.

Anonymous said...

I've tried exactly what you are suggesting here without any success.

If I use CacheManagerLocator then I get InvalidParameterException, because returned CacheManager is always null.

I tried to go directly via JNDI context and can see the returned Object is null.

However, I can see my caches eagerly started and I can see reference to java:CacheManager in JMX-CONSOLE. So, it seems that CacheManager bound to JNDI is not initialized or the wrong Object is bound.

I'm running JBoss.5.1.0-GA with ALL configuration, so all the jars should be available there.

I even added CacheManager to my application with no effect.

I could not find any help on this topic, so any suggestions on how to make it work would be greatly appreciated.

Thanks, Vadim

Richard Kennard said...

Vadim,

Not sure I can help you much here, because the instructions above work for me/have worked for others.

If you want to do some consulting hours I could probably take a look at it? Please contact me at richard@kennardconsulting.com

Regards,

Richard.