[Mulgara-svn] r207 - in trunk: jxdata/iTQL/data_types jxdata/iTQL/having_queries jxdata/iTQL/standard_queries jxdata/iTQL/transitive jxdata/iTQL/walk src/jar/krule/java/org/mulgara/krule src/jar/resolver/java/org/mulgara/resolver src/jar/server/java/org/mulgara/server

andrae at mulgara.org andrae at mulgara.org
Mon Mar 19 07:01:15 UTC 2007


Author: andrae
Date: 2007-03-19 01:01:14 -0600 (Mon, 19 Mar 2007)
New Revision: 207

Modified:
   trunk/jxdata/iTQL/data_types/queryResult34.txt
   trunk/jxdata/iTQL/having_queries/queryResult10.txt
   trunk/jxdata/iTQL/having_queries/queryResult15.txt
   trunk/jxdata/iTQL/standard_queries/queryResult17.txt
   trunk/jxdata/iTQL/standard_queries/queryResult18.txt
   trunk/jxdata/iTQL/standard_queries/queryResult3.txt
   trunk/jxdata/iTQL/transitive/result13.txt
   trunk/jxdata/iTQL/transitive/result14.txt
   trunk/jxdata/iTQL/walk/queryResult7.txt
   trunk/src/jar/krule/java/org/mulgara/krule/RuleStructure.java
   trunk/src/jar/resolver/java/org/mulgara/resolver/AnswerOperation.java
   trunk/src/jar/resolver/java/org/mulgara/resolver/DatabaseOperationContext.java
   trunk/src/jar/resolver/java/org/mulgara/resolver/DatabaseSession.java
   trunk/src/jar/resolver/java/org/mulgara/resolver/LocalJRDFDatabaseSession.java
   trunk/src/jar/resolver/java/org/mulgara/resolver/MulgaraTransaction.java
   trunk/src/jar/resolver/java/org/mulgara/resolver/MulgaraTransactionManager.java
   trunk/src/jar/resolver/java/org/mulgara/resolver/StatusFormat.java
   trunk/src/jar/resolver/java/org/mulgara/resolver/TransactionalAnswer.java
   trunk/src/jar/server/java/org/mulgara/server/EmbeddedMulgaraServer.java
Log:
This fixes MGR-44 - Error during transaction.commit() causes infinite recursion and looping

The transaction code is still beta, and indeed there are reference counting issues that need resolving in the presence of transaction failures.  There are still warnings being printed by the finalize() method reflecting a failure to dereference adequately from TransactionalAnswer.sessionClose().  This however is a seperate issue to MGR-44, and this fix (moving from a DFA based transaction model to a DPA transaction model) resolves the infinite recursion and makes a fix of the dereferencing issues in sessionClose possible.


Modified: trunk/jxdata/iTQL/data_types/queryResult34.txt
===================================================================
--- trunk/jxdata/iTQL/data_types/queryResult34.txt	2007-03-01 16:41:41 UTC (rev 206)
+++ trunk/jxdata/iTQL/data_types/queryResult34.txt	2007-03-19 07:01:14 UTC (rev 207)
@@ -1,6 +1,6 @@
 ItqlInterpreter error - org.mulgara.query.QueryException: Could not commit insert
 Caused by: (QueryException) Could not commit insert
-Caused by: (QueryException) org.mulgara.query.MulgaraTransactionException: Failed transaction finalised. (ROLLBACK)
+Caused by: (QueryException) org.mulgara.query.MulgaraTransactionException: Transaction rollback triggered
 Caused by: (QueryException) org.mulgara.resolver.spi.ResolverException: Unable to read input statements
 Caused by: (QueryException) org.mulgara.query.TuplesException: Failed to localize node
 Caused by: (QueryException) org.mulgara.resolver.spi.LocalizeException: Unable to localize "<test>test escape char</foo>"^^<http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral> - Couldn't convert Node to SPObject

Modified: trunk/jxdata/iTQL/having_queries/queryResult10.txt
===================================================================
--- trunk/jxdata/iTQL/having_queries/queryResult10.txt	2007-03-01 16:41:41 UTC (rev 206)
+++ trunk/jxdata/iTQL/having_queries/queryResult10.txt	2007-03-19 07:01:14 UTC (rev 207)
@@ -1,5 +1,5 @@
 ItqlInterpreter error - org.mulgara.query.QueryException: Query failed
 Caused by: (QueryException) Query failed
-Caused by: (QueryException) org.mulgara.query.MulgaraTransactionException: Failed transaction finalised. (ROLLBACK)
+Caused by: (QueryException) org.mulgara.query.MulgaraTransactionException: Transaction rollback triggered
 Caused by: (QueryException) Failed to resolve query
 Caused by: (QueryException) org.mulgara.query.TuplesException: No such variable $v in tuples [$s, $k0] (class org.mulgara.resolver.AppendAggregateTuples)

Modified: trunk/jxdata/iTQL/having_queries/queryResult15.txt
===================================================================
--- trunk/jxdata/iTQL/having_queries/queryResult15.txt	2007-03-01 16:41:41 UTC (rev 206)
+++ trunk/jxdata/iTQL/having_queries/queryResult15.txt	2007-03-19 07:01:14 UTC (rev 207)
@@ -1,5 +1,5 @@
 ItqlInterpreter error - org.mulgara.query.QueryException: Query failed
 Caused by: (QueryException) Query failed
-Caused by: (QueryException) org.mulgara.query.MulgaraTransactionException: Failed transaction finalised. (ROLLBACK)
+Caused by: (QueryException) org.mulgara.query.MulgaraTransactionException: Transaction rollback triggered
 Caused by: (QueryException) Failed to resolve query
 Caused by: (QueryException) org.mulgara.query.TuplesException: No such variable $k0 in tuples [$p, $v, $s] (class org.mulgara.resolver.AppendAggregateTuples)

Modified: trunk/jxdata/iTQL/standard_queries/queryResult17.txt
===================================================================
--- trunk/jxdata/iTQL/standard_queries/queryResult17.txt	2007-03-01 16:41:41 UTC (rev 206)
+++ trunk/jxdata/iTQL/standard_queries/queryResult17.txt	2007-03-19 07:01:14 UTC (rev 207)
@@ -1,4 +1,4 @@
 ItqlInterpreter error - org.mulgara.query.QueryException: Could not commit insert
 Caused by: (QueryException) Could not commit insert
-Caused by: (QueryException) org.mulgara.query.MulgaraTransactionException: Failed transaction finalised. (ROLLBACK)
+Caused by: (QueryException) org.mulgara.query.MulgaraTransactionException: Transaction rollback triggered
 Caused by: (QueryException) rmi://localhost/server1#nomodelexistswiththisname is not a Model

Modified: trunk/jxdata/iTQL/standard_queries/queryResult18.txt
===================================================================
--- trunk/jxdata/iTQL/standard_queries/queryResult18.txt	2007-03-01 16:41:41 UTC (rev 206)
+++ trunk/jxdata/iTQL/standard_queries/queryResult18.txt	2007-03-19 07:01:14 UTC (rev 207)
@@ -1,5 +1,5 @@
 ItqlInterpreter error - org.mulgara.query.QueryException: Query failed
 Caused by: (QueryException) Query failed
-Caused by: (QueryException) org.mulgara.query.MulgaraTransactionException: Failed transaction finalised. (ROLLBACK)
+Caused by: (QueryException) org.mulgara.query.MulgaraTransactionException: Transaction rollback triggered
 Caused by: (QueryException) Error resolving [$s $p $o $_from] from mailto:foo at bar.com
 Caused by: (QueryException) Unable to extract hostname from: mailto:foo at bar.com

