[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 ©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