[jbosscache-commits] JBoss Cache SVN: r7584 - in core/trunk/src: main/java/org/jboss/cache/transaction and 1 other directories.

jbosscache-commits at lists.jboss.org jbosscache-commits at lists.jboss.org
Fri Jan 23 07:17:02 EST 2009


Author: manik.surtani at jboss.com
Date: 2009-01-23 07:17:02 -0500 (Fri, 23 Jan 2009)
New Revision: 7584

Added:
   core/trunk/src/test/java/org/jboss/cache/transaction/MarkAsRollbackTest.java
Modified:
   core/trunk/src/main/java/org/jboss/cache/interceptors/TxInterceptor.java
   core/trunk/src/main/java/org/jboss/cache/transaction/TransactionTable.java
Log:
JBCACHE-1468:   JBoss Cache ignores transactions with STATUS_MARKED_ROLLBACK and auto commits data changes

Modified: core/trunk/src/main/java/org/jboss/cache/interceptors/TxInterceptor.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/interceptors/TxInterceptor.java	2009-01-23 12:12:31 UTC (rev 7583)
+++ core/trunk/src/main/java/org/jboss/cache/interceptors/TxInterceptor.java	2009-01-23 12:17:02 UTC (rev 7584)
@@ -323,13 +323,17 @@
             if (trace) log.trace("Created new tx for gtx " + gtx);
 
             if (trace)
+            {
                log.trace("Started new local tx as result of remote prepare: local tx=" + ltx + " (status=" + ltx.getStatus() + "), gtx=" + gtx);
+            }
          }
          else
          {
             //this should be valid
             if (!TransactionTable.isValid(ltx))
+            {
                throw new CacheException("Transaction " + ltx + " not in correct state to be prepared");
+            }
 
             //associate this thread with the local transaction associated with the global transaction, IF the localTx is NOT the current tx.
             if (currentTx == null || !ltx.equals(currentTx))
@@ -372,7 +376,9 @@
          if (command.isOnePhaseCommit())
          {
             if (trace)
+            {
                log.trace("Using one-phase prepare.  Not propagating the prepare call up the stack until called to do so by the sync handler.");
+            }
          }
          else
          {
@@ -562,9 +568,13 @@
             throw new RuntimeException(e2);
          }
          if (e instanceof RuntimeException)
+         {
             throw (RuntimeException) e;
+         }
          else
+         {
             throw new RuntimeException("Commit failed.", e);
+         }
       }
    }
 
@@ -675,9 +685,17 @@
     */
    private GlobalTransaction registerTransaction(Transaction tx, InvocationContext ctx) throws Exception
    {
+      // we have ascertained that the current thread *is* associated with a transaction.  We need to make sure the
+      // transaction is in a valid state before moving on, and throwing an exception if not.
+      boolean txValid = TransactionTable.isValid(tx);
+      if (!txValid)
+      {
+         throw new IllegalStateException("Transaction " + tx + " is not in a valid state to be invoking cache operations on.");
+      }
+
       GlobalTransaction gtx;
 
-      if (TransactionTable.isValid(tx) && transactions.add(tx))
+      if (transactions.add(tx))
       {
          gtx = txTable.getCurrentTransaction(tx, true);
          TransactionContext transactionContext;
@@ -711,17 +729,7 @@
       }
       else
       {
-         if (trace)
-         {
-            if (TransactionTable.isValid(tx)) 
-            { 
-               log.trace("Transaction " + tx + " is already registered.");
-            }
-            else
-            {
-               log.trace("Transaction " + tx + " is not valid!  Status: " + tx.getStatus());
-            }
-         }
+         if (trace) log.trace("Transaction " + tx + " is already registered.");
       }
       return gtx;
    }
@@ -867,7 +875,9 @@
          setTransactionalContext(tx, gtx, transactionContext, ctx);
 
          if (ctx.isOptionsUninitialised() && transactionContext.getOption() != null)
+         {
             ctx.setOptionOverrides(transactionContext.getOption());
+         }
 
          assertCanContinue();
 
