Package org.jboss.cache.transaction

Source Code of org.jboss.cache.transaction.TransactionEntry

/*
* JBoss, the OpenSource J2EE webOS
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.cache.transaction;


import net.jcip.annotations.ThreadSafe;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.cache.CacheImpl;
import org.jboss.cache.CacheSPI;
import org.jboss.cache.Fqn;
import org.jboss.cache.Modification;
import org.jboss.cache.config.Option;
import org.jboss.cache.interceptors.OrderedSynchronizationHandler;
import org.jboss.cache.invocation.CacheInvocationDelegate;
import org.jboss.cache.lock.IdentityLock;
import org.jboss.cache.lock.NodeLock;
import org.jboss.cache.marshall.MethodCall;

import javax.transaction.RollbackException;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.concurrent.CopyOnWriteArrayList;

/**
* Information associated with a {@link GlobalTransaction} about the transaction state.
* <p/>
* A TransactionEntry maintains:
* <ul>
* <li>Handle to local Transactions: there can be more than 1 local TX associated with a GlobalTransaction
* <li>List of modifications ({@link Modification})
* <li>List of nodes that were created as part of lock acquisition. These nodes can be
* safely deleted when a transaction is rolled back
* <li>List of locks ({@link IdentityLock}) that have been acquired by
* this transaction so far
* </ul>
*
* @author <a href="mailto:bela@jboss.org">Bela Ban</a> Apr 14, 2003
* @version $Revision: 5691 $
*/
@ThreadSafe
public class TransactionEntry
{

   private static Log log = LogFactory.getLog(TransactionEntry.class);

   /**
    * Local transaction
    */
   private Transaction ltx = null;
   private Option option;
   private OrderedSynchronizationHandler orderedSynchronizationHandler;

   private boolean forceAsyncReplication = false;
   private boolean forceSyncReplication = false;

   /**
    * List<MethodCall> of modifications ({@link MethodCall}). They will be replicated on TX commit
    */
   private List<MethodCall> modification_list = new LinkedList<MethodCall>();

   // For some reason we see multiple threads accessing this list - even within the same tx.  Could be due to reuse of
   // tx identifiers in the DummyTM, which is where we see this problem.
   private List<MethodCall> cl_mod_list = new CopyOnWriteArrayList<MethodCall>();

   /**
    * List<MethodCall>. List of compensating {@link org.jboss.cache.marshall.MethodCall} objects
    * which revert the ones in <tt>modification_list</tt>. For each entry in the modification list,
    * we have a corresponding entry in this list. A rollback will simply iterate over this list in
    * reverse to undo the modifications. Note that these undo-ops will never be replicated.
    */
   private final List<MethodCall> undo_list = new LinkedList<MethodCall>();

   /**
    * LinkedHashSet<IdentityLock> of locks acquired by the transaction. We use
    * a LinkedHashSet because we need efficient Set semantics (same lock can
    * be added multiple times) but also need guaranteed ordering for use
    * by lock release code (see JBCCACHE-874).
    */
   private LinkedHashSet<NodeLock> locks = new LinkedHashSet<NodeLock>();

   /**
    * A list of dummy uninitialised nodes created by the cache loader interceptor to load data for a
    * given node in this tx.
    */
   private List<Fqn> dummyNodesCreatedByCacheLoader;

   /**
    * List<Fqn> of nodes that have been removed by the transaction
    */
   private List<Fqn> removedNodes = new LinkedList<Fqn>();

   public TransactionEntry(Transaction tx) throws SystemException, RollbackException
   {
      ltx = tx;
      orderedSynchronizationHandler = new OrderedSynchronizationHandler(tx);
   }

   /**
    * Adds a modification to the modification list.
    */
   public void addModification(MethodCall m)
   {
      if (m == null) return;
      modification_list.add(m);
   }

   public void addCacheLoaderModification(MethodCall m)
   {
      if (m != null) cl_mod_list.add(m);
   }

   /**
    * Returns all modifications.
    */
   public List<MethodCall> getModifications()
   {
      return modification_list;
   }

   public List<MethodCall> getCacheLoaderModifications()
   {
      // make sure this isn't modified externally
      return Collections.unmodifiableList(cl_mod_list);
   }

