You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@marmotta.apache.org by wi...@apache.org on 2014/06/13 10:57:35 UTC

[015/100] [abbrv] [partial] Reverting the erroneous merge by Sebastian according to the instructions in INFRA-6876

http://git-wip-us.apache.org/repos/asf/marmotta/blob/582abb5b/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/RepositoryTest.java
----------------------------------------------------------------------
diff --git a/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/RepositoryTest.java b/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/RepositoryTest.java
index 67ec618..5627f1b 100644
--- a/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/RepositoryTest.java
+++ b/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/RepositoryTest.java
@@ -1,13 +1,12 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *      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,
@@ -17,32 +16,41 @@
  */
 package org.apache.marmotta.kiwi.test;
 
+import static org.hamcrest.CoreMatchers.hasItems;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.Matchers.hasItem;
+import static org.hamcrest.Matchers.hasProperty;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.lessThan;
+import static org.junit.Assume.assumeThat;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.sql.SQLException;
+import java.util.ConcurrentModificationException;
+import java.util.List;
+
+import org.apache.commons.lang3.RandomStringUtils;
 import org.apache.marmotta.commons.sesame.repository.ResourceUtils;
-import com.google.common.base.Function;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
-import org.apache.commons.lang.RandomStringUtils;
-import org.apache.marmotta.kiwi.persistence.KiWiDialect;
-import org.apache.marmotta.kiwi.persistence.h2.H2Dialect;
-import org.apache.marmotta.kiwi.persistence.mysql.MySQLDialect;
-import org.apache.marmotta.kiwi.persistence.pgsql.PostgreSQLDialect;
+import org.apache.marmotta.kiwi.config.KiWiConfiguration;
 import org.apache.marmotta.kiwi.sail.KiWiStore;
-import org.apache.marmotta.kiwi.test.helper.DBConnectionChecker;
+import org.apache.marmotta.kiwi.test.junit.KiWiDatabaseRunner;
 import org.hamcrest.CoreMatchers;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
-import org.junit.rules.TestWatcher;
-import org.junit.runner.Description;
 import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
 import org.openrdf.model.Literal;
 import org.openrdf.model.Namespace;
 import org.openrdf.model.Resource;
 import org.openrdf.model.Statement;
 import org.openrdf.model.URI;
+import org.openrdf.query.MalformedQueryException;
+import org.openrdf.query.QueryLanguage;
+import org.openrdf.query.Update;
+import org.openrdf.query.UpdateExecutionException;
 import org.openrdf.repository.Repository;
 import org.openrdf.repository.RepositoryConnection;
 import org.openrdf.repository.RepositoryException;
@@ -52,110 +60,35 @@ import org.openrdf.rio.RDFParseException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.io.IOException;
-import java.io.InputStream;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.List;
-
-import static org.hamcrest.CoreMatchers.hasItems;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.CoreMatchers.notNullValue;
-import static org.hamcrest.Matchers.hasItem;
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assume.assumeThat;
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
 
 /**
- * Test the Sesame repository functionality backed by the KiWi triple store. It will try running over all
- * available databases. Except for in-memory databases like H2 or Derby, database URLs must be passed as
- * system property, or otherwise the test is skipped for this database. Available system properties:
- * <ul>
- *     <li>PostgreSQL:
- *     <ul>
- *         <li>postgresql.url, e.g. jdbc:postgresql://localhost:5433/kiwitest?prepareThreshold=3</li>
- *         <li>postgresql.user (default: lmf)</li>
- *         <li>postgresql.pass (default: lmf)</li>
- *     </ul>
- *     </li>
- *     <li>MySQL:
- *     <ul>
- *         <li>mysql.url, e.g. jdbc:mysql://localhost:3306/kiwitest?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull</li>
- *         <li>mysql.user (default: lmf)</li>
- *         <li>mysql.pass (default: lmf)</li>
- *     </ul>
- *     </li>
- *     <li>H2:
- *     <ul>
- *         <li>h2.url, e.g. jdbc:h2:mem;MVCC=true;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=10</li>
- *         <li>h2.user (default: lmf)</li>
- *         <li>h2.pass (default: lmf)</li>
- *     </ul>
- *     </li>
- * </ul>
- * <p/>
- * Author: Sebastian Schaffert
+ * Test the Sesame repository functionality backed by the KiWi triple store. 
+ *
+ * @author Sebastian Schaffert (sschaffert@apache.org)
  */
