Package org.jboss.cache.interceptors

Source Code of org.jboss.cache.interceptors.CacheLoaderInterceptor

package org.jboss.cache.interceptors;

import org.jboss.cache.CacheException;
import org.jboss.cache.Fqn;
import org.jboss.cache.InvocationContext;
import org.jboss.cache.Node;
import org.jboss.cache.NodeSPI;
import org.jboss.cache.config.Configuration;
import static org.jboss.cache.config.Configuration.CacheMode;
import org.jboss.cache.factories.annotations.Inject;
import org.jboss.cache.factories.annotations.Start;
import org.jboss.cache.loader.CacheLoader;
import org.jboss.cache.loader.CacheLoaderManager;
import org.jboss.cache.lock.NodeLock;
import org.jboss.cache.marshall.MethodCall;
import org.jboss.cache.marshall.MethodCallFactory;
import org.jboss.cache.marshall.MethodDeclarations;
import org.jboss.cache.transaction.GlobalTransaction;
import org.jboss.cache.transaction.TransactionEntry;
import org.jboss.cache.transaction.TransactionTable;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;

/**
* Loads nodes that don't exist at the time of the call into memory from the CacheLoader
*
* @author Bela Ban
* @version $Id: CacheLoaderInterceptor.java 4986 2008-01-04 16:55:01Z manik.surtani@jboss.com $
*/
public class CacheLoaderInterceptor extends MethodDispacherInterceptor implements CacheLoaderInterceptorMBean
{
   private long m_cacheLoads = 0;
   private long m_cacheMisses = 0;
   private TransactionTable txTable = null;
   protected boolean isActivation = false;
   protected CacheLoader loader;
   protected CacheLoaderManager clm;
   protected boolean usingOptimisticInvalidation = false;

   public CacheLoaderInterceptor()
   {
      initLogger();
   }

   /**
    * True if CacheStoreInterceptor is in place.
    * This allows us to skip loading keys for remove(Fqn, key) and put(Fqn, key).
    * It also affects removal of node data and listing children.
    */
   protected boolean useCacheStore = true;

   @Inject
   protected void injectDependencies(TransactionTable txTable, CacheLoaderManager clm, Configuration configuration)
   {
      this.txTable = txTable;
      this.clm = clm;
      CacheMode mode = configuration.getCacheMode();
      usingOptimisticInvalidation = configuration.isNodeLockingOptimistic() && mode.isInvalidation();
   }

   @Start
   protected void startInterceptor()
   {
      loader = clm.getCacheLoader();
   }

   @Override
   protected Object handlePutDataEraseMethod(InvocationContext ctx, GlobalTransaction gt, Fqn fqn, Map newData, boolean createUndoOps, boolean eraseContents) throws Throwable
   {
      if (fqn != null)
      {
         loadIfNeeded(ctx, fqn, null, false, true, false, ctx.getMethodCall(), getTransactionEntry(ctx), false, false, false);
      }
      return nextInterceptor(ctx);
   }

   @Override
   protected Object handlePutDataMethod(InvocationContext ctx, GlobalTransaction tx, Fqn fqn, Map data, boolean createUndoOps) throws Throwable
   {
      if (fqn != null)
      {
         loadIfNeeded(ctx, fqn, null, false, true, false, ctx.getMethodCall(), getTransactionEntry(ctx), false, false, false);
      }
      return nextInterceptor(ctx);
   }

   @Override
   protected Object handlePutForExternalReadMethod(InvocationContext ctx, GlobalTransaction tx, Fqn fqn, Object key, Object value) throws Throwable
   {
      if (fqn != null)
      {
         loadIfNeeded(ctx, fqn, key, false, useCacheStore, !useCacheStore, ctx.getMethodCall(), getTransactionEntry(ctx), false, false, false);
      }
      return nextInterceptor(ctx);
   }

   @Override
   protected Object handlePutKeyValueMethod(InvocationContext ctx, GlobalTransaction gtx, Fqn fqn, Object key, Object value, boolean createUndoOps) throws Throwable
   {
      if (fqn != null)
      {
         loadIfNeeded(ctx, fqn, key, false, useCacheStore, !useCacheStore, ctx.getMethodCall(), getTransactionEntry(ctx), false, false, false);
      }
      return nextInterceptor(ctx);
   }