   /**
    * Adds an undo operation to the undo list.
    *
    * @see #undoOperations
    */
   public void addUndoOperation(MethodCall m)
   {
      undo_list.add(m);
   }

   /**
    * Adds the node that has been removed.
    *
    * @param fqn
    */
   public void addRemovedNode(Fqn fqn)
   {
      removedNodes.add(fqn);
   }

   /**
    * Gets the list of removed nodes.
    */
   public List<Fqn> getRemovedNodes()
   {
      return new ArrayList<Fqn>(removedNodes);
   }

   /**
    * Returns the undo operations in use.
    * Note:  This list may be concurrently modified.
    */
   public List<MethodCall> getUndoOperations()
   {
      return undo_list;
   }

   /**
    * Sets the local transaction for this entry.
    */
   public void setTransaction(Transaction tx)
   {
      ltx = tx;
   }

   /**
    * Returns a local transaction associated with this TransactionEntry
    */
   public Transaction getTransaction()
   {
      return ltx;
   }

   /**
    * Adds a lock to the end of the lock list, if it isn't already present.
    */
   public void addLock(NodeLock l)
   {
      if (l != null)
      {
         synchronized (locks)
         {
            locks.add(l);
         }
      }
   }

   /**
    * Add multiple locks to the lock list.
    *
    * @param newLocks Collection<NodeLock>
    */
   public void addLocks(Collection<NodeLock> newLocks)
   {
      if (newLocks != null)
      {
         synchronized (locks)
         {
            locks.addAll(newLocks);
         }
      }
   }

   /**
    * Returns the locks in use.
    *
    * @return a defensive copy of the internal data structure.
    */
   public List<NodeLock> getLocks()
   {
      synchronized (locks)
      {
         return new ArrayList<NodeLock>(locks);
      }
   }

   /**
    * Releases all locks held by the owner, in reverse order of creation.
    * Clears the list of locks held.
    */
   public void releaseAllLocksLIFO(Object owner)
   {

      boolean trace = log.isTraceEnabled();
      synchronized (locks)
      {
         // Copying out to an array is faster than creating an ArrayList and iterating,
         // since list creation will just copy out to an array internally
         IdentityLock[] lockArray = locks.toArray(new IdentityLock[locks.size()]);
         for (int i = lockArray.length - 1; i >= 0; i--)
         {
            if (trace)
            {
               log.trace("releasing lock for " + lockArray[i].getFqn() + " (" + lockArray[i] + ")");
            }
            lockArray[i].release(owner);
         }
         locks.clear();
      }
   }

   /**
    * Releases all locks held by the owner, in order of creation.
    * Does not clear the list of locks held.
    */
   public void releaseAllLocksFIFO(Object owner)
   {
      // I guess a copy would work as well
      // This seems fairly safe though
      synchronized (locks)
      {
         for (NodeLock lock : locks)
         {
            lock.release(owner);
            if (log.isTraceEnabled())
            {
               log.trace("releasing lock for " + ((IdentityLock) lock).getFqn() + " (" + lock + ")");
            }
         }
      }
   }

   /**
    * Gets the value of the forceAsyncReplication flag.  Used by ReplicationInterceptor and OptimisticReplicationInterceptor
    * when dealing with {@link org.jboss.cache.Cache#putForExternalRead(org.jboss.cache.Fqn,Object,Object)} within
    * a transactional context.
    *
    * @return true if the forceAsyncReplication flag is set to true.
    */
   public boolean isForceAsyncReplication()
   {
      return forceAsyncReplication;
   }

   /**
    * Sets the value of the forceAsyncReplication flag.  Used by ReplicationInterceptor and OptimisticReplicationInterceptor
    * when dealing with {@link org.jboss.cache.Cache#putForExternalRead(org.jboss.cache.Fqn,Object,Object)} within
    * a transactional context. Also used by OptimisticReplicationInterceptor when dealing
    * with {@link org.jboss.cache.config.Option#setForceAsynchronous(boolean)} in a
    * non-transactional context (i.e. with an implicit transaction).
    *
    * @param forceAsyncReplication value of forceAsyncReplication
    */
   public void setForceAsyncReplication(boolean forceAsyncReplication)
   {
      this.forceAsyncReplication = forceAsyncReplication;
      if (forceAsyncReplication)
      {
         forceSyncReplication = false;
      }
   }

