FileDocCategorySizeDatePackage
StatefulTreeCache.javaAPI DocJBoss 4.2.121683Fri Jul 13 20:53:48 BST 2007org.jboss.ejb3.cache.tree

StatefulTreeCache

public class StatefulTreeCache extends Object implements org.jboss.ejb3.cache.ClusteredStatefulCache
Clustered SFSB cache that uses JBoss Cache to cache and replicate bean contexts.
author
Bill Burke
author
Brian Stansberry
version
$Revision: 62818 $

Fields Summary
private static final int
FQN_SIZE
private static final int
DEFAULT_BUCKET_COUNT
private static final String[]
DEFAULT_HASH_BUCKETS
private static org.jboss.cache.config.Option
LOCAL_ONLY_OPTION
private static org.jboss.cache.config.Option
GRAVITATE_OPTION
private ThreadLocal
localActivity
private Logger
log
private org.jboss.ejb3.Pool
pool
private WeakReference
classloader
private org.jboss.cache.TreeCache
cache
private org.jboss.cache.Fqn
cacheNode
private ClusteredStatefulCacheListener
listener
private org.jboss.cache.eviction.RegionManager
evictRegionManager
public static long
MarkInUseWaitTime
protected String[]
hashBuckets
protected int
createCount
protected int
passivatedCount
protected int
removeCount
protected long
removalTimeout
protected RemovalTimeoutTask
removalTask
protected boolean
running
protected Map
beans
protected org.jboss.ejb3.EJBContainer
ejbContainer
Constructors Summary
Methods Summary
private java.lang.RuntimeExceptionconvertToRuntimeException(org.jboss.cache.CacheException e)
Creates a RuntimeException, but doesn't pass CacheException as the cause as it is a type that likely doesn't exist on a client. Instead creates a RuntimeException with the original exception's stack trace.

      RuntimeException re = new RuntimeException(e.getClass().getName() + " " + e.getMessage());
      re.setStackTrace(e.getStackTrace());
      return re;
   
public org.jboss.ejb3.stateful.StatefulBeanContextcreate()


     
   
      StatefulBeanContext ctx = null;
      try
      {
         ctx = (StatefulBeanContext) pool.get();
         if (log.isTraceEnabled())
         {
            log.trace("Caching context " + ctx.getId() + " of type " + ctx.getClass());
         }
         putInCache(ctx);
         ctx.setInUse(true);
         ctx.lastUsed = System.currentTimeMillis();
         ++createCount;
         beans.put(ctx.getId(), ctx.lastUsed);
      }
      catch (EJBException e)
      {
         throw e;
      }
      catch (Exception e)
      {
         throw new EJBException(e);
      }
      return ctx;
   
public org.jboss.ejb3.stateful.StatefulBeanContextcreate(java.lang.Class[] initTypes, java.lang.Object[] initValues)

      StatefulBeanContext ctx = null;
      try
      {
         ctx = (StatefulBeanContext) pool.get(initTypes, initValues);
         if (log.isTraceEnabled())
         {
            log.trace("Caching context " + ctx.getId() + " of type " + ctx.getClass());
         }
         putInCache(ctx);
         ctx.setInUse(true);
         ctx.lastUsed = System.currentTimeMillis();
         ++createCount;
         beans.put(ctx.getId(), ctx.lastUsed);
      }
      catch (EJBException e)
      {
         throw e;
      }
      catch (Exception e)
      {
         throw new EJBException(e);
      }
      return ctx;
   
public voidfinished(org.jboss.ejb3.stateful.StatefulBeanContext ctx)

      synchronized (ctx)
      {
         ctx.setInUse(false);
         ctx.lastUsed = System.currentTimeMillis();
         beans.put(ctx.getId(), ctx.lastUsed);
         // OK, it is free to passivate now.
         evictRegionManager.unmarkNodeCurrentlyInUse(getFqn(ctx.getId()));
      }
   
public org.jboss.ejb3.stateful.StatefulBeanContextget(java.lang.Object key)

      return get(key, true);
   
