Package org.jboss.cache.interceptors

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

package org.jboss.cache.interceptors;

import org.jboss.cache.CacheException;
import org.jboss.cache.Fqn;
import org.jboss.cache.InvocationContext;
import org.jboss.cache.Modification;
import org.jboss.cache.NodeSPI;
import org.jboss.cache.config.CacheLoaderConfig;
import org.jboss.cache.factories.annotations.Start;
import org.jboss.cache.loader.CacheLoader;
import org.jboss.cache.marshall.MethodCall;
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 org.jgroups.Address;

import javax.transaction.SystemException;
import javax.transaction.TransactionManager;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
* Writes modifications back to the store on the way out: stores modifications back
* through the CacheLoader, either after each method call (no TXs), or at TX commit.
*
* @author Bela Ban
* @version $Id: CacheStoreInterceptor.java 5413 2008-03-11 21:59:36Z mircea.markus $
*/
public class CacheStoreInterceptor extends MethodDispacherInterceptor implements CacheStoreInterceptorMBean
{

   protected CacheLoaderConfig loaderConfig = null;
   protected TransactionManager tx_mgr = null;
   protected TransactionTable tx_table = null;
   private HashMap m_txStores = new HashMap();
   private Map<GlobalTransaction, Set<Fqn>> preparingTxs = new ConcurrentHashMap<GlobalTransaction, Set<Fqn>>();
   private long m_cacheStores = 0;
   protected CacheLoader loader;


   public CacheStoreInterceptor()
   {
      initLogger();
   }

   @Start
   protected void startInterceptor()
   {
      loader = cache.getCacheLoaderManager().getCacheLoader();
      this.loaderConfig = cache.getCacheLoaderManager().getCacheLoaderConfig();
      tx_mgr = cache.getTransactionManager();
      tx_table = cache.getTransactionTable();
   }

   /**
    * if this is a shared cache loader and the call is of remote origin, pass up the chain
    */
   @Override
   protected boolean skipMethodCall(InvocationContext ctx)
   {
      if (!ctx.isOriginLocal() && loaderConfig.isShared())
      {
         if (trace)
         {
            log.trace("Passing up method call and bypassing this interceptor since the cache loader is shared and this call originated remotely.");
         }
         return true;
      }
      return false;
   }

   @Override
   protected Object handleCommitMethod(InvocationContext ctx, GlobalTransaction gtx) throws Throwable
   {
      if (inTransaction())
      {
         if (trace) log.trace("transactional so don't put stuff in the cloader yet.");
         if (ctx.isCacheLoaderHasMods())
         {
            // this is a commit call.
            if (trace) log.trace("Calling loader.commit() for gtx " + gtx);
            // sync call (a write) on the loader
            // ignore modified FQNs
            // List fqnsModified = getFqnsFromModificationList(tx_table.get(gtx).getCacheLoaderModifications());
            try
            {
               loader.commit(gtx);
            }
            catch (Throwable t)
            {
               preparingTxs.remove(gtx);
               throw t;
            }
            if (configuration.getExposeManagementStatistics() && getStatisticsEnabled())
            {
               Integer puts = (Integer) m_txStores.get(gtx);
               if (puts != null)
               {
                  m_cacheStores = m_cacheStores + puts;
               }
               m_txStores.remove(gtx);
            }
            Object returnValue = nextInterceptor(ctx);
            // persist additional internal state, if any, and then clean up internal resources
            Set<Fqn> affectedFqns = preparingTxs.remove(gtx);
            if (affectedFqns != null)
            {
               storeInternalState(ctx, affectedFqns);
            }
            return returnValue;
         }
         else
         {
            if (trace) log.trace("Commit called with no modifications; ignoring.");
         }
      }
      return nextInterceptor(ctx);
   }

   @Override
   protected Object handleRollbackMethod(InvocationContext ctx, GlobalTransaction gtx) throws Throwable
   {
      if (inTransaction())
      {
         if (trace) log.trace("transactional so don't put stuff in the cloader yet.");
         if (ctx.isCacheLoaderHasMods())
         {
            // this is a rollback method
            if (preparingTxs.containsKey(gtx))
            {
               preparingTxs.remove(gtx);
               loader.rollback(gtx);
            }
            if (configuration.getExposeManagementStatistics() && getStatisticsEnabled())
            {
               m_txStores.remove(gtx);
            }
         }
         else
         {
            if (trace) log.trace("Rollback called with no modifications; ignoring.");
         }
      }
      return nextInterceptor(ctx);
   }

   @Override
   protected Object handleOptimisticPrepareMethod(InvocationContext ctx, GlobalTransaction gtx, List modifications, Map data, Address address, boolean onePhaseCommit) throws Throwable
   {
      if (inTransaction())
      {
         if (trace) log.trace("transactional so don't put stuff in the cloader yet.");
         prepareCacheLoader(gtx, ctx.getMethodCall().isOnePhaseCommitPrepareMehod());
      }
      return nextInterceptor(ctx);
   }

