[Mulgara-svn] r580 - in branches/mgr-73/src/jar: resolver/java/org/mulgara/resolver resolver-store/java/org/mulgara/resolver/store

andrae at mulgara.org andrae at mulgara.org
Tue Nov 27 08:27:36 UTC 2007


Author: andrae
Date: 2007-11-27 02:27:35 -0600 (Tue, 27 Nov 2007)
New Revision: 580

Added:
   branches/mgr-73/src/jar/resolver/java/org/mulgara/resolver/ExternalTransactionUnitTest.java
Modified:
   branches/mgr-73/src/jar/resolver-store/java/org/mulgara/resolver/store/StatementStoreXAResource.java
   branches/mgr-73/src/jar/resolver/java/org/mulgara/resolver/InternalResolver.java
   branches/mgr-73/src/jar/resolver/java/org/mulgara/resolver/MulgaraExternalTransaction.java
   branches/mgr-73/src/jar/resolver/java/org/mulgara/resolver/MulgaraExternalTransactionFactory.java
   branches/mgr-73/src/jar/resolver/java/org/mulgara/resolver/MulgaraXAResource.java
Log:
Success!  We now have basic external transactions working and tested.

Still to do:
 - Rollback and error handling
 - Suspension and Resumption of transactions
 - Read-only transaction handling
 - Lots more tests
 - RMI based XAResource support
 - Testing External Jotm integration



