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 2013/02/19 13:51:59 UTC

[48/52] [partial] code contribution, initial import of relevant modules of LMF-3.0.0-SNAPSHOT based on revision 4bf944319368 of the default branch at https://code.google.com/p/lmf/

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/sail/KiWiReasoningSail.java
----------------------------------------------------------------------
diff --git a/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/sail/KiWiReasoningSail.java b/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/sail/KiWiReasoningSail.java
new file mode 100644
index 0000000..229a306
--- /dev/null
+++ b/kiwi/kiwi-reasoner/src/main/java/org/apache/marmotta/kiwi/reasoner/sail/KiWiReasoningSail.java
@@ -0,0 +1,401 @@
+/**
+ * Copyright (C) 2013 Salzburg Research.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.marmotta.kiwi.reasoner.sail;
+
+import info.aduna.iteration.CloseableIteration;
+import info.aduna.iteration.ExceptionConvertingIteration;
+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;
+import org.apache.marmotta.kiwi.reasoner.model.program.Program;
+import org.apache.marmotta.kiwi.reasoner.model.program.Rule;
+import org.apache.marmotta.kiwi.reasoner.parser.KWRLProgramParser;
+import org.apache.marmotta.kiwi.reasoner.parser.KWRLProgramParserBase;
+import org.apache.marmotta.kiwi.reasoner.parser.ParseException;
+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.transactions.api.TransactionalSail;
+import org.apache.marmotta.kiwi.transactions.wrapper.TransactionalSailWrapper;
+import org.openrdf.sail.SailException;
+import org.openrdf.sail.StackableSail;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.sql.SQLException;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * This sail adds the KWRL reasoner to the stack of sails. Because the reasoner is tightly coupled with the
+ * database schema of KiWi, it requires that the persistence on the root of the stack is a KiWiStore.
+ * <p/>
+ * Author: Sebastian Schaffert (sschaffert@apache.org)
+ */
+public class KiWiReasoningSail extends TransactionalSailWrapper {
+
+    private static Logger log = LoggerFactory.getLogger(KiWiReasoningSail.class);
+
+    private ReasoningConfiguration   config;
+
+    private ReasoningEngine          engine;
+
+    private KiWiReasoningPersistence persistence;
+
+    private boolean initialized = false;
+
+    public KiWiReasoningSail(TransactionalSail parent, ReasoningConfiguration config) {
+        super(parent);
+        this.config = config;
+    }
+
+    @Override
+    public void initialize() throws SailException {
+        synchronized (this) {
+            if(!initialized) {
+                super.initialize();
+
+                KiWiStore store = getBaseStore();
+
+                try {
+                    persistence = new KiWiReasoningPersistence(store.getPersistence(), getValueFactory());
+                    persistence.initDatabase();
+
+                    engine      = new ReasoningEngine(persistence,this,config);
+                    addTransactionListener(engine);
+
+                    initialized = true;
+                } catch (SQLException e) {
+                    log.error("error initializing reasoning database",e);
+                    throw new SailException("error initializing reasoning database",e);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void shutDown() throws SailException {
+        engine.shutdown();
+        super.shutDown();
+    }
+
+    /**
+     * Return the KiWi store that is at the base of the SAIL stack. Throws an IllegalArgumentException in case the base
+     * store is not a KiWi store.
+     *
+     * @return
+     */
+    public KiWiStore getBaseStore() {
+        StackableSail current = this;
+        while(current != null && current.getBaseSail() instanceof StackableSail) {
+            current = (StackableSail) current.getBaseSail();
+        }
+        if(current != null && current.getBaseSail() instanceof KiWiStore) {
+            return (KiWiStore) current.getBaseSail();
+        } else {
+            throw new IllegalStateException("the base store is not a KiWiStore (type: "+current.getBaseSail().getClass().getCanonicalName()+")!");
+        }
+    }
+
+    public ReasoningConfiguration getConfig() {
+        return config;
+    }
+
+    /**
+     * Add a program to the reasoner using the given name. The program data will be read from the stream passed as
+     * second argument. The program is persisted to the database and the reasoning engine is
+     * notified of the added rules and immediately calculates the inferences. Inferencing in this case is
+     * synchronous, so the method only returns when the first round of reasoning is completed for all added
+     * rules.
+     * <p/>
+     * If a program with this name already exists, a SailException is thrown. To update existing programs,
+     * please use updateProgram().
+     *
+     * @param name a unique name for the program
+     * @param data the program data in KWRL syntax
+     * @throws IOException    in case the stream cannot be read
+     * @throws SailException  in case the program already exists
+     * @throws ParseException in case the program cannot be parsed
+     */
+    public void addProgram(String name, InputStream data) throws IOException, SailException, ParseException {
+        KWRLProgramParserBase parser = new KWRLProgramParser(getValueFactory(), data);
+        Program p = parser.parseProgram();
+        p.setName(name);
+
+        addProgram(p);
+    }
+
+    /**
+     * Add a program to the reasoner. The program is persisted to the database and the reasoning engine is
+     * notified of the added rules and immediately calculates the inferences. Inferencing in this case is
+     * synchronous, so the method only returns when the first round of reasoning is completed for all added
+     * rules.
+     * <p/>
+     * If a program with this name already exists, a SailException is thrown. To update existing programs,
+     * please use updateProgram().
+     *
+     * @param program the program data in KWRL syntax
+     * @throws SailException  in case the program already exists
+     */
+    public void addProgram(Program program) throws SailException {
+        // store program in the database
+        try {
+            KiWiReasoningConnection connection = persistence.getConnection();
+            try {
+                // should not throw an exception and the program should have a database ID afterwards
+                connection.storeProgram(program);
+                connection.commit();
+            } finally {
+                connection.close();
+            }
+        } catch (SQLException ex) {
+            throw new SailException("cannot store program in database",ex);
+        }
+
+        engine.loadPrograms();
+
+        // now add all added rules to the reasoner
+        for(Rule rule : program.getRules()) {
+            engine.notifyAddRule(rule);
+        }
+    }
+
+    /**
+     * Update the program with the name given as argument using the data provided in the stream.
+     * This method will first calculate the difference between the
+     * previous version of the program and the new version of the program. It then updates the program in
+     * the database and notifies the engine of all removed and added rules.
+     *
+     * @throws IOException    in case the stream cannot be read
+     * @throws SailException  in case the program already exists
+     * @throws ParseException in case the program cannot be parsed
+     */
+    public void updateProgram(String name, InputStream data) throws IOException, SailException, ParseException  {
+        KWRLProgramParserBase parser = new KWRLProgramParser(getValueFactory(), data);
+        Program p = parser.parseProgram();
+        p.setName(name);
+
+        updateProgram(p);
+    }
+
+
+    /**
+     * Update the program given as argument. This method will first calculate the difference between the
+     * previous version of the program and the new version of the program. It then updates the program in
+     * the database and notifies the engine of all removed and added rules.
+     *
+     * @param program  the updated version of the program
+     * @throws SailException in case a database error occurs
+     */
+    public void updateProgram(Program program) throws SailException {
+        Set<Rule> added = new HashSet<Rule>();
+        Set<Rule> removed = new HashSet<Rule>();
+        try {
+            KiWiReasoningConnection connection = persistence.getConnection();
+            try {
+                // load old version of program and calculate difference
+                Program old = connection.loadProgram(program.getName());
+                if(old != null) {
+                    for(Rule r : old.getRules()) {
+                        if(!program.getRules().contains(r)) {
+                            removed.add(r);
+                        }
+                    }
+                    for(Rule r : program.getRules()) {
+                        if(!old.getRules().contains(r)) {
+                            added.add(r);
+                        }
+                    }
+
+                }
+
+                // store program in the database
+                connection.updateProgram(program);
+                connection.commit();
+            } finally {
+                connection.close();
+            }
+        } catch (SQLException ex) {
+            throw new SailException("cannot store program in database",ex);
+        }
+
+        engine.loadPrograms();
+
+        // if rules have been removed, clean up
+        if(removed.size() > 0) {
+            engine.notifyRemoveRules();
+        }
+
+        // now add all added rules to the reasoner
+        for(Rule rule : added) {
+            engine.notifyAddRule(rule);
+        }
+    }
+
+    /**
+     * List all reasoning programs currently stored in the triplestore.
+     *
+     * @return
+     */
+    public CloseableIteration<Program,SailException> listPrograms() throws SailException {
+        try {
+            final KiWiReasoningConnection connection = persistence.getConnection();
+
+            return new ExceptionConvertingIteration<Program, SailException>(connection.listPrograms()) {
+                /**
+                 * Converts an exception from the underlying iteration to an exception of
+                 * type <tt>X</tt>.
+                 */
+                @Override
+                protected SailException convert(Exception e) {
+                    return new SailException(e);
+                }
+
+                @Override
+                protected void handleClose() throws SailException {
+                    super.handleClose();
+
+                    try {
+                        connection.commit();
+                        connection.close();
+                    } catch (SQLException ex) {
+                        throw new SailException("database error while committing/closing connection");
+                    }
+                }
+            };
+        } catch (SQLException ex) {
+            throw new SailException("cannot list programs in database",ex);
+        }
+
+    }
+
+
+    /**
+     * Return the program with the given name. In case the program does not exist, the method will
+     * return null.
+     *
+     * @param name the unique name of the program to retrieve
+     * @return the parsed program, or null in case a program with the given name does not exist
+     * @throws SailException  in case an error occurs
+     */
+    public Program getProgram(String name) throws SailException {
+        try {
+            KiWiReasoningConnection connection = persistence.getConnection();
+            try {
+                // should not throw an exception and the program should have a database ID afterwards
+                Program p = connection.loadProgram(name);
+                connection.commit();
+                return p;
+            } finally {
+                connection.close();
+            }
+        } catch (SQLException ex) {
+            throw new SailException("cannot load program from database",ex);
+        }
+    }
+
+
+    /**
+     * Remove the program with the given name. This method will first remove the program from the database and
+     * then inform the reasoning engine to run cleanups.
+     * <p/>
+     * If a program with this name does not exist, does nothing
+     *
+     * @param name the unique name of the program to remove
+     * @throws SailException
+     */
+    public void deleteProgram(String name) throws SailException {
+        try {
+            KiWiReasoningConnection connection = persistence.getConnection();
+            try {
+                Program p = connection.loadProgram(name);
+                connection.deleteProgram(p);
+                connection.commit();
+            } finally {
+                connection.close();
+            }
+        } catch (SQLException ex) {
+            throw new SailException("cannot load program from database",ex);
+        }
+        engine.loadPrograms();
+        engine.notifyRemoveRules();
+    }
+
+
+    /**
+     * Clean all inferred triples and re-run all reasoning rules.
+     */
+    public void reRunPrograms() {
+        engine.reRunPrograms();
+    }
+
+    /**
+     * Return a reference to the underlying reasoning engine.
+     * @return
+     */
+    public ReasoningEngine getEngine() {
+        return engine;
+    }
+
+    /**
+     * Return a reference to the underlying database persistence layer.
+     * @return
+     */
+    public KiWiReasoningPersistence getPersistence() {
+        return persistence;
+    }
+
+    /**
+     * List the justifications for the triple with the id given as argument. For informational purposes.
+     *
+     * @param tripleId
+     * @return
+     * @throws SailException
+     */
+    public CloseableIteration<Justification,SailException> justify(long tripleId) throws SailException {
+        try {
+            final KiWiReasoningConnection connection = persistence.getConnection();
+
+            return new ExceptionConvertingIteration<Justification, SailException>(connection.listJustificationsForTriple(tripleId)) {
+                /**
+                 * Converts an exception from the underlying iteration to an exception of
+                 * type <tt>X</tt>.
+                 */
+                @Override
+                protected SailException convert(Exception e) {
+                    return new SailException(e);
+                }
+
+                @Override
+                protected void handleClose() throws SailException {
+                    super.handleClose();
+
+                    try {
+                        connection.commit();
+                        connection.close();
+                    } catch (SQLException ex) {
+                        throw new SailException("database error while committing/closing connection");
+                    }
+                }
+            };
+        } catch (SQLException ex) {
+            throw new SailException("cannot list programs in database",ex);
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/kiwi-reasoner/src/main/javacc/org/apache/marmotta/kiwi/reasoner/parser/KWRLProgramParser.jj
----------------------------------------------------------------------
diff --git a/kiwi/kiwi-reasoner/src/main/javacc/org/apache/marmotta/kiwi/reasoner/parser/KWRLProgramParser.jj b/kiwi/kiwi-reasoner/src/main/javacc/org/apache/marmotta/kiwi/reasoner/parser/KWRLProgramParser.jj
new file mode 100644
index 0000000..0d033bf
--- /dev/null
+++ b/kiwi/kiwi-reasoner/src/main/javacc/org/apache/marmotta/kiwi/reasoner/parser/KWRLProgramParser.jj
@@ -0,0 +1,352 @@
+/*
+ * Copyright (c) 2013 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+/** JavaCC parser of simple KWRL programs.
+ *
+ * Simplified for the Linked Media Framework (2011), adapted for Apache Marmotta (2013).
+ * 
+ * 
+ * @author Jakub Kotowski
+ * @author Sebastian Schaffert
+ *
+ */
+ // ($1 http://rdf.com/type $2), ($1 http://rdf.com/subclass $3) -> ($3 http://rdf.com/superclass http://zoo.org/animal)
+ // (http://www.seznam.cz/subj http://www.seznam.cz/prop http://www.seznam.cz/obj) -> (http://sign-o-matic.com/ont#subj http://sign-o-matic.com/ont#prop http://sign-o-matic.com/ont#obj)
+ //($1 http://ex.com/p $2), ($2 http://ex.com/p $1), ($1 http://ex.com/sc "ahoj kocko") -> ($4 $5 'a tady zkusime neco delsiho a hroznejsiho):""#$@%///...')
+ //($1 http://ex.com/p $2), ($2 http://ex.com/p $1), ($1 http://ex.com/sc "ahoj kocko") -> ($4 $5 "a tady zkusime neco delsiho a hroznejsiho):#$@%///..."^^http://example.com/type#string)
+ //($1 http://ex.com/p $2) -> (uri(http://ex.com/p) @ $3  $4  $5)
+ //($1 http://ex.com/p $2) -> (uri(http://ex.com/p) @ $3  $4  uri(http://www.kiwi-project.eu/core/tagging/) @ $5)
+
+ /**
+@prefix rdf: <http://w3c.org/rdf/>
+@prefix rdfs: <http://w3c.org/rdfs/pat/hayes/mt#>
+
+(($1 http://rdf.com/type $2) , ($1 http://rdf.com/subclass $3)) -> ($3 http://rdf.com/superclass http://zoo.org/animal)
+(($1 rdf:type $2) , ($1 rdf:subclass $3)) -> ($3 rdf:superclass xxx:animal)
+rdf-subclass-transitivity: (($c1 rdfs:subClassOf $c2) , ($c2 rdfs:subClassOf $c3)) -> ($c1 rdfs:subClassOf $c3),($3 rdf:superclass xxx:animal)
+(($1 rdf:type $1') , ($1' rdf:subclass $3)) -> ($3 rdf:superclass xxx:animal)
+
+(($1 rdf:type $1');($2 rdf:type $2')), not ($3 rdf:type $3') , 
+(($3 rdf:type $3'),not ($3 rdf:type $3'))
+									-> inconsistency
+ */
+ 
+options {
+  STATIC=false;
+  KEEP_LINE_COLUMN = false;
+  CACHE_TOKENS=true;
+//  FORCE_LA_CHECK=true;
+//  CHOICE_AMBIGUITY_CHECK=5;
+  LOOKAHEAD=2147483647;
+//  DEBUG_PARSER=true;
+//  DEBUG_TOKEN_MANAGER=true;
+//  DEBUG_LOOKAHEAD=true;
+}
+
+PARSER_BEGIN(KWRLProgramParser)
+package org.apache.marmotta.kiwi.reasoner.parser;
+
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.InputStream;
+import org.openrdf.model.*;
+import java.util.*;
+import org.apache.marmotta.kiwi.reasoner.model.program.*;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+public class KWRLProgramParser extends KWRLProgramParserBase {
+
+    public KWRLProgramParser(Reader in, ValueFactory valueFactory) {
+        this(in);
+
+        setValueFactory(valueFactory);
+    }
+
+    public KWRLProgramParser(ValueFactory valueFactory, String content) {
+        this(new StringReader(content));
+
+        setValueFactory(valueFactory);
+    }
+
+    public KWRLProgramParser(ValueFactory valueFactory, InputStream in) {
+        this(in);
+
+        setValueFactory(valueFactory);
+    }
+
+    public KWRLProgramParser(ValueFactory valueFactory, InputStream in, String encoding) {
+        this(in, encoding);
+
+         setValueFactory(valueFactory);
+    }
+
+
+}
+PARSER_END(KWRLProgramParser)
+
+SKIP :
+{
+ 	" "
+|	"\r"
+|	"\t"
+|	"\n"
+}
+
+MORE:
+{
+ "\"" : WithinString
+}
+
+<WithinString> TOKEN:
+{
+  <STRLIT: "\""> : DEFAULT
+}
+
+<WithinString> MORE:
+{
+  <~["\n","\r"]>
+}
+
+TOKEN : /* OPERATORS */
+{
+	< AND: "," > |
+	< OR: ";" > |
+	< NOT: "not" > |
+  	< THEN: "->" > |
+  	< TYPE: "^^" > 
+}
+
+TOKEN :
+{
+  	< LEFTP: "(" > |
+  	< RIGHTP: ")" > |
+  	< INCONSISTENCY: "inconsistency" > |
+  	//URICHARS should oficially include "(" and ")", I excluded them because of resulting conflicts with the rule grammar  	  
+  	//< URICHARS: ["a"-"z","A"-"Z","0"-"9",";","/","?",":","@","&","=","+","$",".","-","_","!","~","*","'","%"] > |
+    // URI is oficially more general see the comment below
+    //TODO: parser now allows URIs ending with # so that the same URI token spec can be used for namespaces too                                                                    
+    < URI: ["a"-"z","A"-"Z"](["a"-"z","A"-"Z","0"-"9","+","-","."])* "://"  (["a"-"z","A"-"Z","0"-"9",";","/","?",":","@","&","=","+","$",".","-","_","!","~","*","'","%"])+ ("#" (["a"-"z","A"-"Z","0"-"9",";","/","?",":","@","&","=","+","$",".","-","_","!","~","*","'","%"])*)? | "#" (["a"-"z","A"-"Z","0"-"9",";","/","?",":","@","&","=","+","$",".","-","_","!","~","*","'","%"])+> |
+    < NS_URI: <IDENTIFIER> ":" (<URICHAR>)+ > | 
+    < VARIABLE: "$" <IDENTIFIER> > |
+    < URICONSTRUCTION: "uri" > |
+    < IDENTIFIER: ["a"-"z","A"-"Z","0"-"9","_"](["a"-"z","A"-"Z","0"-"9","_","'","-"])* > |
+    < #URICHAR: ["a"-"z","A"-"Z","0"-"9",";","/","?",":","@","&","=","+","$",".","-","_","!","~","*","'","%"] >
+}
+//https://javacc.dev.java.net/doc/javaccgrm.html
+//	If the label is preceded by a "#", then this regular expression may not be referred to from expansion units, but only from within other regular expressions. When the "#" is present, the regular expression is referred to as a "private regular expression". 
+//  If the "#" is omitted, the token manager will erroneously recognize a string like E123 as a legal token of kind EXPONENT (instead of IDENTIFIER in the Java grammar).
+ 
+// (([a-zA-Z][0-9a-zA-Z+\\-\\.]*:)?/{0,2}[0-9a-zA-Z;/?:@&=+$\\.\\-_!~*'()%]+)? (#[0-9a-zA-Z;/?:@&=+$\\.\\-_!~*'()%]+)?
+// http://aspn.activestate.com/ASPN/Mail/Message/xml-dev/754445
+
+Program Program() :
+{
+    Program program = new Program();
+    int nullNameCount = 0;
+    Rule rule;
+    Token name = null;
+    Token prefix = null;
+     Token uri;
+}
+{
+  (
+    "@prefix" prefix = <IDENTIFIER> ":" "<" uri = <URI> ">" (";")? {
+        program.addNamespace(prefix.image, uri.image );
+        namespaces.put(prefix.image, uri.image);
+    }
+  )*
+
+  (
+    (name = <IDENTIFIER> ":")? rule = Rule()
+    { 
+    if (name == null) {
+    	nullNameCount++;
+    	rule.setName("rule_" + nullNameCount);
+    } else {
+    	rule.setName(name.image);
+    } 
+    
+    program.addRule(rule); name=null; //it's declared above, once set it doesn't get cleared
+    }
+  )+
+  <EOF>
+  {
+    return program;
+  }
+}
+
+
+Rule Rule() : {
+    Pattern head;
+    List<Pattern> body;
+    Rule rule = new Rule();
+    startRule();
+}
+{
+    body = Body() <THEN> head = HeadTriplePattern() (";"|".")?
+    {
+        rule.setHead(head);
+        rule.setBody(body);
+        endRule();
+        return rule;
+    }
+}
+
+
+List<Pattern> Body() : {
+    Pattern       pattern, pattern2;
+    List<Pattern> body = new LinkedList<Pattern>();
+}
+{
+    pattern = BodyTriplePattern() { body.add(pattern); } ( "," pattern2 = BodyTriplePattern() { body.add(pattern2); } )*
+    { return body; }
+}
+
+
+Pattern BodyTriplePattern() : {Field subject; Field property; Field object; }
+{
+	<LEFTP> subject = Subject() property = Property() object = BodyObject() <RIGHTP>
+	{
+	  return new Pattern(subject, property, object);
+	}
+}
+
+Pattern HeadTriplePattern() : {Field subject; Field property; Field object; }
+{
+	<LEFTP> subject = HeadSubject() property = Property() object = HeadObject() <RIGHTP>
+	{
+	  return new Pattern(subject, property, object);
+	}
+}
+
+Field HeadSubject() : { ResourceField uri = null; VariableField variable = null; }
+{
+(
+	uri = Uri() | variable = Variable()
+)
+	{
+		if (uri != null) {
+			return uri;
+		}
+		
+		return variable;
+	}
+}
+
+Field Property() : { ResourceField uri = null; VariableField variable = null; }
+{
+(
+	uri = Uri() | variable = Variable()
+)
+	{
+		if (uri != null)
+			return uri;
+			
+		return variable;
+	}	
+}
+
+Field HeadObject() : { ResourceField uri = null; VariableField variable = null; LiteralField literal = null; }
+{
+	(
+	  uri = Uri() | variable = Variable() | literal = Literal()
+	)
+	{
+		if (uri != null)
+			return uri;
+			
+		if (variable != null)
+			return variable;
+			
+		return literal;
+	}	
+}
+
+Field Subject() : { ResourceField uri = null; VariableField variable = null; }
+{
+(
+	uri = Uri() | variable = Variable()
+)
+	{
+		if (uri != null)
+			return uri;
+			
+		if (variable != null)
+			return variable;
+	}
+}
+
+Field BodyObject() : { ResourceField uri = null; VariableField variable = null; LiteralField literal = null; }
+{
+	(
+	  uri = Uri() | variable = Variable() | literal = Literal() 
+	)
+	{
+		if (uri != null)
+			return uri;
+			
+		if (variable != null)
+			return variable;
+			
+		return literal;
+	}	
+}
+
+ResourceField Uri() : { Token uri; }
+{
+	(
+	    "<" uri = <URI> ">"
+	    {
+	      return getResource(uri.image);
+	    }
+	  | uri = <URI> // backwards compatibility
+	    {
+	      log.warn("DEPRECATION: please enclose URIs always in < and >; URI was: {}", uri.image);
+	      return getResource(uri.image);
+	    }
+	  | uri = <NS_URI>
+	    {
+	      return getResourceByNS(uri.image);
+	    }
+	)
+}
+
+VariableField Variable() : {Token t;}
+{
+    t = <VARIABLE>
+    {
+      return getVariable(t.image);
+    }
+}
+
+//TODO language tags
+LiteralField Literal() : {Token literal = null; Token type = null;}
+{
+(
+   literal = <STRLIT>  (<TYPE> type = <URI>)? | "\"\""
+){ //have to remove the leading " character first
+	if (type != null) {
+	    return new LiteralField(resolveLiteral(literal.image.substring(1, literal.image.length()-1), Locale.getDefault(), type.image));
+	}
+	
+	return new LiteralField(resolveLiteral(literal.image.substring(1, literal.image.length()-1), Locale.getDefault(), null));
+  }
+
+}
+
+

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/h2/create_reasoner_tables.sql
----------------------------------------------------------------------
diff --git a/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/h2/create_reasoner_tables.sql b/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/h2/create_reasoner_tables.sql
new file mode 100644
index 0000000..cb24c14
--- /dev/null
+++ b/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/h2/create_reasoner_tables.sql
@@ -0,0 +1,62 @@
+CREATE SEQUENCE seq_programs;
+CREATE SEQUENCE seq_rules;
+CREATE SEQUENCE seq_justifications;
+
+-- a table for representing metadata about complete reasoner programs
+CREATE TABLE reasoner_programs (
+  id          bigint         NOT NULL,
+  name        varchar(64) NOT NULL,
+  description TEXT,
+  PRIMARY KEY (id)
+);
+
+-- reasoner rules consist of an id, a program name, a rule name, and the parsable content in sKWRL syntax
+CREATE TABLE reasoner_rules (
+  id          bigint         NOT NULL,
+  name        varchar(64),
+  description TEXT,
+  body        TEXT        NOT NULL,
+  createdAt   TIMESTAMP   NOT NULL,
+  PRIMARY KEY (id)
+);
+
+-- a table for representing namespace configurations per reasoner program
+CREATE TABLE reasoner_program_namespaces (
+  program_id  bigint           NOT NULL REFERENCES reasoner_programs(id) ON DELETE CASCADE,
+  ns_prefix   VARCHAR(64)   NOT NULL,
+  ns_uri      VARCHAR(2048) NOT NULL
+);
+
+-- join table between programs and rules
+CREATE TABLE reasoner_program_rules (
+  program_id     bigint         NOT NULL REFERENCES reasoner_programs(id) ON DELETE CASCADE,
+  rule_id        bigint         NOT NULL REFERENCES reasoner_rules(id) ON DELETE CASCADE
+);
+
+-- justifications: support a single triple by other base triples and rules
+CREATE TABLE reasoner_justifications (
+  id        bigint      NOT NULL,
+  triple_id bigint      NOT NULL REFERENCES triples(id),
+  createdAt TIMESTAMP   NOT NULL,
+  PRIMARY KEY (id)
+);
+
+-- join table from justifications to supporting triples
+CREATE TABLE reasoner_just_supp_triples (
+  justification_id  bigint NOT NULL REFERENCES reasoner_justifications(id) ON DELETE CASCADE,
+  triple_id         bigint NOT NULL REFERENCES triples(id) ON DELETE CASCADE
+);
+
+
+-- join table from justifications to supporting rules
+CREATE TABLE reasoner_just_supp_rules (
+  justification_id  bigint NOT NULL REFERENCES reasoner_justifications(id) ON DELETE CASCADE,
+  rule_id           bigint NOT NULL REFERENCES reasoner_rules(id) ON DELETE CASCADE
+);
+
+
+CREATE INDEX idx_justification_triple ON reasoner_justifications (triple_id);
+CREATE INDEX idx_just_supp_rules_just ON reasoner_just_supp_rules(justification_id);
+CREATE INDEX idx_just_supp_rules_rule ON reasoner_just_supp_rules(rule_id);
+CREATE INDEX idx_just_supp_triples_just ON reasoner_just_supp_triples(justification_id);
+CREATE INDEX idx_just_supp_triples_triple ON reasoner_just_supp_triples(triple_id);

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/h2/drop_reasoner_tables.sql
----------------------------------------------------------------------
diff --git a/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/h2/drop_reasoner_tables.sql b/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/h2/drop_reasoner_tables.sql
new file mode 100644
index 0000000..00109e5
--- /dev/null
+++ b/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/h2/drop_reasoner_tables.sql
@@ -0,0 +1,17 @@
+DROP INDEX idx_justification_triple;
+DROP INDEX idx_just_supp_rules_just;
+DROP INDEX idx_just_supp_rules_rule;
+DROP INDEX idx_just_supp_triples_just;
+DROP INDEX idx_just_supp_triples_triple;
+
+DROP TABLE IF EXISTS reasoner_just_supp_rules;
+DROP TABLE IF EXISTS reasoner_just_supp_triples;
+DROP TABLE IF EXISTS reasoner_justifications;
+DROP TABLE IF EXISTS reasoner_program_rules;
+DROP TABLE IF EXISTS reasoner_rules;
+DROP TABLE IF EXISTS reasoner_program_namespaces;
+DROP TABLE IF EXISTS reasoner_programs;
+
+DROP SEQUENCE IF EXISTS seq_rules;
+DROP SEQUENCE IF EXISTS seq_justifications;
+DROP SEQUENCE IF EXISTS seq_programs;

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/h2/statements.properties
----------------------------------------------------------------------
diff --git a/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/h2/statements.properties b/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/h2/statements.properties
new file mode 100644
index 0000000..36677ee
--- /dev/null
+++ b/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/h2/statements.properties
@@ -0,0 +1,72 @@
+#
+# Copyright (C) 2013 Salzburg Research.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# get sequence numbers
+seq.rules             = SELECT nextval('seq_rules')
+seq.programs          = SELECT nextval('seq_programs')
+seq.justifications    = SELECT nextval('seq_justifications')
+
+# rules
+rules.insert          = INSERT INTO reasoner_rules (id,name,description,body,createdAt) VALUES (?,?,?,?,now())
+rules.load_by_id      = SELECT id, name, description, body, createdAt FROM reasoner_rules WHERE id = ?
+rules.load_by_program = SELECT R.id, R.name, R.description, R.body, R.createdAt \
+  FROM reasoner_rules R, reasoner_program_rules J \
+  WHERE J.program_id = ? AND J.rule_id = R.id
+
+# delete a rule by id; the entries in reasoner_program_rules are deleted by cascading
+rules.delete_by_id    = DELETE FROM reasoner_rules WHERE id = ?
+
+
+namespaces.load_by_program = SELECT ns_prefix, ns_uri FROM reasoner_program_namespaces WHERE program_id = ?
+namespaces.load_by_rule    = SELECT NS.ns_prefix, NS.ns_uri FROM reasoner_program_namespaces NS, reasoner_program_rules R \
+  WHERE NS.program_id = R.program_id AND R.rule_id = ?
+
+programs.insert       = INSERT INTO reasoner_programs (id, name, description) VALUES (?,?,?)
+programs.add_rule     = INSERT INTO reasoner_program_rules (program_id, rule_id) VALUES (?,?)
+programs.add_ns       = INSERT INTO reasoner_program_namespaces (program_id, ns_prefix, ns_uri) VALUES (?,?,?)
+programs.load_by_id   = SELECT id, name, description FROM reasoner_programs WHERE id = ?
+programs.load_by_name = SELECT id, name, description FROM reasoner_programs WHERE name = ?
+programs.list         = SELECT id, name, description FROM reasoner_programs
+programs.delete       = DELETE FROM reasoner_programs WHERE id = ?
+programs.update_desc  = UPDATE reasoner_programs SET description = ? WHERE id = ?
+programs.delete_ns    = DELETE FROM reasoner_program_namespaces WHERE program_id = ? AND ns_prefix = ? AND ns_uri = ?
+programs.delete_rule  = DELETE FROM reasoner_program_rules WHERE program_id = ? AND rule_id = ?
+
+justifications.insert     = INSERT INTO reasoner_justifications (id, triple_id, createdAt) VALUES (?,?,?)
+justifications.add_triple = INSERT INTO reasoner_just_supp_triples (justification_id, triple_id) VALUES (?,?)
+justifications.add_rule   = INSERT INTO reasoner_just_supp_rules (justification_id, rule_id) VALUES (?,?)
+
+justifications.load_by_id     = SELECT id, triple_id, createdAt FROM reasoner_justifications WHERE id = ?
+justifications.load_by_triple = SELECT id, triple_id, createdAt FROM reasoner_justifications WHERE triple_id = ?
+justifications.load_by_striple = SELECT DISTINCT J.id, J.triple_id, J.createdAt \
+  FROM reasoner_justifications J, reasoner_just_supp_triples T \
+  WHERE J.id = T.justification_id AND T.triple_id = ?
+justifications.load_by_srule   = SELECT DISTINCT J.id, J.triple_id, J.createdAt \
+  FROM reasoner_justifications J, reasoner_just_supp_rules R \
+  WHERE J.id = R.justification_id AND R.rule_id = ?
+justifications.load_rules     = SELECT rule_id FROM reasoner_just_supp_rules WHERE justification_id = ?
+justifications.load_triples   = SELECT triple_id FROM reasoner_just_supp_triples WHERE justification_id = ?
+
+justifications.del_triple = DELETE FROM reasoner_just_supp_triples WHERE justification_id = ?
+justifications.del_rule   = DELETE FROM reasoner_just_supp_rules WHERE justification_id = ?
+justifications.delete     = DELETE FROM reasoner_justifications WHERE id = ?
+
+justifications.delete_all_triples = DELETE FROM reasoner_just_supp_triples
+justifications.delete_all_rules   = DELETE FROM reasoner_just_supp_rules
+justifications.delete_all         = DELETE FROM reasoner_justifications
+
+justifications.list_unsupported = SELECT T.id,T.subject,T.predicate,T.object,T.context,T.deleted,T.inferred,T.creator,T.createdAt,T.deletedAt \
+  FROM triples T WHERE T.deleted = false AND T.inferred = true AND NOT EXISTS (SELECT id FROM reasoner_justifications WHERE triple_id = T.id)

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/mysql/create_reasoner_tables.sql
----------------------------------------------------------------------
diff --git a/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/mysql/create_reasoner_tables.sql b/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/mysql/create_reasoner_tables.sql
new file mode 100644
index 0000000..d81ea21
--- /dev/null
+++ b/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/mysql/create_reasoner_tables.sql
@@ -0,0 +1,75 @@
+CREATE TABLE seq_programs (id BIGINT NOT NULL);
+INSERT INTO seq_programs(id) VALUES (0);
+
+CREATE TABLE seq_rules (id BIGINT NOT NULL);
+INSERT INTO seq_rules VALUES (0);
+
+CREATE TABLE seq_justifications (id BIGINT NOT NULL);
+INSERT INTO seq_justifications(id) VALUES (0);
+
+-- Sequences in MySQL:
+-- UPDATE sequence SET id=LAST_INSERT_ID(id+1);
+-- SELECT LAST_INSERT_ID();
+
+
+-- a table for representing metadata about complete reasoner programs
+CREATE TABLE reasoner_programs (
+  id          bigint         NOT NULL,
+  name        varchar(64) NOT NULL,
+  description TEXT,
+  PRIMARY KEY (id)
+)  CHARACTER SET utf8 COLLATE utf8_bin;
+
+-- reasoner rules consist of an id, a program name, a rule name, and the parsable content in sKWRL syntax
+CREATE TABLE reasoner_rules (
+  id          bigint         NOT NULL,
+  name        varchar(64),
+  description TEXT,
+  body        TEXT        NOT NULL,
+  createdAt   TIMESTAMP   NOT NULL,
+  PRIMARY KEY (id)
+)  CHARACTER SET utf8 COLLATE utf8_bin;
+
+
+-- a table for representing namespace configurations per reasoner program
+CREATE TABLE reasoner_program_namespaces (
+  program_id  bigint           NOT NULL REFERENCES reasoner_programs(id) ON DELETE CASCADE,
+  ns_prefix   VARCHAR(64)   NOT NULL,
+  ns_uri      VARCHAR(2048) NOT NULL,
+  PRIMARY KEY (program_id, ns_prefix)
+)  CHARACTER SET utf8 COLLATE utf8_bin;
+
+-- join table between programs and rules
+CREATE TABLE reasoner_program_rules (
+  program_id     bigint         NOT NULL REFERENCES reasoner_programs(id) ON DELETE CASCADE,
+  rule_id        bigint         NOT NULL REFERENCES reasoner_rules(id) ON DELETE CASCADE
+)  CHARACTER SET utf8 COLLATE utf8_bin;
+
+
+-- justifications: support a single triple by other base triples and rules
+CREATE TABLE reasoner_justifications (
+  id        bigint      NOT NULL,
+  triple_id bigint      NOT NULL REFERENCES triples(id),
+  createdAt TIMESTAMP   NOT NULL,
+  PRIMARY KEY (id)
+)  CHARACTER SET utf8 COLLATE utf8_bin;
+
+-- join table from justifications to supporting triples
+CREATE TABLE reasoner_just_supp_triples (
+  justification_id  bigint NOT NULL REFERENCES reasoner_justifications(id) ON DELETE CASCADE,
+  triple_id         bigint NOT NULL REFERENCES triples(id) ON DELETE CASCADE
+)  CHARACTER SET utf8 COLLATE utf8_bin;
+
+
+-- join table from justifications to supporting rules
+CREATE TABLE reasoner_just_supp_rules (
+  justification_id  bigint NOT NULL REFERENCES reasoner_justifications(id) ON DELETE CASCADE,
+  rule_id           bigint NOT NULL REFERENCES reasoner_rules(id) ON DELETE CASCADE
+)  CHARACTER SET utf8 COLLATE utf8_bin;
+
+
+CREATE INDEX idx_justification_triple ON reasoner_justifications (triple_id);
+CREATE INDEX idx_just_supp_rules_just ON reasoner_just_supp_rules(justification_id);
+CREATE INDEX idx_just_supp_rules_rule ON reasoner_just_supp_rules(rule_id);
+CREATE INDEX idx_just_supp_triples_just ON reasoner_just_supp_triples(justification_id);
+CREATE INDEX idx_just_supp_triples_triple ON reasoner_just_supp_triples(triple_id);

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/mysql/drop_reasoner_tables.sql
----------------------------------------------------------------------
diff --git a/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/mysql/drop_reasoner_tables.sql b/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/mysql/drop_reasoner_tables.sql
new file mode 100644
index 0000000..992acbc
--- /dev/null
+++ b/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/mysql/drop_reasoner_tables.sql
@@ -0,0 +1,17 @@
+DROP INDEX idx_justification_triple ON reasoner_justifications;
+DROP INDEX idx_just_supp_rules_just ON reasoner_just_supp_rules;
+DROP INDEX idx_just_supp_rules_rule ON reasoner_just_supp_rules;
+DROP INDEX idx_just_supp_triples_just ON reasoner_just_supp_triples;
+DROP INDEX idx_just_supp_triples_triple ON reasoner_just_supp_triples;
+
+DROP TABLE IF EXISTS reasoner_just_supp_rules;
+DROP TABLE IF EXISTS reasoner_just_supp_triples;
+DROP TABLE IF EXISTS reasoner_justifications;
+DROP TABLE IF EXISTS reasoner_program_rules;
+DROP TABLE IF EXISTS reasoner_rules;
+DROP TABLE IF EXISTS reasoner_program_namespaces;
+DROP TABLE IF EXISTS reasoner_programs;
+
+DROP TABLE IF EXISTS seq_rules;
+DROP TABLE IF EXISTS seq_justifications;
+DROP TABLE IF EXISTS seq_programs;

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/mysql/statements.properties
----------------------------------------------------------------------
diff --git a/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/mysql/statements.properties b/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/mysql/statements.properties
new file mode 100644
index 0000000..c559dd3
--- /dev/null
+++ b/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/mysql/statements.properties
@@ -0,0 +1,78 @@
+#
+# Copyright (C) 2013 Salzburg Research.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# get sequence numbers
+seq.rules.prep         = UPDATE seq_rules SET id=LAST_INSERT_ID(id+1);
+seq.rules              = SELECT LAST_INSERT_ID();
+
+seq.programs.prep      = UPDATE seq_programs SET id=LAST_INSERT_ID(id+1);
+seq.programs           = SELECT LAST_INSERT_ID();
+
+seq.justifications.prep= UPDATE seq_justifications SET id=LAST_INSERT_ID(id+1);
+seq.justifications     = SELECT LAST_INSERT_ID();
+
+
+# rules
+rules.insert          = INSERT INTO reasoner_rules (id,name,description,body,createdAt) VALUES (?,?,?,?,now())
+rules.load_by_id      = SELECT id, name, description, body, createdAt FROM reasoner_rules WHERE id = ?
+rules.load_by_program = SELECT R.id, R.name, R.description, R.body, R.createdAt \
+  FROM reasoner_rules R, reasoner_program_rules J \
+  WHERE J.program_id = ? AND J.rule_id = R.id
+
+# delete a rule by id; the entries in reasoner_program_rules are deleted by cascading
+rules.delete_by_id    = DELETE FROM reasoner_rules WHERE id = ?
+
+
+namespaces.load_by_program = SELECT ns_prefix, ns_uri FROM reasoner_program_namespaces WHERE program_id = ?
+namespaces.load_by_rule    = SELECT NS.ns_prefix, NS.ns_uri FROM reasoner_program_namespaces NS, reasoner_program_rules R \
+  WHERE NS.program_id = R.program_id AND R.rule_id = ?
+
+programs.insert       = INSERT INTO reasoner_programs (id, name, description) VALUES (?,?,?)
+programs.add_rule     = INSERT INTO reasoner_program_rules (program_id, rule_id) VALUES (?,?)
+programs.add_ns       = INSERT INTO reasoner_program_namespaces (program_id, ns_prefix, ns_uri) VALUES (?,?,?)
+programs.load_by_id   = SELECT id, name, description FROM reasoner_programs WHERE id = ?
+programs.load_by_name = SELECT id, name, description FROM reasoner_programs WHERE name = ?
+programs.list         = SELECT id, name, description FROM reasoner_programs
+programs.delete       = DELETE FROM reasoner_programs WHERE id = ?
+programs.update_desc  = UPDATE reasoner_programs SET description = ? WHERE id = ?
+programs.delete_ns    = DELETE FROM reasoner_program_namespaces WHERE program_id = ? AND ns_prefix = ? AND ns_uri = ?
+programs.delete_rule  = DELETE FROM reasoner_program_rules WHERE program_id = ? AND rule_id = ?
+
+justifications.insert     = INSERT INTO reasoner_justifications (id, triple_id, createdAt) VALUES (?,?,?)
+justifications.add_triple = INSERT INTO reasoner_just_supp_triples (justification_id, triple_id) VALUES (?,?)
+justifications.add_rule   = INSERT INTO reasoner_just_supp_rules (justification_id, rule_id) VALUES (?,?)
+
+justifications.load_by_id     = SELECT id, triple_id, createdAt FROM reasoner_justifications WHERE id = ?
+justifications.load_by_triple = SELECT id, triple_id, createdAt FROM reasoner_justifications WHERE triple_id = ?
+justifications.load_by_striple = SELECT DISTINCT J.id, J.triple_id, J.createdAt \
+  FROM reasoner_justifications J, reasoner_just_supp_triples T \
+  WHERE J.id = T.justification_id AND T.triple_id = ?
+justifications.load_by_srule   = SELECT DISTINCT J.id, J.triple_id, J.createdAt \
+  FROM reasoner_justifications J, reasoner_just_supp_rules R \
+  WHERE J.id = R.justification_id AND R.rule_id = ?
+justifications.load_rules     = SELECT rule_id FROM reasoner_just_supp_rules WHERE justification_id = ?
+justifications.load_triples   = SELECT triple_id FROM reasoner_just_supp_triples WHERE justification_id = ?
+
+justifications.del_triple = DELETE FROM reasoner_just_supp_triples WHERE justification_id = ?
+justifications.del_rule   = DELETE FROM reasoner_just_supp_rules WHERE justification_id = ?
+justifications.delete     = DELETE FROM reasoner_justifications WHERE id = ?
+
+justifications.delete_all_triples = DELETE FROM reasoner_just_supp_triples
+justifications.delete_all_rules   = DELETE FROM reasoner_just_supp_rules
+justifications.delete_all         = DELETE FROM reasoner_justifications
+
+justifications.list_unsupported = SELECT T.id,T.subject,T.predicate,T.object,T.context,T.deleted,T.inferred,T.creator,T.createdAt,T.deletedAt \
+  FROM triples T WHERE T.deleted = false AND T.inferred = true AND NOT EXISTS (SELECT id FROM reasoner_justifications WHERE triple_id = T.id)

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/pgsql/create_reasoner_tables.sql
----------------------------------------------------------------------
diff --git a/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/pgsql/create_reasoner_tables.sql b/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/pgsql/create_reasoner_tables.sql
new file mode 100644
index 0000000..e9d5384
--- /dev/null
+++ b/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/pgsql/create_reasoner_tables.sql
@@ -0,0 +1,64 @@
+CREATE SEQUENCE seq_programs;
+CREATE SEQUENCE seq_rules;
+CREATE SEQUENCE seq_justifications;
+
+-- a table for representing metadata about complete reasoner programs
+CREATE TABLE reasoner_programs (
+  id          bigint         NOT NULL,
+  name        varchar(64) NOT NULL,
+  description TEXT,
+  PRIMARY KEY (id)
+);
+
+-- reasoner rules consist of an id, a program name, a rule name, and the parsable content in sKWRL syntax
+CREATE TABLE reasoner_rules (
+  id          bigint         NOT NULL,
+  name        varchar(64),
+  description TEXT,
+  body        TEXT        NOT NULL,
+  createdAt   TIMESTAMP   NOT NULL,
+  PRIMARY KEY (id)
+);
+
+
+-- a table for representing namespace configurations per reasoner program
+CREATE TABLE reasoner_program_namespaces (
+  program_id  bigint           NOT NULL REFERENCES reasoner_programs(id) ON DELETE CASCADE,
+  ns_prefix   VARCHAR(64)   NOT NULL,
+  ns_uri      VARCHAR(2048) NOT NULL,
+  PRIMARY KEY (program_id, ns_prefix)
+);
+
+-- join table between programs and rules
+CREATE TABLE reasoner_program_rules (
+  program_id     bigint         NOT NULL REFERENCES reasoner_programs(id) ON DELETE CASCADE ,
+  rule_id        bigint         NOT NULL REFERENCES reasoner_rules(id) ON DELETE CASCADE
+);
+
+-- justifications: support a single triple by other base triples and rules
+CREATE TABLE reasoner_justifications (
+  id        bigint      NOT NULL,
+  triple_id bigint      NOT NULL REFERENCES triples(id),
+  createdAt TIMESTAMP   NOT NULL,
+  PRIMARY KEY (id)
+);
+
+-- join table from justifications to supporting triples
+CREATE TABLE reasoner_just_supp_triples (
+  justification_id  bigint NOT NULL REFERENCES reasoner_justifications(id) ON DELETE CASCADE,
+  triple_id         bigint NOT NULL REFERENCES triples(id) ON DELETE CASCADE
+);
+
+
+-- join table from justifications to supporting rules
+CREATE TABLE reasoner_just_supp_rules (
+  justification_id  bigint NOT NULL REFERENCES reasoner_justifications(id) ON DELETE CASCADE,
+  rule_id           bigint NOT NULL REFERENCES reasoner_rules(id) ON DELETE CASCADE
+);
+
+
+CREATE INDEX idx_justification_triple ON reasoner_justifications (triple_id);
+CREATE INDEX idx_just_supp_rules_just ON reasoner_just_supp_rules(justification_id);
+CREATE INDEX idx_just_supp_rules_rule ON reasoner_just_supp_rules(rule_id);
+CREATE INDEX idx_just_supp_triples_just ON reasoner_just_supp_triples(justification_id);
+CREATE INDEX idx_just_supp_triples_triple ON reasoner_just_supp_triples(triple_id);

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/pgsql/drop_reasoner_tables.sql
----------------------------------------------------------------------
diff --git a/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/pgsql/drop_reasoner_tables.sql b/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/pgsql/drop_reasoner_tables.sql
new file mode 100644
index 0000000..00109e5
--- /dev/null
+++ b/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/pgsql/drop_reasoner_tables.sql
@@ -0,0 +1,17 @@
+DROP INDEX idx_justification_triple;
+DROP INDEX idx_just_supp_rules_just;
+DROP INDEX idx_just_supp_rules_rule;
+DROP INDEX idx_just_supp_triples_just;
+DROP INDEX idx_just_supp_triples_triple;
+
+DROP TABLE IF EXISTS reasoner_just_supp_rules;
+DROP TABLE IF EXISTS reasoner_just_supp_triples;
+DROP TABLE IF EXISTS reasoner_justifications;
+DROP TABLE IF EXISTS reasoner_program_rules;
+DROP TABLE IF EXISTS reasoner_rules;
+DROP TABLE IF EXISTS reasoner_program_namespaces;
+DROP TABLE IF EXISTS reasoner_programs;
+
+DROP SEQUENCE IF EXISTS seq_rules;
+DROP SEQUENCE IF EXISTS seq_justifications;
+DROP SEQUENCE IF EXISTS seq_programs;

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/pgsql/statements.properties
----------------------------------------------------------------------
diff --git a/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/pgsql/statements.properties b/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/pgsql/statements.properties
new file mode 100644
index 0000000..4517e3c
--- /dev/null
+++ b/kiwi/kiwi-reasoner/src/main/resources/org/apache/marmotta/kiwi/persistence/pgsql/statements.properties
@@ -0,0 +1,72 @@
+#
+# Copyright (C) 2013 Salzburg Research.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# get sequence numbers
+seq.rules             = SELECT nextval('seq_rules')
+seq.programs          = SELECT nextval('seq_programs')
+seq.justifications    = SELECT nextval('seq_justifications')
+
+# rules
+rules.insert          = INSERT INTO reasoner_rules (id,name,description,body,createdAt) VALUES (?,?,?,?,now())
+rules.load_by_id      = SELECT id, name, description, body, createdAt FROM reasoner_rules WHERE id = ?
+rules.load_by_program = SELECT R.id, R.name, R.description, R.body, R.createdAt \
+  FROM reasoner_rules R, reasoner_program_rules J \
+  WHERE J.program_id = ? AND J.rule_id = R.id
+
+# delete a rule by id; the entries in reasoner_program_rules are deleted by cascading
+rules.delete_by_id    = DELETE FROM reasoner_rules WHERE id = ?
+
+
+namespaces.load_by_program = SELECT ns_prefix, ns_uri FROM reasoner_program_namespaces WHERE program_id = ?
+namespaces.load_by_rule    = SELECT NS.ns_prefix, NS.ns_uri FROM reasoner_program_namespaces NS, reasoner_program_rules R \
+  WHERE NS.program_id = R.program_id AND R.rule_id = ?
+
+programs.insert       = INSERT INTO reasoner_programs (id, name, description) VALUES (?,?,?)
+programs.add_rule     = INSERT INTO reasoner_program_rules (program_id, rule_id) VALUES (?,?)
+programs.add_ns       = INSERT INTO reasoner_program_namespaces (program_id, ns_prefix, ns_uri) VALUES (?,?,?)
+programs.load_by_id   = SELECT id, name, description FROM reasoner_programs WHERE id = ?
+programs.load_by_name = SELECT id, name, description FROM reasoner_programs WHERE name = ?
+programs.list         = SELECT id, name, description FROM reasoner_programs
+programs.delete       = DELETE FROM reasoner_programs WHERE id = ?
+programs.update_desc  = UPDATE reasoner_programs SET description = ? WHERE id = ?
+programs.delete_ns    = DELETE FROM reasoner_program_namespaces WHERE program_id = ? AND ns_prefix = ? AND ns_uri = ?
+programs.delete_rule  = DELETE FROM reasoner_program_rules WHERE program_id = ? AND rule_id = ?
+
+justifications.insert     = INSERT INTO reasoner_justifications (id, triple_id, createdAt) VALUES (?,?,?)
+justifications.add_triple = INSERT INTO reasoner_just_supp_triples (justification_id, triple_id) VALUES (?,?)
+justifications.add_rule   = INSERT INTO reasoner_just_supp_rules (justification_id, rule_id) VALUES (?,?)
+
+justifications.load_by_id     = SELECT id, triple_id, createdAt FROM reasoner_justifications WHERE id = ?
+justifications.load_by_triple = SELECT id, triple_id, createdAt FROM reasoner_justifications WHERE triple_id = ?
+justifications.load_by_striple = SELECT DISTINCT J.id, J.triple_id, J.createdAt \
+  FROM reasoner_justifications J, reasoner_just_supp_triples T \
+  WHERE J.id = T.justification_id AND T.triple_id = ?
+justifications.load_by_srule   = SELECT DISTINCT J.id, J.triple_id, J.createdAt \
+  FROM reasoner_justifications J, reasoner_just_supp_rules R \
+  WHERE J.id = R.justification_id AND R.rule_id = ?
+justifications.load_rules     = SELECT rule_id FROM reasoner_just_supp_rules WHERE justification_id = ?
+justifications.load_triples   = SELECT triple_id FROM reasoner_just_supp_triples WHERE justification_id = ?
+
+justifications.del_triple = DELETE FROM reasoner_just_supp_triples WHERE justification_id = ?
+justifications.del_rule   = DELETE FROM reasoner_just_supp_rules WHERE justification_id = ?
+justifications.delete     = DELETE FROM reasoner_justifications WHERE id = ?
+
+justifications.delete_all_triples = DELETE FROM reasoner_just_supp_triples
+justifications.delete_all_rules   = DELETE FROM reasoner_just_supp_rules
+justifications.delete_all         = DELETE FROM reasoner_justifications
+
+justifications.list_unsupported = SELECT T.id,T.subject,T.predicate,T.object,T.context,T.deleted,T.inferred,T.creator,T.createdAt,T.deletedAt \
+  FROM triples T WHERE T.deleted = false AND T.inferred = true AND NOT EXISTS (SELECT id FROM reasoner_justifications WHERE triple_id = T.id)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/kiwi-reasoner/src/test/java/org/apache/marmotta/kiwi/reasoner/test/engine/ReasoningEngineTest.java
----------------------------------------------------------------------
diff --git a/kiwi/kiwi-reasoner/src/test/java/org/apache/marmotta/kiwi/reasoner/test/engine/ReasoningEngineTest.java b/kiwi/kiwi-reasoner/src/test/java/org/apache/marmotta/kiwi/reasoner/test/engine/ReasoningEngineTest.java
new file mode 100644
index 0000000..d58e40b
--- /dev/null
+++ b/kiwi/kiwi-reasoner/src/test/java/org/apache/marmotta/kiwi/reasoner/test/engine/ReasoningEngineTest.java
@@ -0,0 +1,559 @@
+/*
+ * Copyright (c) 2013 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.marmotta.kiwi.reasoner.test.engine;
+
+import info.aduna.iteration.Iterations;
+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;
+import org.apache.marmotta.kiwi.reasoner.model.program.Program;
+import org.apache.marmotta.kiwi.reasoner.model.program.Rule;
+import org.apache.marmotta.kiwi.reasoner.parser.KWRLProgramParser;
+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.transactions.api.TransactionalSail;
+import org.apache.marmotta.kiwi.transactions.model.TransactionData;
+import org.apache.marmotta.kiwi.transactions.sail.KiWiTransactionalSail;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+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.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
+ */
+@RunWith(Parameterized.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 TransactionalSail tsail;
+    private KiWiPersistence persistence;
+    private KiWiReasoningPersistence rpersistence;
+    private ReasoningEngine engine;
+
+    private Repository repository;
+
+
+    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);
+    }
+
+
+    @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");
+        tsail      = new KiWiTransactionalSail(store);
+        repository = new SailRepository(tsail);
+        repository.initialize();
+
+        rpersistence = new KiWiReasoningPersistence(persistence, repository.getValueFactory());
+        rpersistence.initDatabase();
+
+
+        // store a program, very simple transitive and symmetric rules:
+        // ($1 ex:property $2), ($2 ex:property $3) -> ($1 ex:property $3)
+        // ($1 ex:symmetric $2) -> ($2 ex:symmetric $1)
+        KWRLProgramParserBase parser = new KWRLProgramParser(repository.getValueFactory(), this.getClass().getResourceAsStream("simple.kwrl"));
+        Program p = parser.parseProgram();
+        p.setName("simple");
+
+        KiWiReasoningConnection connection = rpersistence.getConnection();
+        try {
+            // should not throw an exception and the program should have a database ID afterwards
+            connection.storeProgram(p);
+            connection.commit();
+        } finally {
+            connection.close();
+        }
+
+        // instantiate reasoning engine, will load the programs into memory
+        engine = new ReasoningEngine(rpersistence,tsail,new ReasoningConfiguration());
+
+    }
+
+    @After
+    public void dropDatabase() throws Exception {
+        engine.shutdown();
+
+        rpersistence.dropDatabase();
+
+        persistence.dropDatabase();
+        persistence.shutdown();
+
+        repository.shutDown();
+    }
+
+
+    /**
+     * Test the reasoning engine by incrementally adding and later removing triples through explicit calls to the
+     * reasoning engine methods. This test checks pure in-memory processing for rule2, which only contains a
+     * single query pattern.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void testIncrementalReasoningMemory() throws Exception {
+        RepositoryConnection con = repository.getConnection();
+        KiWiReasoningConnection rcon = rpersistence.getConnection();
+        try {
+            con.begin();
+            // create a triple (ex:a ex:symmetric ex:b); this should trigger rule 2 of the reasoner and add the
+            // inverse relationship
+            Resource subject  = con.getValueFactory().createURI(NS+"a");
+            URI      property = con.getValueFactory().createURI(NS+"symmetric");
+            Resource object   = con.getValueFactory().createURI(NS+"b");
+
+            con.add(subject,property,object);
+            con.commit();
+
+            // load the statement from the connection so we can add it to the reasoner
+            List<Statement> statements = Iterations.asList(con.getStatements(subject,property,object, false));
+            Assert.assertEquals(1,statements.size());
+
+            // add triple to engine
+            TransactionData data = new TransactionData();
+            data.getAddedTriples().add(statements.get(0));
+            engine.afterCommit(data);
+
+            // wait for reasoning to complete
+            while(engine.isRunning()) {
+                log.debug("sleeping for 100ms to let engine finish processing ... ");
+                Thread.sleep(100);
+            }
+            con.begin();
+
+            // after the engine completes, we check whether
+            // 1) the expected inferred triple exists
+            // 2) the inferred triple is properly justified (based on rule2 and on the triple contained in the transaction data)
+            List<Statement> inferred = Iterations.asList(con.getStatements(object,property,subject, true));
+            Assert.assertEquals("number of inferred triples differs from expected result",1,inferred.size());
+
+            KiWiTriple triple = (KiWiTriple)inferred.get(0);
+            List<Justification> justifications = Iterations.asList(rcon.listJustificationsForTriple(triple));
+            Assert.assertEquals("number of justifications for triple differs from expected result",1,justifications.size());
+
+            Justification j = justifications.get(0);
+            Assert.assertEquals("number of supporting triples differs from expected result",1,j.getSupportingTriples().size());
+            Assert.assertEquals("number of supporting rules differs from expected result",1,j.getSupportingRules().size());
+
+            Assert.assertThat("supporting triple does not match expectation", j.getSupportingTriples(), hasItem((KiWiTriple)statements.get(0)));
+
+            con.commit();
+
+
+            // now remove again the base triple and inform the reasoner about it, as a consequence, the inferred
+            // triple should also be removed
+            con.remove(subject,property,object);
+            con.commit();
+            TransactionData data2 = new TransactionData();
+            data2.getRemovedTriples().add(statements.get(0));
+            engine.afterCommit(data2);
+
+            // wait for reasoning to complete
+            while(engine.isRunning()) {
+                log.debug("sleeping for 100ms to let engine finish processing ... ");
+                Thread.sleep(100);
+            }
+            con.begin();
+
+            List<Statement> inferred2 = Iterations.asList(con.getStatements(object,property,subject, true));
+            Assert.assertEquals("number of inferred triples differs from expected result", 0, inferred2.size());
+
+            con.commit();
+            rcon.commit();
+        } finally {
+            con.close();
+            rcon.close();
+        }
+    }
+
+
+    /**
+     * Test the reasoning engine by incrementally adding and later removing triples through explicit calls to the
+     * reasoning engine methods. This test checks rule1 and thus involves both, in-memory pattern matching and database
+     * queries.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void testIncrementalReasoningConjunction() throws Exception {
+        RepositoryConnection con = repository.getConnection();
+        KiWiReasoningConnection rcon = rpersistence.getConnection();
+        try {
+            con.begin();
+            // create a triple (ex:a ex:symmetric ex:b); this should trigger rule 2 of the reasoner and add the
+            // inverse relationship
+            Resource a  = con.getValueFactory().createURI(NS+"a");
+            URI      property = con.getValueFactory().createURI(NS+"transitive");
+            Resource b   = con.getValueFactory().createURI(NS+"b");
+            Resource c   = con.getValueFactory().createURI(NS+"c");
+            Resource d   = con.getValueFactory().createURI(NS+"d");
+
+            con.add(a,property,b);
+            con.add(b,property,c);
+            con.commit();
+
+            // load the statement from the connection so we can add it to the reasoner
+            List<Statement> statements = Iterations.asList(con.getStatements(null,property,null, false));
+            Assert.assertEquals(2,statements.size());
+
+            // add triples to engine
+            TransactionData data = new TransactionData();
+            data.getAddedTriples().addAll(statements);
+            engine.afterCommit(data);
+
+            // wait for reasoning to complete
+            while(engine.isRunning()) {
+                log.debug("sleeping for 100ms to let engine finish processing ... ");
+                Thread.sleep(100);
+            }
+            con.begin();
+
+            // after the engine completes, we check whether
+            // 1) the expected inferred triple exists (must be (ex:a ex:transitive ex:c) )
+            // 2) the inferred triple is properly justified (based on rule2 and on the triple contained in the transaction data)
+            List<Statement> inferred = Iterations.asList(con.getStatements(a,property,c, true));
+            Assert.assertEquals("number of inferred triples differs from expected result",1,inferred.size());
+
+            KiWiTriple triple = (KiWiTriple)inferred.get(0);
+            List<Justification> justifications = Iterations.asList(rcon.listJustificationsForTriple(triple));
+            Assert.assertEquals("number of justifications for triple differs from expected result",1,justifications.size());
+
+            Justification j = justifications.get(0);
+            Assert.assertEquals("number of supporting triples differs from expected result",2,j.getSupportingTriples().size());
+            Assert.assertEquals("number of supporting rules differs from expected result",1,j.getSupportingRules().size());
+
+            Assert.assertThat("supporting triple does not match expectation", j.getSupportingTriples(), hasItem((KiWiTriple)statements.get(0)));
+            Assert.assertThat("supporting triple does not match expectation", j.getSupportingTriples(), hasItem((KiWiTriple)statements.get(1)));
+
+            con.commit();
+
+
+            // add another triple and check if the incremental reasoning works
+            con.add(c,property,d);
+            con.commit();
+
+            // load the statement from the connection so we can add it to the reasoner
+            List<Statement> statements2 = Iterations.asList(con.getStatements(c,property,d, false));
+            Assert.assertEquals(1, statements2.size());
+
+            // add triples to engine
+            TransactionData data2 = new TransactionData();
+            data2.getAddedTriples().addAll(statements2);
+            engine.afterCommit(data2);
+
+            // wait for reasoning to complete
+            while(engine.isRunning()) {
+                log.debug("sleeping for 100ms to let engine finish processing ... ");
+                Thread.sleep(100);
+            }
+            con.begin();
+
+
+            // after the engine completes, we check whether the expected inferred triples exist
+            // (must be (ex:a ex:transitive ex:d) and (ex:b ex:transitive ex:d) )
+            List<Statement> inferred2 = Iterations.asList(con.getStatements(a,property,d, true));
+            Assert.assertEquals("number of inferred triples differs from expected result", 1, inferred2.size());
+
+            List<Statement> inferred3 = Iterations.asList(con.getStatements(b,property,d, true));
+            Assert.assertEquals("number of inferred triples differs from expected result", 1, inferred3.size());
+
+
+            // now remove again the base triple and inform the reasoner about it, as a consequence, the inferred
+            // triple should also be removed
+            con.remove(c, property, d);
+            con.commit();
+            TransactionData data3 = new TransactionData();
+            data3.getRemovedTriples().add(statements2.get(0));
+            engine.afterCommit(data3);
+
+            // wait for reasoning to complete
+            while(engine.isRunning()) {
+                log.debug("sleeping for 100ms to let engine finish processing ... ");
+                Thread.sleep(100);
+            }
+            con.begin();
+
+            List<Statement> inferred4 = Iterations.asList(con.getStatements(a,property,d, true));
+            Assert.assertEquals("number of inferred triples differs from expected result", 0, inferred4.size());
+
+            List<Statement> inferred5 = Iterations.asList(con.getStatements(b,property,d, true));
+            Assert.assertEquals("number of inferred triples differs from expected result", 0, inferred5.size());
+
+            con.commit();
+            rcon.commit();
+        } finally {
+            con.close();
+            rcon.close();
+        }
+    }
+
+
+    /**
+     * Test running a full reasoning over the triple store based on the simple program and the simple.ttl data file.
+     * Test if the expected triples are present. Since we are only evaluating a single reasoning round, we cannot
+     * expect more complicated triples that involve chaining.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void testFullReasoning() throws Exception {
+        RepositoryConnection con = repository.getConnection();
+        KiWiReasoningConnection rcon = rpersistence.getConnection();
+        try {
+            // add some triples
+            con.begin();
+
+            Resource a   = con.getValueFactory().createURI(NS+"a");
+            Resource b   = con.getValueFactory().createURI(NS+"b");
+            Resource c   = con.getValueFactory().createURI(NS+"c");
+            Resource d   = con.getValueFactory().createURI(NS+"d");
+            URI      t   = con.getValueFactory().createURI(NS+"transitive");
+            URI      s   = con.getValueFactory().createURI(NS+"symmetric");
+
+            con.add(this.getClass().getResourceAsStream("simple.ttl"),"http://localhost/resource/", RDFFormat.TURTLE);
+            con.commit();
+
+            // run the full reasoner
+            engine.reRunPrograms();
+
+            // wait for reasoning to complete
+            while(engine.isRunning()) {
+                log.debug("sleeping for 100ms to let engine finish processing ... ");
+                Thread.sleep(100);
+            }
+            con.begin();
+
+            // after reasoning is finished, we expect to find the following triples:
+
+            Assert.assertTrue("expected inferred triple not found", con.hasStatement(a,t,c,true));
+            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));
+
+            con.commit();
+        } finally {
+            con.close();
+            rcon.close();
+        }
+
+    }
+
+
+    /**
+     * Test adding and removing rules to an already inferred state of the triple store. When a rule is added, all
+     * possible new inferences should be added to the inferred triples. When a rule is removed, all inferences
+     * based on this rule should also be removed.
+     *
+     * @throws Exception
+     */
+    //@Test
+    public void testAddRemoveRule() throws Exception {
+        RepositoryConnection con = repository.getConnection();
+        KiWiReasoningConnection rcon = rpersistence.getConnection();
+        try {
+            // add some triples
+            con.begin();
+
+            Resource a   = con.getValueFactory().createURI(NS+"a");
+            Resource b   = con.getValueFactory().createURI(NS+"b");
+            Resource c   = con.getValueFactory().createURI(NS+"c");
+            Resource d   = con.getValueFactory().createURI(NS+"d");
+            URI      t   = con.getValueFactory().createURI(NS+"transitive");
+            URI      s   = con.getValueFactory().createURI(NS+"symmetric");
+
+            con.add(this.getClass().getResourceAsStream("simple.ttl"),"http://localhost/resource/", RDFFormat.TURTLE);
+            con.commit();
+
+            // run the full reasoner
+            engine.reRunPrograms();
+
+            // wait for reasoning to complete
+            while(engine.isRunning()) {
+                log.debug("sleeping for 100ms to let engine finish processing ... ");
+                Thread.sleep(100);
+            }
+            con.begin();
+
+            // after reasoning is finished, we expect to find the following triples:
+
+            Assert.assertTrue("expected inferred triple not found", con.hasStatement(a,t,c,true));
+            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));
+
+            con.commit();
+
+
+            // now we remove the rule2 (symmetric rule) from the program, update the program in the database and then
+            // inform the reasoning engine about the removed rule
+            Program p = rcon.loadProgram("simple");
+            Rule removed = null;
+            Iterator<Rule> it = p.getRules().iterator();
+            while(it.hasNext()) {
+                Rule r = it.next();
+                if(r.getName().equals("rule2")) {
+                    it.remove();
+                    removed = r;
+                }
+            }
+            Assert.assertNotNull("rule 2 not found in program", removed);
+            rcon.updateProgram(p);
+            rcon.commit();
+
+            engine.notifyRemoveRules();
+
+            // after removing, the inferred symmetric triple should be gone, but the others should still exist
+            con.begin();
+            Assert.assertTrue("expected inferred triple not found", con.hasStatement(a,t,c,true));
+            Assert.assertTrue("expected inferred triple not found", con.hasStatement(b, t, d, true));
+            Assert.assertFalse("unexpected inferred triple found", con.hasStatement(b, s, a, true));
+            con.commit();
+
+
+            // let's add the rule again to the program, update the database, and inform the engine
+            p.getRules().add(removed);
+            rcon.updateProgram(p);
+            rcon.commit();
+
+            engine.notifyAddRule(removed);
+
+            // after adding, the inferred symmetric triple should again be present
+            con.begin();
+            Assert.assertTrue("expected inferred triple not found", con.hasStatement(a,t,c,true));
+            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));
+            con.commit();
+
+        } finally {
+            con.close();
+            rcon.close();
+        }
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/kiwi-reasoner/src/test/java/org/apache/marmotta/kiwi/reasoner/test/parser/KWRLProgramParserTest.java
----------------------------------------------------------------------
diff --git a/kiwi/kiwi-reasoner/src/test/java/org/apache/marmotta/kiwi/reasoner/test/parser/KWRLProgramParserTest.java b/kiwi/kiwi-reasoner/src/test/java/org/apache/marmotta/kiwi/reasoner/test/parser/KWRLProgramParserTest.java
new file mode 100644
index 0000000..d60a5da
--- /dev/null
+++ b/kiwi/kiwi-reasoner/src/test/java/org/apache/marmotta/kiwi/reasoner/test/parser/KWRLProgramParserTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2013 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.marmotta.kiwi.reasoner.test.parser;
+
+import org.apache.marmotta.kiwi.reasoner.model.program.Program;
+import org.apache.marmotta.kiwi.reasoner.parser.KWRLProgramParser;
+import org.apache.marmotta.kiwi.reasoner.parser.KWRLProgramParserBase;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.openrdf.repository.Repository;
+import org.openrdf.repository.sail.SailRepository;
+import org.openrdf.sail.memory.MemoryStore;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * Add file description here!
+ * <p/>
+ * Author: Sebastian Schaffert (sschaffert@apache.org)
+ */
+@RunWith(Parameterized.class)
+public class KWRLProgramParserTest {
+
+    private Repository repository;
+
+    private String filename;
+
+    @Parameterized.Parameters(name="KWRL Program Test {index}: {0}")
+    public static Collection<Object[]> data() {
+        ArrayList<Object[]> list = new ArrayList<Object[]>();
+        for(int i=1; i<=4; i++) {
+            list.add(new Object[] {"test-"+String.format("%03d",i)});
+        }
+        return list;
+    }
+
+
+    public KWRLProgramParserTest(String filename) {
+        this.filename = filename;
+    }
+
+    @Before
+    public void setup() throws Exception {
+        repository = new SailRepository(new MemoryStore());
+        repository.initialize();
+    }
+
+
+    @After
+    public void shutdown() throws Exception {
+        repository.shutDown();
+    }
+
+    @Test
+    public void testParseProgram() throws Exception {
+        KWRLProgramParserBase parser = new KWRLProgramParser(repository.getValueFactory(), this.getClass().getResourceAsStream(filename+".kwrl"));
+        Program p = parser.parseProgram();
+
+        Assert.assertNotNull(p);
+        Assert.assertFalse(p.getRules().isEmpty());
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-marmotta/blob/c32963d5/kiwi/kiwi-reasoner/src/test/java/org/apache/marmotta/kiwi/reasoner/test/parser/KWRLRuleParserTest.java
----------------------------------------------------------------------
diff --git a/kiwi/kiwi-reasoner/src/test/java/org/apache/marmotta/kiwi/reasoner/test/parser/KWRLRuleParserTest.java b/kiwi/kiwi-reasoner/src/test/java/org/apache/marmotta/kiwi/reasoner/test/parser/KWRLRuleParserTest.java
new file mode 100644
index 0000000..b7a0d68
--- /dev/null
+++ b/kiwi/kiwi-reasoner/src/test/java/org/apache/marmotta/kiwi/reasoner/test/parser/KWRLRuleParserTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2013 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.marmotta.kiwi.reasoner.test.parser;
+
+import com.google.common.collect.ImmutableMap;
+import org.apache.marmotta.kiwi.reasoner.model.program.Rule;
+import org.apache.marmotta.kiwi.reasoner.parser.KWRLProgramParser;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.openrdf.repository.Repository;
+import org.openrdf.repository.sail.SailRepository;
+import org.openrdf.sail.memory.MemoryStore;
+
+/**
+ * Test parsing of individual rules
+ * <p/>
+ * Author: Sebastian Schaffert (sschaffert@apache.org)
+ */
+public class KWRLRuleParserTest {
+
+    private Repository repository;
+
+    @Before
+    public void setup() throws Exception {
+        repository = new SailRepository(new MemoryStore());
+        repository.initialize();
+    }
+
+
+    @After
+    public void shutdown() throws Exception {
+        repository.shutDown();
+    }
+
+    @Test
+    public void testRule1() throws Exception {
+        String rule = "($1 http://www.w3.org/2000/01/rdf-schema#subClassOf $2), ($2 http://www.w3.org/2000/01/rdf-schema#subClassOf $3) -> ($1 http://www.w3.org/2000/01/rdf-schema#subClassOf $3)";
+        Rule r = KWRLProgramParser.parseRule(rule,repository.getValueFactory());
+
+        Assert.assertNotNull(r);
+        Assert.assertEquals(2, r.getBody().size());
+        Assert.assertTrue(r.getHead().getSubject().isVariableField());
+        Assert.assertTrue(r.getHead().getProperty().isResourceField());
+        Assert.assertTrue(r.getHead().getObject().isVariableField());
+    }
+
+    @Test
+    public void testRule2() throws Exception {
+        String rule = "($1 rdfs:subClassOf $2), ($2 rdfs:subClassOf $3) -> ($1 rdfs:subClassOf $3)";
+        Rule r = KWRLProgramParser.parseRule(rule, ImmutableMap.of("rdfs", "http://www.w3.org/2000/01/rdf-schema#"), repository.getValueFactory());
+
+        Assert.assertNotNull(r);
+        Assert.assertEquals(2, r.getBody().size());
+        Assert.assertTrue(r.getHead().getSubject().isVariableField());
+        Assert.assertTrue(r.getHead().getProperty().isResourceField());
+        Assert.assertTrue(r.getHead().getObject().isVariableField());
+    }
+
+}