[Mulgara-svn] r1066 - branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver

ronald at mulgara.org ronald at mulgara.org
Mon Jul 7 12:54:52 UTC 2008


Author: ronald
Date: 2008-07-07 05:54:51 -0700 (Mon, 07 Jul 2008)
New Revision: 1066

Added:
   branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/MockResolver.java
   branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/MockResolverFactory.java
Modified:
   branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/AdvDatabaseSessionUnitTest.java
   branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/ExternalTransactionUnitTest.java
   branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/MulgaraExternalTransaction.java
   branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/MulgaraInternalTransaction.java
Log:
Final tweaks to heuristic-rollback, plus tests for this stuff.

Fix heuristic rollback of internal transactions for all transaction states.
Add checks to execute() for external transactions throw an exception if the
transaction has been rolled back or otherwise completed.

A mock-resolver has been added for testing that can delay arbitrary operations
in order to set up certain states when timeouts trigger.


Modified: branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/AdvDatabaseSessionUnitTest.java
===================================================================
--- branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/AdvDatabaseSessionUnitTest.java	2008-07-07 12:54:45 UTC (rev 1065)
+++ branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/AdvDatabaseSessionUnitTest.java	2008-07-07 12:54:51 UTC (rev 1066)
@@ -205,6 +205,7 @@
 
       database.addResolverFactory("org.mulgara.resolver.url.URLResolverFactory", null);
       database.addResolverFactory("org.mulgara.resolver.xsd.XSDResolverFactory", null);
+      database.addResolverFactory("org.mulgara.resolver.MockResolverFactory", null);
     }
   }
 
