/*
* 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.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.Collections;
import java.util.List;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.RollbackException;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
// Third party packages
import junit.framework.*; // JUnit
import org.apache.log4j.Logger; // Log4J
import org.jrdf.graph.ObjectNode;
import org.jrdf.graph.PredicateNode;
import org.jrdf.graph.SubjectNode;
import org.objectweb.jotm.Jotm;
import org.objectweb.transaction.jta.TMService;
// Locally written packages
import org.mulgara.query.*;
import org.mulgara.query.rdf.URIReferenceImpl;
import org.mulgara.query.rdf.TripleImpl;
import org.mulgara.server.Session;
import org.mulgara.server.SessionFactory;
import org.mulgara.server.driver.SessionFactoryFinder;
/**
* Regression test to test JTA integration with external JOTM instance.
*
* @created 2008-01-11
* @author <a href="mailto:andrae@netymon.com">Andrae Muys</a>
* @company <A href="mailto:mail@netymon.com">Netymon Pty Ltd</A>
* @copyright ©2008 <a href="http://www.topazproject.org/">The Topaz Foundation</a>
* @licence Apache License v2.0
*/
public class JotmTransactionStandaloneIntTst extends TestCase {
/** Logger. */
private static Logger logger =
Logger.getLogger(JotmTransactionStandaloneIntTst.class.getName());
private static final URI databaseURI;
private static final URI modelURI;
private static final URI model2URI;
static {
try {
databaseURI = new URI("rmi://localhost/server1");
modelURI = new URI("rmi://localhost/server1#jotmmodel");
model2URI = new URI("rmi://localhost/server1#jotmmodel2");
} catch (URISyntaxException e) {
throw new Error("Bad hardcoded URI", e);
}
}
private static SessionFactory sessionFactory;
private static TMService txService;
private static TransactionManager txManager;
public JotmTransactionStandaloneIntTst(String name) {
super(name);
}
public static Test suite() {
TestSuite suite = new TestSuite();
suite.addTest(new JotmTransactionStandaloneIntTst("setup"));
suite.addTest(new JotmTransactionStandaloneIntTst("testTrivalExplicit"));
suite.addTest(new JotmTransactionStandaloneIntTst("testSessionCloseRollback"));
suite.addTest(new JotmTransactionStandaloneIntTst("testTrivialExplicitAgain"));
suite.addTest(new JotmTransactionStandaloneIntTst("testBasicQuery"));
suite.addTest(new JotmTransactionStandaloneIntTst("testMultipleEnlist"));
suite.addTest(new JotmTransactionStandaloneIntTst("testMultipleQuery"));
suite.addTest(new JotmTransactionStandaloneIntTst("testBasicReadOnlyQuery"));
suite.addTest(new JotmTransactionStandaloneIntTst("testConcurrentQuery"));
suite.addTest(new JotmTransactionStandaloneIntTst("testRepeatGetXAQuery"));
suite.addTest(new JotmTransactionStandaloneIntTst("testConcurrentReadWrite"));
suite.addTest(new JotmTransactionStandaloneIntTst("testSubqueryQuery"));
suite.addTest(new JotmTransactionStandaloneIntTst("testTrivalImplicit"));
suite.addTest(new JotmTransactionStandaloneIntTst("cleanup"));
return suite;
}
public void setup() throws Exception {
logger.info("Doing setup");
sessionFactory = SessionFactoryFinder.newSessionFactory(databaseURI);
txService = new Jotm(true, false); // local, unbound.
txManager = txService.getTransactionManager();
}
public void cleanup() throws Exception {
logger.info("Doing cleanup");
txService.stop();
}
//
// Test cases
//
@SuppressWarnings("unused")
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 explicit transaction.
* As a side-effect, creates the model required by the next tests.
*/
public void testTrivalExplicit() throws URISyntaxException {
logger.info("testTrivalExplicit");
try {
txManager.begin();
Session session = sessionFactory.newSession();
txManager.getTransaction().enlistResource(session.getXAResource());
try {
session.createModel(modelURI, null);
txManager.commit();
} finally {
session.close();
}
} catch (Exception e) {
fail(e);
}
}
public void testSessionCloseRollback() throws URISyntaxException {
logger.info("testSessionCloseRollback");
try {
URI fileURI = new File("data/xatest-model1.rdf").toURI();
txManager.begin();
Session session = sessionFactory.newSession();
txManager.getTransaction().enlistResource(session.getXAResource());
try {
try {
session.setModel(modelURI, fileURI);
} finally {
session.close();
}
} finally {
try {
txManager.commit();
} catch (HeuristicRollbackException eh) { // This is my expectation.
logger.warn("HeuristicRollback detected successfully", eh);
} catch (RollbackException er) { // This would also meet the spec.
logger.warn("Rollback detected successfully", er);
} catch (Exception e) {
logger.warn("Exception from Jotm", e);
throw e;
}
}
} catch (Exception e) {
fail(e);
}
}
public void testTrivialExplicitAgain() throws URISyntaxException {
logger.info("testTrivialExplicitAgain");
try {
URI fileURI = new File("data/xatest-model1.rdf").toURI();
txManager.begin();
Session session = sessionFactory.newSession();
txManager.getTransaction().enlistResource(session.getXAResource());
try {
session.setModel(modelURI, fileURI);
txManager.commit();
} finally {
session.close();
}
} catch (Exception e) {
fail(e);
}
}
public void testBasicQuery() throws URISyntaxException {
logger.info("testBasicQuery");
try {
txManager.begin();
Session session = sessionFactory.newSession();
try {
txManager.getTransaction().enlistResource(session.getXAResource());
// Evaluate the query
Answer answer = session.query(createQuery(modelURI));
compareResults(expectedResults(), answer);
answer.close();
txManager.commit();
} finally {
session.close();
}
} catch (Exception e) {
fail(e);
}
}
public void testMultipleEnlist() throws URISyntaxException {
logger.info("testMultipleEnlist");
try {
txManager.begin();
Session session = sessionFactory.newSession();
try {
txManager.getTransaction().enlistResource(session.getXAResource());
txManager.getTransaction().enlistResource(session.getXAResource());
txManager.getTransaction().enlistResource(session.getXAResource());
// Evaluate the query
Answer answer = session.query(createQuery(modelURI));
compareResults(expectedResults(), answer);
answer.close();
txManager.commit();
} finally {
session.close();
}
} catch (Exception e) {
fail(e);
}
}
public void testMultipleQuery() throws URISyntaxException {
logger.info("testMultipleQuery");
try {
txManager.begin();
Session session = sessionFactory.newSession();
txManager.getTransaction().enlistResource(session.getXAResource());
try {
// Evaluate the query
Answer answer1 = session.query(createQuery(modelURI));
Answer answer2 = session.query(createQuery(modelURI));
compareResults(answer1, answer2);
answer1.close();
answer2.close();
txManager.commit();
} finally {
session.close();
}
} catch (Exception e) {
fail(e);
}
}
public void testBasicReadOnlyQuery() throws URISyntaxException {
logger.info("testBasicReadOnlyQuery");
try {
txManager.begin();
Session session = sessionFactory.newSession();
try {
txManager.getTransaction().enlistResource(session.getReadOnlyXAResource());
// Evaluate the query
Answer answer = session.query(createQuery(modelURI));
compareResults(expectedResults(), answer);
answer.close();
txManager.commit();
} finally {
session.close();
}
} catch (Exception e) {
fail(e);
}
}
public void testConcurrentQuery() throws URISyntaxException {
logger.info("testConcurrentQuery");
try {
txManager.begin();
Session session = sessionFactory.newSession();
XAResource roResource = session.getReadOnlyXAResource();
Transaction tx1 = txManager.getTransaction();
tx1.enlistResource(roResource);
try {
// Evaluate the query
Answer answer1 = session.query(createQuery(modelURI));
tx1 = txManager.suspend();
txManager.begin();
Transaction tx2 = txManager.getTransaction();
tx2.enlistResource(roResource);
Answer answer2 = session.query(createQuery(modelURI));
tx2 = txManager.suspend();
compareResults(answer1, answer2);
answer1.close();
answer2.close();
txManager.resume(tx1);
txManager.commit();
// I believe JTA requires me to call end here - our implementation doesn't care.
tx2.commit();
} finally {
session.close();
}
} catch (Exception e) {
fail(e);
}
}
public void testRepeatGetXAQuery() throws URISyntaxException {
logger.info("testRepeatGetXAQuery");
try {
txManager.begin();
Session session = sessionFactory.newSession();
Transaction tx1 = txManager.getTransaction();
tx1.enlistResource(session.getReadOnlyXAResource());
try {
// Evaluate the query
Answer answer1 = session.query(createQuery(modelURI));
tx1 = txManager.suspend();
txManager.begin();
Transaction tx2 = txManager.getTransaction();
tx2.enlistResource(session.getReadOnlyXAResource());
Answer answer2 = session.query(createQuery(modelURI));
tx2 = txManager.suspend();
compareResults(answer1, answer2);
answer1.close();
answer2.close();
txManager.resume(tx1);
txManager.commit();
// I believe JTA requires me to call end here - our implementation doesn't care.
tx2.commit();
} finally {
session.close();
}
} catch (Exception e) {
fail(e);
}
}
//
// Note: What this test does is a really bad idea - there is no
// isolation provided as each operation is within its own
// transaction. It does however provide a good test.
//
public void testConcurrentReadWrite() throws URISyntaxException {
logger.info("testConcurrentReadWrite");
try {
txManager.begin();
Session session = sessionFactory.newSession();
XAResource roResource = session.getReadOnlyXAResource();
XAResource rwResource = session.getXAResource();
txManager.getTransaction().enlistResource(rwResource);
session.createModel(model2URI, null);
Transaction tx1 = txManager.suspend();
try {
txManager.begin();
txManager.getTransaction().enlistResource(roResource);
// Evaluate the query
Answer answer = session.query(createQuery(modelURI));
Transaction tx2 = txManager.suspend();
answer.beforeFirst();
while (answer.next()) {
txManager.resume(tx1);
session.insert(model2URI, Collections.singleton(new TripleImpl(
(SubjectNode)answer.getObject(0),
(PredicateNode)answer.getObject(1),
(ObjectNode)answer.getObject(2))));
tx1 = txManager.suspend();
}
answer.close();
txManager.resume(tx1);
txManager.commit();
txManager.begin();
txManager.getTransaction().enlistResource(roResource);
Answer answer2 = session.query(createQuery(model2URI));
Transaction tx3 = txManager.suspend();
compareResults(expectedResults(), answer2);
answer2.close();
txManager.begin();
txManager.getTransaction().enlistResource(rwResource);
session.removeModel(model2URI);
txManager.commit();
txManager.resume(tx2);
txManager.commit();
txManager.resume(tx3);
txManager.commit();
} finally {
session.close();
}
} catch (Exception e) {
fail(e);
}
}
public void testSubqueryQuery() throws URISyntaxException {
logger.info("testSubqueryQuery");
try {
txManager.begin();
Session session = sessionFactory.newSession();
txManager.getTransaction().enlistResource(session.getReadOnlyXAResource());
try {
Variable subjectVariable = new Variable("subject");
Variable predicateVariable = new Variable("predicate");
Variable objectVariable = new Variable("object");
List<SelectElement> selectList = new ArrayList<SelectElement>(3);
selectList.add(subjectVariable);
selectList.add(new Subquery(new Variable("k0"), new Query(
Collections.singletonList(objectVariable),
new GraphResource(modelURI), // FROM
new ConstraintImpl(subjectVariable, // WHERE
predicateVariable,
objectVariable),
null, // HAVING
Collections.singletonList( // ORDER BY
new Order(objectVariable, true)
),
null, // LIMIT
0, // OFFSET
true, // DISTINCT
new UnconstrainedAnswer() // GIVEN
)));
// Evaluate the query
Answer answer = session.query(new Query(
selectList, // SELECT
new GraphResource(modelURI), // FROM
new ConstraintImpl(subjectVariable, // WHERE
new URIReferenceImpl(new URI("test:p03")),
objectVariable),
null, // HAVING
Collections.singletonList( // ORDER BY
new Order(subjectVariable, true)
),
null, // LIMIT
0, // OFFSET
true, // DISTINCT
new UnconstrainedAnswer() // GIVEN
));
txManager.suspend();
answer.beforeFirst();
assertTrue(answer.next());
assertEquals(new URIReferenceImpl(new URI("test:s01")),
answer.getObject(0));
Answer sub1 = (Answer)answer.getObject(1);
compareResults(new String[][] { new String[] { "test:o01" },
new String[] { "test:o02" } }, sub1);
sub1.close();
assertTrue(answer.next());
assertEquals(new URIReferenceImpl(new URI("test:s02")),
answer.getObject(0));
Answer sub2 = (Answer)answer.getObject(1);
compareResults(new String[][] { new String[] { "test:o02" },
new String[] { "test:o03" } }, sub2);
// Leave sub2 open.
assertFalse(answer.next());
answer.close();
sub2.close();
// Leave transaction to be closed on session close.
} finally {
session.close();
}
} catch (Exception e) {
fail(e);
}
}
/*
public void testConcurrentSubqueryQuery() throws URISyntaxException {
logger.info("testConcurrentSubqueryQuery");
try {
Session session = database.newSession();
XAResource rwResource = session.getXAResource();
Xid xid1 = new TestXid(1);
rwResource.start(xid1, XAResource.TMNOFLAGS);
try {
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(new Subquery(new Variable("k0"), new Query(
Collections.singletonList(objectVariable),
new GraphResource(modelURI), // FROM
new ConstraintImpl(subjectVariable, // WHERE
predicateVariable,
objectVariable),
null, // HAVING
Collections.singletonList( // ORDER BY
new Order(objectVariable, true)
),
null, // LIMIT
0, // OFFSET
new UnconstrainedAnswer() // GIVEN
)));
// Evaluate the query
Answer answer = session.query(new Query(
selectList, // SELECT
new GraphResource(modelURI), // FROM
new ConstraintImpl(subjectVariable, // WHERE
new URIReferenceImpl(new URI("test:p03")),
objectVariable),
null, // HAVING
Collections.singletonList( // ORDER BY
new Order(subjectVariable, true)
),
null, // LIMIT
0, // OFFSET
new UnconstrainedAnswer() // GIVEN
));
answer.beforeFirst();
assertTrue(answer.next());
assertEquals(new URIReferenceImpl(new URI("test:s01")),
answer.getObject(0));
Answer sub1 = (Answer)answer.getObject(1);
assertTrue(answer.next());
assertEquals(new URIReferenceImpl(new URI("test:s02")),
answer.getObject(0));
Answer sub2 = (Answer)answer.getObject(1);
assertFalse(answer.next());
assertEquals(1, sub1.getNumberOfVariables());
assertEquals(1, sub2.getNumberOfVariables());
sub1.beforeFirst();
sub2.beforeFirst();
assertTrue(sub1.next());
assertTrue(sub2.next());
assertEquals(new URIReferenceImpl(new URI("test:o01")), sub1.getObject(0));
assertEquals(new URIReferenceImpl(new URI("test:o02")), sub2.getObject(0));
rwResource.end(xid1, XAResource.TMSUSPEND);
assertTrue(sub1.next());
assertTrue(sub2.next());
assertEquals(new URIReferenceImpl(new URI("test:o02")), sub1.getObject(0));
assertEquals(new URIReferenceImpl(new URI("test:o03")), sub2.getObject(0));
assertFalse(sub1.next());
assertFalse(sub2.next());
answer.close();
rwResource.end(xid1, XAResource.TMSUCCESS);
rwResource.commit(xid1, true);
} finally {
session.close();
}
} catch (Exception e) {
fail(e);
}
}
public void testExplicitIsolationQuerySingleSession() throws URISyntaxException {
logger.info("testExplicitIsolationQuery");
URI fileURI = new File("data/xatest-model1.rdf").toURI();
try {
Session session = database.newSession();
try {
XAResource roResource = session.getReadOnlyXAResource();
XAResource rwResource = session.getXAResource();
Xid xid1 = new TestXid(1); // Initial create model.
Xid xid2 = new TestXid(2); // Started before setModel.
Xid xid3 = new TestXid(3); // setModel.
Xid xid4 = new TestXid(4); // Started before setModel prepares
Xid xid5 = new TestXid(5); // Started before setModel commits
Xid xid6 = new TestXid(6); // Started after setModel commits
Xid xid7 = new TestXid(7); // Final remove model.
rwResource.start(xid1, XAResource.TMNOFLAGS);
session.createModel(model3URI, null);
rwResource.end(xid1, XAResource.TMSUCCESS);
rwResource.commit(xid1, true);
// Nothing visible.
roResource.start(xid2, XAResource.TMNOFLAGS);
assertChangeNotVisible(session);
roResource.end(xid2, XAResource.TMSUSPEND);
// Perform update
rwResource.start(xid3, XAResource.TMNOFLAGS);
session.setModel(model3URI, new GraphResource(fileURI));
rwResource.end(xid3, XAResource.TMSUSPEND);
// Check uncommitted change not visible
roResource.start(xid4, XAResource.TMNOFLAGS);
assertChangeNotVisible(session);
roResource.end(xid4, XAResource.TMSUSPEND);
// Check original phase unaffected.
roResource.start(xid2, XAResource.TMRESUME);
assertChangeNotVisible(session);
roResource.end(xid2, XAResource.TMSUSPEND);
// Check micro-commit visible to current-phase
rwResource.start(xid3, XAResource.TMRESUME);
assertChangeVisible(session);
// Perform prepare
rwResource.end(xid3, XAResource.TMSUCCESS);
rwResource.prepare(xid3);
// Check original phase unaffected
roResource.start(xid2, XAResource.TMRESUME);
assertChangeNotVisible(session);
roResource.end(xid2, XAResource.TMSUSPEND);
// Check pre-prepare phase unaffected
roResource.start(xid4, XAResource.TMRESUME);
assertChangeNotVisible(session);
roResource.end(xid4, XAResource.TMSUSPEND);
// Check committed phase unaffected.
roResource.start(xid5, XAResource.TMNOFLAGS);
assertChangeNotVisible(session);
roResource.end(xid5, XAResource.TMSUSPEND);
// Do commit
rwResource.commit(xid3, false);
// Check original phase
roResource.start(xid2, XAResource.TMRESUME);
assertChangeNotVisible(session);
roResource.end(xid2, XAResource.TMSUSPEND);
// Check pre-prepare
roResource.start(xid4, XAResource.TMRESUME);
assertChangeNotVisible(session);
roResource.end(xid4, XAResource.TMSUSPEND);
// Check pre-commit
roResource.start(xid5, XAResource.TMRESUME);
assertChangeNotVisible(session);
roResource.end(xid5, XAResource.TMSUSPEND);
// Check committed phase is now updated
roResource.start(xid6, XAResource.TMNOFLAGS);
assertChangeVisible(session);
// Cleanup transactions.
roResource.end(xid6, XAResource.TMSUCCESS);
roResource.end(xid2, XAResource.TMSUCCESS);
roResource.end(xid4, XAResource.TMSUCCESS);
roResource.end(xid5, XAResource.TMSUCCESS);
roResource.commit(xid2, true);
roResource.commit(xid4, true);
roResource.commit(xid5, true);
roResource.commit(xid6, true);
// Cleanup database
rwResource.start(xid7, XAResource.TMNOFLAGS);
session.removeModel(model3URI);
rwResource.end(xid7, XAResource.TMSUCCESS);
rwResource.commit(xid7, true);
} finally {
session.close();
}
} catch (Exception e) {
fail(e);
}
}
public void testExternalInternalIsolation() throws URISyntaxException {
logger.info("testExplicitIsolationQuery");
URI fileURI = new File("data/xatest-model1.rdf").toURI();
try {
Session session1 = database.newSession();
try {
Session session2 = database.newSession();
try {
XAResource roResource = session1.getReadOnlyXAResource();
XAResource rwResource = session1.getXAResource();
Xid xid1 = new TestXid(1); // Initial create model.
Xid xid2 = new TestXid(2); // Main Test.
Xid xid3 = new TestXid(3); // Cleanup test.
rwResource.start(xid1, XAResource.TMNOFLAGS);
session1.createModel(model3URI, null);
rwResource.end(xid1, XAResource.TMSUCCESS);
rwResource.commit(xid1, true);
// Nothing visible.
assertChangeNotVisible(session2);
// Perform update
rwResource.start(xid2, XAResource.TMNOFLAGS);
session1.setModel(model3URI, new GraphResource(fileURI));
rwResource.end(xid2, XAResource.TMSUSPEND);
// Check uncommitted change not visible
assertChangeNotVisible(session2);
// Check micro-commit visible to current-phase
rwResource.start(xid2, XAResource.TMRESUME);
assertChangeVisible(session1);
// Perform prepare
rwResource.end(xid2, XAResource.TMSUCCESS);
rwResource.prepare(xid2);
// Check original phase unaffected
assertChangeNotVisible(session2);
// Do commit
rwResource.commit(xid2, false);
// Check committed phase is now updated
assertChangeVisible(session2);
// Cleanup database
session2.removeModel(model3URI);
} finally {
session2.close();
}
} finally {
session1.close();
}
} catch (Exception e) {
fail(e);
}
}
public void testInternalExternalIsolation() throws URISyntaxException {
logger.info("testExplicitIsolationQuery");
URI fileURI = new File("data/xatest-model1.rdf").toURI();
try {
Session session1 = database.newSession();
try {
Session session2 = database.newSession();
try {
XAResource roResource = session2.getReadOnlyXAResource();
XAResource rwResource = session2.getXAResource();
Xid xid1 = new TestXid(1); // Pre-update
Xid xid2 = new TestXid(2); // Post-update/Pre-commit
Xid xid3 = new TestXid(3); // Post-commit
session1.createModel(model3URI, null);
// Nothing visible.
roResource.start(xid1, XAResource.TMNOFLAGS);
assertChangeNotVisible(session2);
roResource.end(xid1, XAResource.TMSUSPEND);
// Perform update with autocommit off
session1.setAutoCommit(false);
session1.setModel(model3URI, new GraphResource(fileURI));
// Check uncommitted change not visible
roResource.start(xid2, XAResource.TMNOFLAGS);
assertChangeNotVisible(session2);
roResource.end(xid2, XAResource.TMSUSPEND);
// Check original phase unaffected.
roResource.start(xid1, XAResource.TMRESUME);
assertChangeNotVisible(session2);
roResource.end(xid1, XAResource.TMSUSPEND);
// Check micro-commit visible to current-phase
assertChangeVisible(session1);
session1.setAutoCommit(true);
// Check original phase unaffected
roResource.start(xid1, XAResource.TMRESUME);
assertChangeNotVisible(session2);
roResource.end(xid1, XAResource.TMSUSPEND);
// Check pre-commit phase unaffected
roResource.start(xid2, XAResource.TMRESUME);
assertChangeNotVisible(session2);
roResource.end(xid2, XAResource.TMSUSPEND);
// Check committed phase is now updated and write-lock available
rwResource.start(xid3, XAResource.TMNOFLAGS);
assertChangeVisible(session2);
// Check internal transaction read-only
assertChangeVisible(session1);
// Cleanup transactions.
rwResource.end(xid3, XAResource.TMSUCCESS);
roResource.end(xid2, XAResource.TMSUCCESS);
roResource.end(xid1, XAResource.TMSUCCESS);
roResource.commit(xid1, true);
roResource.commit(xid2, true);
rwResource.commit(xid3, true);
// Cleanup database (check write-lock available again)
session1.removeModel(model3URI);
} finally {
session2.close();
}
} finally {
session1.close();
}
} catch (Exception e) {
fail(e);
}
}
private void assertChangeVisible(Session session) throws Exception {
// Evaluate the query
Answer answer = session.query(createQuery(model3URI));
compareResults(expectedResults(), answer);
answer.close();
}
private void assertChangeNotVisible(Session session) throws Exception {
// Evaluate the query
Answer answer = session.query(createQuery(model3URI));
answer.beforeFirst();
assertFalse(answer.next());
answer.close();
}
//
// Test two simultaneous, explicit transactions, in two threads. The second one should block
// until the first one sets auto-commit back to true.
//
public void testConcurrentExplicitTxn() throws URISyntaxException {
logger.info("testConcurrentExplicitTxn");
URI fileURI = new File("data/xatest-model1.rdf").toURI();
try {
Session session1 = database.newSession();
try {
XAResource resource1 = session1.getXAResource();
resource1.start(new TestXid(1), XAResource.TMNOFLAGS);
session1.createModel(model3URI, null);
resource1.end(new TestXid(1), XAResource.TMSUCCESS);
resource1.commit(new TestXid(1), true);
resource1.start(new TestXid(2), XAResource.TMNOFLAGS);
session1.setModel(model3URI, new GraphResource(fileURI));
final boolean[] tx2Started = new boolean[] { false };
Thread t2 = new Thread("tx2Test") {
public void run() {
try {
Session session2 = database.newSession();
XAResource resource2 = session2.getXAResource();
try {
resource2.start(new TestXid(3), XAResource.TMNOFLAGS);
synchronized (tx2Started) {
tx2Started[0] = true;
tx2Started.notify();
}
// Evaluate the query
Answer answer = session2.query(createQuery(model3URI));
compareResults(expectedResults(), answer);
answer.close();
resource2.end(new TestXid(3), XAResource.TMSUCCESS);
resource2.commit(new TestXid(3), true);
} finally {
session2.close();
}
} catch (Exception e) {
fail(e);
}
}
};
t2.start();
synchronized (tx2Started) {
if (!tx2Started[0]) {
try {
tx2Started.wait(2000L);
} catch (InterruptedException ie) {
logger.error("wait for tx2-started interrupted", ie);
fail(ie);
}
}
assertFalse("second transaction should still be waiting for write lock", tx2Started[0]);
}
resource1.commit(new TestXid(2), true);
synchronized (tx2Started) {
if (!tx2Started[0]) {
try {
tx2Started.wait(2000L);
} catch (InterruptedException ie) {
logger.error("wait for tx2-started interrupted", ie);
fail(ie);
}
assertTrue("second transaction should've started", tx2Started[0]);
}
}
try {
t2.join(2000L);
} catch (InterruptedException ie) {
logger.error("wait for tx2-terminated interrupted", ie);
fail(ie);
}
assertFalse("second transaction should've terminated", t2.isAlive());
resource1.start(new TestXid(4), XAResource.TMNOFLAGS);
session1.removeModel(model3URI);
resource1.end(new TestXid(4), XAResource.TMSUCCESS);
resource1.commit(new TestXid(4), true);
} finally {
session1.close();
}
} catch (Exception e) {
fail(e);
}
}
//*
// Test two simultaneous transactions, in two threads. The second one should block
// until the first one sets auto-commit back to true.
///
public void testExternalInternalConcurrentTxn() throws URISyntaxException {
logger.info("testExternalInternalConcurrentTxn");
URI fileURI = new File("data/xatest-model1.rdf").toURI();
try {
Session session1 = database.newSession();
try {
XAResource resource1 = session1.getXAResource();
resource1.start(new TestXid(1), XAResource.TMNOFLAGS);
session1.createModel(model3URI, null);
resource1.end(new TestXid(1), XAResource.TMSUCCESS);
resource1.commit(new TestXid(1), true);
resource1.start(new TestXid(2), XAResource.TMNOFLAGS);
session1.setModel(model3URI, new GraphResource(fileURI));
final boolean[] tx2Started = new boolean[] { false };
Thread t2 = new Thread("tx2Test") {
public void run() {
try {
Session session2 = database.newSession();
try {
session2.setAutoCommit(false);
synchronized (tx2Started) {
tx2Started[0] = true;
tx2Started.notify();
}
// Evaluate the query
Answer answer = session2.query(createQuery(model3URI));
compareResults(expectedResults(), answer);
answer.close();
session2.setAutoCommit(true);
} finally {
session2.close();
}
} catch (Exception e) {
fail(e);
}
}
};
t2.start();
synchronized (tx2Started) {
if (!tx2Started[0]) {
try {
tx2Started.wait(2000L);
} catch (InterruptedException ie) {
logger.error("wait for tx2-started interrupted", ie);
fail(ie);
}
}
assertFalse("second transaction should still be waiting for write lock", tx2Started[0]);
}
resource1.commit(new TestXid(2), true);
synchronized (tx2Started) {
if (!tx2Started[0]) {
try {
tx2Started.wait(2000L);
} catch (InterruptedException ie) {
logger.error("wait for tx2-started interrupted", ie);
fail(ie);
}
assertTrue("second transaction should've started", tx2Started[0]);
}
}
try {
t2.join(2000L);
} catch (InterruptedException ie) {
logger.error("wait for tx2-terminated interrupted", ie);
fail(ie);
}
assertFalse("second transaction should've terminated", t2.isAlive());
resource1.start(new TestXid(4), XAResource.TMNOFLAGS);
session1.removeModel(model3URI);
resource1.end(new TestXid(4), XAResource.TMSUCCESS);
resource1.commit(new TestXid(4), true);
} finally {
session1.close();
}
} catch (Exception e) {
fail(e);
}
}
//*
// Test two simultaneous transactions, in two threads. The second one should block
// until the first one sets auto-commit back to true.
///
public void testInternalExternalConcurrentTxn() throws URISyntaxException {
logger.info("testInternalExternalConcurrentTxn");
URI fileURI = new File("data/xatest-model1.rdf").toURI();
try {
Session session1 = database.newSession();
try {
session1.createModel(model3URI, null);
session1.setAutoCommit(false);
session1.setModel(model3URI, new GraphResource(fileURI));
final boolean[] tx2Started = new boolean[] { false };
Thread t2 = new Thread("tx2Test") {
public void run() {
try {
Session session2 = database.newSession();
try {
XAResource resource = session2.getXAResource();
resource.start(new TestXid(1), XAResource.TMNOFLAGS);
synchronized (tx2Started) {
tx2Started[0] = true;
tx2Started.notify();
}
// Evaluate the query
Answer answer = session2.query(createQuery(model3URI));
compareResults(expectedResults(), answer);
answer.close();
resource.end(new TestXid(1), XAResource.TMSUCCESS);
resource.rollback(new TestXid(1));
} finally {
session2.close();
}
} catch (Exception e) {
fail(e);
}
}
};
t2.start();
synchronized (tx2Started) {
if (!tx2Started[0]) {
try {
tx2Started.wait(2000L);
} catch (InterruptedException ie) {
logger.error("wait for tx2-started interrupted", ie);
fail(ie);
}
}
assertFalse("second transaction should still be waiting for write lock", tx2Started[0]);
}
session1.commit();
synchronized (tx2Started) {
if (!tx2Started[0]) {
try {
tx2Started.wait(2000L);
} catch (InterruptedException ie) {
logger.error("wait for tx2-started interrupted", ie);
fail(ie);
}
}
assertFalse("second transaction should still be waiting for write lock", tx2Started[0]);
}
session1.setAutoCommit(true);
synchronized (tx2Started) {
if (!tx2Started[0]) {
try {
tx2Started.wait(2000L);
} catch (InterruptedException ie) {
logger.error("wait for tx2-started interrupted", ie);
fail(ie);
}
assertTrue("second transaction should've started", tx2Started[0]);
}
}
try {
t2.join(2000L);
} catch (InterruptedException ie) {
logger.error("wait for tx2-terminated interrupted", ie);
fail(ie);
}
assertFalse("second transaction should've terminated", t2.isAlive());
session1.removeModel(model3URI);
} finally {
session1.close();
}
} catch (Exception e) {
fail(e);
}
}
//*
// Test two simultaneous transactions, in two threads. The second one should block
// until the first one sets auto-commit back to true.
///
public void testExternalInternalConcurrentTxnRollback() throws URISyntaxException {
logger.info("testExternalInternalConcurrentTxnRollback");
URI fileURI = new File("data/xatest-model1.rdf").toURI();
try {
Session session1 = database.newSession();
try {
XAResource resource1 = session1.getXAResource();
resource1.start(new TestXid(1), XAResource.TMNOFLAGS);
session1.createModel(model3URI, null);
resource1.end(new TestXid(1), XAResource.TMSUCCESS);
resource1.commit(new TestXid(1), true);
resource1.start(new TestXid(2), XAResource.TMNOFLAGS);
session1.setModel(model3URI, new GraphResource(fileURI));
final boolean[] tx2Started = new boolean[] { false };
Thread t2 = new Thread("tx2Test") {
public void run() {
try {
Session session2 = database.newSession();
try {
session2.setAutoCommit(false);
synchronized (tx2Started) {
tx2Started[0] = true;
tx2Started.notify();
}
// Evaluate the query
Answer answer = session2.query(createQuery(model3URI));
answer.beforeFirst();
assertFalse(answer.next());
answer.close();
session2.setAutoCommit(true);
} finally {
session2.close();
}
} catch (Exception e) {
fail(e);
}
}
};
t2.start();
synchronized (tx2Started) {
if (!tx2Started[0]) {
try {
tx2Started.wait(2000L);
} catch (InterruptedException ie) {
logger.error("wait for tx2-started interrupted", ie);
fail(ie);
}
}
assertFalse("second transaction should still be waiting for write lock", tx2Started[0]);
}
resource1.rollback(new TestXid(2));
synchronized (tx2Started) {
if (!tx2Started[0]) {
try {
tx2Started.wait(2000L);
} catch (InterruptedException ie) {
logger.error("wait for tx2-started interrupted", ie);
fail(ie);
}
assertTrue("second transaction should've started", tx2Started[0]);
}
}
try {
t2.join(2000L);
} catch (InterruptedException ie) {
logger.error("wait for tx2-terminated interrupted", ie);
fail(ie);
}
assertFalse("second transaction should've terminated", t2.isAlive());
resource1.start(new TestXid(4), XAResource.TMNOFLAGS);
session1.removeModel(model3URI);
resource1.end(new TestXid(4), XAResource.TMSUCCESS);
resource1.commit(new TestXid(4), true);
} finally {
session1.close();
}
} catch (Exception e) {
fail(e);
}
}
//*
// Test two simultaneous transactions, in two threads. The second one should block
// until the first one sets auto-commit back to true.
///
public void testInternalExternalConcurrentTxnRollback() throws URISyntaxException {
logger.info("testInternalExternalConcurrentTxnRollback");
URI fileURI = new File("data/xatest-model1.rdf").toURI();
try {
Session session1 = database.newSession();
try {
session1.createModel(model3URI, null);
session1.setAutoCommit(false);
session1.setModel(model3URI, new GraphResource(fileURI));
final boolean[] tx2Started = new boolean[] { false };
Thread t2 = new Thread("tx2Test") {
public void run() {
try {
Session session2 = database.newSession();
try {
XAResource resource = session2.getXAResource();
resource.start(new TestXid(1), XAResource.TMNOFLAGS);
synchronized (tx2Started) {
tx2Started[0] = true;
tx2Started.notify();
}
// Evaluate the query
Answer answer = session2.query(createQuery(model3URI));
answer.beforeFirst();
assertFalse(answer.next());
answer.close();
resource.end(new TestXid(1), XAResource.TMFAIL);
resource.rollback(new TestXid(1));
} finally {
session2.close();
}
} catch (Exception e) {
fail(e);
}
}
};
t2.start();
synchronized (tx2Started) {
if (!tx2Started[0]) {
try {
tx2Started.wait(2000L);
} catch (InterruptedException ie) {
logger.error("wait for tx2-started interrupted", ie);
fail(ie);
}
}
assertFalse("second transaction should still be waiting for write lock", tx2Started[0]);
}
session1.rollback();
synchronized (tx2Started) {
if (!tx2Started[0]) {
try {
tx2Started.wait(2000L);
} catch (InterruptedException ie) {
logger.error("wait for tx2-started interrupted", ie);
fail(ie);
}
}
assertFalse("second transaction should still be waiting for write lock", tx2Started[0]);
}
session1.setAutoCommit(true);
synchronized (tx2Started) {
if (!tx2Started[0]) {
try {
tx2Started.wait(2000L);
} catch (InterruptedException ie) {
logger.error("wait for tx2-started interrupted", ie);
fail(ie);
}
assertTrue("second transaction should've started", tx2Started[0]);
}
}
try {
t2.join(2000L);
} catch (InterruptedException ie) {
logger.error("wait for tx2-terminated interrupted", ie);
fail(ie);
}
assertFalse("second transaction should've terminated", t2.isAlive());
session1.removeModel(model3URI);
} finally {
session1.close();
}
} catch (Exception e) {
fail(e);
}
}
public void testExplicitRollbackIsolationQuery() throws URISyntaxException {
logger.info("testExplicitRollbackIsolationQuery");
URI fileURI = new File("data/xatest-model1.rdf").toURI();
try {
Session session = database.newSession();
XAResource roResource = session.getReadOnlyXAResource();
XAResource rwResource = session.getXAResource();
try {
rwResource.start(new TestXid(1), XAResource.TMNOFLAGS);
session.createModel(model3URI, null);
rwResource.end(new TestXid(1), XAResource.TMSUCCESS);
rwResource.commit(new TestXid(1), true);
rwResource.start(new TestXid(2), XAResource.TMNOFLAGS);
session.setModel(model3URI, new GraphResource(fileURI));
rwResource.end(new TestXid(2), XAResource.TMSUSPEND);
roResource.start(new TestXid(3), XAResource.TMNOFLAGS);
// Evaluate the query
Answer answer = session.query(createQuery(model3URI));
answer.beforeFirst();
assertFalse(answer.next());
answer.close();
roResource.end(new TestXid(3), XAResource.TMSUCCESS);
roResource.commit(new TestXid(3), true);
rwResource.end(new TestXid(2), XAResource.TMFAIL);
rwResource.rollback(new TestXid(2));
roResource.start(new TestXid(4), XAResource.TMNOFLAGS);
selectList = new ArrayList(3);
selectList.add(subjectVariable);
selectList.add(predicateVariable);
selectList.add(objectVariable);
// Evaluate the query
answer = session.query(createQuery(model3URI));
answer.beforeFirst();
assertFalse(answer.next());
answer.close();
roResource.end(new TestXid(4), XAResource.TMFAIL);
roResource.rollback(new TestXid(4));
} finally {
session.close();
}
} catch (Exception e) {
fail(e);
}
}
*/
public void testTrivalImplicit() throws URISyntaxException {
logger.info("testTrivialImplicit");
try {
Session session = sessionFactory.newSession();
try {
session.removeModel(modelURI);
} finally {
session.close();
}
} catch (Exception e) {
fail(e);
}
}
//
// Internal methods
//
private Query createQuery(URI model) {
Variable subjectVariable = new Variable("subject");
Variable predicateVariable = new Variable("predicate");
Variable objectVariable = new Variable("object");
List<SelectElement> selectList = new ArrayList<SelectElement>(3);
selectList.add(subjectVariable);
selectList.add(predicateVariable);
selectList.add(objectVariable);
return new Query(
selectList, // SELECT
new GraphResource(model), // 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
true, // DISTINCT
new UnconstrainedAnswer() // GIVEN
);
}
private String[][] expectedResults() {
return new String[][] {
{ "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" },
};
}
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.info("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());
}
@SuppressWarnings("unused")
private static class DummyXAResource implements XAResource {
public void end(Xid xid, int flags) throws XAException {}
public void forget(Xid xid) throws XAException {}
public int getTransactionTimeout() throws XAException { return 0; }
public int prepare(Xid xid) throws XAException { return 0; }
public Xid[] recover(int flag) throws XAException { return new Xid[] {}; }
public void rollback(Xid xid) throws XAException {}
public boolean setTransactionTimeout(int seconds) throws XAException { return false; }
public void start(Xid xid, int flags) throws XAException {}
public void commit(Xid xid, boolean twophase) throws XAException {}
public boolean isSameRM(XAResource xa) { return xa == this; }
}
}