   @Override
   protected Object handleMoveMethod(InvocationContext ctx, Fqn from, Fqn to) throws Throwable
   {
      if (from != null)
      {
         if (to != null)
         {
            loadIfNeeded(ctx, to, null, false, false, true, ctx.getMethodCall(), getTransactionEntry(ctx), false, true, false);
         }
         loadIfNeeded(ctx, from, null, false, false, true, ctx.getMethodCall(), getTransactionEntry(ctx), true, true, false);
      }

      return nextInterceptor(ctx);
   }

   @Override
   protected Object handleAddChildMethod(InvocationContext ctx, GlobalTransaction tx, Fqn parentFqn, Object childName, Node cn, boolean createUndoOps) throws Throwable
   {

      if (parentFqn != null)
      {
         loadIfNeeded(ctx, parentFqn, null, false, false, false, ctx.getMethodCall(), getTransactionEntry(ctx), false, false, false);
      }

      return nextInterceptor(ctx);
   }

   @Override
   protected Object handleGetKeyValueMethod(InvocationContext ctx, Fqn fqn, Object key, boolean sendNodeEvent) throws Throwable
   {
      if (fqn != null)
      {
         loadIfNeeded(ctx, fqn, key, false, false, true, ctx.getMethodCall(), getTransactionEntry(ctx), false, false, false);
      }
      return nextInterceptor(ctx);
   }

   @Override
   protected Object handleGetNodeMethod(InvocationContext ctx, Fqn fqn) throws Throwable
   {
      if (fqn != null)
      {
         loadIfNeeded(ctx, fqn, null, false, false, true, ctx.getMethodCall(), getTransactionEntry(ctx), false, false, !usingOptimisticInvalidation);
      }
      return nextInterceptor(ctx);
   }

   @Override
   protected Object handleGetChildrenNamesMethod(InvocationContext ctx, Fqn fqn) throws Throwable
   {
      if (fqn != null)
      {
         loadIfNeeded(ctx, fqn, null, false, false, false, ctx.getMethodCall(), getTransactionEntry(ctx), false, false, true);
      }
      return nextInterceptor(ctx);
   }

   @Override
   protected Object handleReleaseAllLocksMethod(InvocationContext ctx, Fqn fqn) throws Throwable
   {
      if (fqn != null)
      {
         loadIfNeeded(ctx, fqn, null, false, false, true, ctx.getMethodCall(), getTransactionEntry(ctx), false, false, false);
      }
      return nextInterceptor(ctx);
   }

   @Override
   protected Object handlePrintMethod(InvocationContext ctx, Fqn fqn) throws Throwable
   {
      if (fqn != null)
      {
         loadIfNeeded(ctx, fqn, null, false, false, true, ctx.getMethodCall(), getTransactionEntry(ctx), false, false, false);
      }
      return nextInterceptor(ctx);
   }

   @Override
   protected Object handleGetKeysMethod(InvocationContext ctx, Fqn fqn) throws Throwable
   {
      if (fqn != null)
      {
         loadIfNeeded(ctx, fqn, null, true, false, true, ctx.getMethodCall(), getTransactionEntry(ctx), false, false, false);
      }
      return nextInterceptor(ctx);
   }

   @Override
   protected Object handleGetDataMapMethod(InvocationContext ctx, Fqn fqn) throws Throwable
   {
      if (fqn != null)
      {
         loadIfNeeded(ctx, fqn, null, true, false, true, ctx.getMethodCall(), getTransactionEntry(ctx), false, false, false);
      }
      return nextInterceptor(ctx);
   }