-@RunWith(Parameterized.class)
+@RunWith(KiWiDatabaseRunner.class)
 public class RepositoryTest {
 
     private static Logger log = LoggerFactory.getLogger(RepositoryTest.class);
 
-    /**
-     * Return database configurations if the appropriate parameters have been set.
-     *
-     * @return an array (database name, url, user, password)
-     */
-    @Parameterized.Parameters(name="Database Test {index}: {0} at {1}")
-    public static Iterable<Object[]> databases() {
-        String[] databases = {"H2", "PostgreSQL", "MySQL"};
-
-        List<Object[]> result = new ArrayList<Object[]>(databases.length);
-        for(String database : databases) {
-            if(System.getProperty(database.toLowerCase()+".url") != null) {
-                result.add(new Object[] {
-                        database,
-                        System.getProperty(database.toLowerCase()+".url"),
-                        System.getProperty(database.toLowerCase()+".user","lmf"),
-                        System.getProperty(database.toLowerCase()+".pass","lmf")
-                });
-            }
-        }
-        return result;
-    }
-
-
-    private KiWiDialect dialect;
-
-    private String jdbcUrl;
-
-    private String jdbcUser;
-
-    private String jdbcPass;
-
     private Repository repository;
 
-	private KiWiStore store;
+    private KiWiStore store;
 
-    public RepositoryTest(String database, String jdbcUrl, String jdbcUser, String jdbcPass) {
-        this.jdbcPass = jdbcPass;
-        this.jdbcUrl = jdbcUrl;
-        this.jdbcUser = jdbcUser;
+    private final KiWiConfiguration kiwiConfiguration;
+
+    public RepositoryTest(KiWiConfiguration kiwiConfiguration) {
+        this.kiwiConfiguration = kiwiConfiguration;
 
-        if("H2".equals(database)) {
-            this.dialect = new H2Dialect();
-        } else if("MySQL".equals(database)) {
-            this.dialect = new MySQLDialect();
-        } else if("PostgreSQL".equals(database)) {
-            this.dialect = new PostgreSQLDialect();
-        }
-        
-        DBConnectionChecker.checkDatabaseAvailability(jdbcUrl, jdbcUser, jdbcPass, this.dialect);
     }
 
-	@Before
+    @Before
     public void initDatabase() throws RepositoryException {
-        store = new KiWiStore("test",jdbcUrl,jdbcUser,jdbcPass,dialect, "http://localhost/context/default", "http://localhost/context/inferred" );
-		repository = new SailRepository(store);
+        store = new KiWiStore(kiwiConfiguration);
+        repository = new SailRepository(store);
         repository.initialize();
     }
 
@@ -166,19 +99,6 @@ public class RepositoryTest {
         repository.shutDown();
     }
 
-    final Logger logger =
-            LoggerFactory.getLogger(RepositoryTest.class);
-
-    @Rule
-    public TestWatcher watchman = new TestWatcher() {
-        /**
-         * Invoked when a test is about to start
-         */
-        @Override
-        protected void starting(Description description) {
-            logger.info("{} being run...", description.getMethodName());
-        }
-    };
 
     /**
      * Test importing data; the test will load a small sample RDF file and check whether the expected resources are
@@ -225,7 +145,8 @@ public class RepositoryTest {
         );
 
         // test if the result has the expected size
-        Assert.assertEquals(4, resources.size());
+        //FIXME: this test is no longer valid, because resource existance is not bound to use as subject
+        //Assert.assertEquals(4, resources.size());
 
         // test if the result contains all resources that have been used as subject
         Assert.assertThat(resources, hasItems(
@@ -329,7 +250,9 @@ public class RepositoryTest {
             );
 
             // test if the result has the expected size
-            Assert.assertEquals(4, resources.size());
+            // FIXME: MARMOTTA-39 (no xsd:string, so one resource is "missing")
+            // Assert.assertEquals(31, resources.size());
+            Assert.assertEquals(30, resources.size());
 
             // test if the result contains all resources that have been used as subject
             Assert.assertThat(resources, hasItems(
@@ -355,7 +278,7 @@ public class RepositoryTest {
             // the resource hans_meier should not be contained in the list of resources
             List<String> resources2 = ImmutableList.copyOf(
                     Iterables.transform(
-                            ResourceUtils.listResources(connection),
+                            ResourceUtils.listSubjects(connection),
                             new Function<Resource, String>() {
                                 @Override
                                 public String apply(Resource input) {
@@ -366,7 +289,7 @@ public class RepositoryTest {
             );
 
             // test if the result has the expected size
-            Assert.assertEquals(3, resources2.size());
+            //Assert.assertEquals(3, resources2.size());
 
             // test if the result does not contain the removed resource
             Assert.assertThat(resources2, not(hasItem(
@@ -545,7 +468,329 @@ public class RepositoryTest {
             connection4.close();
         }
 
+        // test repeated adding/removing inside the same transaction
+        Literal object5 = repository.getValueFactory().createLiteral(RandomStringUtils.randomAlphanumeric(8));
+        RepositoryConnection connection5 = repository.getConnection();
+        try {
+            Assert.assertFalse(connection5.hasStatement(subject, predicate, object5, true));
+
+            connection5.add(subject,predicate,object5);
+            Assert.assertTrue(connection5.hasStatement(subject,predicate,object5,true));
+
+            connection5.remove(subject,predicate,object5);
+            Assert.assertFalse(connection5.hasStatement(subject,predicate,object5,true));
+
+            connection5.add(subject,predicate,object5);
+            Assert.assertTrue(connection5.hasStatement(subject,predicate,object5,true));
+            connection5.commit();
+        } finally {
+            connection5.close();
+        }
+
+        RepositoryConnection connection6 = repository.getConnection();
+        try {
+            Assert.assertTrue(connection6.hasStatement(subject, predicate, object5, true));
+
+            connection6.commit();
+        } finally {
+            connection6.close();
+        }
+
+    }
+
+    @Test
+    public void testRepeatedAddRemoveCrossTransaction() throws RepositoryException {
+        String value = RandomStringUtils.randomAlphanumeric(8);
+
+        URI subject = repository.getValueFactory().createURI("http://localhost/resource/" + RandomStringUtils.randomAlphanumeric(8));
+        URI predicate = repository.getValueFactory().createURI("http://localhost/resource/" + RandomStringUtils.randomAlphanumeric(8));
+        Literal object1 = repository.getValueFactory().createLiteral(value);
+
+        RepositoryConnection connection1 = repository.getConnection();
+        try {
+            connection1.add(subject,predicate,object1);
+            connection1.commit();
+
+            Assert.assertTrue(connection1.hasStatement(subject,predicate,object1,true));
+
+            connection1.commit();
+        } finally {
+            connection1.close();
+        }
+
+        RepositoryConnection connection2 = repository.getConnection();
+        try {
+            connection2.remove(subject, predicate, object1);
+            Assert.assertFalse(connection2.hasStatement(subject, predicate, object1, true));
+
+            connection2.add(subject,predicate,object1);
+            Assert.assertTrue(connection2.hasStatement(subject, predicate, object1, true));
+
+            connection2.commit();
+        } finally {
+            connection2.close();
+        }
+
+        RepositoryConnection connection3 = repository.getConnection();
+        try {
+            Assert.assertTrue(connection3.hasStatement(subject, predicate, object1, true));
+            connection3.commit();
+        } finally {
+            connection3.close();
+        }
+    }
+
+    @Test
+    public void testRepeatedAddRemoveSPARQL() throws RepositoryException, MalformedQueryException, UpdateExecutionException {
+        String value = RandomStringUtils.randomAlphanumeric(8);
+
+        URI subject = repository.getValueFactory().createURI("http://localhost/resource/" + RandomStringUtils.randomAlphanumeric(8));
+        URI predicate = repository.getValueFactory().createURI("http://localhost/resource/" + RandomStringUtils.randomAlphanumeric(8));
+        Literal object1 = repository.getValueFactory().createLiteral(value);
+
+        RepositoryConnection connection1 = repository.getConnection();
+        try {
+            connection1.add(subject,predicate,object1);
+            connection1.commit();
+
+            Assert.assertTrue(connection1.hasStatement(subject,predicate,object1,true));
+
+            connection1.commit();
+        } finally {
+            connection1.close();
+        }
+
+        RepositoryConnection connection2 = repository.getConnection();
+        try {
+            String query = String.format("DELETE { <%s> <%s> ?v } INSERT { <%s> <%s> ?v . } WHERE { <%s> <%s> ?v }", subject.stringValue(), predicate.stringValue(), subject.stringValue(), predicate.stringValue(), subject.stringValue(), predicate.stringValue());
+
+            Update u = connection2.prepareUpdate(QueryLanguage.SPARQL, query);
+            u.execute();
 
+            connection2.commit();
+        } finally {
+            connection2.close();
+        }
+
+        RepositoryConnection connection3 = repository.getConnection();
+        try {
+            Assert.assertTrue(connection3.hasStatement(subject, predicate, object1, true));
+            connection3.commit();
+        } finally {
+            connection3.close();
+        }
     }
 
+
+    /**
+     * Test the rollback functionality of the triple store by adding a triple, rolling back, adding the triple again.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void testRollback() throws Exception {
+        String value = RandomStringUtils.randomAlphanumeric(8);
+
+        URI subject = repository.getValueFactory().createURI("http://localhost/resource/" + RandomStringUtils.randomAlphanumeric(8));
+        URI predicate = repository.getValueFactory().createURI("http://localhost/resource/" + RandomStringUtils.randomAlphanumeric(8));
+        Literal object = repository.getValueFactory().createLiteral(value);
+
+        RepositoryConnection connection1 = repository.getConnection();
+        try {
+            connection1.begin();
+            connection1.add(subject,predicate,object);
+            connection1.rollback();
+
+        } finally {
+            connection1.close();
+        }
+
+        RepositoryConnection connection2 = repository.getConnection();
+        try {
+            connection2.begin();
+            Assert.assertFalse(connection2.hasStatement(subject,predicate,object,true));
+
+            connection2.add(subject,predicate,object);
+            connection2.commit();
+
+            Assert.assertTrue(connection2.hasStatement(subject,predicate,object,true));
+
+            connection2.commit();
+        } finally {
+            connection2.close();
+        }
+
+    }
+
+    /**
+     * This test is for a strange bug that happens when running SPARQL updates that delete and reinsert a triple in
+     * the same transaction. See https://issues.apache.org/jira/browse/MARMOTTA-283
+     */
+    @Test
+    public void testMARMOTTA283() throws RepositoryException, RDFParseException, IOException, MalformedQueryException, UpdateExecutionException {
+
+        InputStream rdfXML = this.getClass().getResourceAsStream("demo-data.foaf");
+        assumeThat("Could not load test-data: demo-data.foaf", rdfXML, notNullValue(InputStream.class));
+
+        RepositoryConnection connectionRDF = repository.getConnection();
+        try {
+            connectionRDF.add(rdfXML, "http://localhost/foaf/", RDFFormat.RDFXML);
+            connectionRDF.commit();
+        } finally {
+            connectionRDF.close();
+        }
+
+
+        String update = "DELETE { ?s ?p ?o } INSERT { <http://localhost:8080/LMF/resource/hans_meier> <http://xmlns.com/foaf/0.1/name> \"Hans Meier\" . <http://localhost:8080/LMF/resource/hans_meier> <http://xmlns.com/foaf/0.1/based_near> <http://dbpedia.org/resource/Traunstein> . <http://localhost:8080/LMF/resource/hans_meier> <http://xmlns.com/foaf/0.1/interest> <http://rdf.freebase.com/ns/en.linux> } WHERE { ?s ?p ?o . FILTER ( ?s = <http://localhost:8080/LMF/resource/hans_meier> ) }";
+
+        RepositoryConnection connectionUpdate = repository.getConnection();
+        try {
+            Update u = connectionUpdate.prepareUpdate(QueryLanguage.SPARQL, update);
+            u.execute();
+            connectionUpdate.commit();
+        } finally {
+            connectionUpdate.close();
+        }
+
+        // now there should be two triples
+        RepositoryConnection connectionVerify = repository.getConnection();
+        try {
+            URI hans_meier = repository.getValueFactory().createURI("http://localhost:8080/LMF/resource/hans_meier");
+            URI foaf_name  = repository.getValueFactory().createURI("http://xmlns.com/foaf/0.1/name");
+            URI foaf_based_near = repository.getValueFactory().createURI("http://xmlns.com/foaf/0.1/based_near");
+            URI foaf_interest = repository.getValueFactory().createURI("http://xmlns.com/foaf/0.1/interest");
+            URI freebase_linux = repository.getValueFactory().createURI("http://rdf.freebase.com/ns/en.linux");
+            URI traunstein = repository.getValueFactory().createURI("http://dbpedia.org/resource/Traunstein");
+
+            Assert.assertTrue(connectionVerify.hasStatement(hans_meier,foaf_name,null, true));
+            Assert.assertTrue(connectionVerify.hasStatement(hans_meier,foaf_based_near,traunstein, true));
+            Assert.assertTrue(connectionVerify.hasStatement(hans_meier,foaf_interest,freebase_linux, true));
+
+            connectionVerify.commit();
+        } finally {
+            connectionVerify.close();
+        }
+    }
+
+    /**
+     * This test is for a strange bug that happens when running SPARQL updates that delete and reinsert a triple in
+     * the same transaction. It is similar to #testMARMOTTA283, but simulates the issue in more detail.
+     * See https://issues.apache.org/jira/browse/MARMOTTA-283
+     */
+    @Test
+    public void testMARMOTTA283_2() throws RepositoryException, RDFParseException, IOException, MalformedQueryException, UpdateExecutionException {
+
+        //insert quadruples
+        String insert =
+                "WITH <http://resource.org/video>" +
+                "INSERT {" +
+                "   <http://resource.org/video> <http://ontology.org#hasFragment> <http://resource.org/fragment1>." +
+                "   <http://resource.org/annotation1> <http://ontology.org#hasTarget> <http://resource.org/fragment1>." +
+                "   <http://resource.org/annotation1> <http://ontology.org#hasBody> <http://resource.org/subject1>." +
+                "   <http://resource.org/fragment1> <http://ontology.org#shows> <http://resource.org/subject1>." +
+                "} WHERE {}";
+
+        RepositoryConnection connectionInsert = repository.getConnection();
+        try {
+            Update u = connectionInsert.prepareUpdate(QueryLanguage.SPARQL, insert);
+            u.execute();
+            connectionInsert.commit();
+        } finally {
+            connectionInsert.close();
+        }
+
+        //update quadruples
+        String update =
+                "WITH <http://resource.org/video>" +
+                "DELETE { " +
+                "   ?annotation ?p ?v." +
+                "   ?fragment ?r ?s." +
+                "   <http://resource.org/video> <http://ontology.org#hasFragment> ?fragment." +
+                "} INSERT {" +
+                "   <http://resource.org/video> <http://ontology.org#hasFragment> <http://resource.org/fragment1>." +
+                "   <http://resource.org/annotation1> <http://ontology.org#hasTarget> <http://resource.org/fragment1>." +
+                "   <http://resource.org/annotation1> <http://ontology.org#hasBody> <http://resource.org/subject1>." +
+                "   <http://resource.org/fragment1> <http://ontology.org#shows> <http://resource.org/subject1>." +
+                "} WHERE {" +
+                "   ?annotation <http://ontology.org#hasTarget> ?fragment." +
+                "   ?annotation ?p ?v." +
+                "   OPTIONAL {" +
+                "       ?fragment ?r ?s" +
+                "   }" +
+                "   FILTER (?fragment = <http://resource.org/fragment1>)" +
+                "} ";
+
+        RepositoryConnection connectionUpdate = repository.getConnection();
+        try {
+            Update u = connectionUpdate.prepareUpdate(QueryLanguage.SPARQL, update);
+            u.execute();
+            connectionUpdate.commit();
+        } finally {
+            connectionUpdate.close();
+        }
+
+        //check quadruples
+        RepositoryConnection connectionVerify = repository.getConnection();
+        try {
+            URI video = repository.getValueFactory().createURI("http://resource.org/video");
+            URI hasFragment  = repository.getValueFactory().createURI("http://ontology.org#hasFragment");
+            URI fragment = repository.getValueFactory().createURI("http://resource.org/fragment1");
+            URI annotation = repository.getValueFactory().createURI("http://resource.org/annotation1");
+            URI hasTarget = repository.getValueFactory().createURI("http://ontology.org#hasTarget");
+            URI hasBody = repository.getValueFactory().createURI("http://ontology.org#hasBody");
+            URI subject = repository.getValueFactory().createURI("http://resource.org/subject1");
+            URI shows = repository.getValueFactory().createURI("http://ontology.org#shows");
+
+            Assert.assertTrue(connectionVerify.hasStatement(video,hasFragment,fragment,true,video));
+            Assert.assertTrue(connectionVerify.hasStatement(annotation,hasTarget,fragment,true,video));
+            Assert.assertTrue(connectionVerify.hasStatement(annotation,hasBody,subject,true,video));
+            Assert.assertTrue(connectionVerify.hasStatement(fragment,shows,subject,true,video));
+
+            connectionVerify.commit();
+        } finally {
+            connectionVerify.close();
+        }
+    }
+
+    /**
+     * Test the concurrent connection problem reported in MARMOTTA-236 for facading:
+     * - get two parallel connections
+     * - add triple in connection 1; should be available in connection 1 and not in connection 2
+     * - add same triple in connection 2; should be available in both, connection 1 and connection 2 or
+     *   fail-fast by throwing a ConcurrentModificationException
+     * @throws Exception
+     */
+    @Test
+    public void testMARMOTTA236() throws Exception {
+        RepositoryConnection con1 = repository.getConnection();
+        RepositoryConnection con2 = repository.getConnection();
+
+        try {
+            URI r1 = repository.getValueFactory().createURI("http://localhost/"+ RandomStringUtils.randomAlphanumeric(8));
+            URI r2 = repository.getValueFactory().createURI("http://localhost/"+ RandomStringUtils.randomAlphanumeric(8));
+            URI r3 = repository.getValueFactory().createURI("http://localhost/"+ RandomStringUtils.randomAlphanumeric(8));
+
+            con1.begin();
+            con1.add(r1,r2,r3);
+
+            Assert.assertTrue(con1.hasStatement(r1,r2,r3,true));
+
+            con2.begin();
+            Assert.assertFalse(con2.hasStatement(r1,r2,r3,true));
+
+            con2.add(r1,r2,r3);
+
+            Assert.assertTrue(con2.hasStatement(r1,r2,r3,true));
+
+            con2.rollback();
+            con1.commit();
+        } catch (ConcurrentModificationException ex) {
+
+        } finally {
+            con1.close();
+            con2.close();
+        }
+
+
+    }
 }

http://git-wip-us.apache.org/repos/asf/marmotta/blob/582abb5b/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/helper/DBConnectionChecker.java
----------------------------------------------------------------------
diff --git a/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/helper/DBConnectionChecker.java b/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/helper/DBConnectionChecker.java
index 6b429f4..bce19b6 100644
--- a/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/helper/DBConnectionChecker.java
+++ b/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/helper/DBConnectionChecker.java
@@ -1,13 +1,12 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you 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
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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
+ *      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,
@@ -17,33 +16,84 @@
  */
 package org.apache.marmotta.kiwi.test.helper;
 
-import org.apache.marmotta.kiwi.persistence.KiWiDialect;
-import org.junit.Assume;
-
 import java.sql.Connection;
 import java.sql.DriverManager;
 import java.sql.SQLException;
 
+import org.apache.marmotta.kiwi.config.KiWiConfiguration;
+import org.apache.marmotta.kiwi.persistence.KiWiDialect;
+import org.junit.Assume;
+import org.junit.internal.AssumptionViolatedException;
+
 public class DBConnectionChecker {
 
-	private DBConnectionChecker() {
-		// static only
-	}
-	
-	public static void checkDatabaseAvailability(String jdbcUrl, String jdbcUser,
-			String jdbcPass, KiWiDialect dialect) {
-		try {
-	    	Class.forName(dialect.getDriverClass());
-			Connection conn = DriverManager.getConnection(jdbcUrl, jdbcUser, jdbcPass);
+    private DBConnectionChecker() {
+        // static only
+    }
+
+    /**
+     * Check availability of the Database.
+     * @param jdbcUrl - the jdbcURL
+     * @param jdbcUser - the user
+     * @param jdbcPass - the password
+     * @param dialect - the {@link KiWiDialect}
+     * @throws AssumptionViolatedException if the database is not available.
+     */
+    public static void checkDatabaseAvailability(String jdbcUrl, String jdbcUser,
+            String jdbcPass, KiWiDialect dialect) throws AssumptionViolatedException {
+        try {
+            Class.forName(dialect.getDriverClass());
+            Connection conn = DriverManager.getConnection(jdbcUrl, jdbcUser, jdbcPass);
             conn.setAutoCommit(false);
-			Assume.assumeTrue("Database not available", conn.isValid(1000));
-			conn.commit();
-			conn.close();
-		} catch (SQLException e) {
-			Assume.assumeNoException("Database not available", e);
-		} catch (ClassNotFoundException e) {
-			Assume.assumeNoException("Missing DB driver", e);
-		}
-	}
+            Assume.assumeTrue("Database not available", conn.isValid(1000));
+            conn.commit();
+            conn.close();
+        } catch (SQLException e) {
+            Assume.assumeNoException("Database not available", e);
+        } catch (ClassNotFoundException e) {
+            Assume.assumeNoException("Missing DB driver", e);
+        }
+    }
+
+    /**
+     * Check availability of the Database.
+     * @param config the {@link KiWiConfiguration} to test
+     * @throws AssumptionViolatedException if the database is not available.
+     */
+    public static void checkDatabaseAvailability(KiWiConfiguration config) throws AssumptionViolatedException {
+        checkDatabaseAvailability(config.getJdbcUrl(), config.getDbUser(), config.getDbPassword(), config.getDialect());
+    }
+
+    /**
+     * Check the availability of the Database.
+     * @param jdbcUrl - the jdbcURL
+     * @param jdbcUser - the user
+     * @param jdbcPass - the password
+     * @param dialect - the {@link KiWiDialect}
+     * @return {@code true} if the database is available, {@code false} if not
+     */
+    public static boolean isDatabaseAvailable(String jdbcUrl, String jdbcUser,
+            String jdbcPass, KiWiDialect dialect) {
+        try {
+            checkDatabaseAvailability(jdbcUrl, jdbcUser, jdbcPass, dialect);
+            return true;
+        } catch (AssumptionViolatedException ave) {
+            return false;
+        }
+    }
+
+    /**
+     * Check availability of the Database.
+     * @param config the {@link KiWiConfiguration} to test
+     * @return {@code true} if the database is available, {@code false} if not
+     */
+    public static boolean isDatabaseAvailable(KiWiConfiguration config) {
+        try {
+            checkDatabaseAvailability(config);
+            return true;
+        } catch (AssumptionViolatedException ave) {
+            return false;
+        }
+    }
 
 }

http://git-wip-us.apache.org/repos/asf/marmotta/blob/582abb5b/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/junit/DatabaseRunnerTest1.java
----------------------------------------------------------------------
diff --git a/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/junit/DatabaseRunnerTest1.java b/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/junit/DatabaseRunnerTest1.java
new file mode 100644
index 0000000..cee043e
--- /dev/null
+++ b/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/junit/DatabaseRunnerTest1.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+package org.apache.marmotta.kiwi.test.junit;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+
+import org.apache.marmotta.kiwi.config.KiWiConfiguration;
+import org.apache.marmotta.kiwi.persistence.h2.H2Dialect;
+import org.apache.marmotta.kiwi.persistence.mysql.MySQLDialect;
+import org.apache.marmotta.kiwi.persistence.pgsql.PostgreSQLDialect;
+import org.apache.marmotta.kiwi.test.junit.KiWiDatabaseRunner.ForDialects;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(KiWiDatabaseRunner.class)
+public class DatabaseRunnerTest1 {
+
+    private final KiWiConfiguration dbConfig;
+
+    public DatabaseRunnerTest1(KiWiConfiguration dbConfig) {
+        this.dbConfig = dbConfig;
+    }
+    
+    @Test
+    public void testDatabase() {
+        assertNotNull(dbConfig);
+    }
+
+    @Test
+    @ForDialects(H2Dialect.class)
+    public void testOnlyH2() {
+        assertThat(dbConfig.getDialect(), instanceOf(H2Dialect.class));
+    }
+    
+    @Test
+    @ForDialects(PostgreSQLDialect.class)
+    public void testOnlyPostgreSQL() {
+        assertThat(dbConfig.getDialect(), instanceOf(PostgreSQLDialect.class));
+    }
+
+    @Test
+    @ForDialects(MySQLDialect.class)
+    public void testOnlyMySQL() {
+        assertThat(dbConfig.getDialect(), instanceOf(MySQLDialect.class));
+    }
+
+    @Test
+    @ForDialects({PostgreSQLDialect.class, MySQLDialect.class})
+    public void testNotH2() {
+        assertThat(dbConfig.getDialect(), not(instanceOf(H2Dialect.class)));
+    }
+    
+    @Test
+    @ForDialects({H2Dialect.class, MySQLDialect.class})
+    public void testNotPostgreSQL() {
+        assertThat(dbConfig.getDialect(), not(instanceOf(PostgreSQLDialect.class)));
+    }
+
+    @Test
+    @ForDialects({PostgreSQLDialect.class, H2Dialect.class})
+    public void testNotMySQL() {
+        assertThat(dbConfig.getDialect(), not(instanceOf(MySQLDialect.class)));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/marmotta/blob/582abb5b/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/junit/DatabaseRunnerTest2.java
----------------------------------------------------------------------
diff --git a/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/junit/DatabaseRunnerTest2.java b/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/junit/DatabaseRunnerTest2.java
new file mode 100644
index 0000000..d903698
--- /dev/null
+++ b/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/junit/DatabaseRunnerTest2.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+package org.apache.marmotta.kiwi.test.junit;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.not;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+
+import org.apache.marmotta.kiwi.config.KiWiConfiguration;
+import org.apache.marmotta.kiwi.persistence.h2.H2Dialect;
+import org.apache.marmotta.kiwi.persistence.mysql.MySQLDialect;
+import org.apache.marmotta.kiwi.persistence.pgsql.PostgreSQLDialect;
+import org.apache.marmotta.kiwi.test.junit.KiWiDatabaseRunner.ForDialects;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(KiWiDatabaseRunner.class)
+public class DatabaseRunnerTest2 {
+
+    @KiWiDatabaseRunner.KiWiConfig
+    public KiWiConfiguration dbConfig;
+    
+    @Test
+    public void testDatabase() {
+        assertNotNull(dbConfig);
+    }
+
+    @Test
+    @ForDialects(H2Dialect.class)
+    public void testOnlyH2() {
+        assertThat(dbConfig.getDialect(), instanceOf(H2Dialect.class));
+    }
+    
+    @Test
+    @ForDialects(PostgreSQLDialect.class)
+    public void testOnlyPostgreSQL() {
+        assertThat(dbConfig.getDialect(), instanceOf(PostgreSQLDialect.class));
+    }
+
+    @Test
+    @ForDialects(MySQLDialect.class)
+    public void testOnlyMySQL() {
+        assertThat(dbConfig.getDialect(), instanceOf(MySQLDialect.class));
+    }
+
+    @Test
+    @ForDialects({PostgreSQLDialect.class, MySQLDialect.class})
+    public void testNotH2() {
+        assertThat(dbConfig.getDialect(), not(instanceOf(H2Dialect.class)));
+    }
+    
+    @Test
+    @ForDialects({H2Dialect.class, MySQLDialect.class})
+    public void testNotPostgreSQL() {
+        assertThat(dbConfig.getDialect(), not(instanceOf(PostgreSQLDialect.class)));
+    }
+
+    @Test
+    @ForDialects({PostgreSQLDialect.class, H2Dialect.class})
+    public void testNotMySQL() {
+        assertThat(dbConfig.getDialect(), not(instanceOf(MySQLDialect.class)));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/marmotta/blob/582abb5b/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/junit/KiWiDatabaseRunner.java
----------------------------------------------------------------------
diff --git a/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/junit/KiWiDatabaseRunner.java b/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/junit/KiWiDatabaseRunner.java
new file mode 100644
index 0000000..170fdba
--- /dev/null
+++ b/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/junit/KiWiDatabaseRunner.java
@@ -0,0 +1,340 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+package org.apache.marmotta.kiwi.test.junit;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.marmotta.kiwi.config.KiWiConfiguration;
+import org.apache.marmotta.kiwi.persistence.KiWiDialect;
+import org.apache.marmotta.kiwi.persistence.h2.H2Dialect;
+import org.apache.marmotta.kiwi.persistence.mysql.MySQLDialect;
+import org.apache.marmotta.kiwi.persistence.pgsql.PostgreSQLDialect;
+import org.apache.marmotta.kiwi.test.helper.DBConnectionChecker;
+import org.junit.internal.AssumptionViolatedException;
+import org.junit.rules.MethodRule;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Runner;
+import org.junit.runner.notification.RunNotifier;
+import org.junit.runners.BlockJUnit4ClassRunner;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Suite;
+import org.junit.runners.model.FrameworkField;
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.InitializationError;
+import org.junit.runners.model.Statement;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Specialized {@link Parameterized} runner for UnitTests that injects the database config for KiWi.
+ * <p>
+ * Except for in-memory databases like H2 or Derby, database URLs must be passed as
+ * system property, or otherwise the test is skipped for this database. Available system properties:
+ * <ul>
+ *     <li>PostgreSQL:
+ *     <ul>
+ *         <li>postgresql.url, e.g. jdbc:postgresql://localhost:5433/kiwitest?prepareThreshold=3</li>
+ *         <li>postgresql.user (default: kiwi)</li>
+ *         <li>postgresql.pass (default: kiwi)</li>
+ *     </ul>
+ *     </li>
+ *     <li>MySQL:
+ *     <ul>
+ *         <li>mysql.url, e.g. jdbc:mysql://localhost:3306/kiwitest?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull</li>
+ *         <li>mysql.user (default: kiwi)</li>
+ *         <li>mysql.pass (default: kiwi)</li>
+ *     </ul>
+ *     </li>
+ *     <li>H2:
+ *     <ul>
+ *         <li>h2.url, e.g. jdbc:h2:mem:kiwitest;MVCC=true;DB_CLOSE_ON_EXIT=TRUE</li>
+ *         <li>h2.user (default: kiwi)</li>
+ *         <li>h2.pass (default: kiwi)</li>
+ *     </ul>
+ *     </li>
+ * </ul>
+ * @author Jakob Frank <ja...@apache.org>
+ *
+ */
+public class KiWiDatabaseRunner extends Suite {
+
+    /**
+     * Assign the {@link KiWiConfiguration} to all member fields with {@link KiWiConfig}-Annotation.
+     * If this annotation is used, the class must only have the default constructor.
+     */
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target(ElementType.FIELD)
+    public static @interface KiWiConfig {     
+    }
+    
+    /**
+     * Only execute with {@link KiWiConfiguration}s for these {@link KiWiDialect}s.
+     */
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ElementType.TYPE, ElementType.METHOD})
+    public static @interface ForDialects {
+        Class<? extends KiWiDialect>[] value();
+    }
+    
+    private final ArrayList<Runner> runners = new ArrayList<Runner>();
+    
+    private final List<Class<? extends KiWiDialect>> forDialects;
+    
+    public KiWiDatabaseRunner(Class<?> klass) throws Throwable {
+        super(klass, Collections.<Runner>emptyList());
+        
+        ForDialects d = klass.getAnnotation(ForDialects.class);
+        if (d != null) {
+            ArrayList<Class<? extends KiWiDialect>> forDialects = new ArrayList<>();
+            for (Class<? extends KiWiDialect> dialect : d.value()) {
+                forDialects.add(dialect);
+            }
+            this.forDialects = Collections.unmodifiableList(forDialects);
+        } else {
+            forDialects = null;
+        }
+        
+        createRunners();
+    }
+
+    private void createRunners() throws InitializationError {
+        List<KiWiConfiguration> configs = new ArrayList<>();
+        createKiWiConfig("H2", new H2Dialect(), configs);
+        createKiWiConfig("PostgreSQL", new PostgreSQLDialect(), configs);
+        createKiWiConfig("MySQL", new MySQLDialect(), configs);
+
+        for (KiWiConfiguration config : configs) {
+            final DatabaseTestClassRunner runner = new DatabaseTestClassRunner(getTestClass().getJavaClass(), config);
+            runners.add(runner);
+        }
+    }
+
+    private void createKiWiConfig(String database, KiWiDialect dialect, List<KiWiConfiguration> configs) {
+        if (forDialects != null && !forDialects.contains(dialect.getClass())) {
+            return;
+        }
+        KiWiConfiguration c = createKiWiConfig(database, dialect);
+        if (c!=null) configs.add(c);
+    }
+    
+    public static KiWiConfiguration createKiWiConfig(String database, KiWiDialect dialect) {
+        final KiWiConfiguration config;
+        if(!(dialect instanceof H2Dialect) && System.getProperty(database.toLowerCase()+".url") != null) {
+            config = new KiWiConfiguration(
+                    database,
+                    System.getProperty(database.toLowerCase()+".url"),
+                    System.getProperty(database.toLowerCase()+".user","kiwi"),
+                    System.getProperty(database.toLowerCase()+".pass","kiwi"),
+                    dialect);
+        } else if (dialect instanceof H2Dialect) {
+            config = new KiWiConfiguration(
+                    "default-H2", 
+                    "jdbc:h2:mem:kiwitest;MVCC=true;DB_CLOSE_ON_EXIT=TRUE;DB_CLOSE_DELAY=-1",
+                    "kiwi", "kiwi", 
+                    dialect);
+        } else {
+            return null;
+        }
+        config.setDefaultContext("http://localhost/context/default");
+        config.setInferredContext("http://localhost/context/inferred");
+        return config;
+    }
+
+    @Override
+    protected List<Runner> getChildren() {
+        return runners;
+    }
+    
+    private class DatabaseTestClassRunner extends BlockJUnit4ClassRunner {
+
+        private final KiWiConfiguration config;
+        
+        private final Logger logger;
+
+        private final CheckDBRule checkDB;
+        private final ExecutionLogger loggerRule;
+
+        public DatabaseTestClassRunner(Class<?> klass, KiWiConfiguration config)
+                throws InitializationError {
+            super(klass);
+            logger = LoggerFactory.getLogger(klass);
+            this.config = config;
+            
+            checkDB = new CheckDBRule(config);
+            loggerRule = new ExecutionLogger();
+        }
+        
+        @Override
+        protected void runChild(FrameworkMethod method, RunNotifier notifier) {
+            final ForDialects forD = method.getAnnotation(ForDialects.class);
+            if (forD != null) {
+                if (ArrayUtils.contains(forD.value(), config.getDialect().getClass())) {
+                    super.runChild(method, notifier);
+                } else {
+                    notifier.fireTestIgnored(describeChild(method));
+                }
+            } else {
+                super.runChild(method, notifier);
+            }
+
+        }
+        
+        @Override
+        protected Object createTest() throws Exception {
+            if (fieldAnnotated()) {
+                Object testInstance = getTestClass().getOnlyConstructor().newInstance();
+                List<FrameworkField> configFields = getFieldsAnnotatedByKiWiConfig();
+                for (FrameworkField field : configFields) {
+                    try {
+                        field.getField().set(testInstance, config);
+                    } catch (IllegalArgumentException iae) {
+                        throw new Exception(getTestClass().getName() + ": Trying to set " + field.getName() + " that has a wrong type.");
+                    }
+                }
+                return testInstance;
+            }
+            return getTestClass().getOnlyConstructor().newInstance(config);
+        }
+        
+        @Override
+        protected List<MethodRule> rules(Object target) {
+            LinkedList<MethodRule> rules = new LinkedList<>();
+            rules.add(loggerRule);
+            rules.addAll(super.rules(target));
+            rules.add(checkDB);
+            return rules;
+        }
+        
+        @Override
+        protected String getName() {
+            return "KiWi-Triplestore - " + config.getName();
+        }
+        
+        @Override
+        protected String testName(FrameworkMethod method) {
+            return method.getName() + "(" + config.getName() + ")";
+        }
+        
+        @Override
+        protected void validateConstructor(List<Throwable> errors) {
+            validateOnlyOneConstructor(errors);
+            if (fieldAnnotated()) {
+                validateZeroArgConstructor(errors);
+            }
+        }
+        
+        @Override
+        protected void validateFields(List<Throwable> errors) {
+            super.validateFields(errors);
+            if (fieldAnnotated()) {
+                List<FrameworkField> configFields = getFieldsAnnotatedByKiWiConfig();
+                for (FrameworkField field : configFields) {
+                    if (!field.getType().isAssignableFrom(KiWiConfiguration.class)) {
+                        errors.add(new Exception(String.format("Invalid type %s for field %s, must be %s", field.getType().getName(), field.getName(), KiWiConfiguration.class.getSimpleName())));
+                    }
+                }
+            }
+        }
+        
+        @Override
+        protected Statement classBlock(RunNotifier notifier) {
+            return childrenInvoker(notifier);
+        }
+
+        @Override
+        protected Annotation[] getRunnerAnnotations() {
+            return new Annotation[0];
+        }
+        
+        private class CheckDBRule implements MethodRule {
+
+            private final AssumptionViolatedException assume;
+
+            public CheckDBRule(KiWiConfiguration dbConfig) {
+                AssumptionViolatedException ex = null;
+                try {
+                    DBConnectionChecker.checkDatabaseAvailability(dbConfig);
+                } catch (AssumptionViolatedException ave) {
+                    ex = ave;
+                }
+                this.assume = ex;
+            }
+            
+            @Override
+            public Statement apply(final Statement base, final FrameworkMethod method,
+                    Object target) {
+                return new Statement() {
+                    @Override
+                    public void evaluate() throws Throwable {
+                        if (assume != null) {
+                            logger.info("{} skipped because database is not available", testName(method));
+                            throw assume;
+                        }
+                        base.evaluate();
+                    }
+                };
+            }
+            
+        }
+        
+        private class ExecutionLogger extends TestWatcher implements MethodRule {
+
+
+            @Override
+            public Statement apply(final Statement base, final FrameworkMethod method,
+                    Object target) {
+                return new Statement() {
+                    @Override
+                    public void evaluate() throws Throwable {
+                        logger.info("{} starting...", testName(method));
+                        try {
+                            base.evaluate();
+                            logger.debug("{} SUCCESS", testName(method));
+                        } catch (AssumptionViolatedException e) {
+                            logger.info("{} Ignored: {}", testName(method), e.getMessage());
+                            throw e;
+                        } catch (Throwable t) {
+                            logger.warn("{} FAILED: {}", testName(method), t.getMessage());
+                            throw t;
+                        }
+                    }
+                };
+            }
+            
+        }
+        
+    }
+    
+    private boolean fieldAnnotated() {
+        return !getFieldsAnnotatedByKiWiConfig().isEmpty();
+    }
+
+    private List<FrameworkField> getFieldsAnnotatedByKiWiConfig() {
+        return getTestClass().getAnnotatedFields(KiWiConfig.class);
+    }
+    
+    
+}

http://git-wip-us.apache.org/repos/asf/marmotta/blob/582abb5b/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/sesame/KiWiLocaleTest.java
----------------------------------------------------------------------
diff --git a/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/sesame/KiWiLocaleTest.java b/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/sesame/KiWiLocaleTest.java
new file mode 100644
index 0000000..7815127
--- /dev/null
+++ b/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/sesame/KiWiLocaleTest.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+package org.apache.marmotta.kiwi.test.sesame;
+
+import org.apache.marmotta.kiwi.config.KiWiConfiguration;
+import org.apache.marmotta.kiwi.sail.KiWiStore;
+import org.apache.marmotta.kiwi.sail.KiWiValueFactory;
+import org.apache.marmotta.kiwi.test.junit.KiWiDatabaseRunner;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.openrdf.model.Literal;
+import org.openrdf.sail.SailConnection;
+import org.openrdf.sail.SailException;
+
+import java.sql.SQLException;
+
+/**
+ * Tests for testing locales against the KiWi Triple store 
+ * (and its implementation of the ValueFactory)
+ * 
+ * @author Sergio Fernández <wi...@apache.org>
+ */
+@RunWith(KiWiDatabaseRunner.class)
+public class KiWiLocaleTest  {
+
+    private final KiWiConfiguration kiwiConfig;
+    private KiWiStore store;
+    private KiWiValueFactory vf;
+    
+    public KiWiLocaleTest(KiWiConfiguration kiwiConfig) {
+        this.kiwiConfig = kiwiConfig;
+    }
+    
+    @Before
+    public void initialize() throws SailException {
+    	store = new KiWiStore(kiwiConfig);
+    	store.initialize();
+    	vf = new KiWiValueFactory(store, "http://example.org");
+    }
+    
+    @After
+    public void shutdown() throws SailException, SQLException {
+        store.getPersistence().dropDatabase();
+    	store.shutDown();
+    	store = null;
+    	vf = null;
+    }
+    
+    /** 
+     * Tests creating BCP47 literals (see MARMOTTA-115 for further details)
+     */
+    @Test
+    public void createBCP47LiteralsTests() {
+    	Literal enLiteral = vf.createLiteral("Hungary", "en");
+    	Assert.assertEquals("Hungary", enLiteral.getLabel());
+    	Assert.assertEquals("en", enLiteral.getLanguage());
+    	Literal warLiteral = vf.createLiteral("Hungary", "war");
+    	Assert.assertEquals("Hungary", warLiteral.getLabel());
+    	Assert.assertEquals("war", warLiteral.getLanguage());
+    }
+
+    /** 
+     * Tests creating invalid BCP47 literals (see MARMOTTA-115 for further details)
+     */
+    @Test
+    public void createBCP47LiteralsInvalidTests() {
+    	Literal invalidLangLiteral = vf.createLiteral("Hungary", "invalid-bcp47-languagetag");
+    	Assert.assertEquals("Hungary", invalidLangLiteral.getLabel());
+    	Assert.assertNull(invalidLangLiteral.getLanguage());
+    }
+
+//    /** 
+//     * Tests adding BCP47 literals (see MARMOTTA-115 for further details)
+//     */
+//    @Test
+//    public void addBCP47LiteralsTests() throws SailException {
+//    	SailConnection conn = store.getConnection();
+//        try {
+//        	conn.begin();
+//            conn.commit();
+//        } finally {
+//            conn.close();
+//        }
+//    	
+//    }
+//    
+//    /** 
+//     * Tests importing BCP47 literals (see MARMOTTA-115 for further details)
+//     */
+//    @Test
+//    public void importBCP47LiteralsTests() throws SailException {
+//    	SailConnection connection = store.getConnection();
+//    	
+//    }
+
+}

http://git-wip-us.apache.org/repos/asf/marmotta/blob/582abb5b/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/sesame/KiWiSailConcurrencyTest.java
----------------------------------------------------------------------
diff --git a/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/sesame/KiWiSailConcurrencyTest.java b/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/sesame/KiWiSailConcurrencyTest.java
new file mode 100644
index 0000000..c5661c7
--- /dev/null
+++ b/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/sesame/KiWiSailConcurrencyTest.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+package org.apache.marmotta.kiwi.test.sesame;
+
+import org.apache.marmotta.kiwi.config.KiWiConfiguration;
+import org.apache.marmotta.kiwi.sail.KiWiStore;
+import org.apache.marmotta.kiwi.test.junit.KiWiDatabaseRunner;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.openrdf.sail.Sail;
+import org.openrdf.sail.SailConcurrencyTest;
+import org.openrdf.sail.SailException;
+import org.openrdf.sail.helpers.SailWrapper;
+
+import java.sql.SQLException;
+
+import static org.junit.Assert.fail;
+
+/**
+ * Run the Sesame {@link SailConcurrencyTest} suite.
+ * @author Jakob Frank <ja...@apache.org>
+ */
+@RunWith(KiWiDatabaseRunner.class)
+public class KiWiSailConcurrencyTest extends SailConcurrencyTest {
+
+    private final KiWiConfiguration kiwiConfig;
+
+    public KiWiSailConcurrencyTest(KiWiConfiguration kiwiConfig) {
+        super(String.format("%s (%S)", KiWiSailConcurrencyTest.class.getSimpleName(), kiwiConfig.getName()));
+        this.kiwiConfig = kiwiConfig;
+    }
+    
+    @Override
+    protected Sail createSail() throws SailException {
+        Sail store = new SailWrapper(new KiWiStore(kiwiConfig)) {
+            @Override
+            public void shutDown() throws SailException {
+                try {
+                    ((KiWiStore)getBaseSail()).getPersistence().dropDatabase();
+                } catch (SQLException e) {
+                    fail("SQL exception while deleting database");
+                }
+
+                super.shutDown();
+            }
+        };
+        return store;
+    }
+
+    @Override
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+    }
+    
+    @Override
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+    
+    @Override
+    @Test
+    public void testGetContextIDs() throws Exception {
+        super.testGetContextIDs();
+    }
+}

http://git-wip-us.apache.org/repos/asf/marmotta/blob/582abb5b/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/sesame/KiWiSailInterruptTest.java
----------------------------------------------------------------------
diff --git a/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/sesame/KiWiSailInterruptTest.java b/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/sesame/KiWiSailInterruptTest.java
new file mode 100644
index 0000000..710f128
--- /dev/null
+++ b/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/sesame/KiWiSailInterruptTest.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+package org.apache.marmotta.kiwi.test.sesame;
+
+import org.apache.marmotta.kiwi.config.KiWiConfiguration;
+import org.apache.marmotta.kiwi.sail.KiWiStore;
+import org.apache.marmotta.kiwi.test.junit.KiWiDatabaseRunner;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.openrdf.sail.Sail;
+import org.openrdf.sail.SailException;
+import org.openrdf.sail.SailInterruptTest;
+
+/**
+ * Run the Sesame {@link SailInterruptTest} suite.
+ * @author Jakob Frank <ja...@apache.org>
+ *
+ */
+@RunWith(KiWiDatabaseRunner.class)
+public class KiWiSailInterruptTest extends SailInterruptTest {
+
+    private final KiWiConfiguration kiwiConfig;
+
+    public KiWiSailInterruptTest(KiWiConfiguration kiwiConfig) {
+        super(String.format("%s (%S)", KiWiSailInterruptTest.class.getSimpleName(), kiwiConfig.getName()));
+        this.kiwiConfig = kiwiConfig;
+        
+    }
+    
+    
+    
+    @Override
+    protected Sail createSail() throws SailException {
+        KiWiStore store = new KiWiStore(kiwiConfig);
+        return store;
+    }
+    
+    @Override
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+    }
+    
+    @Override
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+    
+    @Override
+    @Test
+    public void testQueryInterrupt() throws Exception {
+        super.testQueryInterrupt();
+    }
+}

http://git-wip-us.apache.org/repos/asf/marmotta/blob/582abb5b/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/sesame/KiWiStoreTest.java
----------------------------------------------------------------------
diff --git a/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/sesame/KiWiStoreTest.java b/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/sesame/KiWiStoreTest.java
new file mode 100644
index 0000000..081dae4
--- /dev/null
+++ b/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/sesame/KiWiStoreTest.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+package org.apache.marmotta.kiwi.test.sesame;
+
+import org.apache.marmotta.kiwi.config.KiWiConfiguration;
+import org.apache.marmotta.kiwi.sail.KiWiStore;
+import org.apache.marmotta.kiwi.test.junit.KiWiDatabaseRunner;
+import org.junit.runner.RunWith;
+import org.openrdf.sail.RDFStoreTest;
+import org.openrdf.sail.Sail;
+import org.openrdf.sail.SailException;
+import org.openrdf.sail.helpers.SailWrapper;
+
+import java.sql.SQLException;
+
+import static org.junit.Assert.fail;
+
+/**
+ * Run the Sesame {@link RDFStoreTest} suite.
+ * @author Jakob Frank <ja...@apache.org>
+ */
+@RunWith(KiWiDatabaseRunner.class)
+public class KiWiStoreTest extends RDFStoreTest {
+
+    private final KiWiConfiguration kiwiConfig;
+    
+    public KiWiStoreTest(KiWiConfiguration kiwiConfig) {
+        this.kiwiConfig = kiwiConfig;
+    }
+    
+    @Override
+    protected Sail createSail() throws SailException {
+        Sail store = new SailWrapper(new KiWiStore(kiwiConfig)) {
+            @Override
+            public void shutDown() throws SailException {
+                try {
+                    ((KiWiStore)getBaseSail()).getPersistence().dropDatabase();
+                } catch (SQLException e) {
+                    fail("SQL exception while deleting database");
+                }
+
+                super.shutDown();
+            }
+        };
+        store.initialize();
+        return store;
+    }
+
+
+
+
+}

http://git-wip-us.apache.org/repos/asf/marmotta/blob/582abb5b/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/sesame/repository/KiWiRepositoryConnectionTest.java
----------------------------------------------------------------------
diff --git a/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/sesame/repository/KiWiRepositoryConnectionTest.java b/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/sesame/repository/KiWiRepositoryConnectionTest.java
new file mode 100644
index 0000000..f71f964
--- /dev/null
+++ b/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/sesame/repository/KiWiRepositoryConnectionTest.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+package org.apache.marmotta.kiwi.test.sesame.repository;
+
+import org.apache.marmotta.kiwi.config.KiWiConfiguration;
+import org.apache.marmotta.kiwi.persistence.h2.H2Dialect;
+import org.apache.marmotta.kiwi.sail.KiWiStore;
+import org.apache.marmotta.kiwi.test.junit.KiWiDatabaseRunner;
+import org.junit.runner.RunWith;
+import org.openrdf.repository.Repository;
+import org.openrdf.repository.RepositoryConnectionTest;
+import org.openrdf.repository.sail.SailRepository;
+import org.openrdf.sail.Sail;
+import org.openrdf.sail.SailException;
+import org.openrdf.sail.helpers.SailWrapper;
+
+import java.sql.SQLException;
+
+import static org.junit.Assert.fail;
+
+/**
+ * Run the {@link RepositoryConnectionTest}s.
+ * @author Jakob Frank <ja...@apache.org>
+ *
+ */
+@RunWith(KiWiDatabaseRunner.class)
+public class KiWiRepositoryConnectionTest extends RepositoryConnectionTest {
+
+    private final KiWiConfiguration config;
+
+    public KiWiRepositoryConnectionTest(KiWiConfiguration config) {
+        this.config = config;
+    }
+    
+    /* (non-Javadoc)
+     * @see org.openrdf.repository.RepositoryConnectionTest#createRepository()
+     */
+    @Override
+    protected Repository createRepository() throws Exception {
+        config.setDefaultContext(null);
+        Sail store = new SailWrapper(new KiWiStore(config)) {
+            @Override
+            public void shutDown() throws SailException {
+                try {
+                    ((KiWiStore)getBaseSail()).getPersistence().dropDatabase();
+                } catch (SQLException e) {
+                    fail("SQL exception while deleting database");
+                }
+
+                super.shutDown();
+            }
+        };
+        return new SailRepository(store);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/marmotta/blob/582abb5b/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/sesame/repository/KiWiRepositoryTest.java
----------------------------------------------------------------------
diff --git a/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/sesame/repository/KiWiRepositoryTest.java b/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/sesame/repository/KiWiRepositoryTest.java
new file mode 100644
index 0000000..7291859
--- /dev/null
+++ b/libraries/kiwi/kiwi-triplestore/src/test/java/org/apache/marmotta/kiwi/test/sesame/repository/KiWiRepositoryTest.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+package org.apache.marmotta.kiwi.test.sesame.repository;
+
+import org.apache.marmotta.kiwi.config.KiWiConfiguration;
+import org.apache.marmotta.kiwi.sail.KiWiStore;
+import org.apache.marmotta.kiwi.test.junit.KiWiDatabaseRunner;
+import org.junit.Ignore;
+import org.junit.runner.RunWith;
+import org.openrdf.repository.Repository;
+import org.openrdf.repository.RepositoryTest;
+import org.openrdf.repository.sail.SailRepository;
+import org.openrdf.sail.Sail;
+import org.openrdf.sail.SailException;
+import org.openrdf.sail.helpers.SailWrapper;
+
+import java.sql.SQLException;
+
+import static org.junit.Assert.fail;
+
+/**
+ * Run the {@link RepositoryTest}s.
+ * @author Jakob Frank <ja...@apache.org>
+ *
+ */
+@RunWith(KiWiDatabaseRunner.class)
+public class KiWiRepositoryTest extends RepositoryTest {
+
+    private final KiWiConfiguration config;
+
+    private KiWiStore store;
+
+    public KiWiRepositoryTest(KiWiConfiguration config) {
+        this.config = config;
+    }
+
+    /* (non-Javadoc)
+     * @see org.openrdf.repository.RepositoryTest#createRepository()
+     */
+    @Override
+    protected Repository createRepository() throws Exception {
+        store = new KiWiStore(config);
+        return new SailRepository(store);
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        store.getPersistence().dropDatabase();
+        super.tearDown();
+    }
+}

http://git-wip-us.apache.org/repos/asf/marmotta/blob/582abb5b/libraries/kiwi/kiwi-tripletable/pom.xml
----------------------------------------------------------------------
diff --git a/libraries/kiwi/kiwi-tripletable/pom.xml b/libraries/kiwi/kiwi-tripletable/pom.xml
index 3aa74e9..2c8a49d 100644
--- a/libraries/kiwi/kiwi-tripletable/pom.xml
+++ b/libraries/kiwi/kiwi-tripletable/pom.xml
@@ -21,7 +21,7 @@
     <parent>
         <groupId>org.apache.marmotta</groupId>
         <artifactId>kiwi-parent</artifactId>
-        <version>3.1.0-incubating-SNAPSHOT</version>
+        <version>3.1.0-incubating</version>
     </parent>
 
     <artifactId>kiwi-tripletable</artifactId>
@@ -46,6 +46,42 @@
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.apache.marmotta</groupId>
+            <artifactId>marmotta-commons</artifactId>
+        </dependency>
+
+        <dependency>
+            <artifactId>junit</artifactId>
+            <groupId>junit</groupId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <artifactId>hamcrest-core</artifactId>
+            <groupId>org.hamcrest</groupId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <artifactId>hamcrest-library</artifactId>
+            <groupId>org.hamcrest</groupId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <scope>test</scope>
+        </dependency>
+
 
     </dependencies>
 </project>

http://git-wip-us.apache.org/repos/asf/marmotta/blob/582abb5b/libraries/kiwi/kiwi-tripletable/src/main/java/org/apache/marmotta/kiwi/model/caching/IntArray.java
----------------------------------------------------------------------
diff --git a/libraries/kiwi/kiwi-tripletable/src/main/java/org/apache/marmotta/kiwi/model/caching/IntArray.java b/libraries/kiwi/kiwi-tripletable/src/main/java/org/apache/marmotta/kiwi/model/caching/IntArray.java
index f870611..549bddf 100644
--- a/libraries/kiwi/kiwi-tripletable/src/main/java/org/apache/marmotta/kiwi/model/caching/IntArray.java
+++ b/libraries/kiwi/kiwi-tripletable/src/main/java/org/apache/marmotta/kiwi/model/caching/IntArray.java
@@ -44,12 +44,16 @@ public final class IntArray implements Comparable<IntArray> {
 
     public IntArray(int[] data) {
         this.data = data;
+    }
 
-        Hasher hasher = hashFunction.newHasher();
-        for(int i : data) {
-            hasher.putInt(i);
+    private void ensureHashCode() {
+        if(goodHashCode == null) {
+            Hasher hasher = hashFunction.newHasher();
+            for(int i : data) {
+                hasher.putInt(i);
+            }
+            goodHashCode = hasher.hash();
         }
-        goodHashCode = hasher.hash();
     }
 
     public static final IntArray createSPOCKey(Resource subject, URI property, Value object, Resource context){
@@ -72,7 +76,7 @@ public final class IntArray implements Comparable<IntArray> {
 
     }
 
-    public static final IntArray createSPOCMaxKey(Resource subject, URI property, Value object, URI context){
+    public static final IntArray createSPOCMaxKey(Resource subject, URI property, Value object, Resource context){
 
         // the cache key is generated by appending the bytes of the hashcodes of subject, property, object, context and inferred and
         // storing them as a BigInteger; generating the cache key should thus be very efficient
@@ -112,7 +116,7 @@ public final class IntArray implements Comparable<IntArray> {
 
     }
 
-    public static final IntArray createCSPOMaxKey(Resource subject, URI property, Value object, URI context){
+    public static final IntArray createCSPOMaxKey(Resource subject, URI property, Value object, Resource context){
 
         // the cache key is generated by appending the bytes of the hashcodes of subject, property, object, context and inferred and
         // storing them as a BigInteger; generating the cache key should thus be very efficient
@@ -165,6 +169,7 @@ public final class IntArray implements Comparable<IntArray> {
 
     @Override
     public int hashCode() {
+        ensureHashCode();
         return goodHashCode.hashCode();
     }
 }

http://git-wip-us.apache.org/repos/asf/marmotta/blob/582abb5b/libraries/kiwi/kiwi-tripletable/src/main/java/org/apache/marmotta/kiwi/model/caching/TripleTable.java
----------------------------------------------------------------------
diff --git a/libraries/kiwi/kiwi-tripletable/src/main/java/org/apache/marmotta/kiwi/model/caching/TripleTable.java b/libraries/kiwi/kiwi-tripletable/src/main/java/org/apache/marmotta/kiwi/model/caching/TripleTable.java
index 8b40ca5..d05b644 100644
--- a/libraries/kiwi/kiwi-tripletable/src/main/java/org/apache/marmotta/kiwi/model/caching/TripleTable.java
+++ b/libraries/kiwi/kiwi-tripletable/src/main/java/org/apache/marmotta/kiwi/model/caching/TripleTable.java
@@ -18,21 +18,16 @@
 package org.apache.marmotta.kiwi.model.caching;
 
 import com.google.common.base.Predicate;
-import com.google.common.collect.Sets;
+import com.google.common.collect.Collections2;
+import org.apache.marmotta.commons.collections.EquivalenceHashSet;
+import org.apache.marmotta.commons.sesame.model.StatementCommons;
 import org.openrdf.model.Resource;
 import org.openrdf.model.Statement;
 import org.openrdf.model.URI;
 import org.openrdf.model.Value;
 
 import java.io.Serializable;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.NavigableMap;
-import java.util.Set;
-import java.util.TreeMap;
+import java.util.*;
 
 /**
  * A triple table that allows efficient in-memory operations over large collections of triples. This can be used as
@@ -61,14 +56,14 @@ public class TripleTable<Triple extends Statement> implements Set<Triple>, Seria
     private NavigableMap<IntArray,Triple> indexCSPO;
 
     public TripleTable() {
-        data = new HashSet<Triple>();
+        data = StatementCommons.newQuadrupleSet();
         indexSPOC = new TreeMap<IntArray, Triple>();
         indexCSPO = new TreeMap<IntArray, Triple>();
     }
 
 
     public TripleTable(Collection<Triple> triples) {
-        data = new HashSet<Triple>(triples.size());
+        data = StatementCommons.newQuadrupleSet();
         indexSPOC = new TreeMap<IntArray, Triple>();
         indexCSPO = new TreeMap<IntArray, Triple>();
         addAll(triples);
@@ -83,7 +78,7 @@ public class TripleTable<Triple extends Statement> implements Set<Triple>, Seria
      * @return the number of elements in this set (its cardinality)
      */
     @Override
-    public int size() {
+    public synchronized int size() {
         return data.size();
     }
 
@@ -93,7 +88,7 @@ public class TripleTable<Triple extends Statement> implements Set<Triple>, Seria
      * @return <tt>true</tt> if this set contains no elements
      */
     @Override
-    public boolean isEmpty() {
+    public synchronized boolean isEmpty() {
         return data.isEmpty();
     }
 
@@ -111,7 +106,7 @@ public class TripleTable<Triple extends Statement> implements Set<Triple>, Seria
      *         set does not permit null elements (optional)
      */
     @Override
-    public boolean contains(Object o) {
+    public synchronized boolean contains(Object o) {
         return data.contains(o);
     }
 
@@ -144,7 +139,7 @@ public class TripleTable<Triple extends Statement> implements Set<Triple>, Seria
      * @return an array containing all the elements in this set
      */
     @Override
-    public Object[] toArray() {
+    public synchronized Object[] toArray() {
         return data.toArray();
     }
 
@@ -191,7 +186,7 @@ public class TripleTable<Triple extends Statement> implements Set<Triple>, Seria
      * @throws NullPointerException if the specified array is null
      */
     @Override
-    public <T> T[] toArray(T[] a) {
+    public synchronized <T> T[] toArray(T[] a) {
         return data.toArray(a);
     }
 
@@ -226,7 +221,7 @@ public class TripleTable<Triple extends Statement> implements Set<Triple>, Seria
      *         prevents it from being added to this set
      */
     @Override
-    public boolean add(Triple triple) {
+    public synchronized boolean add(Triple triple) {
         indexSPOC.put(IntArray.createSPOCKey(triple.getSubject(), triple.getPredicate(), triple.getObject(), triple.getContext()),triple);
         indexCSPO.put(IntArray.createCSPOKey(triple.getSubject(), triple.getPredicate(), triple.getObject(), triple.getContext()),triple);
         return data.add(triple);
@@ -252,7 +247,7 @@ public class TripleTable<Triple extends Statement> implements Set<Triple>, Seria
      *         is not supported by this set
      */
     @Override
-    public boolean remove(Object o) {
+    public synchronized boolean remove(Object o) {
         if(o instanceof Statement) {
             Statement triple = (Statement)o;
             indexSPOC.remove(IntArray.createSPOCKey(triple.getSubject(), triple.getPredicate(), triple.getObject(), triple.getContext()));
@@ -278,7 +273,7 @@ public class TripleTable<Triple extends Statement> implements Set<Triple>, Seria
      * @see    #contains(Object)
      */
     @Override
-    public boolean containsAll(Collection<?> c) {
+    public synchronized boolean containsAll(Collection<?> c) {
         return data.containsAll(c);
     }
 
@@ -305,7 +300,7 @@ public class TripleTable<Triple extends Statement> implements Set<Triple>, Seria
      * @see #add(Object)
      */
     @Override
-    public boolean addAll(Collection<? extends Triple> c) {
+    public synchronized boolean addAll(Collection<? extends Triple> c) {
         boolean modified = false;
         for(Triple t : c) {
             modified = add(t) || modified;
@@ -333,7 +328,7 @@ public class TripleTable<Triple extends Statement> implements Set<Triple>, Seria
      * @see #remove(Object)
      */
     @Override
-    public boolean retainAll(Collection<?> c) {
+    public synchronized boolean retainAll(Collection<?> c) {
         Iterator<Map.Entry<IntArray,Triple>> it = indexSPOC.entrySet().iterator();
         while(it.hasNext()) {
             if(!c.contains(it.next().getValue())) {
@@ -369,7 +364,7 @@ public class TripleTable<Triple extends Statement> implements Set<Triple>, Seria
      * @see #contains(Object)
      */
     @Override
-    public boolean removeAll(Collection<?> c) {
+    public synchronized boolean removeAll(Collection<?> c) {
         boolean modified = false;
         for(Object o : c) {
             modified = remove(o) || modified;
@@ -385,7 +380,7 @@ public class TripleTable<Triple extends Statement> implements Set<Triple>, Seria
      *         is not supported by this set
      */
     @Override
-    public void clear() {
+    public synchronized void clear() {
         data.clear();
         indexSPOC.clear();
         indexCSPO.clear();
@@ -394,13 +389,15 @@ public class TripleTable<Triple extends Statement> implements Set<Triple>, Seria
     /**
      * Return a subset of the triples matching the filter criteria. Arguments with null value are treated as wildcards.
      *
+     *
      * @param subject
      * @param property
      * @param object
      * @param context
+     * @param wildcardContext
      * @return
      */
-    public Collection<Triple> listTriples(final Resource subject, final URI property, final Value object, final URI context) {
+    public synchronized Collection<Triple> listTriples(final Resource subject, final URI property, final Value object, final Resource context, boolean wildcardContext) {
         // in special cases we can make use of the index
         if(subject != null && property != null && object != null && context != null) {
             IntArray key = IntArray.createSPOCKey(subject, property, object, context);
@@ -410,9 +407,10 @@ public class TripleTable<Triple extends Statement> implements Set<Triple>, Seria
             } else {
                 return Collections.emptyList();
             }
-        } else if(  (subject != null && property != null && object != null)
+        } else if(wildcardContext &&
+                (  (subject != null && property != null && object != null)
                  || (subject != null && property != null)
-                 || subject != null) {
+                 || subject != null)) {
             IntArray fromKey = IntArray.createSPOCKey(subject, property, object, context);
             IntArray toKey   = IntArray.createSPOCMaxKey(subject, property, object, context);
 
@@ -446,13 +444,20 @@ public class TripleTable<Triple extends Statement> implements Set<Triple>, Seria
                 }
             };
 
-            return Sets.filter(data, p);
+            return Collections2.filter(data, p);
         }
     }
 
+    public synchronized Collection<Resource> listContextIDs() {
+        Set<Resource> result = new HashSet<>();
+        for(Triple t : data) {
+            result.add(t.getContext());
+        }
+        return result;
+    }
 
     @Override
-    public boolean equals(Object o) {
+    public synchronized boolean equals(Object o) {
         if (this == o) return true;
         if (o == null || getClass() != o.getClass()) return false;
 
@@ -465,7 +470,9 @@ public class TripleTable<Triple extends Statement> implements Set<Triple>, Seria
     }
 
     @Override
-    public int hashCode() {
+    public synchronized int hashCode() {
         return data.hashCode();
     }
+
+
 }