/******************************************************************************* * Copyright (c) 2007 Cambridge Semantics Incorporated. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Cambridge Semantics Incorporated *******************************************************************************/ package org.openanzo.client; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.concurrent.TimeoutException; import org.openanzo.rdf.MemQuadStore; import org.openanzo.rdf.Statement; import org.openanzo.rdf.URI; import org.openanzo.rdf.utils.test.Condition; import org.openanzo.rdf.utils.test.TestUtilities; import org.openanzo.rdf.utils.test.TestUtilities.TestData; import org.openanzo.services.IPrecondition; import org.openanzo.services.impl.Precondition; /** * Unit tests for the TransactionQueue class. * * @author Joe Betz <jpbetz@cambridgesemantics.com> * */ public class TestTransactionQueue extends TestConfiguration { private TransactionQueue createQueue() { return new TransactionQueue(new TransactionPersistence(new MemQuadStore())); } /** * verifies that the begin and commit methods modify the inTransaction state correctly. * * @throws Exception */ public void testBeginCommit() throws Exception { TransactionQueue transactionQueue = createQueue(); assertFalse(transactionQueue.inTransaction()); transactionQueue.begin(); assertTrue(transactionQueue.inTransaction()); transactionQueue.commit(); } /** * verifies that the add and remove methods modify the commitedTransaction state correctly. * * @throws Exception */ public void testAddRemove() throws Exception { TransactionQueue transactionQueue = createQueue(); assertEquals(0, transactionQueue.committedTransactions.size()); transactionQueue.begin(); Transaction transaction = transactionQueue.getIsolatedTransaction().currentTransaction; transactionQueue.add(TestData.stmt1); transactionQueue.remove(TestData.stmt2); transactionQueue.commit(); assertEquals(1, transactionQueue.committedTransactions.size()); assertTrue(transactionQueue.committedTransactions.contains(transaction)); assertNull(transactionQueue.getOrCreateIsolatedTransaction().currentTransaction); } /** * Runs a test of the walkTransactionTree method. Nests transactions with preconditions and then during the walk keeps track of the order the transactions * are walked by building up a list of the preconditions of the transactions and making sure this matches what is expected. * * @throws Exception */ public void testTreeWalk() throws Exception { Precondition p1 = new Precondition(), p2 = new Precondition(), p3 = new Precondition(), p4 = new Precondition(), p5 = new Precondition(); TransactionQueue queue = createQueue(); // normally the preconditions would be an array but for testing we can // pass in a string queue.begin(p1); queue.add(TestData.stmt1); assertNotNull(queue.getIsolatedTransaction().currentTransaction); queue.begin(p2); queue.begin(p3); queue.commit(); queue.begin(p4); queue.commit(); queue.commit(); queue.begin(p5); queue.commit(); queue.commit(); assertNull(queue.getOrCreateIsolatedTransaction().currentTransaction); assertEquals(1, queue.committedTransactions.size()); final List<IPrecondition> labels = new ArrayList<IPrecondition>(); Transaction.MapFunction walk = new Transaction.MapFunction() { public void call(Transaction transaction) { if (transaction.preconditions != null) { for (IPrecondition precondition : transaction.preconditions) { labels.add(precondition); } } } }; queue.committedTransactions.get(0).walkTransactionTree(walk); assertEquals(5, labels.size()); Iterator<IPrecondition> iter = labels.iterator(); assertEquals(p1, iter.next()); assertEquals(p2, iter.next()); assertEquals(p3, iter.next()); assertEquals(p4, iter.next()); assertEquals(p5, iter.next()); } /** * Verifies the a filter request within nested transactions correctly filters out the appropriate statements. * * @throws Exception */ public void testFilter() throws Exception { TransactionQueue queue = createQueue(); Set<Statement> statements = new HashSet<Statement>(); statements.add(TestData.stmt1); statements.add(TestData.stmt2); // don't add stmt3 because we want to test that Transaction filtering is working. queue.begin(); assertTrue(queue.inTransaction()); queue.add(TestData.stmt1); queue.remove(TestData.stmt2); queue.add(TestData.stmt3); queue.begin(); assertTrue(queue.inTransaction()); queue.add(TestData.stmt4); queue.begin(); queue.add(TestData.stmt5); queue.commit(); queue.add(TestData.stmt6); queue.filter(statements, TestData.subj1, null, null, (URI[]) null); queue.commit(); assertTrue(queue.inTransaction()); queue.commit(); assertFalse(queue.inTransaction()); // check idempotency of filter queue.filter(statements, TestData.subj1, null, null, (URI[]) null); assertTrue(statements.contains(TestData.stmt1)); assertFalse(statements.contains(TestData.stmt2)); assertTrue(statements.contains(TestData.stmt3)); assertTrue(statements.contains(TestData.stmt4)); assertTrue(statements.contains(TestData.stmt5)); assertTrue(statements.contains(TestData.stmt6)); } /** * Verifies that two threads are isolated from one another and that the transaction isolation is objects (IsolatedTransaction) are cleaned up when they are * no longer needed. * * @throws Exception */ public void testMultiThreaded() throws Exception { TransactionQueue queue = createQueue(); Runner1 runner1 = new Runner1(queue); Runner2 runner2 = new Runner2(queue); Thread thread1 = new Thread(runner1); Thread thread2 = new Thread(runner2); thread1.start(); thread2.start(); thread1.join(); thread2.join(); assertEquals(2, queue.committedTransactions.size()); Transaction transaction1 = queue.committedTransactions.get(0); assertTrue(transaction1.additions.contains(TestData.stmt1)); Transaction transaction2 = queue.committedTransactions.get(1); assertTrue(transaction2.additions.contains(TestData.stmt2)); assertNull(queue.isolatedTransactions.get()); assertTrue(runner1.isIsolated); assertTrue(runner2.isIsolated); } private static int token = 0; private static final class Runner1 implements Runnable { TransactionQueue queue; boolean isIsolated; Statement stmt = TestData.stmt1; public Runner1(TransactionQueue queue) { this.queue = queue; } public void run() { try { queue.begin(); token = 1; TestUtilities.waitFor(new Condition() { @Override public boolean check() { return token >= 2; } }); queue.add(TestData.stmt1); token = 3; TestUtilities.waitFor(new Condition() { @Override public boolean check() { return token >= 4; } }); List<Statement> stmts = new ArrayList<Statement>(); queue.filter(stmts, stmt.getSubject(), stmt.getPredicate(), stmt.getObject(), stmt.getNamedGraphUri()); isIsolated = stmts.contains(TestData.stmt1) && !stmts.contains(TestData.stmt2); token = 5; TestUtilities.waitFor(new Condition() { @Override public boolean check() { return token >= 6; } }); queue.commit(); token = 7; } catch (InterruptedException e) { } catch (TimeoutException e) { throw new RuntimeException(e); } } } private static final class Runner2 implements Runnable { TransactionQueue queue; boolean isIsolated; Statement stmt = TestData.stmt2; public Runner2(TransactionQueue queue) { this.queue = queue; } public void run() { try { TestUtilities.waitFor(new Condition() { @Override public boolean check() { return token >= 1; } }); queue.begin(); token = 2; TestUtilities.waitFor(new Condition() { @Override public boolean check() { return token >= 3; } }); queue.add(TestData.stmt2); token = 4; TestUtilities.waitFor(new Condition() { @Override public boolean check() { return token >= 5; } }); List<Statement> stmts = new ArrayList<Statement>(); queue.filter(stmts, stmt.getSubject(), stmt.getPredicate(), stmt.getObject(), stmt.getNamedGraphUri()); isIsolated = stmts.contains(TestData.stmt2) && !stmts.contains(TestData.stmt1); token = 6; TestUtilities.waitFor(new Condition() { @Override public boolean check() { return token >= 7; } }); queue.commit(); } catch (InterruptedException e) { } catch (TimeoutException e) { throw new RuntimeException(e); } } } }