@@ -950,14 +951,14 @@
     // test idle timeout
     try {
       Session session1 = database.newSession();
-      session1.setIdleTimeout(10000);
+      session1.setIdleTimeout(1000);
       try {
         session1.createModel(model3URI, null);
         logger.debug("Obtaining autocommit for session1");
         session1.setAutoCommit(false);
         logger.debug("Obtained autocommit for session1");
 
-        Thread t2 = new Thread("tx2Test") {
+        Thread t2 = new Thread("tx2IdleTest") {
           public void run() {
             try {
               Session session2 = database.newSession();
@@ -986,9 +987,9 @@
         t2.start();
 
         session1.setModel(model3URI, new ModelResource(fileURI));
-        logger.debug("Sleeping for 20sec");
-        Thread.sleep(20000);
-        logger.debug("Slept for 20sec");
+        logger.debug("Sleeping for 1sec");
+        Thread.sleep(1000);
+        logger.debug("Slept for 1sec");
         try {
           t2.join(2000L);
         } catch (InterruptedException ie) {
@@ -997,19 +998,7 @@
         }
         assertFalse("second transaction should've terminated", t2.isAlive());
 
-        boolean qeThrown = false;
-        try {
-          session1.commit();
-        } catch (QueryException eq) {
-          qeThrown = true;
-        } catch (IllegalStateException ise) {
-          qeThrown = true;
-        }
-
-        assertTrue("Commit should have failed due to idle timeout", qeThrown);
-
-        logger.debug("Releasing autocommit for session1");
-        session1.setAutoCommit(true);
+        rollbackTimedOutTxn(session1);
       } finally {
         session1.close();
       }
@@ -1020,14 +1009,14 @@
     // test transaction timeout
     try {
       Session session1 = database.newSession();
-      session1.setTransactionTimeout(10000);
+      session1.setTransactionTimeout(1000);
       try {
         session1.createModel(model3URI, null);
         logger.debug("Obtaining autocommit for session1");
         session1.setAutoCommit(false);
         logger.debug("Obtained autocommit for session1");
 
-        Thread t2 = new Thread("tx2Test") {
+        Thread t2 = new Thread("tx2TxToTest") {
           public void run() {
             try {
               Session session2 = database.newSession();
@@ -1056,9 +1045,9 @@
         t2.start();
 
         session1.setModel(model3URI, new ModelResource(fileURI));
-        logger.debug("Sleeping for 10sec");
-        Thread.sleep(10000);
-        logger.debug("Slept for 10sec");
+        logger.debug("Sleeping for 1sec");
+        Thread.sleep(1000);
+        logger.debug("Slept for 1sec");
         try {
           t2.join(2000L);
         } catch (InterruptedException ie) {
@@ -1067,28 +1056,162 @@
         }
         assertFalse("second transaction should've terminated", t2.isAlive());
 
-        boolean qeThrown = false;
+        rollbackTimedOutTxn(session1);
+      } finally {
+        session1.close();
+      }
+    } catch (Exception e) {
+      fail(e);
+    }
+
+    // test transaction timeout interrupting active operation
+    try {
+      Session session1 = database.newSession();
+      try {
+        session1.setTransactionTimeout(1000L);
+        session1.setAutoCommit(false);
+        logger.debug("Started transaction for session1");
+
+        URI delayTwoSecs = new URI("foo://mulgara/timeoutTest?active=mr&wait=2000");
+        session1.createModel(delayTwoSecs, new URI(Mulgara.NAMESPACE + "MockModel"));
+
         try {
-          session1.commit();
-        } catch (QueryException eq) {
-          qeThrown = true;
-        } catch (IllegalStateException ise) {
-          qeThrown = true;
+          Answer answer = session1.query(createQuery(delayTwoSecs));
+          fail("query should've gotten interrupted");
+        } catch (QueryException qe) {
+          logger.debug("query was interrupted", qe);
         }
 
-        assertTrue("Commit should have failed due to lock timeout", qeThrown);
+        rollbackTimedOutTxn(session1);
+      } finally {
+        session1.close();
+      }
+    } catch (Exception e) {
+      fail(e);
+    }
 
-        logger.debug("Releasing autocommit for session1");
-        session1.setAutoCommit(true);
+    // test transaction timeout timing out not-yet-active operation
+    try {
+      Session session1 = database.newSession();
+      try {
+        session1.setTransactionTimeout(1000L);
+        session1.setAutoCommit(false);
+        logger.debug("Started transaction for session1");
+
+        logger.debug("Sleeping for 2sec");
+        Thread.sleep(2000);
+        logger.debug("Slept for 2sec");
+
+        try {
+          Answer answer = session1.query(createQuery(model3URI));
+          fail("query should've gotten interrupted");
+        } catch (QueryException qe) {
+          logger.debug("query was interrupted", qe);
+        }
+
+        rollbackTimedOutTxn(session1);
       } finally {
         session1.close();
       }
     } catch (Exception e) {
       fail(e);
     }
+
+    // test transaction timeout while operation is waiting for mutex
+    try {
+      final Session session1 = database.newSession();
+      try {
+        session1.setTransactionTimeout(1000L);
+        session1.setAutoCommit(false);
+        logger.debug("Started transaction for session1");
+
+        final URI delayTwoSecs = new URI("foo://mulgara/timeoutTest?active=mr&hardWait=2000");
+        session1.createModel(delayTwoSecs, new URI(Mulgara.NAMESPACE + "MockModel"));
+
+        Thread t2 = new Thread("timeoutTest") {
+          public void run() {
+            try {
+              try {
+                // this acquires mutex and holds it for 2s
+                Answer answer = session1.query(createQuery(delayTwoSecs));
+                Thread.sleep(100L);   // allow rollback to proceed
+                answer.beforeFirst();
+                assertFalse(answer.next());
+                answer.close();
+                fail("query should've gotten interrupted");
+              } catch (TuplesException te) {
+                logger.debug("query was interrupted", te);
+              }
+            } catch (Exception e) {
+              fail(e);
+            }
+          }
+        };
+        t2.start();
+        Thread.sleep(100L);
+
+        // blocks, waiting for mutex; when it does get it, it should rollback or see a rb
+        try {
+          Answer answer = session1.query(createQuery(model3URI));
+          Thread.sleep(100L);   // allow rollback to proceed
+          answer.beforeFirst();
+          assertFalse(answer.next());
+          answer.close();
+          fail("query should've gotten aborted");
+        } catch (QueryException qe) {
+          logger.debug("query was aborted", qe);
+        } catch (TuplesException te) {
+          logger.debug("query was aborted", te);
+        }
+
+        rollbackTimedOutTxn(session1);
+      } finally {
+        session1.close();
+      }
+    } catch (Exception e) {
+      fail(e);
+    }
+
+    // test transaction timeout on autocommit transaction
+    try {
+      final Session session1 = database.newSession();
+      try {
+        session1.setTransactionTimeout(1000L);
+
+        final URI delayTwoSecs = new URI("foo://mulgara/timeoutTest?active=mr&hardWait=2000");
+        session1.createModel(delayTwoSecs, new URI(Mulgara.NAMESPACE + "MockModel"));
+
+        try {
+          // this acquires mutex and holds it for 2s
+          Answer answer = session1.query(createQuery(delayTwoSecs));
+          Thread.sleep(100L);
+          answer.beforeFirst();
+          assertFalse(answer.next());
+          answer.close();
+          fail("query should've gotten interrupted");
+        } catch (TuplesException te) {
+          logger.debug("query was interrupted", te);
+        }
+      } finally {
+        session1.close();
+      }
+    } catch (Exception e) {
+      fail(e);
+    }
   }
 
+  private void rollbackTimedOutTxn(Session session) throws QueryException {
+    try {
+      session.commit();
+      fail("Commit should have failed due to transaction timeout");
+    } catch (QueryException qe) {
+    }
 
+    logger.debug("Rolling back transaction on session");
+    session.rollback();
+    session.setAutoCommit(true);
+  }
+
   public void testPrefixingWithUnbound() throws URISyntaxException {
     logger.warn("testPrefixingWithUnbound");
     URI fileURI  = new File("data/prefix-unbound.rdf").toURI();

Modified: branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/ExternalTransactionUnitTest.java
===================================================================
--- branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/ExternalTransactionUnitTest.java	2008-07-07 12:54:45 UTC (rev 1065)
+++ branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/ExternalTransactionUnitTest.java	2008-07-07 12:54:51 UTC (rev 1066)
@@ -43,6 +43,7 @@
 
 // Locally written packages
 import org.mulgara.query.*;
+import org.mulgara.query.rdf.Mulgara;
 import org.mulgara.query.rdf.URIReferenceImpl;
 import org.mulgara.query.rdf.TripleImpl;
 import org.mulgara.server.Session;
@@ -196,6 +197,7 @@
                    "org.mulgara.content.rdfxml.RDFXMLContentHandler");
 
       database.addResolverFactory("org.mulgara.resolver.url.URLResolverFactory", null);
+      database.addResolverFactory("org.mulgara.resolver.MockResolverFactory", null);
     }
   }
 
@@ -1523,7 +1525,7 @@
     // test idle timeout
     try {
       Session session1 = database.newSession();
-      session1.setIdleTimeout(10000);
+      session1.setIdleTimeout(1000);
 
       try {
         XAResource resource = session1.getXAResource();
@@ -1539,7 +1541,7 @@
         resource.start(xid, XAResource.TMNOFLAGS);
         logger.debug("Started transaction for session1");
 
-        Thread t2 = new Thread("tx2Test") {
+        Thread t2 = new Thread("tx2IdleTest") {
           public void run() {
             try {
               Session session2 = database.newSession();
@@ -1568,9 +1570,9 @@
         t2.start();
 
         session1.setModel(model3URI, new ModelResource(fileURI));
-        logger.debug("Sleeping for 20sec");
-        Thread.sleep(20000);
-        logger.debug("Slept for 20sec");
+        logger.debug("Sleeping for 1sec");
+        Thread.sleep(1000);
+        logger.debug("Slept for 1sec");
         try {
           t2.join(2000L);
         } catch (InterruptedException ie) {
@@ -1579,25 +1581,7 @@
         }
         assertFalse("second transaction should've terminated", t2.isAlive());
 
-        boolean xaeThrown = false;
-        try {
-          resource.end(xid, XAResource.TMSUCCESS);
-          resource.commit(xid, true);
-        } catch (XAException xae) {
-          xaeThrown = true;
-        }
-
-        assertTrue("Commit should have failed due to idle timeout", xaeThrown);
-
-        logger.debug("Rolling back transaction on session1");
-        try {
-          resource.rollback(xid);
-          fail("Rollback after timeout should have thrown XA_HEURRB");
-        } catch (XAException xae) {
-          assertEquals("Rollback after timeout should have thrown XA_HEURRB",
-                       XAException.XA_HEURRB, xae.errorCode);
-          resource.forget(xid);
-        }
+        rollbackTimedOutTxn(resource, xid, true);
       } finally {
         session1.close();
       }
@@ -1619,8 +1603,8 @@
         resource.commit(xid, true);
 
         logger.debug("Starting transaction for session1");
-        assertTrue(resource.setTransactionTimeout(10));
-        assertEquals(10, resource.getTransactionTimeout());
+        assertTrue(resource.setTransactionTimeout(1));
+        assertEquals(1, resource.getTransactionTimeout());
         resource.start(xid, XAResource.TMNOFLAGS);
         logger.debug("Started transaction for session1");
 
@@ -1653,9 +1637,9 @@
         t2.start();
 
         session1.setModel(model3URI, new ModelResource(fileURI));
-        logger.debug("Sleeping for 10sec");
-        Thread.sleep(10000);
-        logger.debug("Slept for 10sec");
+        logger.debug("Sleeping for 1sec");
+        Thread.sleep(1000);
+        logger.debug("Slept for 1sec");
         try {
           t2.join(2000L);
         } catch (InterruptedException ie) {
@@ -1664,25 +1648,103 @@
         }
         assertFalse("second transaction should've terminated", t2.isAlive());
 
-        boolean xaeThrown = false;
+        rollbackTimedOutTxn(resource, xid, true);
+      } finally {
+        session1.close();
+      }
+    } catch (Exception e) {
+      fail(e);
+    }
+
+    // test transaction timeout interrupting active operation
+    try {
+      Session session1 = database.newSession();
+      try {
+        XAResource resource = session1.getXAResource();
+
+        assertTrue(resource.setTransactionTimeout(1));
+        assertEquals(1, resource.getTransactionTimeout());
+
+        Xid xid = new TestXid(1);
+        resource.start(xid, XAResource.TMNOFLAGS);
+        logger.debug("Started transaction for session1");
+
+        URI delayTwoSecs = new URI("foo://mulgara/timeoutTest?active=mr&hardWait=2000");
+        session1.createModel(delayTwoSecs, new URI(Mulgara.NAMESPACE + "MockModel"));
+
         try {
-          resource.end(xid, XAResource.TMSUCCESS);
-          resource.commit(xid, true);
-        } catch (XAException xae) {
-          xaeThrown = true;
+          Answer answer = session1.query(createQuery(delayTwoSecs));
+          Thread.sleep(100L);
+          answer.beforeFirst();
+          assertFalse(answer.next());
+          answer.close();
+          fail("query should've gotten interrupted");
+        } catch (TuplesException te) {
+          logger.debug("query was interrupted", te);
         }
 
-        assertTrue("Commit should have failed due to transaction timeout", xaeThrown);
+        rollbackTimedOutTxn(resource, xid, true);
+      } finally {
+        session1.close();
+      }
+    } catch (Exception e) {
+      fail(e);
+    }
 
-        logger.debug("Rolling back transaction on session1");
+    // test transaction timeout while operation is waiting for mutex
+    try {
+      final Session session1 = database.newSession();
+      try {
+        XAResource resource = session1.getXAResource();
+
+        assertTrue(resource.setTransactionTimeout(1));
+        assertEquals(1, resource.getTransactionTimeout());
+
+        Xid xid = new TestXid(1);
+        resource.start(xid, XAResource.TMNOFLAGS);
+        logger.debug("Started transaction for session1");
+
+        final URI delayTwoSecs = new URI("foo://mulgara/timeoutTest?active=mr&hardWait=2000");
+        session1.createModel(delayTwoSecs, new URI(Mulgara.NAMESPACE + "MockModel"));
+
+        Thread t2 = new Thread("timeoutTest") {
+          public void run() {
+            try {
+              try {
+                // this acquires mutex and holds it for 2s
+                Answer answer = session1.query(createQuery(delayTwoSecs));
+                Thread.sleep(100L);   // allow rollback to proceed
+                answer.beforeFirst();
+                assertFalse(answer.next());
+                answer.close();
+                fail("query should've gotten interrupted");
+              } catch (TuplesException te) {
+                logger.debug("query was interrupted", te);
+              }
+            } catch (Exception e) {
+              fail(e);
+            }
+          }
+        };
+        t2.start();
+        Thread.sleep(100L);
+
+        // blocks, waiting for mutex; when it does get it, it should rollback or see a rb
         try {
-          resource.rollback(xid);
-          fail("Rollback after timeout should have thrown XA_HEURRB");
-        } catch (XAException xae) {
-          assertEquals("Rollback after timeout should have thrown XA_HEURRB",
-                       XAException.XA_HEURRB, xae.errorCode);
-          resource.forget(xid);
+          Answer answer = session1.query(createQuery(model3URI));
+          Thread.sleep(100L);   // allow rollback to proceed
+          answer.beforeFirst();
+          assertFalse(answer.next());
+          answer.close();
+          fail("query should've gotten aborted");
+        } catch (TuplesException te) {
+          logger.debug("query was aborted", te);
         }
+
+        t2.join(500);
+        assertFalse("timeout-test thread should've terminated", t2.isAlive());
+
+        rollbackTimedOutTxn(resource, xid, true);
       } finally {
         session1.close();
       }
@@ -1691,7 +1753,27 @@
     }
   }
 
+  private void rollbackTimedOutTxn(XAResource resource, Xid xid, boolean tryCommit) throws XAException {
+    try {
+      resource.end(xid, XAResource.TMSUCCESS);
+      if (tryCommit) {
+        resource.commit(xid, true);
+        fail("Commit should have failed due to transaction timeout");
+      }
+    } catch (XAException xae) {
+    }
 
+    logger.debug("Rolling back transaction");
+    try {
+      resource.rollback(xid);
+      fail("Rollback after timeout should have thrown XA_HEURRB");
+    } catch (XAException xae) {
+      assertEquals("Rollback after timeout should have thrown XA_HEURRB",
+                   XAException.XA_HEURRB, xae.errorCode);
+      resource.forget(xid);
+    }
+  }
+
   /**
    * Test various operations after a transaction fails.
    */

Added: branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/MockResolver.java
===================================================================
--- branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/MockResolver.java	                        (rev 0)
+++ branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/MockResolver.java	2008-07-07 12:54:51 UTC (rev 1066)
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2008 The Topaz Foundation 
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ *
+ * Contributions:
+ */
+
+package org.mulgara.resolver;
+
+// Java 2 standard packages;
+import java.net.URI;
+
+// Third party packages
+import javax.transaction.xa.XAResource;
+
+import org.apache.log4j.Logger;
+import org.jrdf.graph.Node;
+import org.jrdf.graph.URIReference;
+
+// Locally written packages
+import org.mulgara.query.Constraint;
+import org.mulgara.query.LocalNode;
+import org.mulgara.query.QueryException;
+import org.mulgara.query.TuplesException;
+import org.mulgara.resolver.spi.DummyXAResource;
+import org.mulgara.resolver.spi.EmptyResolution;
+import org.mulgara.resolver.spi.GlobalizeException;
+import org.mulgara.resolver.spi.Resolution;
+import org.mulgara.resolver.spi.Resolver;
+import org.mulgara.resolver.spi.ResolverException;
+import org.mulgara.resolver.spi.ResolverSession;
+import org.mulgara.resolver.spi.Statements;
+
+/**
+ * Simple mock resolver. All operations are dummys: model creation, removing, and modifying are
+ * no-ops, and queries return an empty resolution. The model name is parsed for commands to
+ * execute: the model must have a query string consisting of an '&' separated list of commands
+ * where each command is of the form "name=value" (the value may be empty) (i.e. follow http
+ * query string syntax for parameters).
+ *
+ * <p>Currently the only commands supported: are 'active', 'wait', and 'hardWait':
+ * <dl>
+ *   <dt>active</dt>
+ *   <dd>the value must be any combination of the characters 'c', 'd', 'm', 'r', 'f', 'n', or
+ *       'l', which stand for 'Resolver.createModel', 'Resolver.dropModel', 'Resolver.modifyModel',
+ *       'Resolver.resolve', 'Answer.beforeFirst', 'Answer.next', and 'Answer.cLose', respectively.
+ *       The commands after this one are only executed for these operations. If not specified, the
+ *       default is 'cdmrfnl' (i.e. all operations)</dd>
+ *   <dt>wait</dt>
+ *   <dd>the value is the number of milliseconds to sleep before continuing.</dd>
+ *   <dt>hardWait</dt>
+ *   <dd>like <var>wait</var>, but ignore interrupts.</dd>
+ * </dl>
+ *
+ * @created 2009-07-05
+ * @author Ronald Tschalär
+ * @copyright &copy;2008 <a href="http://www.topazproject.org/">Topaz Foundation</a>
+ * @licence Apache License v2.0
+ */
+public class MockResolver implements Resolver {
+  /** Logger */
+  private static Logger logger = Logger.getLogger(MockResolver.class);
+
+  /** The session that this resolver is associated with */
+  private final ResolverSession resolverSession;
+
+  MockResolver(ResolverSession resolverSession) {
+    this.resolverSession = resolverSession;
+  }
+
+  public void createModel(long model, URI modelTypeURI) throws ResolverException {
+    processCommands(model, 'c', ResolverException.class);
+  }
+
+  public XAResource getXAResource() {
+    return new DummyXAResource(10);
+  }
+
+  public void modifyModel(long model, Statements statements, boolean occurs) throws ResolverException {
+    processCommands(model, 'm', ResolverException.class);
+  }
+
+  public void removeModel(long model) throws ResolverException {
+    processCommands(model, 'd', ResolverException.class);
+  }
+
+  public Resolution resolve(Constraint constraint) throws QueryException {
+    long model = ((LocalNode) constraint.getModel()).getValue();
+    processCommands(model, 'r', QueryException.class);
+    return new MockEmptyResolution(constraint, model);
+  }
+
+  public void abort() {}
+
+  private <T extends Throwable> void processCommands(long model, char op, Class<T> exc) throws T {
+    URI modelUri = toURI(model, exc);
+    logger.debug("model-uri='" + modelUri + "'");
+
+    String query = modelUri.getQuery();
+    if (query == null) {
+      logger.debug("no query found, no commands");
+      return;
+    }
+
+    for (String param : query.split("&")) {
+      String name  = param.substring(0, param.indexOf('='));
+      String value = param.substring(param.indexOf('=') + 1);
+
+      logger.debug("processing command '" + name + "' with value '" + value + "'");
+
+      if (name.equals("active")) {
+        if (value.indexOf(op) < 0) {
+          break;
+        }
+      } else if (name.equals("wait")) {
+        logger.debug("sleeping '" + value + "' milliseconds");
+        try {
+          Thread.sleep(Long.parseLong(value));
+        } catch (InterruptedException ie) {
+          throw MulgaraTransactionFactory.newExceptionOrCause(exc, "sleep interrupted", ie);
+        }
+      } else if (name.equals("hardWait")) {
+        logger.debug("sleeping '" + value + "' milliseconds");
+        long targetDate = System.currentTimeMillis() + Long.parseLong(value);
+        while (true) {
+          long wait = targetDate - System.currentTimeMillis();
+          if (wait <= 0)
+            break;
+          try {
+            Thread.sleep(wait);
+          } catch (InterruptedException ie) {
+          }
+        }
+      } else {
+        logger.info("Unknown command '" + name + "' - ignoring");
+      }
+    }
+  }
+
+  private <T extends Throwable> URI toURI(long model, Class<T> exc) throws T {
+    try {
+      Node globalModel = resolverSession.globalize(model);
+      return ((URIReference) globalModel).getURI();
+    } catch (GlobalizeException ge) {
+      throw MulgaraTransactionFactory.newExceptionOrCause(exc, "Couldn't globalize model", ge);
+    }
+  }
+
+  private class MockEmptyResolution extends EmptyResolution {
+    private final long model;
+
+    public MockEmptyResolution(Constraint constraint, long model) {
+      super(constraint, true);
+      this.model = model;
+    }
+
+    public void beforeFirst(long[] prefix, int suffixTruncation) throws TuplesException {
+      processCommands(model, 'f', TuplesException.class);
+      super.beforeFirst(prefix, suffixTruncation);
+    }
+
+    public boolean next() throws TuplesException {
+      processCommands(model, 'n', TuplesException.class);
+      return super.next();
+    }
+
+    public void close() {
+      processCommands(model, 'l', RuntimeException.class);
+      super.close();
+    }
+  }
+}

Added: branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/MockResolverFactory.java
===================================================================
--- branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/MockResolverFactory.java	                        (rev 0)
+++ branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/MockResolverFactory.java	2008-07-07 12:54:51 UTC (rev 1066)
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2008 The Topaz Foundation 
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ *
+ * Contributions:
+ */
+
+package org.mulgara.resolver;
+
+// Java 2 standard packages
+import java.net.URI;
+
+// Locally written packages
+import org.mulgara.query.rdf.Mulgara;
+import org.mulgara.query.rdf.URIReferenceImpl;
+import org.mulgara.resolver.spi.InitializerException;
+import org.mulgara.resolver.spi.Resolver;
+import org.mulgara.resolver.spi.ResolverException;
+import org.mulgara.resolver.spi.ResolverFactory;
+import org.mulgara.resolver.spi.ResolverFactory.Graph;
+import org.mulgara.resolver.spi.ResolverFactoryException;
+import org.mulgara.resolver.spi.ResolverFactoryInitializer;
+import org.mulgara.resolver.spi.ResolverSession;
+
+/**
+ * Factory for simple mock resolver.
+ *
+ * @created 2009-07-05
+ * @author Ronald Tschalär
+ * @copyright &copy;2008 <a href="http://www.topazproject.org/">Topaz Foundation</a>
+ * @licence Apache License v2.0
+ */
+public class MockResolverFactory implements ResolverFactory {
+  private static URI modelTypeURI = URI.create(Mulgara.NAMESPACE + "MockModel");
+
+  private MockResolverFactory(ResolverFactoryInitializer resolverFactoryInitializer)
+      throws ResolverException, InitializerException {
+    resolverFactoryInitializer.preallocate(new URIReferenceImpl(modelTypeURI));
+    resolverFactoryInitializer.addModelType(modelTypeURI, this);
+  }
+
+  public void close() { }
+  public void delete() { }
+  public Graph[] getDefaultGraphs() { return null; }
+
+  public static ResolverFactory newInstance(ResolverFactoryInitializer resolverFactoryInitializer)
+      throws ResolverException, InitializerException {
+    return new MockResolverFactory(resolverFactoryInitializer);
+  }
+
+  public Resolver newResolver(boolean canWrite, ResolverSession resolverSession, Resolver systemResolver)
+      throws ResolverFactoryException {
+    return new MockResolver(resolverSession);
+  }
+}

Modified: branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/MulgaraExternalTransaction.java
===================================================================
--- branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/MulgaraExternalTransaction.java	2008-07-07 12:54:45 UTC (rev 1065)
+++ branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/MulgaraExternalTransaction.java	2008-07-07 12:54:51 UTC (rev 1066)
@@ -69,6 +69,7 @@
   private boolean hRollback;
   private int heurCode;
   private boolean rollback;
+  private String rollbackCause;
   private volatile long lastActive;
 
   MulgaraExternalTransaction(MulgaraExternalTransactionFactory factory, Xid xid, DatabaseOperationContext context)
@@ -105,6 +106,9 @@
     // we should actually already have the mutex, but let's make sure
     acquireMutex(0L, true, MulgaraTransactionException.class);
     try {
+      if (rollbackCause == null)
+        rollbackCause = errorMessage;
+
       try {
         for (EnlistableResource resource : enlisted) {
           try {
@@ -141,7 +145,13 @@
     }
 
     try {
+      if (hRollback)
+        return;
       hRollback = true;
+
+      if (rollbackCause == null)
+        rollbackCause = cause;
+
       try {
         rollback(xid);
       } catch (XAException xa) {
@@ -157,6 +167,7 @@
   public void execute(Operation operation, DatabaseMetadata metadata) throws MulgaraTransactionException {
     acquireMutex(0, false, MulgaraTransactionException.class);
     try {
+      checkActive(MulgaraTransactionException.class);
       try {
         long la = lastActive;
         lastActive = -1;
@@ -182,6 +193,7 @@
   public AnswerOperationResult execute(AnswerOperation ao) throws TuplesException {
     acquireMutex(0, false, TuplesException.class);
     try {
+      checkActive(TuplesException.class);
       try {
         long la = lastActive;
         lastActive = -1;
@@ -209,6 +221,8 @@
   public void execute(TransactionOperation to) throws MulgaraTransactionException {
     acquireMutex(0, false, MulgaraTransactionException.class);
     try {
+      checkActive(MulgaraTransactionException.class);
+
       long la = lastActive;
       lastActive = -1;
 
@@ -220,6 +234,15 @@
     }
   }
 
+  private <T extends Throwable> void checkActive(Class<T> exc) throws T {
+    if (hRollback)
+      throw factory.newException(exc, "Transaction was heuristically rolled back. Reason: " + rollbackCause);
+    if (rollback)
+      throw factory.newException(exc, "Transaction was rolled back. Reason: " + rollbackCause);
+    if (lastActive < 0)
+      throw factory.newException(exc, "Transaction has been completed");
+  }
+
   public void enlist(EnlistableResource enlistable) throws MulgaraTransactionException {
     acquireMutex(0, false, MulgaraTransactionException.class);
     try {

Modified: branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/MulgaraInternalTransaction.java
===================================================================
--- branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/MulgaraInternalTransaction.java	2008-07-07 12:54:45 UTC (rev 1065)
+++ branches/mgr-121-lockrecovery/src/jar/resolver/java/org/mulgara/resolver/MulgaraInternalTransaction.java	2008-07-07 12:54:51 UTC (rev 1066)
@@ -401,7 +401,31 @@
     }
 
     try {
-      implicitRollback(new MulgaraTransactionException(cause));
+      switch (state) {
+        case DEACTREF:
+          activate();
+          try {
+            implicitRollback(new MulgaraTransactionException(cause));
+          } finally {
+            currentThread = null;
+          }
+          break;
+
+        case ACTUNREF:
+        case ACTREF:
+          implicitRollback(new MulgaraTransactionException(cause));
+          break;
+
+        case CONSTRUCTEDREF:
+        case CONSTRUCTEDUNREF:
+          // no point in starting and then rolling back immediately, so just clean up
+          abortTransaction(cause, new Throwable());
+
+        case FINISHED:
+        case FAILED:
+          // Nothing to do here.
+          return;
+      }
     } finally {
       releaseMutex();
     }




More information about the Mulgara-svn mailing list