public org.jboss.ejb3.stateful.StatefulBeanContextget(java.lang.Object key, boolean markInUse)

      StatefulBeanContext entry = null;
      Fqn id = getFqn(key);
      Boolean active = localActivity.get();
      try
      {
         localActivity.set(Boolean.TRUE);
         Option opt = new Option();
         opt.setForceDataGravitation(true);
         entry = (StatefulBeanContext) cache.get(id, "bean", opt);
      }
      catch (CacheException e)
      {
         RuntimeException re = convertToRuntimeException(e);
         throw re;
      }
      finally
      {
         localActivity.set(active);
      }
      
      if (entry == null)
      {
         throw new NoSuchEJBException("Could not find stateful bean: " + key);
      }
      else if (markInUse && entry.isRemoved())
      {
         throw new NoSuchEJBException("Could not find stateful bean: " + key + 
                                      " (bean was marked as removed)");
      }
      
      entry.postReplicate();
      
      if (markInUse)
      {
         entry.setInUse(true);
         
         // Mark the Fqn telling the eviction thread not to passivate it yet.
         // Note the Fqn we use is relative to the region!
         evictRegionManager.markNodeCurrentlyInUse(id, MarkInUseWaitTime);
         entry.lastUsed = System.currentTimeMillis();
         beans.put(key, entry.lastUsed);
      }
      
      if(log.isTraceEnabled())
      {
         log.trace("get: retrieved bean with cache id " +id.toString());
      }
      
      return entry;
   
public intgetAvailableCount()

      return -1;
   
public intgetCacheSize()

      int count = 0;
	   try 
	   {
          Set children = null;
          for (int i = 0; i < hashBuckets.length; i++)
          {
             children = cache.getChildrenNames(new Fqn(cacheNode, hashBuckets[i]));
             count += (children == null ? 0 : children.size());
          }
		  count = count - passivatedCount;
	   } 
       catch (CacheException e)
	   {
		   log.error("Caught exception calculating cache size", e);
           count = -1;
	   }
	   return count;
   
public intgetCreateCount()

	   return createCount;
   
public intgetCurrentSize()

      return getCacheSize();
   
protected org.w3c.dom.ElementgetElementConfig(java.lang.String regionName, long timeToLiveSeconds, int maxNodes)

      String xml = "<region name=\"" +regionName +"\" policyClass=\"org.jboss.ejb3.cache.tree.AbortableLRUPolicy\">\n" +
               "<attribute name=\"maxNodes\">" +maxNodes +"</attribute>\n" +
               "<attribute name=\"timeToLiveSeconds\">"+ timeToLiveSeconds +"</attribute>\n" +
               "</region>";
      return XmlHelper.stringToElement(xml);
   
private org.jboss.cache.FqngetFqn(java.lang.Object id)

      String beanId = id.toString();
      int index;
      if (id instanceof GUID)
      {
         index = (id.hashCode()& 0x7FFFFFFF) % hashBuckets.length;
      }
      else
      {
         index = (beanId.hashCode()& 0x7FFFFFFF) % hashBuckets.length;
      }
      
      return new Fqn(cacheNode, hashBuckets[index], beanId);
   
public intgetMaxSize()

      return -1;
   
public intgetPassivatedCount()

	   return passivatedCount;
   
public intgetRemoveCount()

      return removeCount;
   
public intgetTotalSize()

      return beans.size();
   
public voidinitialize(org.jboss.ejb3.Container container)

      this.ejbContainer = (EJBContainer) container;
      
      log = Logger.getLogger(getClass().getName() + "." + this.ejbContainer.getEjbName());
      
      this.pool = this.ejbContainer.getPool();
      ClassLoader cl = this.ejbContainer.getClassloader();
      this.classloader = new WeakReference<ClassLoader>(cl);
      
      CacheConfig config = (CacheConfig) this.ejbContainer.resolveAnnotation(CacheConfig.class);
      MBeanServer server = MBeanServerLocator.locateJBoss();
      ObjectName cacheON = new ObjectName(config.name());
      TreeCacheMBean mbean = (TreeCacheMBean) MBeanProxyExt.create(TreeCacheMBean.class, cacheON, server);
      cache = (TreeCache) mbean.getInstance();
      
      cacheNode = new Fqn(new Object[] { this.ejbContainer.getDeploymentQualifiedName() });

      // Try to create an eviction region per ejb
      evictRegionManager = cache.getEvictionRegionManager();
      Element element = getElementConfig(cacheNode.toString(), config.idleTimeoutSeconds(),
              config.maxSize());
      Region region = evictRegionManager.createRegion(cacheNode, element);
      
      cache.registerClassLoader(cacheNode.toString(), cl);
      cache.activateRegion(cacheNode.toString());
      
      log.debug("initialize(): create eviction region: " +region + " for ejb: " +this.ejbContainer.getEjbName());
   
      removalTimeout = config.removalTimeoutSeconds();
      if (removalTimeout > 0)
         removalTask = new RemovalTimeoutTask("SFSB Removal Thread - " + this.ejbContainer.getObjectName().getCanonicalName());
   