@@ -954,7 +964,9 @@
       private void assertCanContinue()
       {
          if (!componentRegistry.invocationsAllowed(true) && (ctx.getOptionOverrides() == null || !ctx.getOptionOverrides().isSkipCacheStatusCheck()))
+         {
             throw new IllegalStateException("Cache not in STARTED state!");
+         }
       }
 
       /**
@@ -981,7 +993,9 @@
       {
          // JBCACHE-1114 -- don't call toString() on tx or it can lead to stack overflow
          if (tx == null)
+         {
             return null;
+         }
 
          return tx.getClass().getName() + "@" + System.identityHashCode(tx);
       }
@@ -1053,7 +1067,9 @@
                   if (result instanceof Throwable)
                   {
                      if (log.isDebugEnabled())
+                     {
                         log.debug("Transaction needs to be rolled back - the cache returned an instance of Throwable for this prepare call (tx=" + tx + " and gtx=" + gtx + ")", (Throwable) result);
+                     }
                      tx.setRollbackOnly();
                      throw (Throwable) result;
                   }
@@ -1074,9 +1090,13 @@
                throw new RuntimeException("setting tx rollback failed ", se);
             }
             if (t instanceof RuntimeException)
+            {
                throw (RuntimeException) t;
+            }
             else
+            {
                throw new RuntimeException("", t);
+            }
          }
          finally
          {

Modified: core/trunk/src/main/java/org/jboss/cache/transaction/TransactionTable.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/transaction/TransactionTable.java	2009-01-23 12:12:31 UTC (rev 7583)
+++ core/trunk/src/main/java/org/jboss/cache/transaction/TransactionTable.java	2009-01-23 12:17:02 UTC (rev 7584)
@@ -103,7 +103,9 @@
    public GlobalTransaction get(Transaction tx)
    {
       if (tx == null)
+      {
          return null;
+      }
       return tx2gtxMap.get(tx);
    }
 
@@ -194,7 +196,9 @@
    public GlobalTransaction remove(Transaction tx)
    {
       if (tx == null)
+      {
          return null;
+      }
       return tx2gtxMap.remove(tx);
    }
 
@@ -223,7 +227,9 @@
    public String toString(boolean printDetails)
    {
       if (!printDetails)
+      {
          return toString();
+      }
       StringBuilder sb = new StringBuilder();
       sb.append("LocalTransactions: ").append(tx2gtxMap.size()).append("\n");
       sb.append("GlobalTransactions: ").append(gtx2ContextMap.size()).append("\n");
@@ -353,14 +359,33 @@
    }
 
    /**
-    * Return s true of tx's status is ACTIVE or PREPARING
+    * Returns true if transaction is STATUS_MARKED_ROLLBACK, false otherwise
+    */
+   public static boolean isMarkedAsRollback(Transaction tx)
+   {
+      if (tx == null) return false;
+      int status;
+      try
+      {
+         status = tx.getStatus();
+         return status == Status.STATUS_MARKED_ROLLBACK;
+      }
+      catch (SystemException e)
+      {
+         return false;
+      }
+   }
+
+
+   /**
+    * Returns true of tx's status is ACTIVE or PREPARING or MARKED_ROLLBACK
     *
     * @param tx
     * @return true if the tx is active or preparing
     */
    public static boolean isValid(Transaction tx)
    {
-      return isActive(tx) || isPreparing(tx);
+      return isActive(tx) || isPreparing(tx) || isMarkedAsRollback(tx);
    }
 
    /**
@@ -369,14 +394,17 @@
    public static void assertTransactionValid(InvocationContext ctx)
    {
       Transaction tx = ctx.getTransaction();
-      if (!isValid(tx)) try
+      if (!isValid(tx))
       {
-         throw new CacheException("Invalid transaction " + tx + ", status = " + (tx == null ? null : tx.getStatus()));
+         try
+         {
+            throw new CacheException("Invalid transaction " + tx + ", status = " + (tx == null ? null : tx.getStatus()));
+         }
+         catch (SystemException e)
+         {
+            throw new CacheException("Exception trying to analyse status of transaction " + tx, e);
+         }
       }
-      catch (SystemException e)
-      {
-         throw new CacheException("Exception trying to analyse status of transaction " + tx, e);
-      }
    }
 
 
@@ -425,13 +453,13 @@
       return gtx;
    }
 
-   @ManagedAttribute(name="numberOfRegisteredTransactions", description = "Number of registered transactions")
+   @ManagedAttribute(name = "numberOfRegisteredTransactions", description = "Number of registered transactions")
    public int getNumberOfRegisteredTransactions()
    {
       return tx2gtxMap.size();
    }
 
-   @ManagedAttribute(name="transactionMap", description = "A String representation of the transaction map")
+   @ManagedAttribute(name = "transactionMap", description = "A String representation of the transaction map")
    public String getTransactionMap()
    {
       return tx2gtxMap.toString();

Added: core/trunk/src/test/java/org/jboss/cache/transaction/MarkAsRollbackTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/transaction/MarkAsRollbackTest.java	                        (rev 0)
+++ core/trunk/src/test/java/org/jboss/cache/transaction/MarkAsRollbackTest.java	2009-01-23 12:17:02 UTC (rev 7584)
@@ -0,0 +1,89 @@
+package org.jboss.cache.transaction;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.jboss.cache.CacheSPI;
+import org.jboss.cache.Fqn;
+import org.jboss.cache.UnitTestCacheFactory;
+import org.jboss.cache.config.Configuration;
+import org.jboss.cache.util.TestingUtil;
+import org.testng.annotations.Test;
+
+import javax.transaction.RollbackException;
+import javax.transaction.TransactionManager;
+
+ at Test(groups = "functional")
+public class MarkAsRollbackTest
+{
+   private static final Fqn fqn = Fqn.fromString("/a/b/c");
+   private static final Log log = LogFactory.getLog(MarkAsRollbackTest.class);
+
+   public void testMarkAsRollbackAfterMods() throws Exception
+   {
+      Configuration c = new Configuration();
+      c.setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName());
+      CacheSPI<String, String> cache = (CacheSPI<String, String>) new UnitTestCacheFactory<String, String>().createCache(c, MarkAsRollbackTest.class);
+      try
+      {
+         TransactionManager tm = cache.getTransactionManager();
+         assert tm != null;
+         tm.begin();
+         cache.put(fqn, "k", "v");
+         assert cache.get(fqn, "k").equals("v");
+         assert cache.getRoot().getChildren().size() == 1;
+         tm.setRollbackOnly();
+         try
+         {
+            tm.commit();
+            assert false : "Should have rolled back";
+         }
+         catch (RollbackException expected)
+         {
+         }
+
+         assert tm.getTransaction() == null : "There should be no transaction in scope anymore!";
+         assert cache.get(fqn, "k") == null : "Expected a null but was " + cache.get(fqn, "k");
+         assert cache.getRoot().getChildren().size() == 0;
+      }
+      finally
+      {
+         log.warn("Cleaning up");
+         TestingUtil.killCaches(cache);
+      }
+   }
+
+   public void testMarkAsRollbackBeforeMods() throws Exception
+   {
+      Configuration c = new Configuration();
+      c.setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName());
+      CacheSPI<String, String> cache = (CacheSPI<String, String>) new UnitTestCacheFactory<String, String>().createCache(c, MarkAsRollbackTest.class);
+      try
+      {
+         TransactionManager tm = cache.getTransactionManager();
+         assert tm != null;
+         tm.begin();
+         tm.setRollbackOnly();
+         cache.put(fqn, "k", "v");
+         assert cache.get(fqn, "k").equals("v");
+         assert cache.getRoot().getChildren().size() == 1;
+         try
+         {
+            tm.commit();
+            assert false : "Should have rolled back";
+         }
+         catch (RollbackException expected)
+         {
+
+         }
+
+         assert tm.getTransaction() == null : "There should be no transaction in scope anymore!";
+         assert cache.get(fqn, "k") == null : "Expected a null but was " + cache.get(fqn, "k");
+         assert cache.getRoot().getChildren().size() == 0;
+      }
+      finally
+      {
+         log.warn("Cleaning up");
+         TestingUtil.killCaches(cache);
+      }
+   }
+}




More information about the jbosscache-commits mailing list