[Mulgara-svn] r1389 - in branches/xa11: conf src/jar/resolver-spi/java/org/mulgara/store/statement src/jar/resolver-store/java/org/mulgara/resolver/store src/jar/resolver-store/java/org/mulgara/store/statement src/jar/resolver-store/java/org/mulgara/store/statement/xa src/jar/resolver-store/java/org/mulgara/store/statement/xa11
pag at mulgara.org
pag at mulgara.org
Tue Dec 2 20:08:37 UTC 2008
Author: pag
Date: 2008-12-02 12:08:36 -0800 (Tue, 02 Dec 2008)
New Revision: 1389
Added:
branches/xa11/src/jar/resolver-store/java/org/mulgara/resolver/store/XA11StatementStoreResolverFactory.java
branches/xa11/src/jar/resolver-store/java/org/mulgara/store/statement/xa11/
branches/xa11/src/jar/resolver-store/java/org/mulgara/store/statement/xa11/XA11StatementStoreImpl.java
branches/xa11/src/jar/resolver-store/java/org/mulgara/store/statement/xa11/XA11StatementStoreImplUnitTest.java
Modified:
branches/xa11/conf/mulgara-config.xml
branches/xa11/src/jar/resolver-spi/java/org/mulgara/store/statement/StatementStoreAbstractUnitTest.java
branches/xa11/src/jar/resolver-store/java/org/mulgara/resolver/store/StatementStoreResolverFactory.java
branches/xa11/src/jar/resolver-store/java/org/mulgara/store/statement/xa/TripleAVLFile.java
Log:
Re-implemented r1380 in this new branch
Modified: branches/xa11/conf/mulgara-config.xml
===================================================================
--- branches/xa11/conf/mulgara-config.xml 2008-12-02 18:59:07 UTC (rev 1388)
+++ branches/xa11/conf/mulgara-config.xml 2008-12-02 20:08:36 UTC (rev 1389)
@@ -92,7 +92,7 @@
<TemporaryNodePoolFactory type="org.mulgara.store.nodepool.memory.MemoryNodePoolFactory"/>
<PersistentStringPoolFactory type="org.mulgara.store.stringpool.xa11.XA11StringPoolFactory" dir="xaStringPool"/>
<TemporaryStringPoolFactory type="org.mulgara.store.stringpool.memory.MemoryStringPoolFactory"/>
- <PersistentResolverFactory type="org.mulgara.resolver.store.StatementStoreResolverFactory" dir="xaStatementStore"/>
+ <PersistentResolverFactory type="org.mulgara.resolver.store.XA11StatementStoreResolverFactory" dir="xaStatementStore"/>
<TemporaryResolverFactory type="org.mulgara.resolver.memory.MemoryResolverFactory" dir="tempStatementStore"/>
<!--
Modified: branches/xa11/src/jar/resolver-spi/java/org/mulgara/store/statement/StatementStoreAbstractUnitTest.java
===================================================================
--- branches/xa11/src/jar/resolver-spi/java/org/mulgara/store/statement/StatementStoreAbstractUnitTest.java 2008-12-02 18:59:07 UTC (rev 1388)
+++ branches/xa11/src/jar/resolver-spi/java/org/mulgara/store/statement/StatementStoreAbstractUnitTest.java 2008-12-02 20:08:36 UTC (rev 1389)
@@ -27,10 +27,9 @@
package org.mulgara.store.statement;
-// Java 2 standard packages
-import java.util.*;
-
// third party packages
+import java.util.Arrays;
+
import junit.framework.*;
import org.apache.log4j.Logger;
@@ -39,6 +38,7 @@
import org.mulgara.store.nodepool.NodePool;
import org.mulgara.store.tuples.TestTuples;
import org.mulgara.store.tuples.Tuples;
+import org.mulgara.store.tuples.TuplesOperations;
import org.mulgara.store.xa.XAStatementStore;
/**
@@ -100,8 +100,6 @@
/**
* Test {@link StatementStore#isEmpty}.
- *
- * @throws Exception EXCEPTION TO DO
*/
public void testIsEmpty() throws Exception {
@@ -119,17 +117,13 @@
store.removeTriples(1, 2, 3, 9);
assertTrue(store.isEmpty());
- }
- catch (UnsupportedOperationException e) {
-
+ } catch (UnsupportedOperationException e) {
log.warn("IsEmpty method unsupported", e);
}
}
/**
* Test {@link StatementStore#existsTriples}.
- *
- * @throws Exception EXCEPTION TO DO
*/
public void testExists() throws Exception {
@@ -137,63 +131,60 @@
long n = NodePool.NONE;
- assertTrue(store.existsTriples(1, 2, 3, n));
- assertTrue(store.existsTriples(1, 2, 4, n));
- assertTrue(store.existsTriples(2, 5, 6, n));
- assertTrue(!store.existsTriples(1, 3, 2, n));
- assertTrue(!store.existsTriples(9, 9, 9, n));
+ assertTrue(store.existsTriples(n, 2, 3, 1));
+ assertTrue(store.existsTriples(n, 2, 4, 2));
+ assertTrue(store.existsTriples(n, 5, 6, 2));
+ assertTrue(!store.existsTriples(n, 3, 2, 1));
+ assertTrue(!store.existsTriples(n, 9, 9, 9));
- assertTrue(store.existsTriples(1, 2, n, n));
- assertTrue(store.existsTriples(2, 5, n, n));
- assertTrue(!store.existsTriples(1, 3, n, n));
- assertTrue(!store.existsTriples(2, 9, n, n));
+ assertTrue(store.existsTriples(1, n, 3, 1));
+ assertTrue(store.existsTriples(2, n, 6, 2));
+ assertTrue(!store.existsTriples(2, n, 4, 2));
+ assertTrue(!store.existsTriples(9, n, 3, 1));
- assertTrue(store.existsTriples(n, 2, 3, n));
- assertTrue(store.existsTriples(n, 2, 4, n));
- assertTrue(!store.existsTriples(n, 1, 3, n));
- assertTrue(!store.existsTriples(n, 2, 9, n));
+ assertTrue(store.existsTriples(n, n, 3, 1));
+ assertTrue(store.existsTriples(n, n, 4, 2));
+ assertTrue(!store.existsTriples(n, n, 3, 2));
+ assertTrue(!store.existsTriples(n, n, 9, 2));
- assertTrue(store.existsTriples(1, n, 3, n));
- assertTrue(store.existsTriples(2, n, 6, n));
- assertTrue(!store.existsTriples(2, n, 4, n));
- assertTrue(!store.existsTriples(9, n, 3, n));
+ assertTrue(store.existsTriples(1, 2, n, 1));
+ assertTrue(store.existsTriples(2, 5, n, 2));
+ assertTrue(!store.existsTriples(1, 3, n, 1));
+ assertTrue(!store.existsTriples(2, 9, n, 2));
- assertTrue(store.existsTriples(1, n, n, n));
- assertTrue(store.existsTriples(2, n, n, n));
- assertTrue(!store.existsTriples(3, n, n, n));
+ assertTrue(store.existsTriples(n, 2, n, 1));
+ assertTrue(store.existsTriples(n, 5, n, 2));
+ assertTrue(!store.existsTriples(n, 3, n, 1));
+ assertTrue(!store.existsTriples(n, 9, n, 2));
- assertTrue(store.existsTriples(n, 2, n, n));
- assertTrue(store.existsTriples(n, 5, n, n));
- assertTrue(!store.existsTriples(n, 1, n, n));
+ assertTrue(store.existsTriples(1, n, n, 1));
+ assertTrue(store.existsTriples(1, n, n, 2));
+ assertTrue(store.existsTriples(2, n, n, 2));
+ assertTrue(!store.existsTriples(3, n, n, 2));
- assertTrue(store.existsTriples(n, n, 3, n));
- assertTrue(store.existsTriples(n, n, 6, n));
- assertTrue(!store.existsTriples(n, n, 5, n));
- }
- catch (UnsupportedOperationException e) {
-
+ assertTrue(store.existsTriples(n, n, n, 1));
+ assertTrue(store.existsTriples(n, n, n, 2));
+ assertTrue(!store.existsTriples(n, n, n, 3));
+ } catch (UnsupportedOperationException e) {
log.warn("Exists method unsupported", e);
}
}
/**
* Test {@link StatementStore#findTuples}.
- *
- * @throws Exception EXCEPTION TO DO
*/
public void testDump() throws Exception {
TestTuples expected = new TestTuples();
- add(expected, store.VARIABLES, new long[] {
- 1, 2, 3, 1});
- add(expected, store.VARIABLES, new long[] {
- 1, 2, 4, 2});
- add(expected, store.VARIABLES, new long[] {
- 2, 5, 6, 2});
+ add(expected, StatementStore.VARIABLES, new long[] {1, 2, 3, 1});
+ add(expected, StatementStore.VARIABLES, new long[] {1, 2, 4, 2});
+ add(expected, StatementStore.VARIABLES, new long[] {2, 5, 6, 2});
Tuples t = store.findTuples(NodePool.NONE, NodePool.NONE, NodePool.NONE, NodePool.NONE);
- assertEquals(expected, t);
+ Tuples r = TuplesOperations.project(t, Arrays.asList(StatementStore.VARIABLES));
+ assertEquals(expected, r);
t.close();
+ r.close();
expected.close();
}
@@ -217,37 +208,37 @@
store.removeTriples(2, 5, 6, 2);
store.removeTriples(1, 2, 4, 2);
- assertTrue(!store.existsTriples(1, 2, 3, NodePool.NONE));
- assertTrue(!store.existsTriples(1, 2, 4, NodePool.NONE));
- assertTrue(!store.existsTriples(2, 5, 6, NodePool.NONE));
+ assertTrue(!store.existsTriples(NodePool.NONE, 2, 3, 1));
+ assertTrue(!store.existsTriples(NodePool.NONE, 2, 4, 2));
+ assertTrue(!store.existsTriples(NodePool.NONE, 5, 6, 2));
}
/**
* Test {@link StatementStore#findTuples}.
- *
- * @throws Exception EXCEPTION TO DO
*/
public void testFindTriplesByNode0() throws Exception {
TestTuples expected = new TestTuples();
Variable[] vars =
- new Variable[] {
- store.VARIABLES[1], store.VARIABLES[2], store.VARIABLES[3]};
- add(expected, vars, new long[] {
- 2, 3, 1});
- add(expected, vars, new long[] {
- 2, 4, 2});
+ new Variable[] {StatementStore.VARIABLES[1], StatementStore.VARIABLES[2], StatementStore.VARIABLES[3]};
+ add(expected, vars, new long[] {2, 3, 1});
+ add(expected, vars, new long[] {2, 4, 2});
- Tuples t = store.findTuples(1, NodePool.NONE, NodePool.NONE, NodePool.NONE);
- assertEquals(expected, t);
- t.close();
- expected.close();
+ try {
+ Tuples t = store.findTuples(1, NodePool.NONE, NodePool.NONE, NodePool.NONE);
+ assertEquals(expected, t);
+ t.close();
+ } catch (IllegalArgumentException e) {
+ // not supported
+ return;
+ } finally {
+ expected.close();
+ }
expected = new TestTuples();
- add(expected, vars, new long[] {
- 5, 6, 2});
+ add(expected, vars, new long[] {5, 6, 2});
- t = store.findTuples(2, NodePool.NONE, NodePool.NONE, NodePool.NONE);
+ Tuples t = store.findTuples(2, NodePool.NONE, NodePool.NONE, NodePool.NONE);
assertEquals(expected, t);
t.close();
expected.close();
@@ -261,17 +252,17 @@
public void testFindTriplesByNode1() throws Exception {
TestTuples expected = new TestTuples();
- Variable[] vars =
- new Variable[] {
- store.VARIABLES[2], store.VARIABLES[0], store.VARIABLES[3]};
- add(expected, vars, new long[] {
- 3, 1, 1});
- add(expected, vars, new long[] {
- 4, 1, 2});
+ Variable[] vars = new Variable[] {StatementStore.VARIABLES[2], StatementStore.VARIABLES[0], StatementStore.VARIABLES[3]};
+ add(expected, vars, new long[] {3, 1, 1});
+ add(expected, vars, new long[] {4, 1, 2});
- Tuples t = store.findTuples(NodePool.NONE, 2, NodePool.NONE, NodePool.NONE);
- assertEquals(expected, t);
- t.close();
+ try {
+ Tuples t = store.findTuples(NodePool.NONE, 2, NodePool.NONE, NodePool.NONE);
+ assertEquals(expected, t);
+ t.close();
+ } catch (IllegalArgumentException e) {
+ // not supported
+ }
expected.close();
}
@@ -283,15 +274,16 @@
public void testFindTriplesByNode2() throws Exception {
TestTuples expected = new TestTuples();
- Variable[] vars =
- new Variable[] {
- store.VARIABLES[0], store.VARIABLES[1], store.VARIABLES[3]};
- add(expected, vars, new long[] {
- 1, 2, 1});
+ Variable[] vars = new Variable[] {StatementStore.VARIABLES[0], StatementStore.VARIABLES[1], StatementStore.VARIABLES[3]};
+ add(expected, vars, new long[] {1, 2, 1});
- Tuples t = store.findTuples(NodePool.NONE, NodePool.NONE, 3, NodePool.NONE);
- assertEquals(expected, t);
- t.close();
+ try {
+ Tuples t = store.findTuples(NodePool.NONE, NodePool.NONE, 3, NodePool.NONE);
+ assertEquals(expected, t);
+ t.close();
+ } catch (IllegalArgumentException e) {
+ // not supported
+ }
expected.close();
}
@@ -303,13 +295,9 @@
public void testFindTriplesByNode3() throws Exception {
TestTuples expected = new TestTuples();
- Variable[] vars =
- new Variable[] {
- store.VARIABLES[0], store.VARIABLES[1], store.VARIABLES[2]};
- add(expected, vars, new long[] {
- 1, 2, 4});
- add(expected, vars, new long[] {
- 2, 5, 6});
+ Variable[] vars = new Variable[] {StatementStore.VARIABLES[0], StatementStore.VARIABLES[1], StatementStore.VARIABLES[2]};
+ add(expected, vars, new long[] {1, 2, 4});
+ add(expected, vars, new long[] {2, 5, 6});
Tuples t = store.findTuples(NodePool.NONE, NodePool.NONE, NodePool.NONE, 2);
assertEquals(expected, t);
@@ -319,64 +307,59 @@
/**
* Test {@link StatementStore#findTuples}.
- *
- * @throws Exception EXCEPTION TO DO
*/
public void testFindTriplesByNode01() throws Exception {
TestTuples expected = new TestTuples();
- Variable[] vars = new Variable[] {
- store.VARIABLES[2], store.VARIABLES[3]};
- add(expected, vars, new long[] {
- 3, 1});
- add(expected, vars, new long[] {
- 4, 2});
+ Variable[] vars = new Variable[] {StatementStore.VARIABLES[2], StatementStore.VARIABLES[3]};
+ add(expected, vars, new long[] {3, 1});
+ add(expected, vars, new long[] {4, 2});
- Tuples t = store.findTuples(1, 2, NodePool.NONE, NodePool.NONE);
- assertEquals(expected, t);
- t.close();
-
- t = store.findTuples(1, 3, NodePool.NONE, NodePool.NONE);
- assertTrue(!expected.equals(t));
- t.close();
+ try {
+ Tuples t = store.findTuples(1, 2, NodePool.NONE, NodePool.NONE);
+ assertEquals(expected, t);
+ t.close();
+
+ t = store.findTuples(1, 3, NodePool.NONE, NodePool.NONE);
+ assertTrue(!expected.equals(t));
+ t.close();
+ } catch (IllegalArgumentException e) {
+ // not supported
+ }
expected.close();
}
/**
* Test {@link StatementStore#findTuples}.
- *
- * @throws Exception EXCEPTION TO DO
*/
public void testFindTriplesByNode02() throws Exception {
TestTuples expected = new TestTuples();
- Variable[] vars = new Variable[] {
- store.VARIABLES[1], store.VARIABLES[3]};
- add(expected, vars, new long[] {
- 2, 1});
+ Variable[] vars = new Variable[] {StatementStore.VARIABLES[1], StatementStore.VARIABLES[3]};
+ add(expected, vars, new long[] {2, 1});
- Tuples t = store.findTuples(1, NodePool.NONE, 3, NodePool.NONE);
- assertEquals(expected, t);
- t.close();
-
- t = store.findTuples(1, NodePool.NONE, 4, NodePool.NONE);
- assertTrue(!expected.equals(t));
- t.close();
+ try {
+ Tuples t = store.findTuples(1, NodePool.NONE, 3, NodePool.NONE);
+ assertEquals(expected, t);
+ t.close();
+
+ t = store.findTuples(1, NodePool.NONE, 4, NodePool.NONE);
+ assertTrue(!expected.equals(t));
+ t.close();
+ } catch (IllegalArgumentException e) {
+ // not supported
+ }
expected.close();
}
/**
* Test {@link StatementStore#findTuples}.
- *
- * @throws Exception EXCEPTION TO DO
*/
public void testFindTriplesByNode03() throws Exception {
TestTuples expected = new TestTuples();
- Variable[] vars = new Variable[] {
- store.VARIABLES[1], store.VARIABLES[2]};
- add(expected, vars, new long[] {
- 2, 4});
+ Variable[] vars = new Variable[] {StatementStore.VARIABLES[1], StatementStore.VARIABLES[2]};
+ add(expected, vars, new long[] {2, 4});
Tuples t = store.findTuples(1, NodePool.NONE, NodePool.NONE, 2);
assertEquals(expected, t);
@@ -386,39 +369,35 @@
/**
* Test {@link StatementStore#findTuples}.
- *
- * @throws Exception EXCEPTION TO DO
*/
public void testFindTriplesByNode12() throws Exception {
TestTuples expected = new TestTuples();
- Variable[] vars = new Variable[] {
- store.VARIABLES[0], store.VARIABLES[3]};
- add(expected, vars, new long[] {
- 1, 1});
+ Variable[] vars = new Variable[] {StatementStore.VARIABLES[0], StatementStore.VARIABLES[3]};
+ add(expected, vars, new long[] {1, 1});
- Tuples t = store.findTuples(NodePool.NONE, 2, 3, NodePool.NONE);
- assertEquals(expected, t);
- t.close();
-
- t = store.findTuples(NodePool.NONE, 2, 4, NodePool.NONE);
- assertTrue(!expected.equals(t));
- t.close();
+ try {
+ Tuples t = store.findTuples(NodePool.NONE, 2, 3, NodePool.NONE);
+ assertEquals(expected, t);
+ t.close();
+
+ t = store.findTuples(NodePool.NONE, 2, 4, NodePool.NONE);
+ assertTrue(!expected.equals(t));
+ t.close();
+ } catch (IllegalArgumentException e) {
+ // not supported
+ }
expected.close();
}
/**
* Test {@link StatementStore#findTuples}.
- *
- * @throws Exception EXCEPTION TO DO
*/
public void testFindTriplesByNode13() throws Exception {
TestTuples expected = new TestTuples();
- Variable[] vars = new Variable[] {
- store.VARIABLES[2], store.VARIABLES[0]};
- add(expected, vars, new long[] {
- 4, 1});
+ Variable[] vars = new Variable[] {StatementStore.VARIABLES[2], StatementStore.VARIABLES[0]};
+ add(expected, vars, new long[] {4, 1});
Tuples t = store.findTuples(NodePool.NONE, 2, NodePool.NONE, 2);
assertEquals(expected, t);
@@ -428,16 +407,12 @@
/**
* Test {@link StatementStore#findTuples}.
- *
- * @throws Exception EXCEPTION TO DO
*/
public void testFindTriplesByNode23() throws Exception {
TestTuples expected = new TestTuples();
- Variable[] vars = new Variable[] {
- store.VARIABLES[0], store.VARIABLES[1]};
- add(expected, vars, new long[] {
- 2, 5});
+ Variable[] vars = new Variable[] {StatementStore.VARIABLES[0], StatementStore.VARIABLES[1]};
+ add(expected, vars, new long[] {2, 5});
Tuples t = store.findTuples(NodePool.NONE, NodePool.NONE, 6, 2);
assertEquals(expected, t);
@@ -447,14 +422,11 @@
/**
* Test {@link StatementStore#findTuples}.
- *
- * @throws Exception EXCEPTION TO DO
*/
public void testFindTriplesByNode013() throws Exception {
- TestTuples expected = new TestTuples(store.VARIABLES[2]);
- Variable[] vars = new Variable[] {
- store.VARIABLES[2]};
+ TestTuples expected = new TestTuples(StatementStore.VARIABLES[2]);
+ Variable[] vars = new Variable[] {StatementStore.VARIABLES[2]};
Tuples t = store.findTuples(2, 6, NodePool.NONE, 1);
assertEquals(expected, t);
t.close();
@@ -463,8 +435,7 @@
assertEquals(expected, t);
t.close();
- add(expected, vars, new long[] {
- 4});
+ add(expected, vars, new long[] {4});
t = store.findTuples(1, 2, NodePool.NONE, 2);
assertEquals(expected, t);
t.close();
@@ -473,20 +444,16 @@
/**
* Test {@link StatementStore#findTuples}.
- *
- * @throws Exception EXCEPTION TO DO
*/
public void testFindTriplesByNode023() throws Exception {
- TestTuples expected = new TestTuples(store.VARIABLES[1]);
- Variable[] vars = new Variable[] {
- store.VARIABLES[1]};
+ TestTuples expected = new TestTuples(StatementStore.VARIABLES[1]);
+ Variable[] vars = new Variable[] {StatementStore.VARIABLES[1]};
Tuples t = store.findTuples(1, NodePool.NONE, 3, 4);
assertEquals(expected, t);
t.close();
- add(expected, vars, new long[] {
- 5});
+ add(expected, vars, new long[] {5});
t = store.findTuples(2, NodePool.NONE, 6, 2);
assertEquals(expected, t);
t.close();
@@ -495,20 +462,16 @@
/**
* Test {@link StatementStore#findTuples}.
- *
- * @throws Exception EXCEPTION TO DO
*/
public void testFindTriplesByNode123() throws Exception {
- TestTuples expected = new TestTuples(store.VARIABLES[0]);
- Variable[] vars = new Variable[] {
- store.VARIABLES[0]};
+ TestTuples expected = new TestTuples(StatementStore.VARIABLES[0]);
+ Variable[] vars = new Variable[] {StatementStore.VARIABLES[0]};
Tuples t = store.findTuples(NodePool.NONE, 2, 3, 4);
assertEquals(expected, t);
t.close();
- add(expected, vars, new long[] {
- 1});
+ add(expected, vars, new long[] {1});
t = store.findTuples(NodePool.NONE, 2, 3, 1);
assertEquals(expected, t);
t.close();
@@ -517,8 +480,6 @@
/**
* Populate the test store.
- *
- * @throws Exception EXCEPTION TO DO
*/
protected void setUp() throws Exception {
@@ -529,46 +490,31 @@
/**
* Close the test store.
- *
- * @throws Exception EXCEPTION TO DO
*/
protected void tearDown() throws Exception {
-
if (store != null) {
-
try {
-
store.close();
- }
- finally {
-
+ } finally {
store = null;
}
}
}
/**
- * METHOD TO DO
- *
- * @param tt PARAMETER TO DO
- * @param vars PARAMETER TO DO
- * @param nodes PARAMETER TO DO
+ * Add a row to a tuples
+ * @param tt The tuples to add to
+ * @param vars The column names in the tuples
+ * @param nodes The values to bind to
*/
protected void add(TestTuples tt, Variable[] vars, long[] nodes) {
- if (vars.length != nodes.length) {
+ if (vars.length != nodes.length) throw new AssertionError();
- throw new AssertionError();
- }
-
for (int i = 0; i < vars.length; ++i) {
-
if (i == 0) {
-
tt.or(vars[i], nodes[i]);
- }
- else {
-
+ } else {
tt.and(vars[i], nodes[i]);
}
}
Modified: branches/xa11/src/jar/resolver-store/java/org/mulgara/resolver/store/StatementStoreResolverFactory.java
===================================================================
--- branches/xa11/src/jar/resolver-store/java/org/mulgara/resolver/store/StatementStoreResolverFactory.java 2008-12-02 18:59:07 UTC (rev 1388)
+++ branches/xa11/src/jar/resolver-store/java/org/mulgara/resolver/store/StatementStoreResolverFactory.java 2008-12-02 20:08:36 UTC (rev 1389)
@@ -71,9 +71,9 @@
private long rdfType;
/** The underlying transactional graph that backs the generated resolvers. */
- private final XAStatementStore statementStore;
+ protected final XAStatementStore statementStore;
- private XAResolverSessionFactory resolverSessionFactory;
+ protected XAResolverSessionFactory resolverSessionFactory;
//
// Constructors
@@ -86,7 +86,7 @@
* @throws IllegalArgumentException {@inheritDoc}
* @throws ResolverException {@inheritDoc}
*/
- private StatementStoreResolverFactory(FactoryInitializer initializer,
+ protected StatementStoreResolverFactory(FactoryInitializer initializer,
XAResolverSessionFactory resolverSessionFactory) throws
InitializerException {
// Validate parameters
@@ -96,7 +96,7 @@
try {
File filePrefix = new File(initializer.getDirectory(), "xa");
- statementStore = new XAStatementStoreImpl(filePrefix.toString());
+ statementStore = createStore(filePrefix.toString());
resolverSessionFactory.registerStatementStore(statementStore);
} catch (Exception e) {
throw new InitializerException("Couldn't initialize XA store", e);
@@ -238,4 +238,15 @@
"Failed to obtain a new ResolverSession", er);
}
}
+
+
+ /**
+ * Creates the required type of store
+ * @param filePrefix The base for the files being used for storage.
+ * @return a new instance of an XAStatementStore
+ * @throws IOException Error accessing the filesystem
+ */
+ protected XAStatementStore createStore(String filePrefix) throws IOException {
+ return new XAStatementStoreImpl(filePrefix.toString());
+ }
}
Added: branches/xa11/src/jar/resolver-store/java/org/mulgara/resolver/store/XA11StatementStoreResolverFactory.java
===================================================================
--- branches/xa11/src/jar/resolver-store/java/org/mulgara/resolver/store/XA11StatementStoreResolverFactory.java (rev 0)
+++ branches/xa11/src/jar/resolver-store/java/org/mulgara/resolver/store/XA11StatementStoreResolverFactory.java 2008-12-02 20:08:36 UTC (rev 1389)
@@ -0,0 +1,64 @@
+/*
+ * 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.opensource.org/licenses/osl-3.0.txt
+ *
+ * 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.
+ */
+
+package org.mulgara.resolver.store;
+
+import java.io.IOException;
+
+import org.mulgara.resolver.spi.FactoryInitializer;
+import org.mulgara.resolver.spi.InitializerException;
+import org.mulgara.resolver.spi.ResolverFactory;
+import org.mulgara.store.statement.xa11.XA11StatementStoreImpl;
+import org.mulgara.store.xa.XAResolverSessionFactory;
+import org.mulgara.store.xa.XAStatementStore;
+
+/**
+ * An extension of StatementStoreResolverFactory which instantiates an XA 1.1 statement store.
+ *
+ * @created Oct 1, 2008
+ * @author Paul Gearon
+ * @copyright © 2008 <a href="http://www.topazproject.org/">The Topaz Project</a>
+ * @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a>
+ */
+public class XA11StatementStoreResolverFactory extends StatementStoreResolverFactory {
+
+ /**
+ * Internal construction of a resolver factory.
+ * @throws InitializerException Unable to initialize.
+ */
+ protected XA11StatementStoreResolverFactory(FactoryInitializer initializer,
+ XAResolverSessionFactory resolverSessionFactory) throws InitializerException {
+ super(initializer, resolverSessionFactory);
+ }
+
+ /**
+ * Factory method for instances of this factory
+ * @param initializer Configuration object for this factory.
+ * @param resolverSessionFactory A factory that this factory can proxy for getting sessions.
+ * @return A new factory for resolver sessions.
+ * @throws InitializerException The factory could not be initialized.
+ */
+ public static ResolverFactory newInstance(FactoryInitializer initializer,
+ XAResolverSessionFactory resolverSessionFactory) throws InitializerException {
+ return new XA11StatementStoreResolverFactory(initializer, resolverSessionFactory);
+ }
+
+ /**
+ * Creates the required type of store
+ * @param filePrefix The base for the files being used for storage.
+ * @return a new instance of an XAStatementStore
+ * @throws IOException Error accessing the filesystem
+ */
+ protected XAStatementStore createStore(String filePrefix) throws IOException {
+ return new XA11StatementStoreImpl(filePrefix.toString());
+ }
+}
Modified: branches/xa11/src/jar/resolver-store/java/org/mulgara/store/statement/xa/TripleAVLFile.java
===================================================================
--- branches/xa11/src/jar/resolver-store/java/org/mulgara/store/statement/xa/TripleAVLFile.java 2008-12-02 18:59:07 UTC (rev 1388)
+++ branches/xa11/src/jar/resolver-store/java/org/mulgara/store/statement/xa/TripleAVLFile.java 2008-12-02 20:08:36 UTC (rev 1389)
@@ -1386,7 +1386,7 @@
}
- long checkIntegrity() {
+ public long checkIntegrity() {
if (this == currentPhase && tripleWriteThread != null)
tripleWriteThread.drain();
Added: branches/xa11/src/jar/resolver-store/java/org/mulgara/store/statement/xa11/XA11StatementStoreImpl.java
===================================================================
--- branches/xa11/src/jar/resolver-store/java/org/mulgara/store/statement/xa11/XA11StatementStoreImpl.java (rev 0)
+++ branches/xa11/src/jar/resolver-store/java/org/mulgara/store/statement/xa11/XA11StatementStoreImpl.java 2008-12-02 20:08:36 UTC (rev 1389)
@@ -0,0 +1,1594 @@
+/*
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (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.mozilla.org/MPL/
+ *
+ * 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.
+ *
+ * The Original Code is the Kowari Metadata Store.
+ *
+ * The Initial Developer of the Original Code is Plugged In Software Pty
+ * Ltd (http://www.pisoftware.com, mailto:info at pisoftware.com). Portions
+ * created by Plugged In Software Pty Ltd are Copyright (C) 2001,2002
+ * Plugged In Software Pty Ltd. All Rights Reserved.
+ *
+ * Contributor(s): N/A.
+ *
+ * [NOTE: The text of this Exhibit A may differ slightly from the text
+ * of the notices in the Source Code files of the Original Code. You
+ * should use the text of this Exhibit A rather than the text found in the
+ * Original Code Source Code for Your Modifications.]
+ *
+ */
+package org.mulgara.store.statement.xa11;
+
+import java.io.*;
+import java.nio.*;
+
+// Java 2 standard packages
+import java.util.*;
+
+// Third party packages
+import org.apache.log4j.Logger;
+
+// Locally written packages
+import org.mulgara.query.*;
+import org.mulgara.store.nodepool.*;
+import org.mulgara.store.statement.*;
+import org.mulgara.store.statement.xa.TripleAVLFile;
+import org.mulgara.store.tuples.StoreTuples;
+import org.mulgara.store.tuples.TuplesOperations;
+import org.mulgara.store.xa.AbstractBlockFile;
+import org.mulgara.store.xa.Block;
+import org.mulgara.store.xa.BlockFile;
+import org.mulgara.store.xa.LockFile;
+import org.mulgara.store.xa.PersistableMetaRoot;
+import org.mulgara.store.xa.SimpleXAResourceException;
+import org.mulgara.store.xa.XAStatementStore;
+import org.mulgara.store.xa.XAUtils;
+import org.mulgara.util.Constants;
+
+/**
+ * An implementation of {@link StatementStore}.
+ *
+ * @created 2008-09-30
+ * @author Paul Gearon
+ * @company <A href="mailto:info at PIsoftware.com">Plugged In Software</A>
+ * @copyright ©2001-2004 <a href="http://www.pisoftware.com/">Plugged In Software Pty Ltd</a>
+ * @licence <a href="{@docRoot}/../../LICENCE">Mozilla Public License v1.1</a>
+ */
+public final class XA11StatementStoreImpl implements XAStatementStore {
+
+ /** Logger. */
+ private final static Logger logger = Logger.getLogger(XA11StatementStoreImpl.class);
+
+ /** The value of the invalid gNode */
+ final static long NONE = NodePool.NONE;
+
+ /** The subject/predicate/object index */
+ final static int TI_3012 = 0;
+
+ /** The predicate/object/subject index */
+ final static int TI_3120 = 1;
+
+ /** The object/subject/predicate index */
+ final static int TI_3201 = 2;
+
+ /** The number of indexes */
+ final static int NR_INDEXES = 3;
+
+ /** The ordering of indexes, as indexed by the TI_ values */
+ private final static int[][] orders = {
+ {3, 0, 1, 2}, // TI_3012
+ {3, 1, 2, 0}, // TI_3120
+ {3, 2, 0, 1} // TI_3201
+ };
+
+ private final static int[] selectIndex = {
+ /* 3XXX */ TI_3012,
+ /* 3XX0 */ TI_3012,
+ /* 3X1X */ TI_3120,
+ /* 3X10 */ TI_3012,
+ /* 32XX */ TI_3201,
+ /* 32X0 */ TI_3201,
+ /* 321X */ TI_3120,
+ /* 3210 */ TI_3012
+ };
+
+ /** A number to identify the correct file type */
+ private final static int FILE_MAGIC = 0xa5e7f21e;
+
+ /** The version of file format */
+ private final static int FILE_VERSION = 9;
+
+ /** Index of the file magic number within each of the two on-disk metaroots. */
+ private final static int IDX_MAGIC = 0;
+
+ /** Index of the file version number within each of the two on-disk metaroots. */
+ private final static int IDX_VERSION = 1;
+
+ /** Index of the valid flag (in ints) within each of the two on-disk metaroots. */
+ private final static int IDX_VALID = 2;
+
+ /** The index of the phase number in the on-disk phase. */
+ private final static int IDX_PHASE_NUMBER = 3;
+
+ /** The size of the header of a metaroot in ints. */
+ private final static int HEADER_SIZE_INTS = 4;
+
+ /** The size of the header of a metaroot in longs. */
+ private final static int HEADER_SIZE_LONGS = (HEADER_SIZE_INTS + 1) / 2;
+
+ /** The size of a metaroot in longs. */
+ private final static int METAROOT_SIZE = HEADER_SIZE_LONGS + Phase.RECORD_SIZE;
+
+ /** The number of metaroots in the metaroot file. */
+ private final static int NR_METAROOTS = 2;
+
+ /** The mask for a bound Subject */
+ private final static int MASK0 = 1;
+
+ /** The mask for a bound Predicate */
+ private final static int MASK1 = 2;
+
+ /** The mask for a bound Object */
+ private final static int MASK2 = 4;
+
+ /** The mask for a bound Graph. This must always be set. */
+ private final static int MASK3 = 8;
+
+ /** The name of the triple store which forms the base name for the graph files. */
+ private String fileName;
+
+ /** The LockFile that protects the graph from being opened twice. */
+ private LockFile lockFile;
+
+ /** The BlockFile for the node pool metaroot file. */
+ private BlockFile metarootFile = null;
+
+ /** The metaroot blocks of the metaroot file. */
+ private Block[] metarootBlocks = new Block[NR_METAROOTS];
+
+ /** An error flag that is set during file initialization if the file version is incorrect */
+ private boolean wrongFileVersion = false;
+
+ /** The files containing indexed triples */
+ private TripleAVLFile[] tripleAVLFiles = new TripleAVLFile[NR_INDEXES];
+
+ /** The current read/write phase. Only the latest phase can write. */
+ private Phase currentPhase = null;
+
+ /**
+ * Determines if modifications can be performed without creating a new
+ * (in-memory) phase. If dirty is false and the current phase is in use (by
+ * unclosed Tupleses) then a new phase must be created to protect the existing
+ * Tupleses before any further modifications are made.
+ */
+ private boolean dirty = true;
+
+ /**
+ * The index of the phase in the metaroot. May be 0 or 1 as the commited phase swaps
+ * between the two metaroots.
+ */
+ private int phaseIndex = 0;
+
+ /** The number of the current phase */
+ private int phaseNumber = 0;
+
+ /** A reference token for keeping the commited phase available until we no longer need it */
+ private Phase.Token committedPhaseToken = null;
+
+ /** A synchronization object for locking access to the committed phase */
+ private Object committedPhaseLock = new Object();
+
+ /** A reference token for keeping the recording phase available until we no longer need it */
+ private Phase.Token recordingPhaseToken = null;
+
+ /**
+ * This flag indicates that the current object has been fully written, and may be considered
+ * as committed when the rest of the system is ready.
+ */
+ private boolean prepared = false;
+
+ /** A set of objects to be informed when nodes are released. */
+ private List<ReleaseNodeListener> releaseNodeListeners = new ArrayList<ReleaseNodeListener>();
+
+
+ /**
+ * Creates a statement store using a base filename.
+ *
+ * @param fileName The base filename to operate from.
+ * @throws IOException The mass storage could not be accessed.
+ */
+ public XA11StatementStoreImpl(String fileName) throws IOException {
+ this.fileName = fileName;
+
+ lockFile = LockFile.createLockFile(fileName + ".g.lock");
+
+ try {
+ // Check that the metaroot file was created with a compatible version of the triplestore.
+ RandomAccessFile metarootRAF = null;
+ try {
+ metarootRAF = new RandomAccessFile(fileName + ".g", "r");
+ if (metarootRAF.length() >= 2 * Constants.SIZEOF_INT) {
+ int fileMagic = metarootRAF.readInt();
+ int fileVersion = metarootRAF.readInt();
+ if (AbstractBlockFile.byteOrder != ByteOrder.BIG_ENDIAN) {
+ fileMagic = XAUtils.bswap(fileMagic);
+ fileVersion = XAUtils.bswap(fileVersion);
+ }
+ wrongFileVersion = fileMagic != FILE_MAGIC || fileVersion != FILE_VERSION;
+ } else {
+ wrongFileVersion = false;
+ }
+ } catch (FileNotFoundException ex) {
+ wrongFileVersion = false;
+ } finally {
+ if (metarootRAF != null) metarootRAF.close();
+ }
+
+ for (int i = 0; i < NR_INDEXES; ++i) {
+ String suffix = ".g_" + orders[i][0] + orders[i][1] + orders[i][2] + orders[i][3];
+ tripleAVLFiles[i] = new TripleAVLFile(fileName + suffix, orders[i]);
+ }
+ } catch (IOException ex) {
+ try {
+ close();
+ } catch (StatementStoreException ex2) { /* no-op */ }
+ throw ex;
+ }
+ }
+
+
+ /**
+ * Returns <code>true</code> if there are no triples in the graph
+ * @return <code>true</code> if there are no triples in the graph
+ */
+ public synchronized boolean isEmpty() {
+ checkInitialized();
+ return currentPhase.isEmpty();
+ }
+
+
+ /**
+ * Returns a count of the number of triples in the graph
+ * @return a count of the number of triples in the graph
+ */
+ public synchronized long getNrTriples() {
+ checkInitialized();
+ return currentPhase.getNrTriples();
+ }
+
+
+ /**
+ * Gets the PhaseNumber attribute of the XAGraphImpl object
+ * @return The PhaseNumber value
+ */
+ public synchronized int getPhaseNumber() {
+ checkInitialized();
+ return phaseNumber;
+ }
+
+
+ /**
+ * Adds a feature to the ReleaseNodeListener attribute of the XAGraphImpl object
+ * @param l The feature to be added to the ReleaseNodeListener attribute
+ */
+ public synchronized void addReleaseNodeListener(ReleaseNodeListener l) {
+ if (!releaseNodeListeners.contains(l)) releaseNodeListeners.add(l);
+ }
+
+
+ /**
+ * Removes a release node listener.
+ * @param l The listener to remove.
+ */
+ public synchronized void removeReleaseNodeListener(ReleaseNodeListener l) {
+ releaseNodeListeners.remove(l);
+ }
+
+
+ /**
+ * Adds a new triple to the graph if it doesn't already exist.
+ * @param node0 the first element of the new triple
+ * @param node1 the second element of the new triple
+ * @param node2 the third element of the new triple
+ * @param node3 the fourth element of the new triple
+ * @throws StatementStoreException Due to structural or IO errors.
+ */
+ public synchronized void addTriple(long node0, long node1, long node2, long node3) throws StatementStoreException {
+ checkInitialized();
+ if (
+ node0 < NodePool.MIN_NODE ||
+ node1 < NodePool.MIN_NODE ||
+ node2 < NodePool.MIN_NODE ||
+ node3 < NodePool.MIN_NODE
+ ) {
+ throw new StatementStoreException(
+ "Attempt to add a triple with node number out of range: " + node0 + " " + node1 + " " + node2 + " " + node3
+ );
+ }
+
+ if (!dirty && currentPhase.isInUse()) {
+ try {
+ new Phase();
+ } catch (IOException ex) {
+ throw new StatementStoreException("I/O error", ex);
+ }
+ }
+
+ currentPhase.addTriple(node0, node1, node2, node3);
+ }
+
+
+ /**
+ * Removes all triples matching the given specification.
+ * @param node0 the value for the first element of the triples
+ * @param node1 the value for the second element of the triples
+ * @param node2 the value for the third element of the triples
+ * @param node3 the value for the fourth element of the triples
+ * @throws StatementStoreException if something exceptional happens
+ */
+ public synchronized void removeTriples(long node0, long node1, long node2, long node3) throws StatementStoreException {
+ checkInitialized();
+ if (node0 != NONE && node1 != NONE && node2 != NONE && node3 != NONE) {
+ if (!dirty && currentPhase.isInUse()) {
+ try {
+ new Phase();
+ } catch (IOException ex) {
+ throw new StatementStoreException("I/O error", ex);
+ }
+ }
+
+ // Remove the triple.
+ currentPhase.removeTriple(node0, node1, node2, node3);
+ } else {
+ // Find all the tuples matching the specification and remove them.
+ StoreTuples tuples = currentPhase.findTuples(node0, node1, node2, node3);
+ try {
+ try {
+ if (!tuples.isEmpty()) {
+ // There is at least one triple to remove so protect the
+ // Tuples as we make changes to the triplestore.
+ try {
+ new Phase();
+ } catch (IOException ex) {
+ throw new StatementStoreException("I/O error", ex);
+ }
+
+ long[] triple = new long[] { node0, node1, node2, node3 };
+ int[] columnMap = tuples.getColumnOrder();
+ int nrColumns = columnMap.length;
+ tuples.beforeFirst();
+ while (tuples.next()) {
+ // Copy the row data over to the triple.
+ for (int col = 0; col < nrColumns; ++col) {
+ triple[columnMap[col]] = tuples.getColumnValue(col);
+ }
+
+ currentPhase.removeTriple(triple[0], triple[1], triple[2], triple[3]);
+ }
+ }
+ } finally {
+ tuples.close();
+ }
+ } catch (TuplesException ex) {
+ throw new StatementStoreException("Exception while iterating over temporary Tuples.", ex);
+ }
+ }
+ }
+
+
+ /**
+ * Finds triples matching the given specification.
+ * @param node0 The 0 node of the triple to find.
+ * @param node1 The 1 node of the triple to find.
+ * @param node2 The 2 node of the triple to find.
+ * @param node3 The 3 node of the triple to find.
+ * @return A set of all the triples which match the search.
+ * @throws StatementStoreException Due to a structural or IO error.
+ */
+ public synchronized StoreTuples findTuples(long node0, long node1, long node2, long node3) throws StatementStoreException {
+ checkInitialized();
+ dirty = false;
+ return currentPhase.findTuples(node0, node1, node2, node3);
+ }
+
+
+ /**
+ * Finds triples matching the given specification and index mask.
+ * @param mask The mask of the index to use. This is only allowable for 3 variables
+ * and a given graph.
+ * @param node0 The 0 node of the triple to find.
+ * @param node1 The 1 node of the triple to find.
+ * @param node2 The 2 node of the triple to find.
+ * @param node3 The 3 node of the triple to find.
+ * @return A set of all the triples which match the search.
+ * @throws StatementStoreException Due to a structural or IO error.
+ */
+ public synchronized StoreTuples findTuples(
+ int mask, long node0, long node1, long node2, long node3
+ ) throws StatementStoreException {
+ checkInitialized();
+ dirty = false;
+ if (!checkMask(mask, node0, node1, node2, node3)) throw new StatementStoreException("Bad explicit index selection for given node pattern.");
+ return currentPhase.findTuples(mask, node0, node1, node2, node3);
+ }
+
+
+ /**
+ * Tests a mask for consistency against the nodes it will be used to find.
+ * @param mask The mask to test.
+ * @param node0 The 0 node of the triple to find.
+ * @param node1 The 1 node of the triple to find.
+ * @param node2 The 2 node of the triple to find.
+ * @param node3 The 3 node of the triple to find. Must not be NONE.
+ * @return <code>true</code> if the mask is consistent with the given nodes.
+ */
+ private static boolean checkMask(int mask, long node0, long node1, long node2, long node3) {
+ // The graph must be bound
+ if (node3 != NONE) return false;
+ if (node0 != NONE && 0 == (mask & MASK0)) return false;
+ if (node1 != NONE && 0 == (mask & MASK1)) return false;
+ if (node2 != NONE && 0 == (mask & MASK2)) return false;
+ return true;
+ }
+
+
+ /**
+ * Returns a StoreTuples which contains all triples in the store. The
+ * parameters provide a hint about how the StoreTuples will be used. This
+ * information is used to select the index from which the StoreTuples will be
+ * obtained.
+ * @param node0Bound specifies that node0 will be bound
+ * @param node1Bound specifies that node1 will be bound
+ * @param node2Bound specifies that node2 will be bound
+ * @return the {@link StoreTuples}
+ * @throws StatementStoreException if something exceptional happens
+ */
+ public synchronized StoreTuples findTuples(boolean node0Bound, boolean node1Bound, boolean node2Bound, boolean node3Bound) throws StatementStoreException {
+ if (!node3Bound) throw new IllegalArgumentException("The graph must be bound");
+ checkInitialized();
+ dirty = false;
+ return currentPhase.findTuples(node0Bound, node1Bound, node2Bound);
+ }
+
+
+ /**
+ * Returns <code>true</code> if any triples match the given specification.
+ * Allows wild cards StatementStore.NONE for any of the node numbers except node3.
+ * @param node0 The 0 node of the triple to find.
+ * @param node1 The 1 node of the triple to find.
+ * @param node2 The 2 node of the triple to find.
+ * @param node3 The 3 node of the triple to find.
+ * @return <code>true</code> if any matching triples exist in the graph.
+ * @throws StatementStoreException Due to a structural or IO error.
+ */
+ public synchronized boolean existsTriples(long node0, long node1, long node2, long node3) throws StatementStoreException {
+ checkInitialized();
+ return currentPhase.existsTriples(node0, node1, node2, node3);
+ }
+
+
+ public XAStatementStore newReadOnlyStatementStore() {
+ return new ReadOnlyGraph();
+ }
+
+
+ public XAStatementStore newWritableStatementStore() {
+ return this;
+ }
+
+
+ /**
+ * Close all files, removing empty space from the ends as required.
+ * @throws StatementStoreException if an error occurs while truncating,
+ * flushing or closing one of the three files.
+ */
+ public synchronized void close() throws StatementStoreException {
+ try {
+ unmap();
+ } finally {
+ try {
+ IOException savedEx = null;
+
+ for (int i = 0; i < NR_INDEXES; ++i) {
+ try {
+ if (tripleAVLFiles[i] != null) tripleAVLFiles[i].close();
+ } catch (IOException ex) {
+ savedEx = ex;
+ }
+ }
+
+ if (metarootFile != null) {
+ try {
+ metarootFile.close();
+ } catch (IOException ex) {
+ savedEx = ex;
+ }
+ }
+
+ if (savedEx != null) throw new StatementStoreException("I/O error closing graph.", savedEx);
+ } finally {
+ if (lockFile != null) {
+ lockFile.release();
+ lockFile = null;
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Close this graph, if it is currently open, and remove all files associated with it.
+ * @throws StatementStoreException Due to an IO error.
+ */
+ public synchronized void delete() throws StatementStoreException {
+ currentPhase = null;
+ try {
+ unmap();
+ } finally {
+ try {
+ IOException savedEx = null;
+
+ for (int i = 0; i < NR_INDEXES; ++i) {
+ try {
+ if (tripleAVLFiles[i] != null) tripleAVLFiles[i].delete();
+ } catch (IOException ex) {
+ savedEx = ex;
+ }
+ }
+
+ if (metarootFile != null) {
+ try {
+ metarootFile.delete();
+ } catch (IOException ex) {
+ savedEx = ex;
+ }
+ }
+
+ if (savedEx != null) throw new StatementStoreException("I/O error deleting graph.", savedEx);
+ } finally {
+ for (int i = 0; i < NR_INDEXES; ++i) tripleAVLFiles[i] = null;
+ metarootFile = null;
+ if (lockFile != null) {
+ lockFile.release();
+ lockFile = null;
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Try to safely close the store if this was not done explicitly.
+ */
+ protected void finalize() {
+ try {
+ close();
+ } catch (Throwable t) {
+ logger.warn("Exception in finalize while trying to close the statement store.", t);
+ }
+ }
+
+
+ /**
+ * A manually tracked reference to this object was released. Does nothing.
+ */
+ public void release() {
+ if (logger.isDebugEnabled()) logger.debug("Release " + this.getClass() + ":" + System.identityHashCode(this));
+ }
+
+
+ /**
+ * This in called in response to the resource being manually refreshed.
+ * This implementation does nothing here.
+ */
+ public void refresh() {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Refresh " + this.getClass() + ":" + System.identityHashCode(this));
+ }
+ }
+
+
+ //
+ // Methods from SimpleXAResource.
+ //
+
+ /**
+ * Clears this store to a fresh state.
+ * @param phaseNumber The phase number to set to.
+ * @throws IOException Error with file access
+ * @throws SimpleXAResourceException Error with the data structures.
+ */
+ public synchronized void clear(int phaseNumber) throws IOException, SimpleXAResourceException {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Clear(" + phaseNumber + ") " + this.getClass() + ":" + System.identityHashCode(this));
+ }
+ if (currentPhase != null) throw new IllegalStateException("Graph already has a current phase.");
+
+ openMetarootFile(true);
+
+ synchronized (committedPhaseLock) {
+ committedPhaseToken = new Phase().use();
+ }
+ this.phaseNumber = phaseNumber;
+ phaseIndex = 1;
+ for (int i = 0; i < NR_INDEXES; ++i) tripleAVLFiles[i].clear();
+
+ new Phase();
+ }
+
+
+ /**
+ * Clear the state of the database.
+ * @throws IOException Filesystem error
+ * @throws SimpleXAResourceException Error in the data structures.
+ */
+ public synchronized void clear() throws IOException, SimpleXAResourceException {
+ if (logger.isDebugEnabled()) logger.debug("Clear " + this.getClass() + ":" + System.identityHashCode(this));
+ if (currentPhase == null) clear(0);
+
+ // could throw an exception if clear() is called after any other
+ // operations are performed. Calling clear() multiple times should be
+ // permitted.
+ }
+
+
+ /**
+ * Perform all the operations for a commit and return when all the data structures are in place.
+ * @throws SimpleXAResourceException Due to a bad transaction state, or an IO error while preparing.
+ */
+ public synchronized void prepare() throws SimpleXAResourceException {
+ if (logger.isDebugEnabled()) logger.debug("Prepare " + this.getClass() + ":" + System.identityHashCode(this));
+ checkInitialized();
+
+ // check that prepare() was not caleld twice
+ if (prepared) throw new SimpleXAResourceException("prepare() called twice.");
+
+ try {
+ // Perform a prepare.
+ recordingPhaseToken = currentPhase.use();
+ Phase recordingPhase = currentPhase;
+ new Phase();
+
+ // Ensure that all data associated with the phase is on disk.
+ for (int i = 0; i < NR_INDEXES; ++i) tripleAVLFiles[i].force();
+
+ // Write the metaroot.
+ int newPhaseIndex = 1 - phaseIndex;
+ int newPhaseNumber = phaseNumber + 1;
+
+ Block block = metarootBlocks[newPhaseIndex];
+ block.putInt(IDX_VALID, 0); // should already be invalid.
+ block.putInt(IDX_PHASE_NUMBER, newPhaseNumber);
+ logger.debug("Writing graph metaroot for phase: " + newPhaseNumber);
+ recordingPhase.writeToBlock(block, HEADER_SIZE_LONGS);
+ block.write();
+ metarootFile.force();
+ block.putInt(IDX_VALID, 1);
+ block.write();
+ metarootFile.force();
+
+ phaseIndex = newPhaseIndex;
+ phaseNumber = newPhaseNumber;
+ prepared = true;
+ } catch (IOException ex) {
+ logger.error("I/O error while performing prepare.", ex);
+ throw new SimpleXAResourceException("I/O error while performing prepare.", ex);
+ } finally {
+ if (!prepared) {
+ // Something went wrong. An exception is on its way out
+ logger.error("Prepare failed.");
+ if (recordingPhaseToken != null) {
+ recordingPhaseToken.release();
+ recordingPhaseToken = null;
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Update the metadata to point to the prepared data structures.
+ * @throws SimpleXAResourceException Due to a bad transaction state, or an IO error.
+ */
+ public synchronized void commit() throws SimpleXAResourceException {
+ if (logger.isDebugEnabled()) logger.debug("Commit " + this.getClass() + ":" + System.identityHashCode(this));
+
+ // check that prepare has been called
+ if (!prepared) throw new SimpleXAResourceException("commit() called without previous prepare().");
+
+ // Perform a commit.
+ try {
+ // Invalidate the metaroot of the old phase.
+ Block block = metarootBlocks[1 - phaseIndex];
+ block.putInt(IDX_VALID, 0);
+ block.write();
+ metarootFile.force();
+
+ // Release the token for the previously committed phase.
+ synchronized (committedPhaseLock) {
+ if (committedPhaseToken != null) committedPhaseToken.release();
+ committedPhaseToken = recordingPhaseToken;
+ }
+ recordingPhaseToken = null;
+ } catch (IOException ex) {
+ logger.fatal("I/O error while performing commit.", ex);
+ throw new SimpleXAResourceException("I/O error while performing commit.", ex);
+ } finally {
+ prepared = false;
+ if (recordingPhaseToken != null) {
+ // Something went wrong! An exception is on its way out
+ recordingPhaseToken.release();
+ recordingPhaseToken = null;
+
+ logger.error("Commit failed. Calling close().");
+ try {
+ close();
+ } catch (Throwable t) {
+ logger.error("Exception on forced close()", t);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Read the state from the metaroot file and use it to set up this object
+ * @return An array of 0, 1, or 2 valid phases that can be selected as the last committed phase.
+ * @throws SimpleXAResourceException Due to an IO error, or a data error in the metaroot file.
+ */
+ public synchronized int[] recover() throws SimpleXAResourceException {
+ if (logger.isDebugEnabled()) logger.debug("Recover " + this.getClass() + ":" + System.identityHashCode(this));
+ if (currentPhase != null) return new int[0];
+ if (wrongFileVersion) throw new SimpleXAResourceException("Wrong metaroot file version.");
+
+ try {
+ openMetarootFile(false);
+ } catch (IOException ex) {
+ throw new SimpleXAResourceException("I/O error", ex);
+ }
+
+ // Count the number of valid phases.
+ int phaseCount = 0;
+ if (metarootBlocks[0].getInt(IDX_VALID) != 0) ++phaseCount;
+ if (metarootBlocks[1].getInt(IDX_VALID) != 0) ++phaseCount;
+
+ // Read the phase numbers.
+ int[] phaseNumbers = new int[phaseCount];
+ int index = 0;
+ if (metarootBlocks[0].getInt(IDX_VALID) != 0) phaseNumbers[index++] = metarootBlocks[0].getInt(IDX_PHASE_NUMBER);
+ if (metarootBlocks[1].getInt(IDX_VALID) != 0) phaseNumbers[index++] = metarootBlocks[1].getInt(IDX_PHASE_NUMBER);
+ return phaseNumbers;
+ }
+
+
+ /**
+ * Choose a phase from the metaroot file to use
+ * @param phaseNumber The number of the phase to select. This must be one of the valid
+ * phases present in the metaroot file.
+ * @throws IOException Due to an error on the filesystem
+ * @throws SimpleXAResourceException If the file structures are incorrect.
+ */
+ public synchronized void selectPhase(int phaseNumber) throws IOException, SimpleXAResourceException {
+ if (logger.isDebugEnabled()) {
+ logger.debug("SelectPhase(" + phaseNumber + ") " + this.getClass() + ":" + System.identityHashCode(this));
+ }
+ if (currentPhase != null) throw new SimpleXAResourceException("selectPhase() called on initialized Graph.");
+ if (metarootFile == null) throw new SimpleXAResourceException("Graph metaroot file is not open.");
+
+ // Locate the metaroot corresponding to the given phase number.
+ if (
+ metarootBlocks[0].getInt(IDX_VALID) != 0 &&
+ metarootBlocks[0].getInt(IDX_PHASE_NUMBER) == phaseNumber
+ ) {
+ phaseIndex = 0;
+ // A new phase will be saved in the other metaroot.
+ } else if (
+ metarootBlocks[1].getInt(IDX_VALID) != 0 &&
+ metarootBlocks[1].getInt(IDX_PHASE_NUMBER) == phaseNumber
+ ) {
+ phaseIndex = 1;
+ // A new phase will be saved in the other metaroot.
+ } else {
+ throw new SimpleXAResourceException("Invalid phase number: " + phaseNumber);
+ }
+
+ // Load a duplicate of the selected phase. The duplicate will have a
+ // phase number which is one higher than the original phase.
+ try {
+ synchronized (committedPhaseLock) {
+ committedPhaseToken = new Phase(metarootBlocks[phaseIndex], HEADER_SIZE_LONGS).use();
+ }
+ this.phaseNumber = phaseNumber;
+ } catch (IllegalStateException ex) {
+ throw new SimpleXAResourceException("Cannot construct initial phase.", ex);
+ }
+ new Phase();
+
+ // Invalidate the on-disk metaroot that the new phase will be saved to.
+ Block block = metarootBlocks[1 - phaseIndex];
+ block.putInt(IDX_VALID, 0);
+ block.write();
+ metarootFile.force();
+ }
+
+
+ /**
+ * Return to the data structure state from the beginning of the transaction.
+ * @throws SimpleXAResourceException Due to an IO error.
+ */
+ public synchronized void rollback() throws SimpleXAResourceException {
+ if (logger.isDebugEnabled()) logger.debug("Rollback " + this.getClass() + ":" + System.identityHashCode(this));
+ checkInitialized();
+ try {
+ if (prepared) {
+ // Restore phaseIndex and phaseNumber to their previous values.
+ phaseIndex = 1 - phaseIndex;
+ --phaseNumber;
+ recordingPhaseToken = null;
+ prepared = false;
+
+ // Invalidate the metaroot of the other phase.
+ Block block = metarootBlocks[1 - phaseIndex];
+ block.putInt(IDX_VALID, 0);
+ block.write();
+ metarootFile.force();
+ }
+ } catch (IOException ex) {
+ throw new SimpleXAResourceException("I/O error while performing rollback (invalidating metaroot)", ex);
+ } finally {
+ try {
+ new Phase(committedPhaseToken.getPhase());
+ } catch (IOException ex) {
+ throw new SimpleXAResourceException("I/O error while performing rollback (new committed phase)", ex);
+ }
+ }
+ }
+
+
+ /**
+ * Get a string representation of the current state of the graph.
+ * @return A string representing the current state
+ */
+ public synchronized String toString() {
+ if (currentPhase == null) return "Uninitialized Graph.";
+ return currentPhase.toString();
+ }
+
+
+ /**
+ * Attempt to cleanly close all mapped files.
+ */
+ public synchronized void unmap() {
+ if (committedPhaseToken != null) {
+ recordingPhaseToken = null;
+ prepared = false;
+
+ try {
+ new Phase(committedPhaseToken.getPhase());
+ } catch (Throwable t) {
+ logger.warn("Exception while rolling back in unmap()", t);
+ }
+ currentPhase = null;
+
+ synchronized (committedPhaseLock) {
+ committedPhaseToken.release();
+ committedPhaseToken = null;
+ }
+ }
+
+ if (tripleAVLFiles != null) {
+ for (int i = 0; i < NR_INDEXES; ++i) {
+ if (tripleAVLFiles[i] != null) tripleAVLFiles[i].unmap();
+ }
+ }
+
+ if (metarootFile != null) {
+ if (metarootBlocks[0] != null) metarootBlocks[0] = null;
+ if (metarootBlocks[1] != null) metarootBlocks[1] = null;
+ metarootFile.unmap();
+ }
+ }
+
+
+ /**
+ * Check that the data structures are valid
+ * @return The number of triples in the database
+ */
+ synchronized long checkIntegrity() {
+ checkInitialized();
+ return currentPhase.checkIntegrity();
+ }
+
+
+ /**
+ * Open the metaroot file and read in the contents
+ * @param clear If <code>true</code> then the file will be reset to empty.
+ * @throws IOException Due to a filesystem error.
+ * @throws SimpleXAResourceException If the data structures are inconsistent.
+ */
+ private void openMetarootFile(boolean clear) throws IOException, SimpleXAResourceException {
+ if (metarootFile == null) {
+ metarootFile = AbstractBlockFile.openBlockFile(fileName + ".g", METAROOT_SIZE * Constants.SIZEOF_LONG, BlockFile.IOType.EXPLICIT);
+
+ long nrBlocks = metarootFile.getNrBlocks();
+ if (nrBlocks != NR_METAROOTS) {
+ if (nrBlocks > 0) {
+ logger.info("Graph metaroot file for triple store \"" + fileName + "\" has invalid number of blocks: " + nrBlocks);
+ if (nrBlocks < NR_METAROOTS) {
+ clear = true;
+ metarootFile.clear();
+ }
+ } else {
+ // Perform initialization on empty file.
+ clear = true;
+ }
+ metarootFile.setNrBlocks(NR_METAROOTS);
+ }
+
+ metarootBlocks[0] = metarootFile.readBlock(0);
+ metarootBlocks[1] = metarootFile.readBlock(1);
+ }
+
+ if (clear) {
+ // Invalidate the metaroots on disk.
+ metarootBlocks[0].putInt(IDX_MAGIC, FILE_MAGIC);
+ metarootBlocks[0].putInt(IDX_VERSION, FILE_VERSION);
+ metarootBlocks[0].putInt(IDX_VALID, 0);
+ metarootBlocks[0].write();
+ metarootBlocks[1].putInt(IDX_MAGIC, 0);
+ metarootBlocks[1].putInt(IDX_VERSION, 0);
+ metarootBlocks[1].putInt(IDX_VALID, 0);
+ metarootBlocks[1].write();
+ metarootFile.force();
+ }
+ }
+
+
+ /**
+ * Tests that the current object has been initialized.
+ * @throws IllegalStateException Throws this unchecked exception if the object is not initialized.
+ */
+ private void checkInitialized() {
+ if (currentPhase == null) throw new IllegalStateException("No current phase. Graph has not been initialized or has been closed.");
+ }
+
+
+ final class ReadOnlyGraph implements XAStatementStore {
+
+ private Phase phase = null;
+
+ private Phase.Token token = null;
+
+
+ /**
+ * Create a read-only graph attached to the current outer database
+ */
+ ReadOnlyGraph() {
+ synchronized (committedPhaseLock) {
+ if (committedPhaseToken == null) {
+ throw new IllegalStateException("Cannot create read only view of uninitialized Graph.");
+ }
+ }
+ }
+
+
+ public synchronized boolean isEmpty() {
+ return phase.isEmpty();
+ }
+
+
+ /**
+ * Returns a count of the number of triples in the graph
+ * @return a count of the number of triples in the graph
+ */
+ public synchronized long getNrTriples() {
+ return phase.getNrTriples();
+ }
+
+
+ /**
+ * Adds a triple to the graph.
+ * @param node0 The 0 node of the triple.
+ * @param node1 The 1 node of the triple.
+ * @param node2 The 2 node of the triple.
+ * @param node3 The 3 node of the triple.
+ */
+ public void addTriple(long node0, long node1, long node2, long node3) throws StatementStoreException {
+ throw new UnsupportedOperationException("Trying to modify a read-only graph.");
+ }
+
+
+ /**
+ * Removes all triples matching the given specification.
+ * @param node0 the value for the first element of the triples
+ * @param node1 the value for the second element of the triples
+ * @param node2 the value for the third element of the triples
+ * @param node3 the value for the fourth element of the triples
+ */
+ public void removeTriples(long node0, long node1, long node2, long node3) throws StatementStoreException {
+ throw new UnsupportedOperationException("Trying to modify a read-only graph.");
+ }
+
+
+ /**
+ * Finds triples matching the given specification.
+ * @param node0 The 0 node of the triple to find.
+ * @param node1 The 1 node of the triple to find.
+ * @param node2 The 2 node of the triple to find.
+ * @param node3 The 3 node of the triple to find.
+ * @return A StoreTuples which contains the triples which match the search.
+ */
+ public synchronized StoreTuples findTuples(long node0, long node1, long node2, long node3) throws StatementStoreException {
+ return phase.findTuples(node0, node1, node2, node3);
+ }
+
+ /**
+ * Finds triples matching the given specification.
+ * @param mask The mask of the index to use. This is only allowable for 3 variables
+ * and a given graph.
+ * @param node0 The 0 node of the triple to find.
+ * @param node1 The 1 node of the triple to find.
+ * @param node2 The 2 node of the triple to find.
+ * @param node3 The 3 node of the triple to find.
+ * @return A StoreTuples which contains the triples which match the search.
+ * @throws StatementStoreException A structural or IO error
+ */
+ public synchronized StoreTuples findTuples(int mask, long node0, long node1, long node2, long node3) throws StatementStoreException {
+ if (!checkMask(mask, node0, node1, node2, node3)) throw new StatementStoreException("Bad explicit index selection for given node pattern.");
+ return phase.findTuples(mask, node0, node1, node2, node3);
+ }
+
+
+ /**
+ * Returns a StoreTuples which contains all triples in the store. The
+ * parameters provide a hint about how the StoreTuples will be used. This
+ * information is used to select the index from which the StoreTuples will
+ * be obtained.
+ * @param node0Bound specifies that node0 will be bound
+ * @param node1Bound specifies that node1 will be bound
+ * @param node2Bound specifies that node2 will be bound
+ * @return the {@link StoreTuples}
+ * @throws StatementStoreException if something exceptional happens
+ */
+ public synchronized StoreTuples findTuples(boolean node0Bound, boolean node1Bound, boolean node2Bound, boolean node3Bound) throws StatementStoreException {
+ if (!node3Bound) throw new IllegalArgumentException("The graph must be bound");
+ return phase.findTuples(node0Bound, node1Bound, node2Bound);
+ }
+
+
+ public synchronized boolean existsTriples(long node0, long node1, long node2, long node3) throws StatementStoreException {
+ return phase.existsTriples(node0, node1, node2, node3);
+ }
+
+
+ public XAStatementStore newReadOnlyStatementStore() {
+ throw new UnsupportedOperationException();
+ }
+
+
+ public XAStatementStore newWritableStatementStore() {
+ throw new UnsupportedOperationException();
+ }
+
+
+ public void close() {
+ throw new UnsupportedOperationException("Trying to close a read-only graph.");
+ }
+
+
+ public void delete() {
+ throw new UnsupportedOperationException("Trying to delete a read-only graph.");
+ }
+
+
+ /**
+ * Release the phase.
+ */
+ public synchronized void release() {
+ if (logger.isDebugEnabled()) logger.debug("Releasing " + this.getClass() + ":" + System.identityHashCode(this));
+ try {
+ if (token != null) token.release();
+ } finally {
+ phase = null;
+ token = null;
+ }
+ }
+
+
+ public synchronized void refresh() {
+ if (logger.isDebugEnabled()) logger.debug("Refreshing " + this.getClass() + ":" + System.identityHashCode(this));
+
+ synchronized (committedPhaseLock) {
+ Phase committedPhase = committedPhaseToken.getPhase();
+ if (phase != committedPhase) {
+ if (token != null) token.release();
+ phase = committedPhase;
+ token = phase.use();
+ }
+ }
+ }
+
+ public void addReleaseNodeListener(ReleaseNodeListener l) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void removeReleaseNodeListener(ReleaseNodeListener l) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void prepare() {
+ if (logger.isDebugEnabled()) logger.debug("Preparing " + this.getClass() + ":" + System.identityHashCode(this));
+ }
+
+ public void commit() {
+ if (logger.isDebugEnabled()) logger.debug("Commit " + this.getClass() + ":" + System.identityHashCode(this));
+ }
+
+ public void rollback() {
+ if (logger.isDebugEnabled()) logger.debug("Rollback " + this.getClass() + ":" + System.identityHashCode(this));
+ }
+
+ public void clear() {
+ if (logger.isDebugEnabled()) logger.debug("Clearing " + this.getClass() + ":" + System.identityHashCode(this));
+ }
+
+ public void clear(int phaseNumber) {
+ if (logger.isDebugEnabled()) logger.debug("Clearing (" + phaseNumber + ") " + this.getClass() + ":" + System.identityHashCode(this));
+ }
+
+ public int[] recover() {
+ if (logger.isDebugEnabled()) logger.debug("Recovering " + this.getClass() + ":" + System.identityHashCode(this));
+ throw new UnsupportedOperationException("Attempting to recover ReadOnlyGraph");
+ }
+
+ public void selectPhase(int phaseNumber) {
+ if (logger.isDebugEnabled()) logger.debug("Selecting Phase " + this.getClass() + ":" + System.identityHashCode(this));
+ throw new UnsupportedOperationException("Attempting to selectPhase of ReadOnlyGraph");
+ }
+
+ public int getPhaseNumber() {
+ return phaseNumber;
+ }
+ }
+
+
+ /**
+ * This class represents the state of the the database at a particular time. Only the most
+ * recent phase can be written to.
+ */
+ final class Phase implements PersistableMetaRoot {
+
+ /** The size of the data this object stores in the metaroot */
+ final static int RECORD_SIZE = TripleAVLFile.Phase.RECORD_SIZE * NR_INDEXES;
+
+ /** Maintaines parallel structural phases between all of the parallel tree data structures */
+ private TripleAVLFile.Phase[] tripleAVLFilePhases = new TripleAVLFile.Phase[NR_INDEXES];
+
+
+ /**
+ * Creates a new phase based on the current state of the database.
+ * This sets the latest phase on the outer statement store.
+ * @throws IOException Error on the filesystem.
+ */
+ Phase() throws IOException {
+ for (int i = 0; i < NR_INDEXES; ++i) tripleAVLFilePhases[i] = tripleAVLFiles[i].new Phase();
+ currentPhase = this;
+ dirty = true;
+ }
+
+
+ /**
+ * A copy constructor for duplicating a phase structure. This sets the latest phase
+ * on the outer statement store.
+ * @throws IOException Error on the filesystem.
+ */
+ Phase(Phase p) throws IOException {
+ assert p != null;
+
+ for (int i = 0; i < NR_INDEXES; ++i) tripleAVLFilePhases[i] = tripleAVLFiles[i].new Phase(p.tripleAVLFilePhases[i]);
+ currentPhase = this;
+ dirty = true;
+ }
+
+
+ /**
+ * Create a phase based on information found in a buffer that came from a metaroot
+ * @param b The buffer containing the phase information.
+ * @param offset The start of the phase information in the buffer
+ * @throws IOException A filesystem error occurred while accessing the buffer.
+ */
+ Phase(Block b, int offset) throws IOException {
+ for (int i = 0; i < NR_INDEXES; ++i) {
+ tripleAVLFilePhases[i] = tripleAVLFiles[i].new Phase(b, offset);
+ offset += TripleAVLFile.Phase.RECORD_SIZE;
+ }
+ currentPhase = this;
+ dirty = false;
+ }
+
+
+ /**
+ * Writes this PersistableMetaRoot to the specified Block. The ints are
+ * written at the specified offset.
+ * @param b The metaroot Block to write this object to.
+ * @param offset The start within the buffer of where the phase information should be written to.
+ */
+ public void writeToBlock(Block b, int offset) {
+ for (int i = 0; i < NR_INDEXES; ++i) {
+ tripleAVLFilePhases[i].writeToBlock(b, offset);
+ offset += TripleAVLFile.Phase.RECORD_SIZE;
+ }
+ }
+
+
+ /**
+ * Create a string representation of the current phase.
+ * @return A string representing this phase
+ */
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < NR_INDEXES; ++i) {
+ StoreTuples ts = tripleAVLFilePhases[i].allTuples();
+ try {
+ sb.append(ts).append('\n');
+ } finally {
+ try {
+ ts.close();
+ } catch (TuplesException ex) {
+ logger.warn("TuplesException while closing Tuples", ex);
+ return ex.toString();
+ }
+ }
+ }
+ return sb.toString();
+ }
+
+
+ /**
+ * Tests if any part of this phase has a reference being kept to it
+ * @return <code>true</code> if any part of this phase is being used.
+ */
+ boolean isInUse() {
+ for (int i = 0; i < NR_INDEXES; ++i) {
+ if (tripleAVLFilePhases[i].isInUse()) return true;
+ }
+ return false;
+ }
+
+
+ /**
+ * Tests if the phase contains any triples
+ * @return <code>true</code> if the phase contains 1 or more triples
+ */
+ boolean isEmpty() {
+ return tripleAVLFilePhases[TI_3012].isEmpty();
+ }
+
+
+ /**
+ * Gets the number of triples in the phase
+ * @return The number of triples in this phase
+ */
+ long getNrTriples() {
+ return tripleAVLFilePhases[TI_3012].getNrTriples();
+ }
+
+
+ /**
+ * Adds a new triple to the graph if it doesn't already exist.
+ * @param node0 the first element of the new triple
+ * @param node1 the second element of the new triple
+ * @param node2 the third element of the new triple
+ * @param node3 the fourth element of the new triple
+ * @throws StatementStoreException An IO or data structure error
+ */
+ void addTriple(long node0, long node1, long node2, long node3) throws StatementStoreException {
+ assert node0 >= NodePool.MIN_NODE;
+ assert node1 >= NodePool.MIN_NODE;
+ assert node2 >= NodePool.MIN_NODE;
+ assert node3 >= NodePool.MIN_NODE;
+
+ //if (
+ // DEBUG && nodePool != null &&
+ // !nodePool.isValid(node0) && !nodePool.isValid(node1) &&
+ // !nodePool.isValid(node2) && !nodePool.isValid(node3)
+ //) throw new AssertionError(
+ // "Attempt to add a triple with an invalid node"
+ //);
+
+ long[] triple = new long[]{node0, node1, node2, node3};
+
+ for (int i = 0; i < NR_INDEXES; ++i) tripleAVLFilePhases[i].asyncAddTriple(triple);
+ }
+
+
+ /**
+ * Removes the specified triple.
+ * @param node0 the value for the first element of the triple
+ * @param node1 the value for the second element of the triple
+ * @param node2 the value for the third element of the triple
+ * @param node3 the value for the fourth element of the triple
+ * @throws StatementStoreException An IO or structural error
+ */
+ void removeTriple(long node0, long node1, long node2, long node3) throws StatementStoreException {
+ if (
+ node0 < NodePool.MIN_NODE ||
+ node1 < NodePool.MIN_NODE ||
+ node2 < NodePool.MIN_NODE ||
+ node3 < NodePool.MIN_NODE
+ ) {
+ throw new StatementStoreException("Attempt to remove a triple with node number out of range: " + node0 + " " + node1 + " " + node2 + " " + node3);
+ }
+
+ try {
+ for (int i = 0; i < NR_INDEXES; ++i) tripleAVLFilePhases[i].removeTriple(node0, node1, node2, node3);
+ // removeTriple listeners can be informed here
+ } catch (IOException e) {
+ throw new StatementStoreException("I/O error", e);
+ }
+ }
+
+
+ /**
+ * Finds triples matching the given specification.
+ * @param variableMask the mask used to indicate the desired index.
+ * @param node0 The 0 node of the triple to find.
+ * @param node1 The 1 node of the triple to find.
+ * @param node2 The 2 node of the triple to find.
+ * @param node3 The 3 node of the triple to find.
+ * @return A StoreTuples containing all the triples which match the search.
+ * @throws StatementStoreException An IO or structural error
+ */
+ StoreTuples findTuples(int variableMask, long node0, long node1, long node2, long node3) throws StatementStoreException {
+ if (
+ node0 < NodePool.NONE ||
+ node1 < NodePool.NONE ||
+ node2 < NodePool.NONE ||
+ node3 < NodePool.NONE
+ ) {
+ // There is at least one query node. Return an empty StoreTuples.
+ return TuplesOperations.empty();
+ }
+
+ if (0 == (variableMask & MASK3)) throw new StatementStoreException("This version of find is for re-ordering graphs, base on a given mask.");
+ try {
+ switch (variableMask) {
+ case MASK3:
+ return tripleAVLFilePhases[TI_3012].findTuples(node3);
+ case MASK0 | MASK3:
+ return tripleAVLFilePhases[TI_3012].findTuples(node3);
+ case MASK1 | MASK3:
+ return tripleAVLFilePhases[TI_3120].findTuples(node3);
+ case MASK0 | MASK1 | MASK3:
+ return tripleAVLFilePhases[TI_3012].findTuples(node3);
+ case MASK2 | MASK3:
+ return tripleAVLFilePhases[TI_3201].findTuples(node3);
+ case MASK0 | MASK2 | MASK3:
+ return tripleAVLFilePhases[TI_3201].findTuples(node3);
+ case MASK1 | MASK2 | MASK3:
+ return tripleAVLFilePhases[TI_3120].findTuples(node3);
+ case MASK0 | MASK1 | MASK2 | MASK3:
+ return tripleAVLFilePhases[TI_3012].findTuples(node3);
+ default:
+ throw new AssertionError();
+ }
+ } catch (IOException ex) {
+ throw new StatementStoreException("I/O error", ex);
+ }
+ }
+
+
+ /**
+ * Finds triples matching the given specification.
+ * @param node0 The 0 node of the triple to find.
+ * @param node1 The 1 node of the triple to find.
+ * @param node2 The 2 node of the triple to find.
+ * @param node3 The 3 node of the triple to find.
+ * @return A StoreTuples containing all the triples which match the search.
+ * @throws StatementStoreException An IO or structural error
+ */
+ StoreTuples findTuples(long node0, long node1, long node2, long node3) throws StatementStoreException {
+ if (
+ node0 < NodePool.NONE ||
+ node1 < NodePool.NONE ||
+ node2 < NodePool.NONE ||
+ node3 < NodePool.NONE
+ ) {
+ // There is at least one query node. Return an empty StoreTuples.
+ return TuplesOperations.empty();
+ }
+
+ int variableMask =
+ (node0 != NONE ? MASK0 : 0) |
+ (node1 != NONE ? MASK1 : 0) |
+ (node2 != NONE ? MASK2 : 0) |
+ (node3 != NONE ? MASK3 : 0);
+
+ if (node3 == NONE && variableMask != 0) {
+ throw new IllegalArgumentException("Graph must be specified");
+ }
+
+
+ try {
+ switch (variableMask) {
+ case 0:
+ return tripleAVLFilePhases[TI_3012].allTuples();
+ case MASK3:
+ return tripleAVLFilePhases[TI_3012].findTuples(node3);
+ case MASK0 | MASK3:
+ return tripleAVLFilePhases[TI_3012].findTuples(node3, node0);
+ case MASK1 | MASK3:
+ return tripleAVLFilePhases[TI_3120].findTuples(node3, node1);
+ case MASK0 | MASK1 | MASK3:
+ return tripleAVLFilePhases[TI_3012].findTuples(node3, node0, node1);
+ case MASK2 | MASK3:
+ return tripleAVLFilePhases[TI_3201].findTuples(node3, node2);
+ case MASK0 | MASK2 | MASK3:
+ return tripleAVLFilePhases[TI_3201].findTuples(node3, node2, node0);
+ case MASK1 | MASK2 | MASK3:
+ return tripleAVLFilePhases[TI_3120].findTuples(node3, node1, node2);
+ case MASK0 | MASK1 | MASK2 | MASK3:
+ if (tripleAVLFilePhases[TI_3012].existsTriple(node0, node1, node2, node3)) {
+ return TuplesOperations.unconstrained();
+ }
+ return TuplesOperations.empty();
+ default:
+ throw new AssertionError("Search structure incorrectly calculated");
+ }
+ } catch (IOException ex) {
+ throw new StatementStoreException("I/O error", ex);
+ }
+ }
+
+
+ StoreTuples findTuples(boolean node0Bound, boolean node1Bound, boolean node2Bound) throws StatementStoreException {
+ // The variable mask does not need MASK3, as this has been taken into account in selectIndex[]
+ int variableMask =
+ (node0Bound ? MASK0 : 0) |
+ (node1Bound ? MASK1 : 0) |
+ (node2Bound ? MASK2 : 0);
+
+ return tripleAVLFilePhases[selectIndex[variableMask]].allTuples();
+ }
+
+
+ /**
+ * Test is there exist triples according to a given pattern
+ * @param node0 A subject gNode, or NONE
+ * @param node1 A predicate gNode, or NONE
+ * @param node2 A object gNode, or NONE
+ * @param node3 A subject gNode. May not be NONE
+ * @return <code>true</code> if there exist triples that match the pattern
+ * @throws StatementStoreException A structural or IO exception
+ */
+ boolean existsTriples(long node0, long node1, long node2, long node3) throws StatementStoreException {
+ if (node3 == NONE) throw new IllegalStateException("Graph must be specified");
+
+ if (
+ node0 < NodePool.NONE ||
+ node1 < NodePool.NONE ||
+ node2 < NodePool.NONE ||
+ node3 < NodePool.NONE
+ ) {
+ // There is at least one query node (comes from the query, but not in the data pool).
+ // Return an empty StoreTuples.
+ return false;
+ }
+
+ int variableMask =
+ (node0 != NONE ? MASK0 : 0) |
+ (node1 != NONE ? MASK1 : 0) |
+ (node2 != NONE ? MASK2 : 0) |
+ MASK3;
+
+ try {
+ switch (variableMask) {
+ case MASK3:
+ return tripleAVLFilePhases[TI_3012].existsTriples(node3);
+ case MASK0 | MASK3:
+ return tripleAVLFilePhases[TI_3012].existsTriples(node3, node0);
+ case MASK1 | MASK3:
+ return tripleAVLFilePhases[TI_3120].existsTriples(node3, node1);
+ case MASK0 | MASK1 | MASK3:
+ return tripleAVLFilePhases[TI_3012].existsTriples(node3, node0, node1);
+ case MASK2 | MASK3:
+ return tripleAVLFilePhases[TI_3201].existsTriples(node3, node2);
+ case MASK0 | MASK2 | MASK3:
+ return tripleAVLFilePhases[TI_3201].existsTriples(node3, node2, node0);
+ case MASK1 | MASK2 | MASK3:
+ return tripleAVLFilePhases[TI_3120].existsTriples(node3, node1, node2);
+ case MASK0 | MASK1 | MASK2 | MASK3:
+ return tripleAVLFilePhases[TI_3012].existsTriple(node3, node0, node1, node2);
+ default:
+ throw new AssertionError("Search structure incorrectly calculated");
+ }
+ } catch (IOException ex) {
+ throw new StatementStoreException("I/O error", ex);
+ }
+ }
+
+
+ /**
+ * Check that each index contains the same number of triples
+ * @throws AssertionError if the indexes contain a differing number of triples
+ */
+ long checkIntegrity() {
+ long nrTriples[] = new long[NR_INDEXES];
+
+ for (int i = 0; i < NR_INDEXES; ++i) nrTriples[i] = tripleAVLFilePhases[i].checkIntegrity();
+
+ for (int i = 1; i < NR_INDEXES; ++i) {
+ if (nrTriples[0] != nrTriples[i]) {
+ StringBuffer sb = new StringBuffer("tripleAVLFiles disagree on the number of triples:");
+ for (int j = 0; j < NR_INDEXES; ++j) sb.append(' ').append(nrTriples[j]);
+ throw new AssertionError(sb.toString());
+ }
+ }
+
+ return nrTriples[0];
+ }
+
+
+ /**
+ * Increment the reference count on this object.
+ * @return A new token representing the reference.
+ */
+ Token use() {
+ return new Token();
+ }
+
+
+ /**
+ * A token to reference the phase, incrementing the reference count from the perpective
+ * of the garbage collector.
+ */
+ final class Token {
+
+ /** A list of tokens from the underlying indexes */
+ private TripleAVLFile.Phase.Token[] tripleAVLFileTokens = new TripleAVLFile.Phase.Token[NR_INDEXES];
+
+ /** The phase being referenced */
+ private Phase phase = Phase.this;
+
+
+ /**
+ * Creates a token. This creates tokens for the underlying objects as well.
+ */
+ Token() {
+ for (int i = 0; i < NR_INDEXES; ++i) tripleAVLFileTokens[i] = tripleAVLFilePhases[i].use();
+ }
+
+
+ /**
+ * Get the phase that this token represents.
+ * @return The phase referenced by this token.
+ */
+ public Phase getPhase() {
+ assert tripleAVLFileTokens != null : "Invalid Token";
+ return phase;
+ }
+
+
+ /**
+ * Reduce the reference count on the referenced phase by releasing this token.
+ * The token may not be used after being released.
+ */
+ public void release() {
+ assert tripleAVLFileTokens != null : "Invalid Token";
+ for (int i = 0; i < NR_INDEXES; ++i) tripleAVLFileTokens[i].release();
+ tripleAVLFileTokens = null;
+ phase = null;
+ }
+
+ }
+
+ }
+
+}
Added: branches/xa11/src/jar/resolver-store/java/org/mulgara/store/statement/xa11/XA11StatementStoreImplUnitTest.java
===================================================================
--- branches/xa11/src/jar/resolver-store/java/org/mulgara/store/statement/xa11/XA11StatementStoreImplUnitTest.java (rev 0)
+++ branches/xa11/src/jar/resolver-store/java/org/mulgara/store/statement/xa11/XA11StatementStoreImplUnitTest.java 2008-12-02 20:08:36 UTC (rev 1389)
@@ -0,0 +1,149 @@
+/*
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (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.mozilla.org/MPL/
+ *
+ * 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.
+ *
+ * The Original Code is the Kowari Metadata Store.
+ *
+ * The Initial Developer of the Original Code is Plugged In Software Pty
+ * Ltd (http://www.pisoftware.com, mailto:info at pisoftware.com). Portions
+ * created by Plugged In Software Pty Ltd are Copyright (C) 2001,2002
+ * Plugged In Software Pty Ltd. All Rights Reserved.
+ *
+ * Contributor(s): N/A.
+ *
+ * [NOTE: The text of this Exhibit A may differ slightly from the text
+ * of the notices in the Source Code files of the Original Code. You
+ * should use the text of this Exhibit A rather than the text found in the
+ * Original Code Source Code for Your Modifications.]
+ *
+ */
+package org.mulgara.store.statement.xa11;
+
+// Java 2 standard packages
+import java.io.*;
+
+// JUnit
+import junit.framework.*;
+
+// log4j
+import org.apache.log4j.*;
+
+// locally written packages
+import org.mulgara.store.statement.*;
+import org.mulgara.util.*;
+
+
+/**
+ * Test case for {@link XA11StatementStoreImpl}.
+ *
+ * @author <a href="http://staff.pisoftware.com/david">David Makepeace</a>
+ * @author <a href="http://staff.pisoftware.com/pag">Paul Gearon</a>
+ * @copyright ©2001-2004 <a href="http://www.pisoftware.com/">Plugged In Software Pty Ltd</a>
+ * @licence <a href="{@docRoot}/../../LICENCE">Mozilla Public License v1.1</a>
+ */
+public class XA11StatementStoreImplUnitTest extends StatementStoreAbstractUnitTest {
+
+ /** Logger. */
+ @SuppressWarnings("unused")
+ private final static Logger logger = Logger.getLogger(XA11StatementStoreImplUnitTest.class);
+
+ /**
+ * start of filenames to build the graph with.
+ */
+ private final static String DBFILENAME = "graphtest";
+
+ /**
+ * Description of the Field
+ */
+ private XA11StatementStoreImpl xaStore;
+
+
+ /**
+ * Named constructor.
+ *
+ * @param name The name of the test.
+ */
+ public XA11StatementStoreImplUnitTest(String name) {
+ super(name);
+ }
+
+
+ /**
+ * Hook for test runner to obtain a test suite from.
+ *
+ * @return The test suite to run.
+ */
+ public static Test suite() {
+ return new TestSuite(XA11StatementStoreImplUnitTest.class);
+ //TestSuite suite = new TestSuite();
+ //suite.addTest(new GraphImplTest("testRemoveTriples"));
+ //suite.addTest(new GraphImplTest("testFindTriplesByNode0"));
+ //suite.addTest(new GraphImplTest("testFindTriplesByNode1"));
+ //suite.addTest(new GraphImplTest("testFindTriplesByNode2"));
+ //suite.addTest(new GraphImplTest("testFindTriplesByNode01"));
+ //suite.addTest(new GraphImplTest("testFindTriplesByNode02"));
+ //suite.addTest(new GraphImplTest("testFindTriplesByNode12"));
+ //return suite;
+ }
+
+
+ /**
+ * Default test runner.
+ *
+ * @param args The command line arguments
+ */
+ public static void main(String[] args) {
+
+ junit.textui.TestRunner.run(suite());
+ }
+
+
+ /**
+ * A method to call for each graph before running tests on it.
+ *
+ * @throws Exception EXCEPTION TO DO
+ */
+ protected void setUp() throws Exception {
+ boolean exceptionOccurred = true;
+ try {
+ // create the graph object, using a new file
+ store = new XA11StatementStoreImpl(
+ TempDir.getTempDir().getPath() + File.separatorChar + DBFILENAME
+ );
+ xaStore = (XA11StatementStoreImpl) store;
+ xaStore.clear();
+ super.setUp();
+ exceptionOccurred = false;
+ }
+ finally {
+ if (exceptionOccurred) {
+ tearDown();
+ }
+ }
+ }
+
+
+ /**
+ * The teardown method for JUnit
+ *
+ * @throws Exception EXCEPTION TO DO
+ */
+ protected void tearDown() throws Exception {
+ if (xaStore != null) {
+ xaStore.unmap();
+ if (System.getProperty("os.name").startsWith("Win")) {
+ // Need this for Windows or truncate() always fails for mapped files.
+ System.gc();
+ System.runFinalization();
+ }
+ }
+ super.tearDown();
+ }
+}
More information about the Mulgara-svn
mailing list