   @Override
   protected Object handlePrepareMethod(InvocationContext ctx, GlobalTransaction gtx, List modification, Address coordinator, boolean onePhaseCommit) throws Throwable
   {
      if (inTransaction())
      {
         if (trace) log.trace("transactional so don't put stuff in the cloader yet.");
         prepareCacheLoader(gtx, ctx.getMethodCall().isOnePhaseCommitPrepareMehod());
      }
      return nextInterceptor(ctx);
   }

   /**
    * remove() methods need to be applied to the CacheLoader before passing up the call: a listener might
    * access an element just removed, causing the CacheLoader to *load* the element before *removing* it.
    */
   @Override
   protected Object handleRemoveNodeMethod(InvocationContext ctx, GlobalTransaction tx, Fqn fqn, boolean createUndoOps) throws Throwable
   {
      if (!inTransaction())
      {
         loader.remove(fqn);
      }
      return nextInterceptor(ctx);
   }

   /**
    * @see #handleRemoveNodeMethod(org.jboss.cache.InvocationContext, org.jboss.cache.transaction.GlobalTransaction, org.jboss.cache.Fqn, boolean)
    */
   @Override
   protected Object handleRemoveKeyMethod(InvocationContext ctx, GlobalTransaction tx, Fqn fqn, Object key, boolean createUndoOps) throws Throwable
   {
      if (!inTransaction())
      {
         Object returnValue = loader.remove(fqn, key);
         nextInterceptor(ctx);
         return returnValue;
      }
      return nextInterceptor(ctx);
   }

   /**
    * @see #handleRemoveNodeMethod(org.jboss.cache.InvocationContext, org.jboss.cache.transaction.GlobalTransaction, org.jboss.cache.Fqn, boolean)
    */
   @Override
   protected Object handleRemoveDataMethod(InvocationContext ctx, GlobalTransaction tx, Fqn fqn, boolean createUndoOps) throws Throwable
   {
      if (!inTransaction())
      {
         loader.removeData(fqn);
         // we need to mark this node as data loaded
         NodeSPI n = peekNode(ctx, fqn, false, false, false);//cache.peek(fqn, false);
         if (n != null)
         {
            n.setDataLoaded(true);
         }
      }
      return nextInterceptor(ctx);
   }

   @Override
   protected Object handleMoveMethod(InvocationContext ctx, Fqn from, Fqn to) throws Throwable
   {
      Object returnValue = nextInterceptor(ctx);
      if (inTransaction())
      {
         return returnValue;
      }
      Fqn newNodeFqn = new Fqn(to, from.getLastElement());
      recursiveMove(from, newNodeFqn);
      loader.remove(from);
      return returnValue;
   }

   @Override
   protected Object handlePutDataEraseMethod(InvocationContext ctx, GlobalTransaction gt, Fqn fqn, Map newData, boolean createUndoOps, boolean eraseContents) throws Throwable
   {
      Object returnValue = nextInterceptor(ctx);
      if (inTransaction())
      {
         return returnValue;
      }
      loader.removeData(fqn);
      // if we are erasing all the data then consider this node loaded
      NodeSPI n = peekNode(ctx, fqn, false, false, false);//cache.peek(fqn, false);
      n.setDataLoaded(true);
      return returnValue;
   }

   @Override
   protected Object handlePutDataMethod(InvocationContext ctx, GlobalTransaction tx, Fqn fqn, Map data, boolean createUndoOps) throws Throwable
   {
      Object returnValue = nextInterceptor(ctx);
      if (inTransaction())
      {
         return returnValue;
      }
      loader.put(fqn, data);
      if (configuration.getExposeManagementStatistics() && getStatisticsEnabled())
      {
         m_cacheStores++;
      }
      return returnValue;
   }

   @Override
   protected Object handlePutForExternalReadMethod(InvocationContext ctx, GlobalTransaction tx, Fqn fqn, Object key, Object value) throws Throwable
   {
      return handlePutKeyValue(ctx, fqn, key, value);
   }

   @Override
   protected Object handlePutKeyValueMethod(InvocationContext ctx, GlobalTransaction gtx, Fqn fqn, Object key, Object value, boolean createUndoOps) throws Throwable
   {
      return handlePutKeyValue(ctx, fqn, key, value);
   }

   private Object handlePutKeyValue(InvocationContext ctx, Fqn fqn, Object key, Object value)
         throws Throwable
   {
      Object returnValue = nextInterceptor(ctx);
      if (inTransaction())
      {
         return returnValue;
      }
      returnValue = loader.put(fqn, key, value);
      if (configuration.getExposeManagementStatistics() && getStatisticsEnabled())
      {
         m_cacheStores++;
      }
      return returnValue;
   }

   private boolean inTransaction() throws SystemException
   {
      return tx_mgr != null && tx_mgr.getTransaction() != null;
   }

   private void storeInternalState(InvocationContext ctx, Set<Fqn> affectedFqns) throws Exception
   {
      if (cache.getConfiguration().isNodeLockingOptimistic())
      {
         for (Fqn f : affectedFqns)
         {
            // NOT going to store tombstones!!
            NodeSPI n = peekNode(ctx, f, false, false, false);
            if (n != null)
            {
               Map internalState = n.getInternalState(true);
               loader.put(f, internalState);
            }
         }
      }
   }

