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:43 UTC

[023/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-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/engine/ReasoningEngine.java
----------------------------------------------------------------------
diff --git a/libraries/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/engine/ReasoningEngine.java b/libraries/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/engine/ReasoningEngine.java
index dd42ae5..69d6cde 100644
--- a/libraries/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/engine/ReasoningEngine.java
+++ b/libraries/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/engine/ReasoningEngine.java
@@ -17,22 +17,22 @@
  */
 package org.apache.marmotta.kiwi.reasoner.engine;
 
+import com.google.common.base.Equivalence;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
 import info.aduna.iteration.CloseableIteration;
 import info.aduna.iteration.EmptyIteration;
 import info.aduna.iteration.Iterations;
 import info.aduna.iteration.SingletonIteration;
+import org.apache.marmotta.commons.sesame.model.StatementCommons;
 import org.apache.marmotta.kiwi.model.caching.TripleTable;
 import org.apache.marmotta.kiwi.model.rdf.KiWiNode;
 import org.apache.marmotta.kiwi.model.rdf.KiWiResource;
 import org.apache.marmotta.kiwi.model.rdf.KiWiTriple;
 import org.apache.marmotta.kiwi.model.rdf.KiWiUriResource;
-import org.apache.marmotta.kiwi.reasoner.model.program.Justification;
-import org.apache.marmotta.kiwi.reasoner.model.program.LiteralField;
-import org.apache.marmotta.kiwi.reasoner.model.program.Pattern;
-import org.apache.marmotta.kiwi.reasoner.model.program.Program;
-import org.apache.marmotta.kiwi.reasoner.model.program.ResourceField;
-import org.apache.marmotta.kiwi.reasoner.model.program.Rule;
-import org.apache.marmotta.kiwi.reasoner.model.program.VariableField;
+import org.apache.marmotta.kiwi.reasoner.model.exception.ReasoningException;
+import org.apache.marmotta.kiwi.reasoner.model.exception.UnjustifiedTripleException;
+import org.apache.marmotta.kiwi.reasoner.model.program.*;
 import org.apache.marmotta.kiwi.reasoner.model.query.QueryResult;
 import org.apache.marmotta.kiwi.reasoner.persistence.KiWiReasoningConnection;
 import org.apache.marmotta.kiwi.reasoner.persistence.KiWiReasoningPersistence;
@@ -51,14 +51,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.sql.SQLException;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
@@ -121,24 +114,32 @@ public class ReasoningEngine implements TransactionListener {
      * In-memory cache to store all patterns that are candidates for matching triples and accessing the rules
      * they belong to.
      */
-    private Map<Pattern,Rule> patternRuleMap;
+    private Multimap<Pattern,Rule> patternRuleMap;
 
     /**
      * Internal counter to count executions of the reasoner (informational purposes only)
      */
     private static long taskCounter = 0;
 
+    private boolean isshutdown = false;
 
     /**
      * The worker thread for the reasoner.
      */
     private SKWRLReasoner reasonerThread;
 
+    protected static Equivalence<Statement> equivalence = StatementCommons.quadrupleEquivalence();
+
     /**
      * A lock to ensure that only once thread at a time is carrying out persistence
      */
     private Lock persistenceLock;
 
+    // for mocking
+    protected ReasoningEngine() {
+
+    }
+
     public ReasoningEngine(KiWiReasoningPersistence persistence, TransactionalSail store, ReasoningConfiguration config) {
         this.persistence = persistence;
         this.store = store;
@@ -153,7 +154,7 @@ public class ReasoningEngine implements TransactionListener {
 
     public void loadPrograms() {
         log.info("program configuration changed, reloading ...");
-        patternRuleMap = new HashMap<Pattern, Rule>();
+        patternRuleMap = HashMultimap.<Pattern,Rule>create();
 
         try {
             KiWiReasoningConnection connection = persistence.getConnection();
@@ -311,7 +312,6 @@ public class ReasoningEngine implements TransactionListener {
 
     /**
      * Stop the currently active reasoning task. Informational purposes only.
-     * @param name
      */
     protected void endTask() {
 
@@ -344,58 +344,59 @@ public class ReasoningEngine implements TransactionListener {
     }
 
     private void executeReasoner(TransactionData data) {
-        updateTaskStatus("fetching worklist");
-        Set<KiWiTriple> newTriples = new HashSet<KiWiTriple>();
-        for(Statement stmt : data.getAddedTriples()) {
-            KiWiTriple t = (KiWiTriple)stmt;
-            if(t.isMarkedForReasoning()) {
-                newTriples.add(t);
-                t.setMarkedForReasoning(false);
+        try {
+            updateTaskStatus("fetching worklist");
+            Set<KiWiTriple> newTriples = StatementCommons.newQuadrupleSet();
+            for(Statement stmt : data.getAddedTriples()) {
+                KiWiTriple t = (KiWiTriple)stmt;
+                if(t.isMarkedForReasoning()) {
+                    newTriples.add(t);
+                    t.setMarkedForReasoning(false);
+                }
             }
-        }
 
-        //taskManagerService.setTaskSteps(newTriples.size() + data.getRemovedTriples().size());
-        // evaluate the rules for all added triples
-        if(newTriples.size() > 0) {
-            long start2 = System.currentTimeMillis();
-            updateTaskStatus("reasoning over " + newTriples.size() + " new triples");
-            processRules(newTriples);
-            log.debug("REASONER: reasoning for {} new triples took {} ms overall",newTriples.size(),System.currentTimeMillis()-start2);
-        }
+            //taskManagerService.setTaskSteps(newTriples.size() + data.getRemovedTriples().size());
+            // evaluate the rules for all added triples
+            if(newTriples.size() > 0) {
+                long start2 = System.currentTimeMillis();
+                updateTaskStatus("reasoning over " + newTriples.size() + " new triples");
+                processRules(newTriples);
+                log.debug("REASONER: reasoning for {} new triples took {} ms overall", newTriples.size(), System.currentTimeMillis() - start2);
+            }
 
-        if(data.getRemovedTriples().size() > 0) {
-            log.debug("cleaning up justifications and inferences for {} triples",data.getRemovedTriples().size());
-            try {
-                KiWiReasoningConnection connection = persistence.getConnection();
+            if(data.getRemovedTriples().size() > 0) {
+                log.debug("cleaning up justifications and inferences for {} triples",data.getRemovedTriples().size());
                 try {
-                    // first clean up justifications that are no longer supported
-                    cleanupJustifications(connection, data.getRemovedTriples());
-
-
-                    // then remove all inferred triples that are no longer supported
-                    cleanupUnsupported(connection);
-
-                    // and finally garbage collect those triples that are inferred and deleted
-                    // garbage collection is now carried out by a thread in the triple store
-                    //garbageCollectTriples();
-                    connection.commit();
-                } catch (SQLException ex) {
-                    connection.rollback();
+                    KiWiReasoningConnection connection = persistence.getConnection();
+                    try {
+                        // first clean up justifications that are no longer supported
+                        cleanupJustifications(connection, data.getRemovedTriples());
+
+
+                        // then remove all inferred triples that are no longer supported
+                        cleanupUnsupported(connection);
+
+                        // and finally garbage collect those triples that are inferred and deleted
+                        // garbage collection is now carried out by a thread in the triple store
+                        //garbageCollectTriples();
+                        connection.commit();
+                    } catch (SQLException ex) {
+                        connection.rollback();
+                        throw ex;
+                    } finally {
+                        connection.close();
+                    }
+                } catch (SailException | SQLException ex) {
+                    log.error("REASONING ERROR: could not clean up unsupported triples, database state will be inconsistent! Message: {}", ex.getMessage());
+                    log.debug("Exception details:", ex);
                     throw ex;
-                } finally {
-                    connection.close();
                 }
-            } catch (SailException ex) {
-                log.error("REPOSITORY ERROR: could not clean up unsupported triples, database state will be inconsistent! Message: {}", ex.getMessage());
-                log.debug("Exception details:", ex);
-            } catch (SQLException ex) {
-                log.error("DATABASE ERROR: could not clean up justifications for triples, database state will be inconsistent! Message: {}", ex.getMessage());
-                log.debug("Exception details:", ex);
-            }
 
 
+            }
+        } catch (SQLException | SailException | ReasoningException ex) {
+            log.error("REASONER: processing of transaction data with ID {} aborted; reason: {}", data.getTransactionId(), ex.getMessage());
         }
-
     }
 
     /**
@@ -483,40 +484,12 @@ public class ReasoningEngine implements TransactionListener {
 
     }
 
-    /**
-     * Store new justifications in the database. The method performs a batched operation to avoid
-     * excessive resource use.
-     *
-     * @param justifications
-     */
-    private void storeJustifications(Iterable<Justification> justifications) {
-        // persist the justifications that have been created in the rule processing
-        long counter = 0;
-        updateTaskStatus("storing new justifications ...");
-
-        try {
-            KiWiReasoningConnection connection = persistence.getConnection();
-            try {
-                connection.storeJustifications(justifications);
-                connection.commit();
-            } catch (SQLException ex) {
-                connection.rollback();
-                throw ex;
-            } finally {
-                connection.close();
-            }
-        } catch (SQLException ex) {
-            log.error("DATABASE ERROR: could not add new justifications for triples, database state will be inconsistent! Message: {}",ex.getMessage());
-            log.debug("Exception details:",ex);
-        }
-    }
-
 
     /**
      * This method iterates over all triples that are passed as argument and
      * checks whether they are used as supporting triples justifications. All
      * such justifications are removed. Triples that are no longer supported
-     * will later be cleaned up by {@link #cleanupUnsupported()}
+     * will later be cleaned up by {@link #cleanupUnsupported(org.apache.marmotta.kiwi.reasoner.persistence.KiWiReasoningConnection)}
      *
      * @param removedTriples
      */
@@ -541,24 +514,30 @@ public class ReasoningEngine implements TransactionListener {
         updateTaskStatus("loading unsupported triples");
 
         CloseableIteration<KiWiTriple,SQLException> tripleIterator = connection.listUnsupportedTriples();
-
-        updateTaskStatus("deleting unsupported triples");
-        SailConnection tc = store.getConnection();
-        KiWiSailConnection ic = getWrappedConnection(tc);
         try {
-            tc.begin();
-            while(tripleIterator.hasNext()) {
-                ic.removeInferredStatement(tripleIterator.next());
-                count++;
+            if(tripleIterator.hasNext()) {
+
+                updateTaskStatus("deleting unsupported triples");
+                SailConnection tc = store.getConnection();
+                KiWiSailConnection ic = getWrappedConnection(tc);
+                try {
+                    tc.begin();
+                    while(tripleIterator.hasNext()) {
+                        ic.removeInferredStatement(tripleIterator.next());
+                        count++;
+                    }
+                    log.debug("removed {} unsupported triples",count);
+                    tc.commit();
+                } catch(SailException ex) {
+                    ic.rollback();
+                    throw ex;
+                } finally {
+                    ic.close();
+                }
             }
-            log.debug("removed {} unsupported triples",count);
-            tc.commit();
-        } catch(SailException ex) {
-            ic.rollback();
-            throw ex;
         } finally {
             Iterations.closeCloseable(tripleIterator);
-            ic.close();
+
         }
     }
 
@@ -567,43 +546,20 @@ public class ReasoningEngine implements TransactionListener {
      *
      * @param addedTriples
      */
-    private void processRules(final Set<KiWiTriple> addedTriples) {
-        try {
-            updateTaskStatus("processing rules ...");
-            // select the rules that have at least one matching pattern; the match method will
-            // return a set of variable bindings that we will be used to prepopulate the bindings
-//            Set<Callable<Boolean>> tasks = new HashSet<Callable<Boolean>>();
-            for(final Pattern pattern : patternRuleMap.keySet()) {
-                for(KiWiTriple triple : addedTriples) {
-                    QueryResult match = matches(pattern,triple);
-                    if(match != null) {
-                        log.debug("pattern {} matched with triple {}", pattern.toString(), triple.toString());
-                        processRule(patternRuleMap.get(pattern), match, pattern);
+    private void processRules(final Set<KiWiTriple> addedTriples) throws SQLException, SailException, ReasoningException {
+        updateTaskStatus("processing rules ...");
+        // select the rules that have at least one matching pattern; the match method will
+        // return a set of variable bindings that we will be used to prepopulate the bindings
+        for(final Pattern pattern : patternRuleMap.keySet()) {
+            for(KiWiTriple triple : addedTriples) {
+                QueryResult match = matches(pattern,triple);
+                if(match != null) {
+                    for(Rule rule : patternRuleMap.get(pattern)) {
+                        log.debug("REASONER(rule '{}'): pattern {} matched with triple {}", rule.getName(), pattern.toString(), triple.toString());
+                        processRule(rule, match, pattern);
                     }
                 }
-                // TODO: for parallel reasoning, the problem is that we should only create one thread per rule and not
-                // one per pattern, otherwise we can get duplicates because the same rule is evaluated twice
-/*
-                tasks.add(new Callable<Boolean>() {
-                    @Override
-                    public Boolean call() throws Exception {
-                        for(KiWiTriple triple : addedTriples) {
-                            QueryResult match = matches(pattern,triple);
-                            if(match != null) {
-                                log.debug("pattern {} matched with triple {}", pattern.toString(), triple.toString());
-                                processRule(patternRuleMap.get(pattern), match, pattern);
-                            }
-                        }
-
-                        return Boolean.TRUE;
-                    }
-                });
-*/
             }
-            //workers.invokeAll(tasks);
-
-        } catch(Exception ex) {
-            log.error("error while processing rules",ex);
         }
     }
 
@@ -615,10 +571,10 @@ public class ReasoningEngine implements TransactionListener {
      * @param rule
      * @param match
      */
-    private void processRule(Rule rule, QueryResult match, Pattern p) {
+    private void processRule(Rule rule, QueryResult match, Pattern p) throws SQLException, SailException, ReasoningException {
 
         // get the variable bindings for the rule evaluation
-        log.debug("REASONER: evaluating rule body for rule {} ...", rule);
+        log.debug("REASONER(rule '{}'): evaluating rule body {} ...", rule.getName() != null ? rule.getName() : rule.getId(), rule);
 
         // create a collection consisting of the body minus the pattern that already matched
         Set<Pattern> body = new HashSet<Pattern>(rule.getBody());
@@ -629,153 +585,143 @@ public class ReasoningEngine implements TransactionListener {
 
         CloseableIteration<QueryResult, SQLException> bodyResult;
 
+        KiWiReasoningConnection connection = persistence.getConnection();
+        SailConnection     sail = store.getConnection();
+        KiWiSailConnection isail = getWrappedConnection(sail);
         try {
-            KiWiReasoningConnection connection = persistence.getConnection();
-            SailConnection     sail = store.getConnection();
-            KiWiSailConnection isail = getWrappedConnection(sail);
-            try {
 
-                // if there are further patterns, evaluate them; if the matched pattern was the only pattern, then
-                // simply take the match as binding
-                if(body.size() > 0) {
-                    bodyResult = connection.query(body,match,null,null,true);
-                } else if(match != null) {
-                    bodyResult = new SingletonIteration<QueryResult, SQLException>(match);
-                } else {
-                    bodyResult = new EmptyIteration<QueryResult, SQLException>();
-                }
+            // if there are further patterns, evaluate them; if the matched pattern was the only pattern, then
+            // simply take the match as binding
+            if(body.size() > 0) {
+                bodyResult = connection.query(body,match,null,null,true);
+            } else if(match != null) {
+                bodyResult = new SingletonIteration<QueryResult, SQLException>(match);
+            } else {
+                bodyResult = new EmptyIteration<QueryResult, SQLException>();
+            }
 
-                // construct triples out of the bindings and the rule heads
-                long counter = 0;
+            // construct triples out of the bindings and the rule heads
+            long counter = 0;
 
-                // initialise a new set of justifications
-                Set<Justification> justifications = new HashSet<Justification>();
+            // initialise a new set of justifications
+            Set<Justification> justifications = new HashSet<Justification>();
 
-                sail.begin();
-                while(bodyResult.hasNext()) {
-                    QueryResult row = bodyResult.next();
-                    Map<VariableField,KiWiNode> binding = row.getBindings();
+            sail.begin();
+            while(bodyResult.hasNext()) {
+                QueryResult row = bodyResult.next();
+                Map<VariableField,KiWiNode> binding = row.getBindings();
 
-                    Resource subject = null;
-                    URI property = null;
-                    Value object;
+                Resource subject = null;
+                URI property = null;
+                Value object;
 
-                    if(rule.getHead().getSubject() != null && rule.getHead().getSubject().isVariableField()) {
-                        if(!binding.get(rule.getHead().getSubject()).isUriResource() && !binding.get(rule.getHead().getSubject()).isAnonymousResource()) {
-                            log.info("cannot use value {} as subject, because it is not a resource",binding.get(rule.getHead().getSubject()));
-                            continue;
-                        }
-                        subject = (KiWiResource)binding.get(rule.getHead().getSubject());
-                    } else if(rule.getHead().getSubject() != null && rule.getHead().getSubject().isResourceField()) {
-                        subject = ((ResourceField)rule.getHead().getSubject()).getResource();
-                    } else
-                        throw new IllegalArgumentException("Subject of rule head may only be a variable or a resource; rule: "+rule);
-
-                    if(rule.getHead().getProperty() != null && rule.getHead().getProperty().isVariableField()) {
-                        if(!binding.get(rule.getHead().getProperty()).isUriResource()) {
-                            log.info("cannot use value {} as property, because it is not a URI resource",binding.get(rule.getHead().getProperty()));
-                            continue;
+                if(rule.getHead().getSubject() != null && rule.getHead().getSubject().isVariableField()) {
+                    if(!binding.get(rule.getHead().getSubject()).isUriResource() && !binding.get(rule.getHead().getSubject()).isAnonymousResource()) {
+                        log.info("cannot use value {} as subject, because it is not a resource",binding.get(rule.getHead().getSubject()));
+                        continue;
+                    }
+                    subject = (KiWiResource)binding.get(rule.getHead().getSubject());
+                } else if(rule.getHead().getSubject() != null && rule.getHead().getSubject().isResourceField()) {
+                    subject = ((ResourceField)rule.getHead().getSubject()).getResource();
+                } else
+                    throw new IllegalArgumentException("Subject of rule head may only be a variable or a resource; rule: "+rule);
+
+                if(rule.getHead().getProperty() != null && rule.getHead().getProperty().isVariableField()) {
+                    if(!binding.get(rule.getHead().getProperty()).isUriResource()) {
+                        log.info("cannot use value {} as property, because it is not a URI resource",binding.get(rule.getHead().getProperty()));
+                        continue;
+                    }
+                    property = (KiWiUriResource)binding.get(rule.getHead().getProperty());
+                } else if(rule.getHead().getProperty() != null && rule.getHead().getProperty().isResourceField()) {
+                    property = (KiWiUriResource)((ResourceField)rule.getHead().getProperty()).getResource();
+                } else
+                    throw new IllegalArgumentException("Property of rule head may only be a variable or a resource; rule: "+rule);
+
+                if(rule.getHead().getObject() != null && rule.getHead().getObject().isVariableField()) {
+                    object = binding.get(rule.getHead().getObject());
+                } else if(rule.getHead().getObject() != null && rule.getHead().getObject().isResourceField()) {
+                    object = ((ResourceField)rule.getHead().getObject()).getResource();
+                } else if(rule.getHead().getObject() != null && rule.getHead().getObject().isLiteralField()) {
+                    object = ((LiteralField)rule.getHead().getObject()).getLiteral();
+                } else
+                    throw new IllegalArgumentException("Object of rule head may only be a variable, a literal, or a resource; rule: "+rule);
+
+
+                KiWiTriple triple = isail.addInferredStatement(subject, property, object);
+
+                Justification justification = new Justification();
+                justification.setTriple(triple);
+                justification.getSupportingRules().add(rule);
+                justification.getSupportingTriples().addAll(row.getJustifications());
+                justifications.add(justification);
+
+                // when the batch size is reached, commit the transaction, save the justifications, and start a new
+                // transaction and new justification set
+                if(++counter % config.getBatchSize() == 0) {
+                    persistenceLock.lock();
+
+                    try {
+
+                        sail.commit();
+
+                        log.debug("adding {} justifications",justifications.size());
+
+                        updateTaskStatus("storing justifications ...");
+                        Set<Justification> baseJustifications = getBaseJustifications(connection,justifications);
+
+                        if(config.isRemoveDuplicateJustifications()) {
+                            removeDuplicateJustifications(connection,baseJustifications);
                         }
-                        property = (KiWiUriResource)binding.get(rule.getHead().getProperty());
-                    } else if(rule.getHead().getProperty() != null && rule.getHead().getProperty().isResourceField()) {
-                        property = (KiWiUriResource)((ResourceField)rule.getHead().getProperty()).getResource();
-                    } else
-                        throw new IllegalArgumentException("Property of rule head may only be a variable or a resource; rule: "+rule);
-
-                    if(rule.getHead().getObject() != null && rule.getHead().getObject().isVariableField()) {
-                        object = binding.get(rule.getHead().getObject());
-                    } else if(rule.getHead().getObject() != null && rule.getHead().getObject().isResourceField()) {
-                        object = ((ResourceField)rule.getHead().getObject()).getResource();
-                    } else if(rule.getHead().getObject() != null && rule.getHead().getObject().isLiteralField()) {
-                        object = ((LiteralField)rule.getHead().getObject()).getLiteral();
-                    } else
-                        throw new IllegalArgumentException("Object of rule head may only be a variable, a literal, or a resource; rule: "+rule);
-
-
-                    KiWiTriple triple = isail.addInferredStatement(subject, property, object);
-
-                    Justification justification = new Justification();
-                    justification.setTriple(triple);
-                    justification.getSupportingRules().add(rule);
-                    justification.getSupportingTriples().addAll(row.getJustifications());
-                    justifications.add(justification);
-
-                    // when the batch size is reached, commit the transaction, save the justifications, and start a new
-                    // transaction and new justification set
-                    if(++counter % config.getBatchSize() == 0) {
-                        persistenceLock.lock();
-
-                        try {
-
-                            sail.commit();
-
-                            log.debug("adding {} justifications",justifications.size());
-
-                            updateTaskStatus("storing justifications ...");
-                            Set<Justification> baseJustifications = getBaseJustifications(connection,justifications);
-
-                            if(config.isRemoveDuplicateJustifications()) {
-                                removeDuplicateJustifications(connection,baseJustifications);
-                            }
-
-                            log.debug("{} justifications added after resolving inferred triples", baseJustifications.size());
-
-                            // persist the justifications that have been created in the rule processing
-                            if(baseJustifications.size() > 0) {
-                                connection.storeJustifications(baseJustifications);
-                            }
-                            connection.commit();
-                            sail.begin();
-                        } finally {
-                            persistenceLock.unlock();
+
+                        log.debug("{} justifications added after resolving inferred triples", baseJustifications.size());
+
+                        // persist the justifications that have been created in the rule processing
+                        if(baseJustifications.size() > 0) {
+                            connection.storeJustifications(baseJustifications);
                         }
-                        justifications.clear();
+                        connection.commit();
+                        sail.begin();
+                    } finally {
+                        persistenceLock.unlock();
                     }
+                    justifications.clear();
                 }
+            }
 
-                persistenceLock.lock();
-                try {
-                    sail.commit();
+            persistenceLock.lock();
+            try {
+                sail.commit();
 
-                    log.debug("adding {} justifications",justifications.size());
-                    updateTaskStatus("storing justifications ...");
-                    Set<Justification> baseJustifications = getBaseJustifications(connection,justifications);
+                log.debug("adding {} justifications",justifications.size());
+                updateTaskStatus("storing justifications ...");
+                Set<Justification> baseJustifications = getBaseJustifications(connection,justifications);
 
-                    if(config.isRemoveDuplicateJustifications()) {
-                        removeDuplicateJustifications(connection,baseJustifications);
-                    }
+                if(config.isRemoveDuplicateJustifications()) {
+                    removeDuplicateJustifications(connection,baseJustifications);
+                }
 
-                    // persist the justifications that have been created in the rule processing
-                    if(baseJustifications.size() > 0) {
-                        connection.storeJustifications(baseJustifications);
-                    }
+                // persist the justifications that have been created in the rule processing
+                if(baseJustifications.size() > 0) {
+                    connection.storeJustifications(baseJustifications);
+                }
 
-                    log.debug("{} justifications added after resolving inferred triples", baseJustifications.size());
+                log.debug("{} justifications added after resolving inferred triples", baseJustifications.size());
 
-                    Iterations.closeCloseable(bodyResult);
-                    connection.commit();
-                } finally {
-                    persistenceLock.unlock();
-                }
-            } catch(SailException ex) {
-                connection.rollback();
-                sail.rollback();
-                throw ex;
-            } catch(SQLException ex) {
-                sail.rollback();
-                connection.rollback();
-                throw ex;
+                Iterations.closeCloseable(bodyResult);
+                connection.commit();
             } finally {
-                connection.close();
-                sail.close();
+                persistenceLock.unlock();
             }
-
-        } catch(SQLException ex) {
-            log.error("DATABASE ERROR: could not process rule, database state will be inconsistent! Message: {}",ex.getMessage());
+        } catch(SailException | SQLException | ReasoningException ex) {
+            log.error("REASONING ERROR: could not process rule, database state will be inconsistent! Message: {}",ex.getMessage());
             log.debug("Exception details:",ex);
-        } catch (SailException ex) {
-            log.error("REPOSITORY ERROR: could not process rule, database state will be inconsistent! Message: {}",ex.getMessage());
-            log.debug("Exception details:", ex);
+
+            connection.rollback();
+            sail.rollback();
+            throw ex;
+        } finally {
+            connection.close();
+            sail.close();
         }
 
     }
@@ -785,12 +731,11 @@ public class ReasoningEngine implements TransactionListener {
      * @param t
      * @return
      */
-    private Collection<Justification> getJustifications(KiWiReasoningConnection connection, KiWiTriple t, Set<Justification> transactionJustifications) throws SQLException {
-        // TODO: transactionJustifications are ignored
+    protected Collection<Justification> getJustifications(KiWiReasoningConnection connection, KiWiTriple t, Set<Justification> transactionJustifications) throws SQLException {
         HashSet<Justification> justifications = new HashSet<Justification>();
         Iterations.addAll(connection.listJustificationsForTriple(t), justifications);
         for(Justification j : transactionJustifications) {
-            if(j.getTriple().equals(t)) {
+            if(equivalence.equivalent(j.getTriple(), t)) {
                 justifications.add(j);
             }
         }
@@ -804,9 +749,9 @@ public class ReasoningEngine implements TransactionListener {
      * @param justifications
      * @return
      */
-    private Set<Justification> getBaseJustifications(KiWiReasoningConnection connection, Set<Justification> justifications) throws SQLException {
+    protected Set<Justification> getBaseJustifications(KiWiReasoningConnection connection, Set<Justification> justifications) throws SQLException, ReasoningException {
         Set<Justification> baseJustifications = new HashSet<Justification>();
-        Map<KiWiTriple,Collection<Justification>> justificationCache = new HashMap<KiWiTriple, Collection<Justification>>();
+        Map<KiWiTriple,Collection<Justification>> justificationCache = StatementCommons.newQuadrupleMap();
 
         for(Justification justification : justifications) {
             KiWiTriple triple = justification.getTriple();
@@ -828,7 +773,7 @@ public class ReasoningEngine implements TransactionListener {
                     }
 
                     if(supportJustifications.size() == 0) {
-                        log.error("error: inferred triple {} is not justified!",support);
+                        throw new UnjustifiedTripleException("error: inferred triple is not justified!", support);
                     }
 
                     // mix the two sets
@@ -864,7 +809,7 @@ public class ReasoningEngine implements TransactionListener {
      */
     private void removeDuplicateJustifications(KiWiReasoningConnection connection, Set<Justification> justifications) throws SQLException {
         // remove duplicate justifications
-        HashMap<KiWiTriple,Collection<Justification>> justificationCache = new HashMap<KiWiTriple, Collection<Justification>>();
+        Map<KiWiTriple,Collection<Justification>> justificationCache = StatementCommons.newQuadrupleMap();
         for(Iterator<Justification> it = justifications.iterator(); it.hasNext(); ) {
             Justification j = it.next();
 
@@ -882,7 +827,7 @@ public class ReasoningEngine implements TransactionListener {
     }
 
 
-    private QueryResult matches(Pattern pattern, KiWiTriple triple) {
+    protected static QueryResult matches(Pattern pattern, KiWiTriple triple) {
         boolean result = true;
 
         QueryResult match = new QueryResult();
@@ -950,8 +895,45 @@ public class ReasoningEngine implements TransactionListener {
     }
 
     public void shutdown() {
-        log.info("shutting down reasoning service ...");
-        reasonerThread.shutdown();
+        shutdown(false);
+    }
+
+    public void shutdown(boolean force) {
+        if(isshutdown)
+            return;
+
+        if(force) {
+            log.warn("forced shutdown of reasoning service initiated, state will be inconsistent ...");
+
+            reasoningQueue.clear();
+            reasonerThread.shutdown(true);
+
+            for(int i = 0; i<5 && isRunning(); i++) {
+                log.warn("reasoner not yet finished, waiting for 100 ms (try={})", i+1);
+                try {
+                    Thread.sleep(100);
+                } catch (InterruptedException e) {
+                }
+            }
+
+            // yes, I know it is unsafe; it is only used when forcefully shutting down on test ends before the database is deleted...
+            reasonerThread.stop();
+
+        } else {
+            log.info("graceful shutdown of reasoning service initiated ...");
+
+            for(int i = 0; i<20 && isRunning(); i++) {
+                log.warn("reasoner not yet finished, waiting for 1 seconds (try={})", i+1);
+                try {
+                    Thread.sleep(1000);
+                } catch (InterruptedException e) {
+                }
+            }
+
+            reasonerThread.shutdown(false);
+        }
+
+        isshutdown = true;
     }
 
     /**
@@ -976,6 +958,7 @@ public class ReasoningEngine implements TransactionListener {
     private class SKWRLReasoner extends Thread {
         private boolean shutdown = false;
         private boolean running  = false;
+        private boolean forceshutdown = false;
 
         private SKWRLReasoner() {
             super("SKWRL Reasoner " + ++indexerCounter);
@@ -983,8 +966,10 @@ public class ReasoningEngine implements TransactionListener {
             start();
         }
 
-        public void shutdown() {
+        public void shutdown(boolean force) {
+            log.info("REASONER: signalling shutdown to reasoner thread");
             shutdown = true;
+            forceshutdown = force;
             this.interrupt();
         }
 
@@ -998,7 +983,7 @@ public class ReasoningEngine implements TransactionListener {
 
             startTask(getName(), TASK_GROUP);
 
-            while (!shutdown || reasoningQueue.size() > 0) {
+            while (!forceshutdown && (!shutdown || reasoningQueue.size() > 0)) {
                 running = false;
                 try {
                     updateTaskStatus("idle");
@@ -1011,10 +996,13 @@ public class ReasoningEngine implements TransactionListener {
                     executeReasoner(data);
                 } catch (InterruptedException ex) {
 
+                } catch (RuntimeException ex) {
+                    // can happen on forced shutdown
                 } catch (Exception ex) {
                     log.warn("reasoning task threw an exception",ex);
                 }
             }
+            running = false;
             try {
                 endTask();
             } catch (Exception ex) {

http://git-wip-us.apache.org/repos/asf/marmotta/blob/582abb5b/libraries/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/exception/ReasoningException.java
----------------------------------------------------------------------
diff --git a/libraries/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/exception/ReasoningException.java b/libraries/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/exception/ReasoningException.java
new file mode 100644
index 0000000..bc25d22
--- /dev/null
+++ b/libraries/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/exception/ReasoningException.java
@@ -0,0 +1,100 @@
+/*
+ * 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.reasoner.model.exception;
+
+/**
+ * Add file description here!
+ *
+ * @author Sebastian Schaffert (sschaffert@apache.org)
+ */
+public class ReasoningException extends Exception {
+
+    /**
+     * Constructs a new exception with {@code null} as its detail message.
+     * The cause is not initialized, and may subsequently be initialized by a
+     * call to {@link #initCause}.
+     */
+    public ReasoningException() {
+    }
+
+    /**
+     * Constructs a new exception with the specified cause and a detail
+     * message of <tt>(cause==null ? null : cause.toString())</tt> (which
+     * typically contains the class and detail message of <tt>cause</tt>).
+     * This constructor is useful for exceptions that are little more than
+     * wrappers for other throwables (for example, {@link
+     * java.security.PrivilegedActionException}).
+     *
+     * @param cause the cause (which is saved for later retrieval by the
+     *              {@link #getCause()} method).  (A <tt>null</tt> value is
+     *              permitted, and indicates that the cause is nonexistent or
+     *              unknown.)
+     * @since 1.4
+     */
+    public ReasoningException(Throwable cause) {
+        super(cause);
+    }
+
+    /**
+     * Constructs a new exception with the specified detail message.  The
+     * cause is not initialized, and may subsequently be initialized by
+     * a call to {@link #initCause}.
+     *
+     * @param message the detail message. The detail message is saved for
+     *                later retrieval by the {@link #getMessage()} method.
+     */
+    public ReasoningException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs a new exception with the specified detail message and
+     * cause.  <p>Note that the detail message associated with
+     * {@code cause} is <i>not</i> automatically incorporated in
+     * this exception's detail message.
+     *
+     * @param message the detail message (which is saved for later retrieval
+     *                by the {@link #getMessage()} method).
+     * @param cause   the cause (which is saved for later retrieval by the
+     *                {@link #getCause()} method).  (A <tt>null</tt> value is
+     *                permitted, and indicates that the cause is nonexistent or
+     *                unknown.)
+     * @since 1.4
+     */
+    public ReasoningException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    /**
+     * Constructs a new exception with the specified detail message,
+     * cause, suppression enabled or disabled, and writable stack
+     * trace enabled or disabled.
+     *
+     * @param message            the detail message.
+     * @param cause              the cause.  (A {@code null} value is permitted,
+     *                           and indicates that the cause is nonexistent or unknown.)
+     * @param enableSuppression  whether or not suppression is enabled
+     *                           or disabled
+     * @param writableStackTrace whether or not the stack trace should
+     *                           be writable
+     * @since 1.7
+     */
+    public ReasoningException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
+        super(message, cause, enableSuppression, writableStackTrace);
+    }
+}

http://git-wip-us.apache.org/repos/asf/marmotta/blob/582abb5b/libraries/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/exception/UnjustifiedTripleException.java
----------------------------------------------------------------------
diff --git a/libraries/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/exception/UnjustifiedTripleException.java b/libraries/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/exception/UnjustifiedTripleException.java
new file mode 100644
index 0000000..853db1c
--- /dev/null
+++ b/libraries/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/exception/UnjustifiedTripleException.java
@@ -0,0 +1,44 @@
+/*
+ * 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.reasoner.model.exception;
+
+import org.apache.marmotta.kiwi.model.rdf.KiWiTriple;
+
+/**
+ * Add file description here!
+ *
+ * @author Sebastian Schaffert (sschaffert@apache.org)
+ */
+public class UnjustifiedTripleException extends ReasoningException {
+
+    private KiWiTriple triple;
+
+    /**
+     * Constructs a new exception with the specified detail message.  The
+     * cause is not initialized, and may subsequently be initialized by
+     * a call to {@link #initCause}.
+     *
+     * @param message the detail message. The detail message is saved for
+     *                later retrieval by the {@link #getMessage()} method.
+     */
+    public UnjustifiedTripleException(String message, KiWiTriple triple) {
+        super(message);
+
+        this.triple = triple;
+    }
+}

http://git-wip-us.apache.org/repos/asf/marmotta/blob/582abb5b/libraries/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/program/Justification.java
----------------------------------------------------------------------
diff --git a/libraries/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/program/Justification.java b/libraries/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/program/Justification.java
index c338c86..5de904e 100644
--- a/libraries/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/program/Justification.java
+++ b/libraries/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/program/Justification.java
@@ -18,7 +18,10 @@
 package org.apache.marmotta.kiwi.reasoner.model.program;
 
 
+import com.google.common.base.Equivalence;
+import org.apache.marmotta.commons.sesame.model.StatementCommons;
 import org.apache.marmotta.kiwi.model.rdf.KiWiTriple;
+import org.openrdf.model.Statement;
 
 import java.util.Date;
 import java.util.HashSet;
@@ -63,8 +66,11 @@ public class Justification  {
      */
     private Date createdAt;
 
+
+    private static Equivalence<Statement> equivalence = StatementCommons.quadrupleEquivalence();
+
     public Justification() {
-        supportingTriples = new HashSet<KiWiTriple>();
+        supportingTriples = StatementCommons.newQuadrupleSet();
         supportingRules   = new HashSet<Rule>();
     }
 
@@ -118,7 +124,7 @@ public class Justification  {
         //if (id != null ? !id.equals(that.id) : that.id != null) return false;
         if (!supportingRules.equals(that.supportingRules)) return false;
         if (!supportingTriples.equals(that.supportingTriples)) return false;
-        if (!triple.equals(that.triple)) return false;
+        if (!equivalence.equivalent(this.triple, that.triple)) return false;
 
         return true;
     }
@@ -126,7 +132,7 @@ public class Justification  {
     @Override
     public int hashCode() {
         int result = 0; // id != null ? id.hashCode() : 0;
-        result = 31 * result + triple.hashCode();
+        result = 31 * result + equivalence.hash(triple);
         result = 31 * result + supportingTriples.hashCode();
         result = 31 * result + supportingRules.hashCode();
         return result;

http://git-wip-us.apache.org/repos/asf/marmotta/blob/582abb5b/libraries/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/program/Pattern.java
----------------------------------------------------------------------
diff --git a/libraries/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/program/Pattern.java b/libraries/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/program/Pattern.java
index 91c9aa4..6fd96f9 100644
--- a/libraries/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/program/Pattern.java
+++ b/libraries/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/program/Pattern.java
@@ -17,6 +17,8 @@
  */
 package org.apache.marmotta.kiwi.reasoner.model.program;
 
+import com.google.common.base.Objects;
+
 import java.util.Map;
 
 /**
@@ -93,28 +95,23 @@ public class Pattern  {
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
 
-        if(context == null && object == null && subject == null && property == null) {
-            return super.equals(o);
+        if(! (o instanceof Pattern)) {
+            return false;
         }
 
         Pattern pattern = (Pattern) o;
 
-        if (context != null ? !context.equals(pattern.context) : pattern.context != null) return false;
-        if (object != null ? !object.equals(pattern.object) : pattern.object != null) return false;
-        if (property != null ? !property.equals(pattern.property) : pattern.property != null) return false;
-        if (subject != null ? !subject.equals(pattern.subject) : pattern.subject != null) return false;
+        if(!Objects.equal(this.context, pattern.context)) return false;
+        if(!Objects.equal(this.object, pattern.object)) return false;
+        if(!Objects.equal(this.property, pattern.property)) return false;
+        if(!Objects.equal(this.subject, pattern.subject)) return false;
 
         return true;
     }
 
     @Override
     public int hashCode() {
-        int result = subject != null ? subject.hashCode() : 0;
-        result = 31 * result + (property != null ? property.hashCode() : 0);
-        result = 31 * result + (object != null ? object.hashCode() : 0);
-        result = 31 * result + (context != null ? context.hashCode() : 0);
-        return result;
+        return Objects.hashCode(subject, property, object, context);
     }
 }

http://git-wip-us.apache.org/repos/asf/marmotta/blob/582abb5b/libraries/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/query/QueryResult.java
----------------------------------------------------------------------
diff --git a/libraries/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/query/QueryResult.java b/libraries/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/query/QueryResult.java
index 94e73ed..5dbf0af 100644
--- a/libraries/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/query/QueryResult.java
+++ b/libraries/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/model/query/QueryResult.java
@@ -17,6 +17,7 @@
  */
 package org.apache.marmotta.kiwi.reasoner.model.query;
 
+import org.apache.marmotta.commons.sesame.model.StatementCommons;
 import org.apache.marmotta.kiwi.model.rdf.KiWiNode;
 import org.apache.marmotta.kiwi.model.rdf.KiWiTriple;
 import org.apache.marmotta.kiwi.reasoner.model.program.VariableField;
@@ -27,7 +28,7 @@ import java.util.Map;
 import java.util.Set;
 
 /**
- * Add file description here!
+ * Represent the result of a pattern query of a rule body against the triple store.
  * <p/>
  * User: sschaffe
  */
@@ -39,7 +40,7 @@ public class QueryResult {
 
     public QueryResult() {
         bindings       = new HashMap<VariableField, KiWiNode>();
-        justifications = new HashSet<KiWiTriple>();
+        justifications = StatementCommons.newQuadrupleSet();
     }
 
     public QueryResult(Map<VariableField, KiWiNode> bindings, Set<KiWiTriple> justifications) {
@@ -51,15 +52,8 @@ public class QueryResult {
         return bindings;
     }
 
-    public void setBindings(Map<VariableField, KiWiNode> bindings) {
-        this.bindings = bindings;
-    }
-
     public Set<KiWiTriple> getJustifications() {
         return justifications;
     }
 
-    public void setJustifications(Set<KiWiTriple> justifications) {
-        this.justifications = justifications;
-    }
 }

http://git-wip-us.apache.org/repos/asf/marmotta/blob/582abb5b/libraries/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/parser/KWRLProgramParserBase.java
----------------------------------------------------------------------
diff --git a/libraries/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/parser/KWRLProgramParserBase.java b/libraries/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/parser/KWRLProgramParserBase.java
index 550da32..0074b4c 100644
--- a/libraries/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/parser/KWRLProgramParserBase.java
+++ b/libraries/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/parser/KWRLProgramParserBase.java
@@ -128,14 +128,13 @@ public abstract class KWRLProgramParserBase {
         return valueFactory.createURI(uri);
     }
 
-
     protected Literal resolveLiteral(Object content, Locale loc, String typeUri) {
         if(typeUri != null) {
             return valueFactory.createLiteral(content.toString(),valueFactory.createURI(typeUri));
         } else if(loc != null) {
             return valueFactory.createLiteral(content.toString(), loc.getLanguage());
         } else {
-            return valueFactory.createLiteral(content);
+            return valueFactory.createLiteral(content.toString());
         }
     }
 

http://git-wip-us.apache.org/repos/asf/marmotta/blob/582abb5b/libraries/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/util/JustificationUtils.java
----------------------------------------------------------------------
diff --git a/libraries/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/util/JustificationUtils.java b/libraries/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/util/JustificationUtils.java
new file mode 100644
index 0000000..8503816
--- /dev/null
+++ b/libraries/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/util/JustificationUtils.java
@@ -0,0 +1,26 @@
+/*
+ * 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.reasoner.util;
+
+/**
+ * Add file description here!
+ *
+ * @author Sebastian Schaffert (sschaffert@apache.org)
+ */
+public class JustificationUtils {
+}

http://git-wip-us.apache.org/repos/asf/marmotta/blob/582abb5b/libraries/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/mysql/statements.properties
----------------------------------------------------------------------
diff --git a/libraries/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/mysql/statements.properties b/libraries/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/mysql/statements.properties
index 68e60f8..499b934 100644
--- a/libraries/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/mysql/statements.properties
+++ b/libraries/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/mysql/statements.properties
@@ -19,12 +19,15 @@
 # get sequence numbers
 seq.rules.prep         = UPDATE seq_rules SET id=LAST_INSERT_ID(id+1);
 seq.rules              = SELECT LAST_INSERT_ID();
+seq.rules.set          = UPDATE seq_rules SET id=?;
 
 seq.programs.prep      = UPDATE seq_programs SET id=LAST_INSERT_ID(id+1);
 seq.programs           = SELECT LAST_INSERT_ID();
+seq.programs.set       = UPDATE seq_programs SET id=?;
 
 seq.justifications.prep= UPDATE seq_justifications SET id=LAST_INSERT_ID(id+1);
 seq.justifications     = SELECT LAST_INSERT_ID();
+seq.justifications.set = UPDATE seq_justifications SET id=?;
 
 
 # rules

http://git-wip-us.apache.org/repos/asf/marmotta/blob/582abb5b/libraries/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/pgsql/statements.properties
----------------------------------------------------------------------
diff --git a/libraries/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/pgsql/statements.properties b/libraries/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/pgsql/statements.properties
index 2d7d788..2f3fa5c 100644
--- a/libraries/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/pgsql/statements.properties
+++ b/libraries/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/pgsql/statements.properties
@@ -18,8 +18,11 @@
 
 # get sequence numbers
 seq.rules             = SELECT nextval('seq_rules')
+seq.rules.set         = SELECT setval('seq_rules',?);
 seq.programs          = SELECT nextval('seq_programs')
+seq.programs.set      = SELECT setval('seq_programs',?);
 seq.justifications    = SELECT nextval('seq_justifications')
+seq.justifications.set= SELECT setval('seq_justifications',?);
 
 # rules
 rules.insert          = INSERT INTO reasoner_rules (id,name,description,body,createdAt) VALUES (?,?,?,?,now())

http://git-wip-us.apache.org/repos/asf/marmotta/blob/582abb5b/libraries/kiwi/kiwi-reasoner/src/test/java/org/apache/marmotta/kiwi/reasoner/test/engine/JustificationResolutionTest.java
----------------------------------------------------------------------
diff --git a/libraries/kiwi/kiwi-reasoner/src/test/java/org/apache/marmotta/kiwi/reasoner/test/engine/JustificationResolutionTest.java b/libraries/kiwi/kiwi-reasoner/src/test/java/org/apache/marmotta/kiwi/reasoner/test/engine/JustificationResolutionTest.java
new file mode 100644
index 0000000..3f1aa48
--- /dev/null
+++ b/libraries/kiwi/kiwi-reasoner/src/test/java/org/apache/marmotta/kiwi/reasoner/test/engine/JustificationResolutionTest.java
@@ -0,0 +1,316 @@
+/*
+ * 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.reasoner.test.engine;
+
+import com.google.common.collect.Sets;
+import info.aduna.iteration.Iterations;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.marmotta.commons.sesame.model.Namespaces;
+import org.apache.marmotta.commons.sesame.model.StatementCommons;
+import org.apache.marmotta.kiwi.model.rdf.*;
+import org.apache.marmotta.kiwi.reasoner.engine.ReasoningEngine;
+import org.apache.marmotta.kiwi.reasoner.model.exception.ReasoningException;
+import org.apache.marmotta.kiwi.reasoner.model.program.Justification;
+import org.apache.marmotta.kiwi.reasoner.model.program.Rule;
+import org.apache.marmotta.kiwi.reasoner.persistence.KiWiReasoningConnection;
+import org.hamcrest.Matcher;
+import org.hamcrest.Matchers;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.openrdf.model.Statement;
+
+import java.sql.SQLException;
+import java.util.*;
+
+import static org.hamcrest.Matchers.*;
+
+/**
+ * Test if the ReasoningEngine's way of resolving base justifications works. Mocks the lookup for base justifications.
+ *
+ * @author Sebastian Schaffert (sschaffert@apache.org)
+ */
+public class JustificationResolutionTest {
+
+    private MockReasoningEngine engine;
+
+    private Map<Statement,Set<Justification>> baseJustifications;
+
+
+    protected static Random rnd = new Random();
+
+
+    private KiWiTriple t1, t2, t3, t4, t5; // base
+    private KiWiTriple i1, i2, i3, i4, i5, i6; // inferred
+    private Justification j1, j2, j3, j4, j5, tj1, tj2, tj3;
+    private Rule r1, r2;
+
+    private KiWiUriResource ctx_inferred;
+
+    @Before
+    public void setup() {
+        engine = new MockReasoningEngine();
+
+        baseJustifications = StatementCommons.newQuadrupleMap();
+
+
+        KiWiUriResource s1 = randomURI();
+        KiWiUriResource s2 = randomURI();
+        KiWiUriResource s3 = randomURI();
+        KiWiUriResource s4 = randomURI();
+        KiWiUriResource p1 = randomURI();
+        KiWiUriResource p2 = randomURI();
+        KiWiNode o1 = randomObject();
+        KiWiNode o2 = randomObject();
+        KiWiNode o3 = randomObject();
+        KiWiNode o4 = randomObject();
+
+        ctx_inferred = randomURI();
+
+        t1 = new KiWiTriple(s1,p1,o1, null);
+        t2 = new KiWiTriple(s1,p1,o2, null);
+        t3 = new KiWiTriple(s2,p1,o3, null);
+        t4 = new KiWiTriple(s1,p1,o1, randomURI());
+        t5 = new KiWiTriple(s3,p1,o1, randomURI());
+
+
+        i1 = new KiWiTriple(s1,p2,o1, ctx_inferred); i1.setInferred(true);
+        i2 = new KiWiTriple(s1,p1,o2, ctx_inferred); i2.setInferred(true);
+        i3 = new KiWiTriple(s3,p1,o3, ctx_inferred); i3.setInferred(true);
+        i4 = new KiWiTriple(s1,p2,o1, ctx_inferred); i4.setInferred(true);
+        i5 = new KiWiTriple(s1,p2,o3, ctx_inferred); i5.setInferred(true);
+
+        // assume i1 is justified by t1 and t2;
+        j1 = new Justification();
+        j1.setTriple(i1);
+        j1.getSupportingTriples().add(t1);
+        j1.getSupportingTriples().add(t2);
+
+        baseJustifications.put(i1, Collections.singleton(j1));
+
+        // assume i2 is justified by t3 and t4, as well as by t2 and t4
+        j2 = new Justification();
+        j2.setTriple(i2);
+        j2.getSupportingTriples().add(t3);
+        j2.getSupportingTriples().add(t4);
+
+
+        j3 = new Justification();
+        j3.setTriple(i2);
+        j3.getSupportingTriples().add(t2);
+        j3.getSupportingTriples().add(t4);
+
+        baseJustifications.put(i2, Sets.newHashSet(j2,j3));
+
+        // assume that i5 as well is justified by two justifications
+        j4 = new Justification();
+        j4.setTriple(i5);
+        j4.getSupportingTriples().add(t1);
+        j4.getSupportingTriples().add(t4);
+
+
+        j5 = new Justification();
+        j5.setTriple(i5);
+        j5.getSupportingTriples().add(t2);
+        j5.getSupportingTriples().add(t5);
+
+
+        baseJustifications.put(i5, Sets.newHashSet(j4,j5));
+
+        // i3 justified by i1 and t3
+        tj1 = new Justification();
+        tj1.setTriple(i3);
+        tj1.getSupportingTriples().add(i1);
+        tj1.getSupportingTriples().add(t3);
+
+
+        // i4 justified by i1 and i2
+        tj2 = new Justification();
+        tj2.setTriple(i4);
+        tj2.getSupportingTriples().add(i1);
+        tj2.getSupportingTriples().add(i2);
+
+
+        // i6 is justified by i2 and i5 (so multiplexing needed)
+        tj3 = new Justification();
+        tj3.setTriple(i6);
+        tj3.getSupportingTriples().add(i2);
+        tj3.getSupportingTriples().add(i5);
+
+    }
+
+    /**
+     * Test substitution of a single inferred triple supporting the triple by a single justification, so
+     * the number of new justifications will be the same as before, but the new justification will only
+     * contain base triples.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void testResolveBaseTriplesSingle() throws Exception {
+
+        Collection<Justification> r1 = engine.getBaseJustifications(null,Collections.singleton(tj1));
+        Assert.assertEquals(1, r1.size());
+
+        Justification tj1r = r1.iterator().next();
+        Assert.assertEquals(3,tj1r.getSupportingTriples().size());
+        Assert.assertTrue(tj1r.getSupportingTriples().contains(t1));
+        Assert.assertTrue(tj1r.getSupportingTriples().contains(t2));
+        Assert.assertTrue(tj1r.getSupportingTriples().contains(t3));
+
+
+
+    }
+
+    /**
+     * Test the substitution of an inferred triple that has several justifications itself; in this case the
+     * result will be split according to the number of justifications of the inferred triple
+     * @throws Exception
+     */
+    @Test
+    public void testResolveBaseTriplesMulti() throws Exception {
+        Collection<Justification> r2 = engine.getBaseJustifications(null,Collections.singleton(tj2));
+
+        // since i2 is justified by two justifications, the result for i4 also needs to have two
+        Assert.assertEquals(2, r2.size());
+
+        Assert.assertThat(r2,Matchers.<Justification>hasItem(hasProperty("supportingTriples", hasItems(t1,t2,t3,t4))));
+        Assert.assertThat(r2,Matchers.<Justification>hasItem(hasProperty("supportingTriples", hasItems(t1,t2,t4))));
+    }
+
+    /**
+     * Test the substitution of more than one justification, the result should include the new base justificatoins for
+     * all justifications in the set
+     * @throws Exception
+     */
+    @Test
+    public void testResolveBaseTriplesSet() throws Exception {
+        Collection<Justification> r3 = engine.getBaseJustifications(null,Sets.newHashSet(tj1, tj2));
+
+        // since i2 is justified by two justifications, the result for i4 also needs to have two
+        Assert.assertEquals(3, r3.size());
+
+        Assert.assertThat(r3,Matchers.<Justification>hasItem(allOf(hasProperty("triple", is(i3)),hasProperty("supportingTriples", hasItems(t1, t2, t3)))));
+        Assert.assertThat(r3,Matchers.<Justification>hasItem(allOf(hasProperty("triple", is(i4)),hasProperty("supportingTriples", hasItems(t1, t2, t3, t4)))));
+        Assert.assertThat(r3,Matchers.<Justification>hasItem(allOf(hasProperty("triple", is(i4)),hasProperty("supportingTriples", hasItems(t1,t2,t4)))));
+    }
+
+    /**
+     * Test the substitution of several inferred triple that have several justifications itself; the result needs to be
+     * multiplexed.
+     * @throws Exception
+     */
+    @Test
+    public void testResolveBaseTriplesMultiplex() throws Exception {
+        Collection<Justification> r4 = engine.getBaseJustifications(null,Collections.singleton(tj3));
+
+        // since i2 is justified by two justifications, the result for i4 also needs to have two
+        Assert.assertEquals(4, r4.size());
+
+        Assert.assertThat(r4,Matchers.<Justification>hasItem(hasProperty("supportingTriples", hasItems(t1,t3,t4))));
+        Assert.assertThat(r4,Matchers.<Justification>hasItem(hasProperty("supportingTriples", hasItems(t2,t3,t4,t5))));
+        Assert.assertThat(r4,Matchers.<Justification>hasItem(hasProperty("supportingTriples", hasItems(t1, t2, t4))));
+        Assert.assertThat(r4,Matchers.<Justification>hasItem(hasProperty("supportingTriples", allOf(hasItems(t2, t4, t5), not(hasItem(t3))))));
+    }
+
+
+    // TODO: a test taking into account transaction justifications
+
+    /**
+     * Test resolution against justifications that are not yet "persisted" but are taken from the current transaction
+     * @throws Exception
+     */
+    @Test
+    public void testTransactionJustifications() throws Exception {
+
+    }
+
+
+    /**
+     * Return a random URI, with a 10% chance of returning a URI that has already been used.
+     * @return
+     */
+    protected KiWiUriResource randomURI() {
+        KiWiUriResource resource = new KiWiUriResource("http://localhost/" + RandomStringUtils.randomAlphanumeric(8));
+        return resource;
+    }
+
+    /**
+     * Return a random RDF value, either a reused object (10% chance) or of any other kind.
+     * @return
+     */
+    protected KiWiNode randomObject() {
+        KiWiNode object;
+        switch(rnd.nextInt(6)) {
+            case 0: object = new KiWiUriResource("http://localhost/" + RandomStringUtils.randomAlphanumeric(8));
+                break;
+            case 1: object = new KiWiAnonResource(RandomStringUtils.randomAscii(8));
+                break;
+            case 2: object = new KiWiStringLiteral(RandomStringUtils.randomAscii(40));
+                break;
+            case 3: object = new KiWiIntLiteral(rnd.nextLong(), new KiWiUriResource(Namespaces.NS_XSD + "integer"));
+                break;
+            case 4: object = new KiWiDoubleLiteral(rnd.nextDouble(), new KiWiUriResource(Namespaces.NS_XSD + "double"));
+                break;
+            case 5: object = new KiWiBooleanLiteral(rnd.nextBoolean(), new KiWiUriResource(Namespaces.NS_XSD + "boolean"));
+                break;
+            default: object = new KiWiUriResource("http://localhost/" + RandomStringUtils.randomAlphanumeric(8));
+                break;
+
+        }
+        return object;
+    }
+
+
+
+    private class MockReasoningEngine extends ReasoningEngine {
+        private MockReasoningEngine() {
+        }
+
+
+        /**
+         * Return the justifications for the triple passed as argument.
+         *
+         * @param t
+         * @return
+         */
+        @Override
+        protected Collection<Justification> getJustifications(KiWiReasoningConnection connection, KiWiTriple t, Set<Justification> transactionJustifications) throws SQLException {
+            HashSet<Justification> justifications = new HashSet<Justification>();
+            justifications.addAll(baseJustifications.get(t));
+            for(Justification j : transactionJustifications) {
+                if(equivalence.equivalent(j.getTriple(), t)) {
+                    justifications.add(j);
+                }
+            }
+            return justifications;
+        }
+
+        /**
+         * For all justifications contained in the set passed as argument, create corresponding base justifications,
+         * i.e. justifications that only contain base triples and no inferred triples.
+         *
+         * @param justifications
+         * @return
+         */
+        @Override
+        public Set<Justification> getBaseJustifications(KiWiReasoningConnection connection, Set<Justification> justifications) throws SQLException, ReasoningException {
+            return super.getBaseJustifications(connection, justifications);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/marmotta/blob/582abb5b/libraries/kiwi/kiwi-reasoner/src/test/java/org/apache/marmotta/kiwi/reasoner/test/engine/ReasoningEngineTest.java
----------------------------------------------------------------------
diff --git a/libraries/kiwi/kiwi-reasoner/src/test/java/org/apache/marmotta/kiwi/reasoner/test/engine/ReasoningEngineTest.java b/libraries/kiwi/kiwi-reasoner/src/test/java/org/apache/marmotta/kiwi/reasoner/test/engine/ReasoningEngineTest.java
index af6bc81..fb2c497 100644
--- a/libraries/kiwi/kiwi-reasoner/src/test/java/org/apache/marmotta/kiwi/reasoner/test/engine/ReasoningEngineTest.java
+++ b/libraries/kiwi/kiwi-reasoner/src/test/java/org/apache/marmotta/kiwi/reasoner/test/engine/ReasoningEngineTest.java
@@ -17,13 +17,18 @@
  */
 package org.apache.marmotta.kiwi.reasoner.test.engine;
 
+import static org.hamcrest.Matchers.hasItem;
+import static org.junit.Assert.fail;
+import info.aduna.iteration.CloseableIteration;
 import info.aduna.iteration.Iterations;
+
+import java.sql.SQLException;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.marmotta.kiwi.config.KiWiConfiguration;
 import org.apache.marmotta.kiwi.model.rdf.KiWiTriple;
-import org.apache.marmotta.kiwi.persistence.KiWiDialect;
 import org.apache.marmotta.kiwi.persistence.KiWiPersistence;
-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.reasoner.engine.ReasoningConfiguration;
 import org.apache.marmotta.kiwi.reasoner.engine.ReasoningEngine;
 import org.apache.marmotta.kiwi.reasoner.model.program.Justification;
@@ -34,7 +39,7 @@ import org.apache.marmotta.kiwi.reasoner.parser.KWRLProgramParserBase;
 import org.apache.marmotta.kiwi.reasoner.persistence.KiWiReasoningConnection;
 import org.apache.marmotta.kiwi.reasoner.persistence.KiWiReasoningPersistence;
 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.apache.marmotta.kiwi.transactions.api.TransactionalSail;
 import org.apache.marmotta.kiwi.transactions.model.TransactionData;
 import org.apache.marmotta.kiwi.transactions.sail.KiWiTransactionalSail;
@@ -45,99 +50,34 @@ 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.Resource;
 import org.openrdf.model.Statement;
 import org.openrdf.model.URI;
 import org.openrdf.repository.Repository;
 import org.openrdf.repository.RepositoryConnection;
+import org.openrdf.repository.RepositoryResult;
 import org.openrdf.repository.sail.SailRepository;
 import org.openrdf.rio.RDFFormat;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-import static org.hamcrest.Matchers.hasItem;
-
 /**
  * This test verifies the functionality of the KiWi Reasonong Engine. Based on a small sample program, it will test
  * both incremental reasoning (by manually adding triples) and full reasoning. After reasoning completes, it will
  * check if the expected inferred triples as well as their justifications are present.
- * <p/>
- * 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>
  *
  * @see org.apache.marmotta.kiwi.persistence.KiWiConnection
  * @see org.apache.marmotta.kiwi.persistence.KiWiPersistence
- * <p/>
- * Author: Sebastian Schaffert
+ * @author Sebastian Schaffert (sschaffert@apache.org)
  */
-@RunWith(Parameterized.class)
+@RunWith(KiWiDatabaseRunner.class)
 public class ReasoningEngineTest {
 
     private static Logger log = LoggerFactory.getLogger(ReasoningEngineTest.class);
 
     private static final String NS = "http://localhost/resource/";
 
-    /**
-     * 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 KiWiStore store;
+     private KiWiStore store;
     private TransactionalSail tsail;
     private KiWiPersistence persistence;
     private KiWiReasoningPersistence rpersistence;
@@ -145,36 +85,23 @@ public class ReasoningEngineTest {
 
     private Repository repository;
 
+    private final KiWiConfiguration config;
 
-    public ReasoningEngineTest(String database, String jdbcUrl, String jdbcUser, String jdbcPass) {
-        this.jdbcPass = jdbcPass;
-        this.jdbcUrl = jdbcUrl;
-        this.jdbcUser = jdbcUser;
-
-        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, dialect);
+    public ReasoningEngineTest(KiWiConfiguration config) {
+        this.config = config;
     }
 
 
     @Before
     public void initDatabase() throws Exception {
-
-        persistence = new KiWiPersistence("test",jdbcUrl,jdbcUser,jdbcPass,dialect);
-        persistence.initDatabase();
-
-
-        store      = new KiWiStore("test",jdbcUrl,jdbcUser,jdbcPass,dialect, "http://localhost/context/default", "http://localhost/context/inferred");
+        store      = new KiWiStore(config);
         tsail      = new KiWiTransactionalSail(store);
         repository = new SailRepository(tsail);
         repository.initialize();
 
+        persistence = store.getPersistence();
+
         rpersistence = new KiWiReasoningPersistence(persistence, repository.getValueFactory());
         rpersistence.initDatabase();
 
@@ -202,26 +129,14 @@ public class ReasoningEngineTest {
 
     @After
     public void dropDatabase() throws Exception {
-        engine.shutdown();
+        engine.shutdown(true);
 
         rpersistence.dropDatabase();
-
         persistence.dropDatabase();
-        persistence.shutdown();
 
         repository.shutDown();
     }
 
-    @org.junit.Rule
-    public TestWatcher watchman = new TestWatcher() {
-        /**
-         * Invoked when a test is about to start
-         */
-        @Override
-        protected void starting(Description description) {
-            log.info("{} being run...", description.getMethodName());
-        }
-    };
 
     /**
      * Test the reasoning engine by incrementally adding and later removing triples through explicit calls to the
@@ -411,6 +326,9 @@ public class ReasoningEngineTest {
                 log.debug("sleeping for 100ms to let engine finish processing ... ");
                 Thread.sleep(100);
             }
+
+            log.debug("reasoning finished, running tests");
+
             con.begin();
 
             List<Statement> inferred4 = Iterations.asList(con.getStatements(a,property,d, true));
@@ -469,6 +387,26 @@ public class ReasoningEngineTest {
             Assert.assertTrue("expected inferred triple not found", con.hasStatement(b,t,d,true));
             Assert.assertTrue("expected inferred triple not found", con.hasStatement(b,s,a,true));
 
+
+            // we also expect that there are justifications for all inferred triples
+            Resource[][] patterns = new Resource[][] {
+                    new Resource[] { a, t, c },
+                    new Resource[] { b, t, d },
+                    new Resource[] { b, s, a }
+            };
+
+
+            RepositoryResult<Statement> result = con.getStatements(null,null,null,true, con.getValueFactory().createURI(store.getInferredContext()));
+            if(result.hasNext()) {
+                while (result.hasNext()) {
+                    Statement stmt1 = result.next();
+
+                    CloseableIteration<Justification, SQLException> justs1 = rcon.listJustificationsForTriple((KiWiTriple) stmt1);
+                    Assert.assertTrue(justs1.hasNext());
+                }
+            } else {
+                fail("no inferred statements found");
+            }
             con.commit();
         } finally {
             con.close();

http://git-wip-us.apache.org/repos/asf/marmotta/blob/582abb5b/libraries/kiwi/kiwi-reasoner/src/test/java/org/apache/marmotta/kiwi/reasoner/test/model/JustificationTest.java
----------------------------------------------------------------------
diff --git a/libraries/kiwi/kiwi-reasoner/src/test/java/org/apache/marmotta/kiwi/reasoner/test/model/JustificationTest.java b/libraries/kiwi/kiwi-reasoner/src/test/java/org/apache/marmotta/kiwi/reasoner/test/model/JustificationTest.java
new file mode 100644
index 0000000..f3a1cb7
--- /dev/null
+++ b/libraries/kiwi/kiwi-reasoner/src/test/java/org/apache/marmotta/kiwi/reasoner/test/model/JustificationTest.java
@@ -0,0 +1,196 @@
+/*
+ * 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.reasoner.test.model;
+
+import org.apache.commons.lang3.RandomStringUtils;
+import org.apache.marmotta.commons.sesame.model.Namespaces;
+import org.apache.marmotta.kiwi.model.rdf.*;
+import org.apache.marmotta.kiwi.reasoner.model.program.Justification;
+import org.apache.marmotta.kiwi.reasoner.model.program.Rule;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.openrdf.model.URI;
+import org.openrdf.model.Value;
+
+import java.util.*;
+
+/**
+ * Test critical functionality of justification objects, primarily equals and similar
+ *
+ * @author Sebastian Schaffert (sschaffert@apache.org)
+ */
+public class JustificationTest {
+
+    protected static Random rnd = new Random();
+
+
+    private KiWiTriple t1, t2, t3, t4; // base
+    private KiWiTriple i1, i2, i3, i4; // inferred
+    private Rule r1, r2;
+
+    private KiWiUriResource ctx_inferred;
+
+    @Before
+    public void setup() {
+        KiWiUriResource s1 = randomURI();
+        KiWiUriResource s2 = randomURI();
+        KiWiUriResource s3 = randomURI();
+        KiWiUriResource s4 = randomURI();
+        KiWiUriResource p1 = randomURI();
+        KiWiUriResource p2 = randomURI();
+        KiWiNode o1 = randomObject();
+        KiWiNode o2 = randomObject();
+        KiWiNode o3 = randomObject();
+        KiWiNode o4 = randomObject();
+
+        ctx_inferred = randomURI();
+
+        t1 = new KiWiTriple(s1,p1,o1, null);
+        t2 = new KiWiTriple(s1,p1,o2, null);
+        t3 = new KiWiTriple(s2,p1,o3, null);
+        t4 = new KiWiTriple(s1,p1,o1, randomURI());
+
+
+        i1 = new KiWiTriple(s1,p2,o1, ctx_inferred); i1.setInferred(true);
+        i2 = new KiWiTriple(s1,p1,o2, ctx_inferred); i2.setInferred(true);
+        i3 = new KiWiTriple(s3,p1,o3, ctx_inferred); i3.setInferred(true);
+        i4 = new KiWiTriple(s1,p2,o1, ctx_inferred); i4.setInferred(true);
+
+
+    }
+
+
+    @Test
+    public void testJustificationEquals() {
+        Justification j1 = new Justification();
+        j1.setTriple(i1);
+        j1.getSupportingTriples().add(t1);
+        j1.getSupportingTriples().add(t4);
+
+        Justification j2 = new Justification();
+        j2.setTriple(i4);
+        j2.getSupportingTriples().add(t4);
+        j2.getSupportingTriples().add(t1);
+
+        Assert.assertEquals(j1,j2);
+
+        // j3 differs in the inferred triple
+        Justification j3 = new Justification();
+        j3.setTriple(i2);
+        j3.getSupportingTriples().add(t4);
+        j3.getSupportingTriples().add(t1);
+
+        Assert.assertNotEquals(j1, j3);
+
+        // j4 differs in the supporting triples
+        Justification j4 = new Justification();
+        j4.setTriple(i1);
+        j4.getSupportingTriples().add(t2);
+        j4.getSupportingTriples().add(t4);
+
+        Assert.assertNotEquals(j1, j4);
+    }
+
+
+    @Test
+    public void testJustificationSet() {
+        Set<Justification> set = new HashSet<>();
+
+        Justification j1 = new Justification();
+        j1.setTriple(i1);
+        j1.getSupportingTriples().add(t1);
+        j1.getSupportingTriples().add(t4);
+        set.add(j1);
+
+        Justification j2 = new Justification();
+        j2.setTriple(i4);
+        j2.getSupportingTriples().add(t4);
+        j2.getSupportingTriples().add(t1);
+
+        Assert.assertTrue(set.contains(j2));
+
+        set.add(j2);
+
+        Assert.assertEquals(1, set.size());
+
+
+        // j3 differs in the inferred triple
+        Justification j3 = new Justification();
+        j3.setTriple(i2);
+        j3.getSupportingTriples().add(t4);
+        j3.getSupportingTriples().add(t1);
+
+        Assert.assertFalse(set.contains(j3));
+
+        set.add(j3);
+
+        Assert.assertEquals(2, set.size());
+
+
+        // j4 differs in the supporting triples
+        Justification j4 = new Justification();
+        j4.setTriple(i1);
+        j4.getSupportingTriples().add(t2);
+        j4.getSupportingTriples().add(t4);
+
+        Assert.assertFalse(set.contains(j4));
+
+        set.add(j4);
+
+        Assert.assertEquals(3, set.size());
+    }
+
+
+
+    /**
+     * Return a random URI, with a 10% chance of returning a URI that has already been used.
+     * @return
+     */
+    protected KiWiUriResource randomURI() {
+        KiWiUriResource resource = new KiWiUriResource("http://localhost/" + RandomStringUtils.randomAlphanumeric(8));
+        return resource;
+    }
+
+    /**
+     * Return a random RDF value, either a reused object (10% chance) or of any other kind.
+     * @return
+     */
+    protected KiWiNode randomObject() {
+        KiWiNode object;
+        switch(rnd.nextInt(6)) {
+            case 0: object = new KiWiUriResource("http://localhost/" + RandomStringUtils.randomAlphanumeric(8));
+                break;
+            case 1: object = new KiWiAnonResource(RandomStringUtils.randomAscii(8));
+                break;
+            case 2: object = new KiWiStringLiteral(RandomStringUtils.randomAscii(40));
+                break;
+            case 3: object = new KiWiIntLiteral(rnd.nextLong(), new KiWiUriResource(Namespaces.NS_XSD + "integer"));
+                break;
+            case 4: object = new KiWiDoubleLiteral(rnd.nextDouble(), new KiWiUriResource(Namespaces.NS_XSD + "double"));
+                break;
+            case 5: object = new KiWiBooleanLiteral(rnd.nextBoolean(), new KiWiUriResource(Namespaces.NS_XSD + "boolean"));
+                break;
+            default: object = new KiWiUriResource("http://localhost/" + RandomStringUtils.randomAlphanumeric(8));
+                break;
+
+        }
+        return object;
+    }
+
+}