   @Override
   protected Object handleRollbackMethod(InvocationContext ctx, GlobalTransaction globalTransaction) throws Throwable
   {
      // clean up nodesCreated map
      if (trace) log.trace("Removing temporarily created nodes from treecache");

      // this needs to be done in reverse order.
      List list = getTransactionEntry(ctx).getDummyNodesCreatedByCacheLoader();
      if (list != null && list.size() > 0)
      {
         ListIterator i = list.listIterator(list.size());
         while (i.hasPrevious())
         {
            Fqn fqn = (Fqn) i.previous();
            try
            {
               cache.evict(fqn, false);
            }
            catch (CacheException e)
            {
               if (trace) log.trace("Unable to evict node " + fqn, e);
            }
         }
      }
      return nextInterceptor(ctx);
   }

   @Override
   protected Object handleRemoveNodeMethod(InvocationContext ctx, GlobalTransaction tx, Fqn fqn, boolean createUndoOps) throws Throwable
   {
      if (cache.getConfiguration().isNodeLockingOptimistic() && fqn != null)
      {
         loadIfNeeded(ctx, fqn, null, false, false, false, ctx.getMethodCall(), getTransactionEntry(ctx), false, false, false);
      }
      return nextInterceptor(ctx);
   }

   @Override
   protected Object handleRemoveKeyMethod(InvocationContext ctx, GlobalTransaction tx, Fqn fqn, Object key, boolean createUndoOps) throws Throwable
   {
      if (fqn != null && !useCacheStore)
      {
         loadIfNeeded(ctx, fqn, key, false, false, false, ctx.getMethodCall(), getTransactionEntry(ctx), false, false, false);
      }
      return nextInterceptor(ctx);
   }

   @Override
   protected Object handleRemoveDataMethod(InvocationContext ctx, GlobalTransaction tx, Fqn fqn, boolean createUndoOps) throws Throwable
   {
      if (fqn != null && !useCacheStore)
      {
         loadIfNeeded(ctx, fqn, null, false, true, false, ctx.getMethodCall(), getTransactionEntry(ctx), false, false, false);
      }
      return nextInterceptor(ctx);
   }

   private void loadIfNeeded(InvocationContext ctx, Fqn fqn, Object key, boolean allKeys, boolean initNode, boolean acquireLock, MethodCall m, TransactionEntry entry, boolean recursive, boolean isMove, boolean bypassLoadingData) throws Throwable
   {
      NodeSPI n = peekNode(ctx, fqn, false, true, true);

      boolean mustLoad = mustLoad(n, key, allKeys);
      if (trace)
      {
         log.trace("load element " + fqn + " mustLoad=" + mustLoad);
      }
      if (mustLoad)
      {
         if (initNode)
         {
            n = createTempNode(fqn, entry);
         }

         // Only attempt to acquire this lock if we need to - i.e., if
         // the lock hasn't already been acquired by the Lock
         // interceptor.  CRUD methods (put, remove) would have acquired
         // this lock - even if the node is not in memory and needs to be
         // loaded.  Non-CRUD methods (put) would NOT have acquired this
         // lock so if we are to load the node from cache loader, we need
         // to acquire a write lock here.  as a 'catch-all', DO NOT
         // attempt to acquire a lock here *anyway*, even for CRUD
         // methods - this leads to a deadlock when you have threads
         // simultaneously trying to create a node.  See
         // org.jboss.cache.loader.deadlock.ConcurrentCreationDeadlockTest
         // - Manik Surtani (21 March 2006)
         if (acquireLock)
         {
            lock(fqn, NodeLock.LockType.WRITE, false);// non-recursive for now
         }

//         if (!initNode && !wasRemovedInTx(fqn, ctx.getGlobalTransaction()))
         if (!wasRemovedInTx(fqn, ctx.getGlobalTransaction()))
         {
            if (bypassLoadingData)
            {
               if (n == null && loader.exists(fqn))
               {
                  // just create a dummy node in memory
                  n = createTempNode(fqn, entry);
               }
            }
            else
            {
               n = loadNode(ctx, fqn, n, entry);
            }
         }
      }

      // The complete list of children aren't known without loading them
      if (recursive || m.getMethodId() == MethodDeclarations.getChildrenNamesMethodLocal_id)
      {
         loadChildren(fqn, n, recursive, isMove);
      }
   }