private voidputInCache(org.jboss.ejb3.stateful.StatefulBeanContext ctx)

      Boolean active = localActivity.get();
      try
      {
         localActivity.set(Boolean.TRUE);
         ctx.preReplicate();
         cache.put(getFqn(ctx.getId()), "bean", ctx);
         ctx.markedForReplication = false;
      }
      finally
      {
         localActivity.set(active);
      }
      
   
public voidremove(java.lang.Object key)

      Fqn id = getFqn(key);
      try
      {
         if(log.isTraceEnabled())
         {
            log.trace("remove: cache id " +id.toString());
         }
         
         Option opt = new Option();
         opt.setForceDataGravitation(true);
         StatefulBeanContext ctx = (StatefulBeanContext) cache.get(id, "bean", opt); 
         
         if (ctx != null)
         {
            if (!ctx.isRemoved())
               pool.remove(ctx);
            
            if (ctx.getCanRemoveFromCache())
            {
               // Do a cluster-wide removal of the ctx
               cache.remove(id);
            }
            else 
            {
               // We can't remove the ctx as it contains live nested beans
               // But, we must replicate it so other nodes know the parent is removed!
               putInCache(ctx);
            }
            
            ++removeCount;
            beans.remove(key);
         }
      }
      catch (CacheException e)
      {
         RuntimeException re = convertToRuntimeException(e);
         throw re;
      }
   
public voidreplicate(org.jboss.ejb3.stateful.StatefulBeanContext ctx)

      // StatefulReplicationInterceptor should only pass us the ultimate
      // parent context for a tree of nested beans, which should always be
      // a standard StatefulBeanContext
      if (ctx instanceof NestedStatefulBeanContext)
      {
         throw new IllegalArgumentException("Received unexpected replicate call for nested context " + ctx.getId());
      }
      
      try
      {
         putInCache(ctx);
      }
      catch (CacheException e)
      {
         RuntimeException re = convertToRuntimeException(e);
         throw re;
      }
   
public voidstart()

      // register to listen for cache events
      
      // TODO this approach may not be scalable when there are many beans 
      // since then we will need to go thru N listeners to figure out which 
      // one this event belongs to. Consider having a singleton listener
      listener = new ClusteredStatefulCacheListener();
      cache.addTreeCacheListener(listener);
      
      if (removalTask != null)
         removalTask.start();
      
      running = true;
   
public voidstop()

      running = false;
      
      // Remove the listener
      cache.removeTreeCacheListener(listener);

      try {
         // Remove locally. We do this to clean up the persistent store,
         // which is not affected by the inactivateRegion call below.
         Option opt = new Option();
      	 opt.setCacheModeLocal(true);
         cache.remove(cacheNode, opt);
      } 
      catch (CacheException e) 
      {
         log.error("Stop(): can't remove bean from the underlying distributed cache");
      }      
      
      try
      {
         cache.inactivateRegion(cacheNode.toString());
      }
      catch (Exception e)
      {
         log.error("Caught exception inactivating region " + cacheNode, e);
      }
      try
      {
         cache.unregisterClassLoader(cacheNode.toString());
      }
      catch (RegionNotFoundException e)
      {
         log.error("Caught exception unregistering classloader from  region " + cacheNode, e);
      }

      // Remove the eviction region
      RegionManager rm = cache.getEvictionRegionManager();
      rm.removeRegion(cacheNode);

      log.debug("stop(): StatefulTreeCache stopped successfully for " +cacheNode);