   /**
    * Gets the value of the forceSyncReplication flag.  Used by ReplicationInterceptor and OptimisticReplicationInterceptor
    * when dealing with {@link org.jboss.cache.Cache#putForExternalRead(org.jboss.cache.Fqn,Object,Object)} within
    * a transactional context.
    *
    * @return true if the forceAsyncReplication flag is set to true.
    */
   public boolean isForceSyncReplication()
   {
      return forceSyncReplication;
   }

   /**
    * Sets the value of the forceSyncReplication flag.  Used by ReplicationInterceptor and OptimisticReplicationInterceptor
    * when dealing with {@link org.jboss.cache.Cache#putForExternalRead(org.jboss.cache.Fqn,Object,Object)} within
    * a transactional context.
    *
    * @param forceSyncReplication value of forceSyncReplication
    */
   public void setForceSyncReplication(boolean forceSyncReplication)
   {
      this.forceSyncReplication = forceSyncReplication;
      if (forceSyncReplication)
      {
         forceAsyncReplication = false;
      }
   }


   /**
    * Posts all undo operations to the CacheImpl.
    */
   public void undoOperations(CacheSPI cache)
   {
      if (log.isTraceEnabled())
      {
         log.trace("undoOperations " + undo_list);
      }
      ArrayList<MethodCall> l;
      synchronized (undo_list)
      {
         l = new ArrayList<MethodCall>(undo_list);
      }
      CacheImpl ci = ((CacheInvocationDelegate) cache).getDelegationTarget();
      for (ListIterator<MethodCall> i = l.listIterator(l.size()); i.hasPrevious();)
      {
         MethodCall undo_op = i.previous();
         undo(undo_op, ci);
      }
   }

   private void undo(MethodCall undo_op, CacheImpl cache)
   {
      try
      {
         Object retval = undo_op.invoke(cache);
         if (retval instanceof Throwable)
         {
            throw (Throwable) retval;
         }
      }
      catch (Throwable t)
      {
         log.error("undo operation failed, error=" + t);
         log.trace(t, t);
      }
   }

   /**
    * Returns debug information about this transaction.
    */
   public String toString()
   {
      StringBuffer sb = new StringBuffer();
      sb.append("TransactionEntry\nmodification_list: ").append(modification_list);
      synchronized (undo_list)
      {
         sb.append("\nundo_list: ").append(undo_list);
      }
      synchronized (locks)
      {
         sb.append("\nlocks: ").append(locks);
      }
      return sb.toString();
   }

   public void loadUninitialisedNode(Fqn fqn)
   {
      if (dummyNodesCreatedByCacheLoader == null)
         dummyNodesCreatedByCacheLoader = new LinkedList<Fqn>();
      dummyNodesCreatedByCacheLoader.add(fqn);
   }

   public List<Fqn> getDummyNodesCreatedByCacheLoader()
   {
      return dummyNodesCreatedByCacheLoader;
   }

   /**
    * Sets a transaction-scope option override
    *
    * @param o
    */
   public void setOption(Option o)
   {
      this.option = o;
   }

   /**
    * Retrieves a transaction scope option override
    */
   public Option getOption()
   {
      return this.option;
   }

   public OrderedSynchronizationHandler getOrderedSynchronizationHandler()
   {
      return orderedSynchronizationHandler;
   }

   public void setOrderedSynchronizationHandler(OrderedSynchronizationHandler orderedSynchronizationHandler)
   {
      this.orderedSynchronizationHandler = orderedSynchronizationHandler;
   }

   /**
    * Returns true if modifications were registered to either modificationList or to class loader modifications list.
    */
   public boolean existModifications()
   {
      return !modification_list.isEmpty() || !cl_mod_list.isEmpty();
   }

   /**
    * Cleans up internal state
    */
   public void reset()
   {
      orderedSynchronizationHandler = null;
      modification_list.clear();
      cl_mod_list.clear();
      option = null;
      locks.clear();
      if (dummyNodesCreatedByCacheLoader != null) dummyNodesCreatedByCacheLoader.clear();
      removedNodes.clear();
   }
}
TOP

Related Classes of org.jboss.cache.transaction.TransactionEntry

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.