Modified: trunk/jxdata/iTQL/standard_queries/queryResult3.txt
===================================================================
--- trunk/jxdata/iTQL/standard_queries/queryResult3.txt	2007-03-01 16:41:41 UTC (rev 206)
+++ trunk/jxdata/iTQL/standard_queries/queryResult3.txt	2007-03-19 07:01:14 UTC (rev 207)
@@ -1,5 +1,5 @@
 ItqlInterpreter error - org.mulgara.query.QueryException: Query failed
 Caused by: (QueryException) Query failed
-Caused by: (QueryException) org.mulgara.query.MulgaraTransactionException: Failed transaction finalised. (ROLLBACK)
+Caused by: (QueryException) org.mulgara.query.MulgaraTransactionException: Transaction rollback triggered
 Caused by: (QueryException) Error resolving [$pmid http://mulgara.org/mulgara/Document#subject "Birds" $_from] from @server@#badmodel
 Caused by: (QueryException) @server@#badmodel is not a Model

Modified: trunk/jxdata/iTQL/transitive/result13.txt
===================================================================
--- trunk/jxdata/iTQL/transitive/result13.txt	2007-03-01 16:41:41 UTC (rev 206)
+++ trunk/jxdata/iTQL/transitive/result13.txt	2007-03-19 07:01:14 UTC (rev 207)
@@ -1,4 +1,4 @@
 ItqlInterpreter error - org.mulgara.query.QueryException: Query failed
 Caused by: (QueryException) Query failed
-Caused by: (QueryException) org.mulgara.query.MulgaraTransactionException: Failed transaction finalised. (ROLLBACK)
+Caused by: (QueryException) org.mulgara.query.MulgaraTransactionException: Transaction rollback triggered
 Caused by: (QueryException) Predicate in transitive function, $p, currently must be bound to a value.

Modified: trunk/jxdata/iTQL/transitive/result14.txt
===================================================================
--- trunk/jxdata/iTQL/transitive/result14.txt	2007-03-01 16:41:41 UTC (rev 206)
+++ trunk/jxdata/iTQL/transitive/result14.txt	2007-03-19 07:01:14 UTC (rev 207)
@@ -1,4 +1,4 @@
 ItqlInterpreter error - org.mulgara.query.QueryException: Query failed
 Caused by: (QueryException) Query failed
-Caused by: (QueryException) org.mulgara.query.MulgaraTransactionException: Failed transaction finalised. (ROLLBACK)
+Caused by: (QueryException) org.mulgara.query.MulgaraTransactionException: Transaction rollback triggered
 Caused by: (QueryException) Predicate in transitive function, $p, currently must be bound to a value.

Modified: trunk/jxdata/iTQL/walk/queryResult7.txt
===================================================================
--- trunk/jxdata/iTQL/walk/queryResult7.txt	2007-03-01 16:41:41 UTC (rev 206)
+++ trunk/jxdata/iTQL/walk/queryResult7.txt	2007-03-19 07:01:14 UTC (rev 207)
@@ -1,8 +1,7 @@
 ItqlInterpreter error - org.mulgara.query.QueryException: Error getting information for answer
 Caused by: (QueryException) Error getting information for answer
 Caused by: (QueryException) org.mulgara.query.TuplesException: Transaction error
-Caused by: (QueryException) org.mulgara.query.MulgaraTransactionException: Transaction already in rollback
-Caused by: (QueryException) org.mulgara.query.MulgaraTransactionException: Failed transaction finalised. (ROLLBACK)
+Caused by: (QueryException) org.mulgara.query.MulgaraTransactionException: Transaction rollback triggered
 Caused by: (QueryException) org.mulgara.query.TuplesException: Couldn't evaluate aggregate
 Caused by: (QueryException) Failed to resolve subquery
 Caused by: (QueryException) The subject: $head and the object: $n are invalid, one must be a variable and the other a fixed value around a predicate.

Modified: trunk/src/jar/krule/java/org/mulgara/krule/RuleStructure.java
===================================================================
--- trunk/src/jar/krule/java/org/mulgara/krule/RuleStructure.java	2007-03-01 16:41:41 UTC (rev 206)
+++ trunk/src/jar/krule/java/org/mulgara/krule/RuleStructure.java	2007-03-19 07:01:14 UTC (rev 207)
@@ -255,7 +255,7 @@
       }
       throw new RulesException("Error getting data within rule: " + currentRule, te);
     } catch (QueryException qe) {
-      logger.error("Error executing rule: " + currentRule);
+      logger.error("Error executing rule: " + currentRule, qe);
       try {
         session.rollback();
       } catch (QueryException e) {

Modified: trunk/src/jar/resolver/java/org/mulgara/resolver/AnswerOperation.java
===================================================================
--- trunk/src/jar/resolver/java/org/mulgara/resolver/AnswerOperation.java	2007-03-01 16:41:41 UTC (rev 206)
+++ trunk/src/jar/resolver/java/org/mulgara/resolver/AnswerOperation.java	2007-03-19 07:01:14 UTC (rev 207)
@@ -18,7 +18,6 @@
 package org.mulgara.resolver;
 
 import org.mulgara.query.TuplesException;
-import org.mulgara.query.MulgaraTransactionException;
 
 public abstract class AnswerOperation {
   // Should use enum here.

Modified: trunk/src/jar/resolver/java/org/mulgara/resolver/DatabaseOperationContext.java
===================================================================
--- trunk/src/jar/resolver/java/org/mulgara/resolver/DatabaseOperationContext.java	2007-03-01 16:41:41 UTC (rev 206)
+++ trunk/src/jar/resolver/java/org/mulgara/resolver/DatabaseOperationContext.java	2007-03-19 07:01:14 UTC (rev 207)
@@ -150,7 +150,8 @@
   private final List               symbolicTransformationList;
   private final boolean            isWriting;
 
-  private WeakHashMap answers;  // Used as a set, all values are null.  Java doesn't provide a WeakHashSet.
+  // Used as a set, all values are null.  Java doesn't provide a WeakHashSet.
+  private WeakHashMap<TransactionalAnswer,Object> answers;
 
   DatabaseOperationContext(Set                   cachedResolverFactorySet,
                            Map                   externalResolverFactoryMap,
@@ -187,7 +188,7 @@
     this.cachedModelSet             = new HashSet();
     this.changedCachedModelSet      = new HashSet();
     this.enlistedResolverMap        = new HashMap();
-    this.answers                    = new WeakHashMap();
+    this.answers                    = new WeakHashMap<TransactionalAnswer,Object>();
   }
 
   //
@@ -827,7 +828,7 @@
 
   public Answer doQuery(Query query) throws Exception
   {
-    Answer result;
+    TransactionalAnswer result;
 
     LocalQuery localQuery = new LocalQuery(query, systemResolver, this);
 
@@ -877,20 +878,30 @@
   }
 
   void clear() throws QueryException {
+    Throwable error = null;
     try {
-      Iterator i = answers.keySet().iterator();
-      while (i.hasNext()) {
-        ((TransactionalAnswer)i.next()).sessionClose();
+      for (TransactionalAnswer answer : answers.keySet()) {
+        try {
+          answer.sessionClose();
+        } catch (Throwable th) {
+          if (error == null) {
+            error = th;
+          }
+        }
       }
       answers.clear();
-    } catch (TuplesException et) {
-      throw new QueryException("Error force-closing answers", et);
+    } finally {
+      try {
+        clearCache();
+      } finally {
+        systemResolver = null;
+        systemModelCacheMap.clear();
+        enlistedResolverMap.clear();
+        if (error != null) {
+          throw new QueryException("Error force-closing answers", error);
+        }
+      }
     }
-
-    clearCache();
-    systemResolver = null;
-    systemModelCacheMap.clear();
-    enlistedResolverMap.clear();
   }
 
   public SystemResolver getSystemResolver() {
@@ -945,18 +956,6 @@
     }
   }
 
-  public void abort() {
-    Iterator i = enlistedResolverMap.values().iterator();
-    while (i.hasNext()) {
-      ((Resolver)i.next()).abort();
-    }
-    try {
-      this.clear();
-    } catch (QueryException eq) {
-      throw new IllegalStateException("Error aborting OperationContext", eq);
-    }
-  }
-
   public void initiate(MulgaraTransaction transaction) throws QueryException {
     try {
       this.transaction = transaction;

Modified: trunk/src/jar/resolver/java/org/mulgara/resolver/DatabaseSession.java
===================================================================
--- trunk/src/jar/resolver/java/org/mulgara/resolver/DatabaseSession.java	2007-03-01 16:41:41 UTC (rev 206)
+++ trunk/src/jar/resolver/java/org/mulgara/resolver/DatabaseSession.java	2007-03-19 07:01:14 UTC (rev 207)
@@ -550,7 +550,7 @@
   public void close() throws QueryException {
     logger.info("Closing session");
     try {
-      transactionManager.terminateCurrentTransactions(this);
+      transactionManager.rollbackCurrentTransactions(this);
     } catch (MulgaraTransactionException em) {
       throw new QueryException("Error closing session. Forced close required", em);
     }

Modified: trunk/src/jar/resolver/java/org/mulgara/resolver/LocalJRDFDatabaseSession.java
===================================================================
--- trunk/src/jar/resolver/java/org/mulgara/resolver/LocalJRDFDatabaseSession.java	2007-03-01 16:41:41 UTC (rev 206)
+++ trunk/src/jar/resolver/java/org/mulgara/resolver/LocalJRDFDatabaseSession.java	2007-03-19 07:01:14 UTC (rev 207)
@@ -122,7 +122,7 @@
    *   external models
    * @throws IllegalArgumentException if any argument is <code>null</code>
    */
-  LocalJRDFDatabaseSession(MulgaraTransactionManager transactionManager,
+  LocalJRDFDatabaseSession(MulgaraTransactionManager transactionManager, 
       List securityAdapterList, List symbolicTransformationList,
       ResolverSessionFactory resolverSessionFactory,
       SystemResolverFactory systemResolverFactory,

Modified: trunk/src/jar/resolver/java/org/mulgara/resolver/MulgaraTransaction.java
===================================================================
--- trunk/src/jar/resolver/java/org/mulgara/resolver/MulgaraTransaction.java	2007-03-01 16:41:41 UTC (rev 206)
+++ trunk/src/jar/resolver/java/org/mulgara/resolver/MulgaraTransaction.java	2007-03-19 07:01:14 UTC (rev 207)
@@ -17,6 +17,8 @@
 package org.mulgara.resolver;
 
 // Java 2 enterprise packages
+import java.util.HashSet;
+import java.util.Set;
 import javax.transaction.RollbackException;
 import javax.transaction.SystemException;
 import javax.transaction.Transaction;
@@ -61,29 +63,41 @@
  * @licence Open Software License v3.0
  */
 public class MulgaraTransaction {
+  /**
+   * This is the state machine switch matching these states.
+      switch (state) {
+        case CONSTRUCTEDREF:
+        case CONSTRUCTEDUNREF:
+        case ACTUNREF:
+        case ACTREF:
+        case DEACTREF:
+        case FINISHED:
+        case FAILED:
+      }
+   */
+  private enum State { CONSTRUCTEDREF, CONSTRUCTEDUNREF, ACTUNREF, ACTREF, DEACTREF, FINISHED, FAILED };
+
   /** Logger.  */
   private static final Logger logger =
     Logger.getLogger(MulgaraTransaction.class.getName());
 
   private MulgaraTransactionManager manager;
   private DatabaseOperationContext context;
+  private Set<EnlistableResource> enlisted;
 
   private Transaction transaction;
   private Thread currentThread;
 
+  private State state;
   private int inuse;
   private int using;
 
-  private final int NO_ROLLBACK = 0;
-  private final int IMPLICIT_ROLLBACK = 1;
-  private final int EXPLICIT_ROLLBACK = 2;
-
-  private int rollback;
   private Throwable rollbackCause;
 
   public MulgaraTransaction(MulgaraTransactionManager manager, DatabaseOperationContext context)
-      throws Exception {
+      throws IllegalArgumentException {
     report("Creating Transaction");
+
     try {
       if (manager == null) {
         throw new IllegalArgumentException("Manager null in MulgaraTransaction");
@@ -92,76 +106,124 @@
       }
       this.manager = manager;
       this.context = context;
+      this.enlisted = new HashSet<EnlistableResource>();
 
       inuse = 0;
       using = 0;
-
-      rollback = NO_ROLLBACK;
+      state = State.CONSTRUCTEDUNREF;
       rollbackCause = null;
     } finally {
-      report("Created Transaction");
+      report("Finished Creating Transaction");
     }
   }
 
 
   synchronized void activate() throws MulgaraTransactionException {
-    report("Activating Transaction");
+//    report("Activating Transaction");
+
     try {
-      if (rollback != NO_ROLLBACK) {
-        throw new MulgaraTransactionException("Attempt to activate failed transaction");
-      }
-
       if (currentThread != null && !currentThread.equals(Thread.currentThread())) {
         throw new MulgaraTransactionException("Concurrent access attempted to transaction: Transaction has NOT been rolledback.");
       }
-      
-      if (manager == null) {
-        errorReport("Attempt to activate terminated transaction");
-        throw new MulgaraTransactionException("Attempt to activate terminated transaction");
-      }
 
-      if (inuse == 0) {
-        if (transaction == null) {
+      switch (state) {
+        case CONSTRUCTEDUNREF:
           startTransaction();
-        } else {
+          inuse = 1;
+          state = State.ACTUNREF;
+          try {
+            context.initiate(this);
+          } catch (Throwable th) {
+            throw implicitRollback(th);
+          }
+          break;
+        case CONSTRUCTEDREF:
+          startTransaction();
+          inuse = 1;
+          using = 1;
+          state = State.ACTREF;
+          try {
+            context.initiate(this);
+          } catch (Throwable th) {
+            throw implicitRollback(th);
+          }
+          break;
+        case DEACTREF:
           resumeTransaction();
-        }
+          inuse = 1;
+          state = State.ACTREF;
+          break;
+        case ACTREF:
+        case ACTUNREF:
+          inuse++;
+          break;
+        case FINISHED:
+          throw new MulgaraTransactionException("Attempt to activate terminated transaction");
+        case FAILED:
+          throw new MulgaraTransactionException("Attempt to activate failed transaction", rollbackCause);
       }
 
-      if (currentThread == null) {
-        currentThread = Thread.currentThread();
+      try {
+        checkActivated();
+      } catch (MulgaraTransactionException em) {
+        throw abortTransaction("Activate failed post-condition check", em);
       }
-
-      inuse++;
-
-      checkActivated();
+    } catch (MulgaraTransactionException em) {
+      throw em;
+    } catch (Throwable th) {
+      throw abortTransaction("Error activating transaction", th);
     } finally {
-      report("Activated transaction");
+//      report("Leaving Activate transaction");
     }
   }
 
+
   private synchronized void deactivate() throws MulgaraTransactionException {
-    report("Deactivating transaction");
+//    report("Deactivating transaction");
+
     try {
-      if (rollback == NO_ROLLBACK) {
-        checkActivated();
-      } // rollback'd transactions are cleaned up on the final deactivation.
+      if (currentThread == null) {
+        throw new MulgaraTransactionException("Transaction not associated with thread");
+      } else if (!currentThread.equals(Thread.currentThread())) {
+        throw new MulgaraTransactionException("Concurrent access attempted to transaction: Transaction has NOT been rolledback.");
+      }
 
-      inuse--;
-
-      if (inuse == 0) {
-        try {
-          if (using == 0) {
-            terminateTransaction();
-          } else {
+      switch (state) {
+        case ACTUNREF:
+          if (inuse == 1) {
+            commitTransaction();
+          }
+          inuse--;
+          break;
+        case ACTREF:
+          if (inuse == 1) {
             suspendTransaction();
           }
-        } finally {
-          currentThread = null;
-        }
+          inuse--;
+          break;
+        case CONSTRUCTEDREF:
+          throw new MulgaraTransactionException("Attempt to deactivate uninitiated refed transaction");
+        case CONSTRUCTEDUNREF:
+          throw new MulgaraTransactionException("Attempt to deactivate uninitiated transaction");
+        case DEACTREF:
+          throw new IllegalStateException("Attempt to deactivate unactivated transaction");
+        case FINISHED:
+          if (inuse < 0) {
+            errorReport("Activation count failure - too many deacts - in finished transaction", null);
+          } else {
+            inuse--;
+          }
+          break;
+        case FAILED:
+          // Nothing to do here.
+          break;
       }
+    } catch (MulgaraTransactionException em) {
+      throw em;
+    } catch (Throwable th) {
+      throw abortTransaction("Error deactivating transaction", th);
     } finally {
-      report("Deactivated Transaction");
+//      report("Leaving Deactivate Transaction");
     }
   }
 
@@ -170,104 +232,144 @@
   //       references a transaction object that won't be started/activated
   //       until it is first used.
   void reference() throws MulgaraTransactionException {
-    report("Referencing Transaction");
     try {
-      using++;
+      report("Referencing Transaction");
+
+      if (currentThread != null && !currentThread.equals(Thread.currentThread())) {
+        throw new MulgaraTransactionException("Concurrent access attempted to transaction: Transaction has NOT been rolledback.");
+      }
+      switch (state) {
+        case CONSTRUCTEDUNREF:
+          state = State.CONSTRUCTEDREF;
+          break;
+        case ACTREF:
+        case ACTUNREF:
+          using++;
+          state = State.ACTREF;
+          break;
+        case DEACTREF:
+          using++;
+          break;
+        case CONSTRUCTEDREF:
+          throw new MulgaraTransactionException("Attempt to reference uninitated transaction twice");
+        case FINISHED:
+          throw new MulgaraTransactionException("Attempt to reference terminated transaction");
+        case FAILED:
+          throw new MulgaraTransactionException("Attempt to reference failed transaction", rollbackCause);
+      }
+    } catch (MulgaraTransactionException em) {
+      throw em;
+    } catch (Throwable th) {
+      report("Error referencing transaction");
+      throw implicitRollback(th);
     } finally {
-      report("Referenced Transaction");
+      report("Leaving Reference Transaction");
     }
   }
 
   void dereference() throws MulgaraTransactionException {
     report("Dereferencing Transaction");
     try {
-      if (using < 1) {
-        throw implicitRollback(new MulgaraTransactionException(
-            "Reference Failure.  Dereferencing while using < 1: " + using));
+      if (currentThread != null && !currentThread.equals(Thread.currentThread())) {
+        throw new MulgaraTransactionException("Concurrent access attempted to transaction: Transaction has NOT been rolledback.");
       }
-      using--;
+      switch (state) {
+        case ACTREF:
+          if (using == 1) {
+            state = State.ACTUNREF;
+          }
+          using--;
+          break;
+        case CONSTRUCTEDREF:
+          state = State.CONSTRUCTEDUNREF;
+          break;
+        case FINISHED:
+        case FAILED:
+          if (using < 1) {
+            errorReport("Reference count failure - too many derefs - in finished transaction", null);
+          } else {
+            using--;
+          }
+          break;
+        case ACTUNREF:
+          throw new IllegalStateException("Attempt to dereference unreferenced transaction");
+        case CONSTRUCTEDUNREF:
+          throw new MulgaraTransactionException("Attempt to dereference uninitated transaction");
+        case DEACTREF:
+          throw new IllegalStateException("Attempt to dereference deactivated transaction");
+      }
+    } catch (MulgaraTransactionException em) {
+      throw em;
+    } catch (Throwable th) {
+      throw implicitRollback(th);
     } finally {
       report("Dereferenced Transaction");
     }
   }
 
-  void execute(Operation operation,
-               ResolverSessionFactory resolverSessionFactory, // FIXME: We shouldn't need this. - only used for backup and restore operations.
-               DatabaseMetadata metadata) throws MulgaraTransactionException {
-    report("Executing Operation");
+  private void startTransaction() throws MulgaraTransactionException {
+    report("Initiating transaction");
     try {
-      activate();
-      try {
-        operation.execute(context,
-                          context.getSystemResolver(),
-                          resolverSessionFactory,
-                          metadata);
-      } catch (Throwable th) {
-        throw implicitRollback(th);
-      } finally {
-        deactivate();
-      }
-    } finally {
-      report("Executed Operation");
+      transaction = manager.transactionStart(this);
+      currentThread = Thread.currentThread();
+    } catch (Throwable th) {
+      throw abortTransaction("Failed to start transaction", th);
     }
   }
 
-  AnswerOperationResult execute(AnswerOperation ao) throws TuplesException {
-    debugReport("Executing AnswerOperation");
+  private void resumeTransaction() throws MulgaraTransactionException {
+//    report("Resuming transaction");
     try {
-      activate();
-      try {
-        ao.execute();
-        return ao.getResult();
-      } catch (Throwable th) {
-        throw implicitRollback(th);
-      } finally {
-        deactivate();
-      }
-    } catch (MulgaraTransactionException em) {
-      throw new TuplesException("Transaction error", em);
-    } finally {
-      debugReport("Executed AnswerOperation");
+      manager.transactionResumed(this, transaction);
+      currentThread = Thread.currentThread();
+    } catch (Throwable th) {
+      abortTransaction("Failed to resume transaction", th);
     }
   }
 
-
-  void execute(TransactionOperation to) throws MulgaraTransactionException {
-    report("Executing TransactionOperation");
+  private void suspendTransaction() throws MulgaraTransactionException {
+//    report("Suspending Transaction");
     try {
-      activate();
-      try {
-        to.execute();
-      } catch (Throwable th) {
-        throw implicitRollback(th);
-      } finally {
-        deactivate();
+      if (using < 1) {
+        throw implicitRollback(
+            new MulgaraTransactionException("Attempt to suspend unreferenced transaction"));
       }
+      transaction = manager.transactionSuspended(this);
+      currentThread = null;
+      state = State.DEACTREF;
+    } catch (Throwable th) {
+      throw implicitRollback(th);
     } finally {
-      report("Executed TransactionOperation");
+//      report("Finished suspending transaction");
     }
   }
 
-
-  MulgaraTransactionException implicitRollback(Throwable cause) throws MulgaraTransactionException {
-    report("Implicit Rollback triggered");
-
-    if (rollback == IMPLICIT_ROLLBACK) {
-      logger.warn("Cascading error, transaction already rolled back", cause);
-      logger.warn("Cascade error, expected initial cause", rollbackCause);
-
-      return new MulgaraTransactionException("Transaction already in rollback", cause);
+  public void commitTransaction() throws MulgaraTransactionException {
+    report("Committing Transaction");
+    try {
+      transaction.commit();
+    } catch (Throwable th) {
+      throw implicitRollback(th);
     }
-
     try {
-      checkActivated();
-      rollback = IMPLICIT_ROLLBACK;
-      rollbackCause = cause;
-      failTransaction();
-      return new MulgaraTransactionException("Transaction in Rollback", cause);
+      try {
+        transaction = null;
+      } finally { try {
+        state = State.FINISHED;
+      } finally { try {
+        context.clear();
+      } finally { try {
+        enlisted.clear();
+      } finally { try {
+        manager.transactionComplete(this);
+      } finally { try {
+        manager = null;
+      } finally {
+        report("Committed transaction");
+      } } } } } }
     } catch (Throwable th) {
-      abortTransaction("Failed to rollback normally", th);
-      throw new MulgaraTransactionException("Abort failed to throw exception", th);
+      errorReport("Error cleaning up transaction post-commit", th);
+      throw new MulgaraTransactionException("Error cleaning up transaction post-commit", th);
     }
   }
 
@@ -277,157 +379,253 @@
    * after all we requested it.
    */
   public void explicitRollback() throws MulgaraTransactionException {
-    try {
-      checkActivated();
-      failTransaction();
-      rollback = EXPLICIT_ROLLBACK;
-    } catch (Throwable th) {
-      abortTransaction("Explicit rollback failed", th);
+    if (currentThread == null) {
+      throw new MulgaraTransactionException("Transaction failed activation check");
+    } else if (!currentThread.equals(Thread.currentThread())) {
+      throw new MulgaraTransactionException("Concurrent access attempted to transaction: Transaction has NOT been rolledback.");
     }
-  }
 
-  private void startTransaction() throws MulgaraTransactionException {
-    report("Initiating transaction");
-    transaction = manager.transactionStart(this);
     try {
-      context.initiate(this);
+      switch (state) {
+        case ACTUNREF:
+        case ACTREF:
+          transaction.rollback();
+          context.clear();
+          enlisted.clear();
+          manager.transactionComplete(this);
+          state = State.FINISHED;
+          break;
+        case DEACTREF:
+          throw new IllegalStateException("Attempt to rollback unactivated transaction");
+        case CONSTRUCTEDREF:
+          throw new MulgaraTransactionException("Attempt to rollback uninitiated ref'd transaction");
+        case CONSTRUCTEDUNREF:
+          throw new MulgaraTransactionException("Attempt to rollback uninitiated unref'd transaction");
+        case FINISHED:
+          throw new MulgaraTransactionException("Attempt to rollback finished transaction");
+        case FAILED:
+          throw new MulgaraTransactionException("Attempt to rollback failed transaction");
+      }
+    } catch (MulgaraTransactionException em) {
+      throw em;
     } catch (Throwable th) {
       throw implicitRollback(th);
     }
   }
 
-  private void resumeTransaction() throws MulgaraTransactionException {
-    report("Resuming transaction");
+  /**
+   * This will endevour to terminate the transaction via a rollback - if this
+   * fails it will abort the transaction.
+   * If the rollback succeeds then this method will return a suitable
+   * MulgaraTransactionException to be thrown by the caller.
+   * If the rollback fails then this method will throw the resulting exception
+   * from abortTransaction().
+   * Post-condition: The transaction is terminated and cleaned up.
+   */
+  MulgaraTransactionException implicitRollback(Throwable cause) throws MulgaraTransactionException {
     try {
-      manager.transactionResumed(this, transaction);
+      report("Implicit Rollback triggered");
+
+      if (currentThread == null) {
+        throw new MulgaraTransactionException("Transaction not associated with thread");
+      } else if (!currentThread.equals(Thread.currentThread())) {
+        throw new MulgaraTransactionException("Concurrent access attempted to transaction: Transaction has NOT been rolledback.");
+      }
+
+      if (rollbackCause != null) {
+        errorReport("Cascading error, transaction already rolled back", cause);
+        errorReport("Cascade error, expected initial cause", rollbackCause);
+
+        return new MulgaraTransactionException("Transaction already in rollback", cause);
+      }
+
+      switch (state) {
+        case ACTUNREF:
+        case ACTREF:
+            try {
+              rollbackCause = cause;
+            } finally { try {
+              transaction.rollback();
+            } finally { try {
+              transaction = null;
+            } finally { try {
+              context.clear();
+            } finally { try {
+              enlisted.clear();
+            } finally { try {
+              state = State.FAILED;
+            } finally { try {
+              manager.transactionComplete(this);
+            } finally {
+              manager = null;
+            } } } } } } }
+            return new MulgaraTransactionException("Transaction rollback triggered", cause);
+        case DEACTREF:
+          throw new IllegalStateException("Attempt to rollback deactivated transaction");
+        case CONSTRUCTEDREF:
+          throw new MulgaraTransactionException("Attempt to rollback uninitiated ref'd transaction");
+        case CONSTRUCTEDUNREF:
+          throw new MulgaraTransactionException("Attempt to rollback uninitiated unref'd transaction");
+        case FINISHED:
+          throw new MulgaraTransactionException("Attempt to rollback finished transaction");
+        case FAILED:
+          throw new MulgaraTransactionException("Attempt to rollback failed transaction");
+        default:
+          throw new MulgaraTransactionException("Unknown state");
+      }
     } catch (Throwable th) {
-      abortTransaction("Failed to resume transaction", th);
+      try {
+        errorReport("Attempt to rollback failed; initiating cause: ", cause);
+      } finally {
+        throw abortTransaction("Failed to rollback normally - see log for inititing cause", th);
+      }
+    } finally {
+      report("Leaving implicitRollback");
     }
   }
 
-  private void suspendTransaction() throws MulgaraTransactionException {
-    report("Suspending Transaction");
+  /**
+   * Forces the transaction to be abandoned, including bypassing JTA to directly
+   * rollback/abort the underlying store-phases if required.
+   * Heavilly nested try{}finally{} should guarentee that even JVM errors should
+   * not prevent this function from cleaning up anything that can be cleaned up.
+   * We have to delegate to the OperationContext the abort() on the resolvers as
+   * only it has full knowledge of which resolvers are associated with this
+   * transaction.
+   */
+  MulgaraTransactionException abortTransaction(String errorMessage, Throwable cause)
+      throws MulgaraTransactionException {
+    // We need to notify the manager here - this is serious, we
+    // need to rollback this transaction, but if we have reached here
+    // we have failed to obtain a valid transaction to rollback!
     try {
-      if (rollback == NO_ROLLBACK) {
-        this.transaction = manager.transactionSuspended(this);
-      } else {
-        terminateTransaction();
-      }
+      try {
+        errorReport(errorMessage + " - Aborting", cause);
+      } finally { try {
+        manager.transactionAborted(this);
+      } finally { try {
+        abortEnlistedResources();
+      } finally { try {
+        context.clear();
+      } finally { try {
+        transaction = null;
+      } finally { try {
+        manager = null;
+      } finally {
+        state = State.FAILED;
+      } } } } } }
+      return new MulgaraTransactionException(errorMessage + " - Aborting", cause);
     } catch (Throwable th) {
-      throw implicitRollback(th);
+      throw new MulgaraTransactionException(errorMessage + " - Failed to abort cleanly", th);
     } finally {
-      report("Finished suspending transaction");
+      report("Leaving abortTransaction");
     }
   }
 
-  private void terminateTransaction() throws MulgaraTransactionException {
-    report("Terminating Transaction: " + rollback);
-//    errorReport("Terminating Transaction: " + rollback);
-    try {
-      switch (rollback) {
-        case NO_ROLLBACK:
-          report("Completing Transaction");
-          try {
-            transaction.commit();
-            transaction = null;
-          } catch (Throwable th) {
-            implicitRollback(th);
-            terminateTransaction();
-          }
-          break;
-        case IMPLICIT_ROLLBACK:
-          report("Completing Implicitly Failed Transaction");
-          // Check that transaction is cleaned up.
-          throw new MulgaraTransactionException(
-              "Failed transaction finalised. (ROLLBACK)", rollbackCause);
-        case EXPLICIT_ROLLBACK:
-          report("Completing explicitly failed transaction (ROLLBACK)");
-          // Check that transaction is cleaned up.
-          break;
-      }
-    } finally {
+  /**
+   * Used to bypass JTA and explicitly abort resources behind the scenes.
+   */
+  private void abortEnlistedResources() {
+    for (EnlistableResource e : enlisted) {
       try {
+        e.abort();
+      } catch (Throwable th) {
         try {
-          context.clear();
-        } catch (QueryException eq) {
-          throw new MulgaraTransactionException("Error clearing context", eq);
-        }
-      } finally {
-        try {
-          manager.transactionComplete(this);
-        } finally {
-          manager = null;
-          inuse = 0;
-          using = 0;
-          report("Terminated transaction");
-        }
+          errorReport("Error aborting enlistable resource", th);
+        } catch (Throwable ignore) { }
       }
     }
   }
 
-  private void failTransaction() throws Throwable {
-    transaction.rollback();
-    manager.transactionFailed(this);
-    context.clear();
-  }
-
-  void abortTransaction(String errorMessage, Throwable th) throws MulgaraTransactionException {
-    // We need to notify the manager here - this is serious, we
-    // can't rollback normally if we can't resume!  The call to
-    // context.abort() is an escape hatch we use to abort the 
-    // current phase behind the scenes.
-    logger.error(errorMessage + " - Aborting", th);
+  void execute(Operation operation,
+               ResolverSessionFactory resolverSessionFactory, // FIXME: We shouldn't need this. - only used for backup and restore operations.
+               DatabaseMetadata metadata) throws MulgaraTransactionException {
+    report("Executing Operation");
     try {
-      manager.transactionAborted(this);
+      activate();
+      try {
+        operation.execute(context,
+                          context.getSystemResolver(),
+                          resolverSessionFactory,
+                          metadata);
+      } catch (Throwable th) {
+        throw implicitRollback(th);
+      } finally {
+        deactivate();
+      }
     } finally {
-      context.abort();
+      report("Executed Operation");
     }
-    throw new MulgaraTransactionException(errorMessage + " - Aborting", th);
   }
 
-  //
-  // Note that OperationContext needs to be decoupled from DatabaseSession, so that it is
-  // recreated for each operation - it also needs to provide an XAResource of it's own for
-  // enlisting in the transaction to clean-up caches and the like.
-  //
-  public void enlist(EnlistableResource enlistable) throws MulgaraTransactionException {
+  AnswerOperationResult execute(AnswerOperation ao) throws TuplesException {
+    debugReport("Executing AnswerOperation");
     try {
-      transaction.enlistResource(enlistable.getXAResource());
-    } catch (Exception e) {
-      throw new MulgaraTransactionException("Error enlisting resolver", e);
+      activate();
+      try {
+        ao.execute();
+        return ao.getResult();
+      } catch (Throwable th) {
+        throw implicitRollback(th);
+      } finally {
+        deactivate();
+      }
+    } catch (MulgaraTransactionException em) {
+      throw new TuplesException("Transaction error", em);
+    } finally {
+      debugReport("Executed AnswerOperation");
     }
   }
 
-  //
-  // Should only be visible to MulgaraTransactionManager.
-  //
 
-  /**
-   * Force transaction to a conclusion.
-   * If we can't activate or commit, then rollback and terminate.
-   * This is called by the manager to indicate that it needs this transaction to
-   * be finished NOW, one way or another.
-   */
-  void completeTransaction() throws MulgaraTransactionException {
+  void execute(TransactionOperation to) throws MulgaraTransactionException {
+    report("Executing TransactionOperation");
     try {
       activate();
-    } catch (Throwable th) {
-      implicitRollback(th);  // let terminate throw this exception if required.
+      try {
+        to.execute();
+      } catch (Throwable th) {
+        throw implicitRollback(th);
+      } finally {
+        deactivate();
+      }
     } finally {
-      terminateTransaction();
+      report("Executed TransactionOperation");
     }
-    // We don't need to deactivate - this method is *only* for use by the
-    // MulgaraTransactionManager!
   }
 
-  protected void finalize() {
-    report("GC-finalize");
-    if (inuse != 0 || using != 0) {
-      errorReport("Referernce counting error in transaction");
+  public void enlist(EnlistableResource enlistable) throws MulgaraTransactionException {
+    try {
+      if (currentThread == null) {
+        throw new MulgaraTransactionException("Transaction not associated with thread");
+      } else if (!currentThread.equals(Thread.currentThread())) {
+        throw new MulgaraTransactionException("Concurrent access attempted to transaction: Transaction has NOT been rolledback.");
+      }
+
+      if (enlisted.contains(enlistable)) {
+        return;
+      }
+
+      switch (state) {
+        case ACTUNREF:
+        case ACTREF:
+          transaction.enlistResource(enlistable.getXAResource());
+          enlisted.add(enlistable);
+          break;
+        case CONSTRUCTEDREF:
+          throw new MulgaraTransactionException("Attempt to enlist resource in uninitated ref'd transaction");
+        case CONSTRUCTEDUNREF:
+          throw new MulgaraTransactionException("Attempt to enlist resource in uninitated unref'd transaction");
+        case DEACTREF:
+          throw new MulgaraTransactionException("Attempt to enlist resource in unactivated transaction");
+        case FINISHED:
+          throw new MulgaraTransactionException("Attempt to enlist resource in finished transaction");
+        case FAILED:
+          throw new MulgaraTransactionException("Attempt to enlist resource in failed transaction");
+      }
+    } catch (Throwable th) {
+      throw implicitRollback(th);
     }
-    if (manager != null || transaction != null) {
-      errorReport("Transaction not terminated properly");
-    }
   }
 
   //
@@ -436,34 +634,67 @@
 
   private void checkActivated() throws MulgaraTransactionException {
     if (currentThread == null) {
-      throw new MulgaraTransactionException("Transaction failed activation check");
+      throw new MulgaraTransactionException("Transaction not associated with thread");
     } else if (!currentThread.equals(Thread.currentThread())) {
       throw new MulgaraTransactionException("Concurrent access attempted to transaction: Transaction has NOT been rolledback.");
-    } else if (inuse < 1) {
-      throw implicitRollback(
-          new MulgaraTransactionException("Mismatched activate/deactivate.  inuse < 1: " + inuse));
-    } else if (using < 0) {
-      throw implicitRollback(
-          new MulgaraTransactionException("Reference Failure.  using < 0: " + using));
+    } else {
+      switch (state) {
+        case ACTUNREF:
+        case ACTREF:
+          if (inuse < 0 || using < 0) {
+            throw new MulgaraTransactionException("Reference Failure, using: " + using + ", inuse: " + inuse);
+          }
+          return;
+        case CONSTRUCTEDREF:
+          throw new MulgaraTransactionException("Transaction (ref) uninitiated");
+        case CONSTRUCTEDUNREF:
+          throw new MulgaraTransactionException("Transaction (unref) uninitiated");
+        case DEACTREF:
+          throw new MulgaraTransactionException("Transaction deactivated");
+        case FINISHED:
+          throw new MulgaraTransactionException("Transaction is terminated");
+        case FAILED:
+          throw new MulgaraTransactionException("Transaction is failed", rollbackCause);
+      }
     }
   }
 
+  protected void finalize() {
+    report("GC-finalize");
+    if (state != State.FINISHED && state != State.FAILED) {
+      errorReport("Finalizing incomplete transaction - aborting...", null);
+      try {
+        abortTransaction("Transaction finalized while still valid", new Throwable());
+      } catch (Throwable th) {
+        errorReport("Attempt to abort transaction from finalize failed", th);
+      }
+    }
+
+    if (state != State.FAILED && (inuse != 0 || using != 0)) {
+      errorReport("Referernce counting error in transaction", null);
+    }
+
+    if (manager != null || transaction != null) {
+      errorReport("Transaction not terminated properly", null);
+    }
+  }
+
   private void report(String desc) {
     if (logger.isInfoEnabled()) {
-      logger.info(desc + ": " + System.identityHashCode(this) +
+      logger.info(desc + ": " + System.identityHashCode(this) + ", state=" + state +
           ", inuse=" + inuse + ", using=" + using);
     }
   }
 
   private void debugReport(String desc) {
     if (logger.isDebugEnabled()) {
-      logger.debug(desc + ": " + System.identityHashCode(this) +
+      logger.debug(desc + ": " + System.identityHashCode(this) + ", state=" + state +
           ", inuse=" + inuse + ", using=" + using);
     }
   }
 
-  private void errorReport(String desc) {
-    logger.error(desc + ": " + System.identityHashCode(this) +
-        ", inuse=" + inuse + ", using=" + using, new Throwable());
+  private void errorReport(String desc, Throwable cause) {
+    logger.error(desc + ": " + System.identityHashCode(this) + ", state=" + state +
+        ", inuse=" + inuse + ", using=" + using, cause != null ? cause : new Throwable());
   }
 }

Modified: trunk/src/jar/resolver/java/org/mulgara/resolver/MulgaraTransactionManager.java
===================================================================
--- trunk/src/jar/resolver/java/org/mulgara/resolver/MulgaraTransactionManager.java	2007-03-01 16:41:41 UTC (rev 206)
+++ trunk/src/jar/resolver/java/org/mulgara/resolver/MulgaraTransactionManager.java	2007-03-19 07:01:14 UTC (rev 207)
@@ -75,23 +75,23 @@
   private MulgaraTransaction userTransaction;
 
   /** Set of sessions whose transactions have been rolledback.*/
-  private Set failedSessions;
+  private Set<Session> failedSessions;
 
   /**
    * Map from transaction to initiating session.
    * FIXME: This is only required for checking while we wait for 1-N.
    *        Remove once 1-N is implemented.
    */
-  private Map sessions;
+  private Map<MulgaraTransaction, Session> sessions;
 
   /**
    * Map from initiating session to set of transactions.
    * Used to clean-up transactions upon session close.
    */
-  private Map transactions;
+  private Map<Session, Set<MulgaraTransaction>> transactions;
 
   /** Map of threads to active transactions. */
-  private Map activeTransactions;
+  private Map<Thread, MulgaraTransaction> activeTransactions;
 
   private TransactionManager transactionManager;
 
@@ -99,10 +99,10 @@
     this.currentWritingSession = null;
     this.userTransaction = null;
 
-    this.failedSessions = new HashSet();
-    this.sessions = new HashMap();
-    this.transactions = new HashMap();
-    this.activeTransactions = new HashMap();
+    this.failedSessions = new HashSet<Session>();
+    this.sessions = new HashMap<MulgaraTransaction, Session>();
+    this.transactions = new HashMap<Session, Set<MulgaraTransaction>>();
+    this.activeTransactions = new HashMap<Thread, MulgaraTransaction>();
 
     this.transactionManager = transactionManagerFactory.newTransactionManager();
   }
@@ -127,8 +127,14 @@
       MulgaraTransaction transaction = write ?
           obtainWriteLock(session) :
           new MulgaraTransaction(this, session.newOperationContext(false));
+
       sessions.put(transaction, session);
 
+      if (!transactions.containsKey(session)) {
+        transactions.put(session, new HashSet<MulgaraTransaction>());
+      }
+      transactions.get(session).add(transaction);
+
       return transaction;
     } catch (MulgaraTransactionException em) {
       throw em;
@@ -152,9 +158,6 @@
       currentWritingSession = session;
       userTransaction = new MulgaraTransaction(this, session.newOperationContext(true));
       return userTransaction;
-    } catch (MulgaraTransactionException em) {
-      releaseWriteLock();
-      throw em;
     } catch (Throwable th) {
       releaseWriteLock();
       throw new MulgaraTransactionException("Error while obtaining write-lock", th);
@@ -199,7 +202,8 @@
         });
         if (userTransaction != null) {
           // transaction referenced by something - need to explicitly end it.
-          userTransaction.completeTransaction();
+          userTransaction.abortTransaction("Rollback failed",
+              new MulgaraTransactionException("Rollback failed to terminate write transaction"));
         }
       } finally {
         failedSessions.add(currentWritingSession);
@@ -228,13 +232,17 @@
         if (session == currentWritingSession) {
           // Within active transaction - commit and finalise.
           try {
-            userTransaction.dereference();
-            userTransaction.completeTransaction();
+            userTransaction.execute(new TransactionOperation() {
+              public void execute() throws MulgaraTransactionException {
+                userTransaction.dereference();
+                userTransaction.commitTransaction();
+              }
+            });
           } finally {
             releaseWriteLock();
           }
         } else if (failedSessions.contains(session)) {
-          // Within failed transaction - cleanup and finalise.
+          // Within failed transaction - cleanup.
           failedSessions.remove(session);
         }
       } else {
@@ -253,50 +261,91 @@
     }
   }
 
-  public synchronized void terminateCurrentTransactions(Session session)
+  public synchronized void rollbackCurrentTransactions(Session session)
       throws MulgaraTransactionException {
-    if (failedSessions.contains(session)) {
-      failedSessions.remove(session);
-      return;
-    }
+    try {
+      if (failedSessions.contains(session)) {
+        failedSessions.remove(session);
+        return;
+      }
 
-    Throwable error = null;
-    try {
-      if (session == currentWritingSession) {
-        logger.error("Terminating session while holding writelock:" + session +
-            ":" + currentWritingSession + ": " + userTransaction);
-        userTransaction.execute(new TransactionOperation() {
-            public void execute() throws MulgaraTransactionException {
-              userTransaction.implicitRollback(
-                new MulgaraTransactionException("Terminating session while holding writelock"));
+      Throwable error = null;
+
+      try {
+        if (session == currentWritingSession) {
+          logger.warn("Terminating session while holding writelock:" + session +
+              ":" + currentWritingSession + ": " + userTransaction);
+          userTransaction.execute(new TransactionOperation() {
+              public void execute() throws MulgaraTransactionException {
+                throw new MulgaraTransactionException("Terminating session while holding writelock");
+              }
+          });
+        }
+      } catch (Throwable th) {
+        error = th;
+      }
+
+      final Throwable trigger = new MulgaraTransactionException("trigger rollback");
+
+      if (transactions.containsKey(session)) {
+        for (MulgaraTransaction transaction : transactions.get(session)) {
+          try {
+            transaction.execute(new TransactionOperation() {
+              public void execute() throws MulgaraTransactionException {
+                throw new MulgaraTransactionException("Rolling back transactions due to session close");
+              }
+            });
+          } catch (MulgaraTransactionException em) {
+            // ignore.
+          } catch (Throwable th) {
+            if (error == null) {
+              error = th;
             }
-        });
+          }
+        }
       }
-    } catch (Throwable th) {
-      error = th;
-    }
 
-    Set trans = (Set)transactions.get(session);
-    if (trans != null) {
-      Iterator i = trans.iterator();
-      while (i.hasNext()) {
-        MulgaraTransaction transaction = (MulgaraTransaction)i.next();
-        logger.error("Active transaction during session termination");
-        Throwable th = transaction.implicitRollback(
-            new MulgaraTransactionException("Terminating session during active transaction"));
-        if (error ==  null) {
-          error = th;
+      if (error != null) {
+        if (error instanceof MulgaraTransactionException) {
+          throw (MulgaraTransactionException)error;
+        } else {
+          throw new MulgaraTransactionException("Error in rollback on session close", error);
         }
-        transaction.completeTransaction();
       }
+    } finally {
+      if (transactions.containsKey(session)) {
+        logger.error("Error in transaction rollback due to session close - aborting");
+        abortCurrentTransactions(session);
+      }
     }
+  }
 
-    if (error != null) {
-      if (error instanceof MulgaraTransactionException) {
-        throw (MulgaraTransactionException)error;
-      } else {
-        throw new MulgaraTransactionException("Aborting session on close", error);
+  private void abortCurrentTransactions(Session session) throws MulgaraTransactionException {
+    try {
+      Throwable error = null;
+      for (MulgaraTransaction transaction : transactions.get(session)) {
+        try {
+          transaction.abortTransaction("Transaction still valid on session close", new Throwable());
+        } catch (Throwable th) {
+          try {
+            if (error == null) {
+              error = th;
+            }
+          } catch (Throwable throw_away) {}
+        }
       }
+
+      if (error != null) {
+        if (error instanceof MulgaraTransactionException) {
+          throw (MulgaraTransactionException)error;
+        } else {
+          throw new MulgaraTransactionException("Error in rollback on session close", error);
+        }
+      }
+    } finally {
+      if (session == currentWritingSession) {
+        logger.error("Failed to abort write-transaction on session close - Server restart required");
+      }
     }
   }
 
@@ -348,25 +397,24 @@
     try {
       if (transaction != activeTransactions.get(Thread.currentThread())) {
         throw new MulgaraTransactionException(
-            "Attempt to commit transaction from outside thread");
+            "Attempt to suspend transaction from outside thread");
       }
 
       return transactionManager.suspend();
-    } catch (Exception e) {
-      logger.error("Attempt to suspend failed", e);
+    } catch (Throwable th) {
+      logger.error("Attempt to suspend failed", th);
       try {
         transactionManager.setRollbackOnly();
-      } catch (Throwable th) {
-        logger.error("Attempt to setRollbackOnly() failed", th);
+      } catch (Throwable t) {
+        logger.error("Attempt to setRollbackOnly() failed", t);
       }
-      throw new MulgaraTransactionException("Suspend failed", e);
+      throw new MulgaraTransactionException("Suspend failed", th);
     } finally {
       activeTransactions.remove(Thread.currentThread());
     }
   }
 
-  public synchronized void transactionComplete(MulgaraTransaction transaction) 
-      throws MulgaraTransactionException {
+  public synchronized void transactionComplete(MulgaraTransaction transaction) {
     if (holdsWriteLock(transaction)) {
       releaseWriteLock();
     }
@@ -377,10 +425,6 @@
     transactions.remove(session);
   }
 
-  public synchronized void transactionFailed(MulgaraTransaction transaction) {
-    // No specific behaviour required here.
-  }
-
   public synchronized void transactionAborted(MulgaraTransaction transaction) {
     try {
       // Make sure this cleans up the transaction metadata - this transaction is DEAD!

Modified: trunk/src/jar/resolver/java/org/mulgara/resolver/StatusFormat.java
===================================================================
--- trunk/src/jar/resolver/java/org/mulgara/resolver/StatusFormat.java	2007-03-01 16:41:41 UTC (rev 206)
+++ trunk/src/jar/resolver/java/org/mulgara/resolver/StatusFormat.java	2007-03-19 07:01:14 UTC (rev 207)
@@ -78,25 +78,4 @@
         return "NOT_A_STATUS_" + status;
     }
   }
-
-  /**
-   * Generate a presentation form for the status of a transaction manager.
-   *
-   * This method will return the message of the exception if an exception
-   * results from querying the transaction manager's status, rather than
-   * throwing the exception.
-   *
-   * @param transactionManager  the transaction manager
-   */
-   /*
-  public static String formatStatus(MulgaraTransactionManager transactionManager)
-  {
-    try {
-      return formatStatus(transactionManager.getStatus());
-    }
-    catch (SystemException e) {
-      return e.getMessage();
-    }
-  }
-  */
 }

Modified: trunk/src/jar/resolver/java/org/mulgara/resolver/TransactionalAnswer.java
===================================================================
--- trunk/src/jar/resolver/java/org/mulgara/resolver/TransactionalAnswer.java	2007-03-01 16:41:41 UTC (rev 206)
+++ trunk/src/jar/resolver/java/org/mulgara/resolver/TransactionalAnswer.java	2007-03-19 07:01:14 UTC (rev 207)
@@ -245,14 +245,70 @@
     report("GC-finalizing");
     if (transaction != null) {
       logger.warn("TransactionalAnswer not closed");
+      /*
+      try {
+        transaction.execute(new AnswerOperation() {
+            public void execute() throws TuplesException {
+              try {
+                answer.close();
+              } finally {
+                try {
+                  transaction.dereference();
+                } catch (MulgaraTransactionException em) {
+                  throw new TuplesException("Error dereferencing transaction", em);
+                }
+              }
+            }
+        });
+      } catch (TuplesException et) {
+        report("Error dereferencing transaction from finalize");
+      } finally {
+        transaction = null;
+        answer = null;
+      }
+*/
+//      try {
+//        sessionClose();
+//      } catch (TuplesException et) {
+//        logger.warn("Error force-closing TransactionAnswer", et);
+//      }
     }
   }
 
 
   void sessionClose() throws TuplesException {
+    if (closing) {
+      report("Session close on closing answer");
+      return;
+    }
+
     if (answer != null) {
       report("Session forced close");
-      close();
+      Throwable error = null;
+      closing = true;
+      try {
+        answer.close();
+      } catch (Throwable th) {
+        error = th;
+      } finally {
+        try {
+          transaction.dereference();
+        } catch (MulgaraTransactionException em) {
+          throw new TuplesException("Error dereferencing transaction", em);
+        } finally {
+          closing = false;
+          answer = null;
+          transaction = null;
+        }
+        if (error != null) {
+          if (error instanceof TuplesException) {
+            throw (TuplesException)error;
+          } else {
+            throw new TuplesException("Error closing answer", error);
+          }
+        }
+      }
+//      close();
     }
   }
 

Modified: trunk/src/jar/server/java/org/mulgara/server/EmbeddedMulgaraServer.java
===================================================================
--- trunk/src/jar/server/java/org/mulgara/server/EmbeddedMulgaraServer.java	2007-03-01 16:41:41 UTC (rev 206)
+++ trunk/src/jar/server/java/org/mulgara/server/EmbeddedMulgaraServer.java	2007-03-19 07:01:14 UTC (rev 207)
@@ -564,7 +564,7 @@
     catch (Exception e) {
 
       // log the error
-      log.error(e);
+      log.error("Exception in main", e);
       e.printStackTrace();
 
       System.exit(5);
@@ -1484,7 +1484,7 @@
     catch (IOException ioe) {
 
       // log the error
-      log.error(ioe.getMessage());
+      log.error(ioe.getMessage(), ioe);
 
       // print the usage
       //System.err.println("Invalid URL - " + ioe.getMessage());




More information about the Mulgara-svn mailing list