Added: branches/mgr-73/src/jar/resolver/java/org/mulgara/resolver/ExternalTransactionUnitTest.java
===================================================================
--- branches/mgr-73/src/jar/resolver/java/org/mulgara/resolver/ExternalTransactionUnitTest.java	2007-11-23 06:16:44 UTC (rev 579)
+++ branches/mgr-73/src/jar/resolver/java/org/mulgara/resolver/ExternalTransactionUnitTest.java	2007-11-27 08:27:35 UTC (rev 580)
@@ -0,0 +1,379 @@
+/*
+ * The contents of this file are subject to the Open Software License
+ * Version 3.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.rosenlaw.com/OSL3.0.htm
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * This file is an original work developed by Netymon Pty Ltd
+ * (http://www.netymon.com, mailto:mail at netymon.com) under contract to 
+ * Topaz Foundation. Portions created under this contract are
+ * Copyright (c) 2007 Topaz Foundation
+ * All Rights Reserved.
+ *
+ * scaffolding based on AdvDatabaseSessionUnitTest.java
+ */
+
+package org.mulgara.resolver;
+
+// Java 2 standard packages
+import java.io.File;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+
+// Third party packages
+import junit.framework.*;        // JUnit
+import org.apache.log4j.Logger;  // Log4J
+
+// 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;
+import org.mulgara.util.FileUtil;
+
+/**
+ * Testing Externally Mediated Transactions. 
+ *
+ * @created 2007-11-27
+ * @author <a href="mailto:andrae at netymon.com">Andrae Muys</a>
+ * @company <a href="http://www.netymon.com/">Netymon Pty Ltd</a>
+ *      Software Pty Ltd</a>
+ * @copyright &copy;2006 <a href="http://www.topazproject.org/">Topaz Project
+ * Foundation</a>
+ *
+ * @licence Open Software License v3.0</a>
+ */
+
+public class ExternalTransactionUnitTest extends TestCase
+{
+  /** Logger.  */
+  private static Logger logger =
+    Logger.getLogger(ExternalTransactionUnitTest.class.getName());
+
+  private static final URI databaseURI;
+
+  private static final URI systemModelURI;
+
+  private static final URI modelURI;
+  private static final URI model2URI;
+  private static final URI model3URI;
+  private static final URI model4URI;
+  private static final URI model5URI;
+
+  static {
+    try {
+      databaseURI    = new URI("local://database");
+      systemModelURI = new URI("local://database#");
+      modelURI       = new URI("local://database#model");
+      model2URI      = new URI("local://database#model2");
+      model3URI      = new URI("local://database#model3");
+      model4URI      = new URI("local://database#model4");
+      model5URI      = new URI("local://database#model5");
+    } catch (URISyntaxException e) {
+      throw new Error("Bad hardcoded URI", e);
+    }
+  }
+
+  private static Database database = null;
+
+  public ExternalTransactionUnitTest(String name)
+  {
+    super(name);
+  }
+
+  public static Test suite()
+  {
+    TestSuite suite = new TestSuite();
+    suite.addTest(new ExternalTransactionUnitTest("testSimpleOnePhaseCommit"));
+    suite.addTest(new ExternalTransactionUnitTest("testSimpleTwoPhaseCommit"));
+    suite.addTest(new ExternalTransactionUnitTest("testBasicQuery"));
+
+    return suite;
+  }
+
+  /**
+   * Create test objects.
+   */
+  public void setUp() throws Exception {
+    if (database == null) {
+      // Create the persistence directory
+      File persistenceDirectory =
+        new File(new File(System.getProperty("cvs.root")), "testDatabase");
+      if (persistenceDirectory.isDirectory()) {
+        if (!FileUtil.deleteDirectory(persistenceDirectory)) {
+          throw new RuntimeException(
+            "Unable to remove old directory " + persistenceDirectory
+          );
+        }
+      }
+      if (!persistenceDirectory.mkdirs()) {
+        throw new Exception("Unable to create directory "+persistenceDirectory);
+      }
+
+      // Define the the node pool factory
+      String nodePoolFactoryClassName =
+        "org.mulgara.store.nodepool.xa.XANodePoolFactory";
+
+      // Define the string pool factory
+      String stringPoolFactoryClassName =
+        "org.mulgara.store.stringpool.xa.XAStringPoolFactory";
+
+      String tempNodePoolFactoryClassName =
+        "org.mulgara.store.nodepool.memory.MemoryNodePoolFactory";
+
+      // Define the string pool factory
+      String tempStringPoolFactoryClassName =
+        "org.mulgara.store.stringpool.memory.MemoryStringPoolFactory";
+
+      // Define the resolver factory used to manage system models
+      String systemResolverFactoryClassName =
+        "org.mulgara.resolver.store.StatementStoreResolverFactory";
+
+      // Define the resolver factory used to manage system models
+      String tempResolverFactoryClassName =
+        "org.mulgara.resolver.memory.MemoryResolverFactory";
+
+      String ruleLoaderFactoryClassName =
+        "org.mulgara.rules.RuleLoaderFactory";
+
+      // Create a database which keeps its system models on the Java heap
+      database = new Database(
+                   databaseURI,
+                   persistenceDirectory,
+                   null,                            // no security domain
+                   new JotmTransactionManagerFactory(),
+                   0,                               // default transaction timeout
+                   nodePoolFactoryClassName,        // persistent
+                   new File(persistenceDirectory, "xaNodePool"),
+                   stringPoolFactoryClassName,      // persistent
+                   new File(persistenceDirectory, "xaStringPool"),
+                   systemResolverFactoryClassName,  // persistent
+                   new File(persistenceDirectory, "xaStatementStore"),
+                   tempNodePoolFactoryClassName,    // temporary nodes
+                   null,                            // no dir for temp nodes
+                   tempStringPoolFactoryClassName,  // temporary strings
+                   null,                            // no dir for temp strings
+                   tempResolverFactoryClassName,    // temporary models
+                   null,                            // no dir for temp models
+                   "",                              // no rule loader
+                   "org.mulgara.content.rdfxml.RDFXMLContentHandler");
+
+      database.addResolverFactory("org.mulgara.resolver.url.URLResolverFactory", null);
+    }
+  }
+
+
+  /**
+  * The teardown method for JUnit
+  */
+  public void tearDown()
+  {
+  }
+
+  //
+  // Test cases
+  //
+
+  private static class TestXid implements Xid {
+    private int xid;
+    public TestXid(int xid) {
+      this.xid = xid;
+    }
+    
+    public int getFormatId() {
+      return 'X';
+    }
+
+    public byte[] getBranchQualifier() {
+      return new byte[] {
+        (byte)(xid >> 0x00),
+        (byte)(xid >> 0x08)
+      };
+    }
+
+    public byte[] getGlobalTransactionId() {
+      return new byte[] {
+        (byte)(xid >> 0x10),
+        (byte)(xid >> 0x18)
+      };
+    }
+  }
+
+  /**
+   * Test the {@link DatabaseSession#create} method.
+   * As a side-effect, creates the model required by the next tests.
+   */
+  public void testSimpleOnePhaseCommit() throws URISyntaxException
+  {
+    logger.info("testSimpleOnePhaseCommit");
+
+    try {
+      DatabaseSession session = (DatabaseSession)database.newSession();
+      XAResource resource = session.getXAResource();
+      Xid xid = new TestXid(1);
+      resource.start(xid, XAResource.TMNOFLAGS);
+      try {
+        session.createModel(modelURI, null);
+        resource.end(xid, XAResource.TMSUCCESS);
+        resource.commit(xid, true);
+      } finally {
+        session.close();
+      }
+    } catch (Exception e) {
+      fail(e);
+    }
+  }
+
+
+  /**
+   * Test two phase commit.
+   * As a side-effect, loads the model required by the next tests.
+   */
+  public void testSimpleTwoPhaseCommit() throws URISyntaxException
+  {
+    logger.info("testSimpleTwoPhaseCommit");
+    URI fileURI  = new File("data/xatest-model1.rdf").toURI();
+
+    try {
+      DatabaseSession session = (DatabaseSession)database.newSession();
+      XAResource resource = session.getXAResource();
+      Xid xid = new TestXid(1);
+      resource.start(xid, XAResource.TMNOFLAGS);
+      try {
+        session.setModel(modelURI, new ModelResource(fileURI));
+        resource.end(xid, XAResource.TMSUCCESS);
+        resource.prepare(xid);
+        resource.commit(xid, false);
+      } finally {
+        session.close();
+      }
+    } catch (Exception e) {
+      fail(e);
+    }
+  }
+
+
+  public void testBasicQuery() throws URISyntaxException {
+    logger.info("Testing basicQuery");
+
+    try {
+      // Load some test data
+      DatabaseSession session = (DatabaseSession)database.newSession();
+      try {
+        XAResource resource = session.getXAResource();
+        Xid xid = new TestXid(1);
+        resource.start(xid, XAResource.TMNOFLAGS);
+
+        Variable subjectVariable   = new Variable("subject");
+        Variable predicateVariable = new Variable("predicate");
+        Variable objectVariable    = new Variable("object");
+
+        List selectList = new ArrayList(3);
+        selectList.add(subjectVariable);
+        selectList.add(predicateVariable);
+        selectList.add(objectVariable);
+
+        // Evaluate the query
+        Answer answer = session.query(new Query(
+          selectList,                                       // SELECT
+          new ModelResource(modelURI),                      // FROM
+          new ConstraintImpl(subjectVariable,               // WHERE
+                         predicateVariable,
+                         objectVariable),
+          null,                                             // HAVING
+          Arrays.asList(new Order[] {                       // ORDER BY
+            new Order(subjectVariable, true),
+            new Order(predicateVariable, true),
+            new Order(objectVariable, true)
+          }),
+          null,                                             // LIMIT
+          0,                                                // OFFSET
+          new UnconstrainedAnswer()                         // GIVEN
+        ));
+        String[][] results = {
+          { "test:s01", "test:p01", "test:o01" },
+          { "test:s01", "test:p02", "test:o01" },
+          { "test:s01", "test:p02", "test:o02" },
+          { "test:s01", "test:p03", "test:o02" },
+          { "test:s02", "test:p03", "test:o02" },
+          { "test:s02", "test:p04", "test:o02" },
+          { "test:s02", "test:p04", "test:o03" },
+          { "test:s02", "test:p05", "test:o03" },
+          { "test:s03", "test:p01", "test:o01" },
+          { "test:s03", "test:p05", "test:o03" },
+          { "test:s03", "test:p06", "test:o01" },
+          { "test:s03", "test:p06", "test:o03" },
+        };
+        compareResults(results, answer);
+        answer.close();
+
+        resource.end(xid, XAResource.TMSUCCESS);
+        resource.commit(xid, true);
+      } finally {
+        session.close();
+      }
+    } catch (Exception e) {
+      fail(e);
+    }
+  }
+
+  //
+  // Internal methods
+  //
+
+  private void compareResults(String[][] expected, Answer answer) throws Exception {
+    try {
+      answer.beforeFirst();
+      for (int i = 0; i < expected.length; i++) {
+        assertTrue("Answer short at row " + i, answer.next());
+        assertEquals(expected[i].length, answer.getNumberOfVariables());
+        for (int j = 0; j < expected[i].length; j++) {
+          URIReferenceImpl uri = new URIReferenceImpl(new URI(expected[i][j]));
+          assertEquals(uri, answer.getObject(j));
+        }
+      }
+      assertFalse(answer.next());
+    } catch (Exception e) {
+      logger.error("Failed test - " + answer);
+      answer.close();
+      throw e;
+    }
+  }
+
+  private void compareResults(Answer answer1, Answer answer2) throws Exception {
+    answer1.beforeFirst();
+    answer2.beforeFirst();
+    assertEquals(answer1.getNumberOfVariables(), answer2.getNumberOfVariables());
+    while (answer1.next()) {
+      assertTrue(answer2.next());
+      for (int i = 0; i < answer1.getNumberOfVariables(); i++) {
+        assertEquals(answer1.getObject(i), answer2.getObject(i));
+      }
+    }
+    assertFalse(answer2.next());
+  }
+
+
+  /**
+   * Fail with an unexpected exception
+   */
+  private void fail(Throwable throwable)
+  {
+    StringWriter stringWriter = new StringWriter();
+    throwable.printStackTrace(new PrintWriter(stringWriter));
+    fail(stringWriter.toString());
+  }
+}