   /**
    * Load the children.
    *
    * @param node may be null if the node was not found.
    */
   private void loadChildren(Fqn fqn, NodeSPI node, boolean recursive, boolean isMove) throws Throwable
   {

      if (node != null && node.isChildrenLoaded())
      {
         if (trace) log.trace("Children already loaded!");
         return;
      }
      Set children_names = loader.getChildrenNames(fqn);

      if (trace)
      {
         log.trace("load children " + fqn + " children=" + children_names);
      }

      // For getChildrenNames null means no children
      if (children_names == null)
      {
         if (node != null)
         {
            if (useCacheStore)
            {
               node.removeChildrenDirect();//getChildrenMapDirect().clear();
            }
            node.setChildrenLoaded(true);
         }
         return;
      }

      // Create if node had not been created already
      if (node == null)
      {
         node = createNodes(fqn, null);// dont care about local transactions
      }

      // Create one DataNode per child, mark as UNINITIALIZED
      for (Object name : children_names)
      {
         Fqn child_fqn = new Fqn(name);// this is a RELATIVE Fqn!!

         // create child if it didn't exist
         NodeSPI child = node.addChildDirect(child_fqn);
         if ((isMove || isActivation) && recursive)
         {
            // load data for children as well!
            child.setInternalState(loader.get(child.getFqn()));
            child.setDataLoaded(true);
         }

         // why are we doing this?!??
//         else
//         {
//            child.setDataLoaded(false);
//         }

         if (recursive)
         {
            loadChildren(child.getFqn(), child, true, isMove);
         }
      }
      lock(fqn, recursive ? NodeLock.LockType.WRITE : NodeLock.LockType.READ, true);// recursive=true: lock entire subtree
      node.setChildrenLoaded(true);
   }

   private boolean mustLoad(NodeSPI n, Object key, boolean allKeys)
   {
      if (n == null)
      {
         if (trace) log.trace("must load, node null");
         return true;
      }

      // check this first!!!
      if (!n.isValid() && cache.getConfiguration().isNodeLockingOptimistic())
      {
         // attempt to load again; this only happens if we have tombstones lying around, or we are using invalidation.
         if (trace) log.trace("loading again from cache loader since in-memory node is marked as invalid");
         return true;
      }

      // JBCACHE-1172 Skip single-key optimization if request needs all keys
      if (!allKeys)
      {
         // if we are not looking for a specific key don't bother loading!
         if (key == null)
         {
            if (trace) log.trace("don't load, key requested is null");
            return false;
         }
         if (n.getKeysDirect().contains(key))
         {
            if (trace) log.trace("don't load, already have necessary key in memory");
            return false;
         }
      }
      if (!n.isDataLoaded())
      {
         if (trace) log.trace("must Load, uninitialized");
         return true;
      }
      return false;
   }

   public long getCacheLoaderLoads()
   {
      return m_cacheLoads;
   }

   public long getCacheLoaderMisses()
   {
      return m_cacheMisses;
   }

   @Override
   public void resetStatistics()
   {
      m_cacheLoads = 0;
      m_cacheMisses = 0;
   }

   @Override
   public Map<String, Object> dumpStatistics()
   {
      Map<String, Object> retval = new HashMap<String, Object>();
      retval.put("CacheLoaderLoads", m_cacheLoads);
      retval.put("CacheLoaderMisses", m_cacheMisses);
      return retval;
   }

   protected void lock(Fqn fqn, NodeLock.LockType lock_type, boolean recursive) throws Throwable
   {

      if (configuration.isNodeLockingOptimistic()) return;

      MethodCall m = MethodCallFactory.create(MethodDeclarations.lockMethodLocal_id,
            fqn, lock_type, recursive);

      // hacky
      cache.getInterceptorChain().get(0).invoke(InvocationContext.fromMethodCall(m));
//      nextInterceptor(m);
   }

   private TransactionEntry getTransactionEntry(InvocationContext ctx)
   {
      GlobalTransaction gtx = ctx.getGlobalTransaction();
      if (gtx != null)
      {
         return txTable.get(gtx);
      }
      return null;
   }