   private void recursiveMove(Fqn fqn, Fqn newFqn) throws Exception
   {
      List fqns = new ArrayList();
      fqns.add(fqn);
      fqns.add(newFqn);
      loader.put(newFqn, loader.get(fqn));
      //recurse
      Set childrenNames = loader.getChildrenNames(fqn);
      if (childrenNames != null)
      {
         for (Object child : childrenNames)
         {
            recursiveMove(new Fqn(fqn, child), new Fqn(newFqn, child));
         }
      }
   }

   public long getCacheLoaderStores()
   {
      return m_cacheStores;
   }

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

   @Override
   public Map<String, Object> dumpStatistics()
   {
      Map<String, Object> retval = new HashMap<String, Object>();
      retval.put("CacheLoaderStores", m_cacheStores);
      return retval;
   }

   private void prepareCacheLoader(GlobalTransaction gtx, boolean onePhase) throws Exception
   {
      List<MethodCall> modifications;
      TransactionEntry entry;
      int txPuts = 0;

      entry = tx_table.get(gtx);
      if (entry == null)
      {
         throw new Exception("entry for transaction " + gtx + " not found in transaction table");
      }
      Set<Fqn> affectedFqns = new HashSet<Fqn>();
      modifications = entry.getCacheLoaderModifications();
      if (modifications.size() == 0)
      {
         if (trace) log.trace("Transaction has not logged any modifications!");
         return;
      }
      if (trace) log.trace("Cache loader modification list: " + modifications);
      List cache_loader_modifications = new ArrayList();
      for (MethodCall methodCall : modifications)
      {
         Modification mod = convertMethodCallToModification(methodCall, affectedFqns);
         cache_loader_modifications.add(mod);

         if (configuration.getExposeManagementStatistics() && getStatisticsEnabled())
         {
            if ((mod.getType() == Modification.ModificationType.PUT_DATA) ||
                  (mod.getType() == Modification.ModificationType.PUT_DATA_ERASE) ||
                  (mod.getType() == Modification.ModificationType.PUT_KEY_VALUE))
            {
               txPuts++;
            }
         }
      }
      if (trace)
      {
         log.trace("Converted method calls to cache loader modifications.  List size: " + cache_loader_modifications.size());
      }
      if (cache_loader_modifications.size() > 0)
      {
         loader.prepare(gtx, cache_loader_modifications, onePhase);

         preparingTxs.put(gtx, affectedFqns);
         if (configuration.getExposeManagementStatistics() && getStatisticsEnabled() && txPuts > 0)
         {
            m_txStores.put(gtx, txPuts);
         }
      }
   }

   /**
    * Converts a method call to a Modification, to be sent to a cache loader.  In addition, adds any affected Fqns to the
    * affectedFqns collection, which is then used by the calling code.
    */
   private Modification convertMethodCallToModification(MethodCall methodCall, Set<Fqn> affectedFqns) throws Exception
   {
      if (trace) log.trace("Converting method call " + methodCall + " to modification.");
      Method method = methodCall.getMethod();
      Object[] args;
      if (method == null)
      {
         throw new Exception("method call has no method: " + methodCall);
      }

      args = methodCall.getArgs();
      Modification mod;
      Fqn fqn = (Fqn) args[1];

      switch (methodCall.getMethodId())
      {
         case MethodDeclarations.putDataMethodLocal_id:
            mod = new Modification(Modification.ModificationType.PUT_DATA,
                  fqn, (Map) args[2]);// data
            break;
         case MethodDeclarations.putDataEraseMethodLocal_id:
            mod = new Modification(Modification.ModificationType.PUT_DATA_ERASE,
                  fqn, (Map) args[2]);// data
            break;
         case MethodDeclarations.putKeyValMethodLocal_id:
            mod = new Modification(Modification.ModificationType.PUT_KEY_VALUE,
                  fqn, args[2], args[3]);// key + value
            break;
         case MethodDeclarations.removeNodeMethodLocal_id:
            mod = new Modification(Modification.ModificationType.REMOVE_NODE,
                  fqn);
            break;
         case MethodDeclarations.removeKeyMethodLocal_id:
            mod = new Modification(Modification.ModificationType.REMOVE_KEY_VALUE,
                  fqn, args[2]);// key
            break;
         case MethodDeclarations.removeDataMethodLocal_id:
            mod = new Modification(Modification.ModificationType.REMOVE_DATA,
                  fqn);
            break;
         case MethodDeclarations.moveMethodLocal_id:
            Fqn moveFrom = (Fqn) args[0];
            affectedFqns.add(moveFrom);
            mod = new Modification(Modification.ModificationType.MOVE, moveFrom, fqn);
            break;
         default:
            throw new CacheException("method call " + method.getName() + " cannot be converted to a modification");
      }
      affectedFqns.add(fqn);
      if (trace) log.trace("Converted " + methodCall + " to Modification of type " + mod.getType());
      return mod;
   }
}
TOP

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

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.