Modified: branches/mgr-73/src/jar/resolver/java/org/mulgara/resolver/InternalResolver.java
===================================================================
--- branches/mgr-73/src/jar/resolver/java/org/mulgara/resolver/InternalResolver.java	2007-11-23 06:16:44 UTC (rev 579)
+++ branches/mgr-73/src/jar/resolver/java/org/mulgara/resolver/InternalResolver.java	2007-11-27 08:27:35 UTC (rev 580)
@@ -126,6 +126,14 @@
                    Resolver        systemResolver)
     throws ResolverFactoryException
   {
+    if (resolver == null) {
+      throw new IllegalArgumentException("Resolver 'null'");
+    } else if (resolverSession == null) {
+      throw new IllegalArgumentException("ResolverSession 'null'");
+    } else if (systemResolver == null) {
+      throw new IllegalArgumentException("SystemResolver 'null'");
+    }
+
     // Initialize fields
     this.resolver        = resolver;
     this.rdfType         = rdfType;

Modified: branches/mgr-73/src/jar/resolver/java/org/mulgara/resolver/MulgaraExternalTransaction.java
===================================================================
--- branches/mgr-73/src/jar/resolver/java/org/mulgara/resolver/MulgaraExternalTransaction.java	2007-11-23 06:16:44 UTC (rev 579)
+++ branches/mgr-73/src/jar/resolver/java/org/mulgara/resolver/MulgaraExternalTransaction.java	2007-11-27 08:27:35 UTC (rev 580)
@@ -67,9 +67,11 @@
   private int heurCode;
   private boolean rollback;
 
-  MulgaraExternalTransaction(MulgaraExternalTransactionFactory resource, DatabaseOperationContext context) {
+  MulgaraExternalTransaction(MulgaraExternalTransactionFactory factory, Xid xid, DatabaseOperationContext context)
+      throws QueryException {
     this.factory = factory;
     this.context = context;
+    this.xid = xid;
 
     this.enlisted = new HashSet<EnlistableResource>();
     this.prepared = new HashSet<EnlistableResource>();
@@ -79,6 +81,8 @@
     this.hRollback = false;
     this.heurCode = 0;
     this.rollback = false;
+
+    this.context.initiate(this);
   }
 
   // We ignore reference counting in external transactions
@@ -156,13 +160,17 @@
 
   public void enlist(EnlistableResource enlistable) throws MulgaraTransactionException {
     try {
-      if (!enlisted.contains(enlistable)) {
-        enlisted.add(enlistable);
-        XAResource res = enlistable.getXAResource();
-        // FIXME: We need to handle this uptodate operation properly.
-        // bringUptodate(res);
-        res.start(xid, XAResource.TMNOFLAGS);
+      XAResource res = enlistable.getXAResource();
+      for (EnlistableResource eres : enlisted) {
+        if (res.isSameRM(eres.getXAResource())) {
+          return;
+        }
       }
+      enlisted.add(enlistable);
+      // FIXME: We need to handle this uptodate operation properly - handle
+      // suspension or mid-prepare/commit.
+      // bringUptodate(res);
+      res.start(xid, XAResource.TMNOFLAGS);
     } catch (XAException ex) {
       throw new MulgaraTransactionException("Failed to enlist resource", ex);
     }

Modified: branches/mgr-73/src/jar/resolver/java/org/mulgara/resolver/MulgaraExternalTransactionFactory.java
===================================================================
--- branches/mgr-73/src/jar/resolver/java/org/mulgara/resolver/MulgaraExternalTransactionFactory.java	2007-11-23 06:16:44 UTC (rev 579)
+++ branches/mgr-73/src/jar/resolver/java/org/mulgara/resolver/MulgaraExternalTransactionFactory.java	2007-11-27 08:27:35 UTC (rev 580)
@@ -24,6 +24,7 @@
 import java.util.Map;
 import java.util.Set;
 import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
 
 // Third party packages
 import org.apache.log4j.Logger;
@@ -82,7 +83,7 @@
     }
   }
 
-  protected MulgaraExternalTransaction createTransaction(final DatabaseSession session, boolean write)
+  protected MulgaraExternalTransaction createTransaction(final DatabaseSession session, Xid xid, boolean write)
       throws MulgaraTransactionException {
     acquireMutex();
     try {
@@ -101,7 +102,7 @@
             }
           });
         try {
-          writeTransaction = new MulgaraExternalTransaction(this, session.newOperationContext(true));
+          writeTransaction = new MulgaraExternalTransaction(this, xid, session.newOperationContext(true));
           associatedTransaction.put(session, writeTransaction);
           sessionXAMap.put(session, writeTransaction);
 
@@ -112,7 +113,7 @@
         }
       } else {
         try {
-          MulgaraExternalTransaction xa = new MulgaraExternalTransaction(this, session.newOperationContext(false));
+          MulgaraExternalTransaction xa = new MulgaraExternalTransaction(this, xid, session.newOperationContext(false));
           associatedTransaction.put(session, xa);
 
           return xa;

Modified: branches/mgr-73/src/jar/resolver/java/org/mulgara/resolver/MulgaraXAResource.java
===================================================================
--- branches/mgr-73/src/jar/resolver/java/org/mulgara/resolver/MulgaraXAResource.java	2007-11-23 06:16:44 UTC (rev 579)
+++ branches/mgr-73/src/jar/resolver/java/org/mulgara/resolver/MulgaraXAResource.java	2007-11-27 08:27:35 UTC (rev 580)
@@ -168,8 +168,14 @@
   }
 
   public boolean isSameRM(XAResource xares) {
-    return false; // FIXME: We need another interface here, as well as some way
-                  // of identifying a local Mulgara instance.
+    if (!xares.getClass().equals(MulgaraXAResource.class)) {
+      return false;
+    } else {
+      // Based on X/Open-XA-TP section 3.2 I believe a 'Resource Manager
+      // Instance' corresponds to a session, as each session 'supports
+      // independent transaction completion'.
+      return ((MulgaraXAResource)xares).session == session;
+    }
   }
 
 
@@ -220,7 +226,7 @@
         } else {
           // FIXME: Need to consider read-only transactions here.
           try {
-            MulgaraExternalTransaction xa = factory.createTransaction(session, true);
+            MulgaraExternalTransaction xa = factory.createTransaction(session, xid, true);
             xa2xid.put(xa, xid);
           } catch (MulgaraTransactionException em) {
             logger.error("Failed to create transaction", em);

Modified: branches/mgr-73/src/jar/resolver-store/java/org/mulgara/resolver/store/StatementStoreXAResource.java
===================================================================
--- branches/mgr-73/src/jar/resolver-store/java/org/mulgara/resolver/store/StatementStoreXAResource.java	2007-11-23 06:16:44 UTC (rev 579)
+++ branches/mgr-73/src/jar/resolver-store/java/org/mulgara/resolver/store/StatementStoreXAResource.java	2007-11-27 08:27:35 UTC (rev 580)
@@ -118,7 +118,8 @@
   public void start(Xid xid, int flags) throws XAException
   {
     if (logger.isDebugEnabled()) {
-      logger.debug("Start " + System.identityHashCode(xid) + " flags=" + formatFlags(flags));
+      logger.debug("Start " + System.identityHashCode(xid) + " flags=" +
+      formatFlags(flags), new Throwable());
     } 
     switch (flags) {
       case XAResource.TMNOFLAGS:
@@ -231,7 +232,7 @@
   public void end(Xid xid, int flags) throws XAException
   {
     if (logger.isDebugEnabled()) {
-      logger.debug("End xid=" + System.identityHashCode(xid) + " flags=" + formatFlags(flags));
+      logger.debug("End " + System.identityHashCode(xid) + " flags=" + formatFlags(flags));
     }
   }
 




More information about the Mulgara-svn mailing list