   /**
    * Returns true if the FQN or parent was removed during the current
    * transaction.
    * This is O(N) WRT to the number of modifications so far.
    */
   private boolean wasRemovedInTx(Fqn fqn, GlobalTransaction t)
   {
      if (t == null)
      {
         return false;
      }
      TransactionEntry entry = txTable.get(t);
      for (MethodCall m : entry.getCacheLoaderModifications())
      {
         if (m.getMethodId() == MethodDeclarations.removeNodeMethodLocal_id
               && fqn.isChildOrEquals((Fqn) m.getArgs()[1]))
         {
            return true;
         }
      }
      return false;
   }

   /**
    * Loads a node from disk; if it exists creates parent TreeNodes.
    * If it doesn't exist on disk but in memory, clears the
    * uninitialized flag, otherwise returns null.
    */
   private NodeSPI loadNode(InvocationContext ctx, Fqn fqn, NodeSPI n, TransactionEntry entry) throws Exception
   {
      if (trace) log.trace("loadNode " + fqn);
      Map nodeData = loadData(fqn);
      if (nodeData != null)
      {
         if (trace) log.trace("Node data is not null, loading");

         cache.getNotifier().notifyNodeLoaded(fqn, true, Collections.emptyMap(), ctx);
         if (isActivation)
         {
            cache.getNotifier().notifyNodeActivated(fqn, true, Collections.emptyMap(), ctx);
         }

         n = createNodes(fqn, entry);
//         n.clearDataDirect();
         n.setInternalState(nodeData);

         // set this node as valid?
         if (usingOptimisticInvalidation) n.setValid(true, false);

         cache.getNotifier().notifyNodeLoaded(fqn, false, nodeData, ctx);
         if (isActivation)
         {
            cache.getNotifier().notifyNodeActivated(fqn, false, nodeData, ctx);
         }
      }

      if (n != null && !n.isDataLoaded())
      {
         if (trace) log.trace("Setting dataLoaded to true");
         n.setDataLoaded(true);
      }
      return n;
   }

   /**
    * Creates a new memory node in preparation for storage.
    */
   private NodeSPI createTempNode(Fqn fqn, TransactionEntry entry) throws Exception
   {
      NodeSPI n = createNodes(fqn, entry);
      n.setDataLoaded(false);
      if (trace)
      {
         log.trace("createTempNode n " + n);
      }
      return n;
   }


   private NodeSPI createNodes(Fqn fqn, TransactionEntry entry) throws Exception
   {
      Fqn tmp_fqn = Fqn.ROOT;

      int size = fqn.size();

      // root node
      NodeSPI n = cache.getRoot();
      for (int i = 0; i < size; i++)
      {
         Object child_name = fqn.get(i);
         tmp_fqn = new Fqn(tmp_fqn, child_name);

         NodeSPI child_node = findChild(n, child_name);
         boolean last = (i == size - 1);

         if (child_node == null)
         {
            if (last)
            {
               child_node = n.addChildDirect(new Fqn(child_name));
               child_node.setDataLoaded(true);
            }
            else
            {
               child_node = n.addChildDirect(new Fqn(child_name));
               child_node.setDataLoaded(false);
            }

            if (entry != null)
            {
               entry.loadUninitialisedNode(tmp_fqn);
            }
         }

         n = child_node;
      }

      return n;
   }

   private NodeSPI findChild(NodeSPI node, Object child_name)
   {
      Map children = node.getChildrenMapDirect();
      if (children == null) return null;
      return (NodeSPI) children.get(child_name);
   }


   private Map loadData(Fqn fqn) throws Exception
   {

      Map nodeData = loader.get(fqn);
      boolean nodeExists = (nodeData != null);
      if (trace) log.trace("nodeExists " + nodeExists);

      if (configuration.getExposeManagementStatistics() && getStatisticsEnabled())
      {
         if (nodeExists)
         {
            m_cacheLoads++;
         }
         else
         {
            m_cacheMisses++;
         }
      }
      return nodeData;
   }

}
TOP

Related Classes of org.jboss.cache.interceptors.CacheLoaderInterceptor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.