Browse Source

Added re-implementation of r2rml-f.

Christophe Debruyne 8 years ago
parent
commit
1248849d98

+ 1 - 0
.gitignore

@@ -20,3 +20,4 @@ org.eclipse.jdt.core.prefs
 org.eclipse.m2e.core.prefs
 
 target/
+/bin/

+ 47 - 1
README.md

@@ -49,7 +49,53 @@ The output, after passing the properties file as an argument to the R2RML proces
         <http://example.com/ns#name>  "SMITH" .
 ```
 
+## Function with R2RML-F
+This implementation of R2RML re-implemented the ideas presented in [1], allowing one to declare and use functions in ECMAScript as (Function Valued) TermMaps in the mapping. R2RML-F extends R2RML's vocabulary with predicates for declaring functions, function calls and parameter bindings. These are declared in the namespace [rrf](http://kdeg.scss.tcd.ie/ns/rrf/index.html).
+
+```
+@prefix rr: <http://www.w3.org/ns/r2rml#> .
+@prefix ex: <http://example.com/ns#> .
+@prefix rrf: <http://kdeg.scss.tcd.ie/ns/rrf#>
+
+<#TriplesMap1>
+    rr:logicalTable [ rr:tableName "EMP" ];
+    rr:subjectMap [
+        rr:template "http://data.example.com/employee/{EMPNO}";
+        rr:class ex:Employee;
+    ];
+    rr:predicateObjectMap [
+        rr:predicate ex:name;
+        rr:objectMap [ rr:column "ENAME" ];
+    ];
+    rr:predicateObjectMap [
+        rr:predicate ex:test;
+        rr:objectMap [
+	        rrf:functionCall [
+	 			rrf:function <#Concat> ;
+	 			rrf:parameterBindings (
+	 				[ rr:column "ENAME" ]
+	 				[ rr:column "EMPNO" ]
+	 			) ;
+	 		] ; 
+	 	]
+    ]    
+    .
+    
+<#Concat>
+	rrf:functionName "concat" ;
+	rrf:functionBody """
+		function concat(var1, var2) {
+		return var1 + " " + var2 ;
+	}
+	""" ;
+.
+```
+
 ## License
-This implementation of R2RML is written by [Christophe Debruyne](http://www.christophedebruyne.be/). 
+This implementation of R2RML is written by [Christophe Debruyne](http://www.christophedebruyne.be/).
+
+## References
+
+[1]  C. Debruyne and D. O'Sullivan. R2RML-F: Towards Sharing and Executing Domain Logic in R2RML Mappings. In Proceedings of the Workshop on Linked Data on the Web, LDOW 2016, co-located with the 25th International World Wide Web Conference (WWW 2016), Montreal, Canada, April 12th, 2016, 2016
 
 This code is copyrighted by [ADAPT - Trinity College Dublin](http://www.adaptcentre.ie/) and released under the [MIT license](http://opensource.org/licenses/MIT).

+ 16 - 0
derby.log

@@ -0,0 +1,16 @@
+----------------------------------------------------------------
+Mon May 16 13:45:37 IST 2016:
+Booting Derby version The Apache Software Foundation - Apache Derby - 10.12.1.1 - (1704137): instance a816c00e-0154-b999-ad38-00000660a100 
+on database directory memory:/Users/chrdebru/GITHUB/r2rml/testing with class loader sun.misc.Launcher$AppClassLoader@5451c3a8 
+Loaded from file:/Users/chrdebru/.m2/repository/org/apache/derby/derby/10.12.1.1/derby-10.12.1.1.jar
+java.vendor=Oracle Corporation
+java.runtime.version=1.8.0_45-b14
+user.dir=/Users/chrdebru/GITHUB/r2rml
+os.name=Mac OS X
+os.arch=x86_64
+os.version=10.11.3
+derby.system.home=null
+Database Class Loader started - derby.database.classpath=''
+----------------------------------------------------------------
+Mon May 16 13:45:39 IST 2016:
+Shutting down instance a816c00e-0154-b999-ad38-00000660a100 on database directory memory:/Users/chrdebru/GITHUB/r2rml/testing with class loader sun.misc.Launcher$AppClassLoader@5451c3a8 

+ 26 - 0
src/r2rml/engine/RRF.java

@@ -0,0 +1,26 @@
+package r2rml.engine;
+
+import org.apache.jena.rdf.model.Property;
+import org.apache.jena.rdf.model.ResourceFactory;
+
+/**
+ * R2RML Class.
+ * 
+ * @author Christophe Debruyne
+ * @version 0.1
+ *
+ */
+public final class RRF {
+	
+	public static final String NS = "http://kdeg.scss.tcd.ie/ns/rrf#";
+	
+	// Classes
+	
+	// Properties
+	public static final Property function = ResourceFactory.createProperty(NS + "function");
+	public static final Property functionCall = ResourceFactory.createProperty(NS + "functionCall");
+	public static final Property functionBody = ResourceFactory.createProperty(NS + "functionBody");
+	public static final Property functionName = ResourceFactory.createProperty(NS + "functionName");
+	public static final Property parameterBindings = ResourceFactory.createProperty(NS + "parameterBindings");
+	
+}

+ 132 - 0
src/r2rml/function/JSEnv.java

@@ -0,0 +1,132 @@
+package r2rml.function;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.script.Invocable;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+import javax.script.ScriptException;
+
+import org.apache.jena.rdf.model.RDFNode;
+import org.apache.jena.rdf.model.Resource;
+import org.apache.jena.rdf.model.Statement;
+import org.apache.log4j.Logger;
+
+import r2rml.engine.RRF;
+
+/**
+ * JSEnv Class.
+ * 
+ * @author Christophe Debruyne
+ * @version 0.1
+ *
+ */
+public class JSEnv {
+
+	private static Logger logger = Logger.getLogger(JSEnv.class.getName());
+	
+	private static ScriptEngineManager manager = new ScriptEngineManager();
+	private static ScriptEngine engine = manager.getEngineByName("javascript");
+	
+	private static Map<Resource, String> nameMap = new HashMap<Resource, String>();
+
+	/**
+	 * Invoking a function with an array of parameters.
+	 * 
+	 * @param functionName
+	 * @param parameters
+	 * @return
+	 * @throws NoSuchMethodException
+	 * @throws ScriptException
+	 */
+	public static String invoke(String functionName, Object... parameters) 
+			throws NoSuchMethodException, ScriptException {
+		Invocable invokeEngine = (Invocable) engine;
+		Object o = invokeEngine.invokeFunction(functionName, parameters);
+		return o.toString();
+	}
+
+	/**
+	 * Loading JavaScript code.
+	 * 
+	 * @param code
+	 * @throws ScriptException
+	 */
+	public static void loadCode(String code) throws ScriptException {
+		engine.eval(code);
+	}
+
+	/**
+	 * A utility function for setting up a new engine and environment.
+	 * 
+	 */
+	public static void reset() {
+		manager = new ScriptEngineManager();
+		manager.getEngineByName("javascript");
+		nameMap.clear();
+	}
+
+	public static String registerFunction(RDFNode node) {
+		if(!node.isResource()) {
+			logger.error("Function valued TermMap's function must be a resource.");
+			logger.error(node);
+			return null;
+		}
+		
+		Resource f = node.asResource();
+		
+		List<Statement> names = f.listProperties(RRF.functionName).toList();
+		if(names.size() != 1) {
+			logger.error("Functions must have exactly one rrf:functionName.");
+			logger.error(f);
+			return null;
+		}
+		
+		if(!names.get(0).getObject().isLiteral()) {
+			logger.error("rrf:functionName must be a literal.");
+			logger.error(f);
+			return null;
+		}
+		
+		String name = names.get(0).getObject().asLiteral().getLexicalForm();
+		
+		if(!nameMap.containsKey(f) && nameMap.values().contains(name)) {
+			logger.error("No two functions can have the same rrf:functionName.");
+			logger.error(f);
+			return null;
+		}
+		
+		nameMap.put(f, name);
+		logger.info("Registered function " + name);
+		
+		List<Statement> bodies = f.listProperties(RRF.functionBody).toList();
+		if(bodies.size() != 1) {
+			logger.error("Functions must have exactly one rrf:functionBody.");
+			logger.error(f);
+			return null;
+		}
+		
+		if(!bodies.get(0).getObject().isLiteral()) {
+			logger.error("rrf:functionBody must be a literal.");
+			logger.error(f);
+			return null;
+		}
+		
+		String body = bodies.get(0).getObject().asLiteral().getLexicalForm();
+		
+		try {
+			loadCode(body);
+			logger.info("Loaded function " + name);
+		} catch (ScriptException e) {
+			logger.error("rrf:functionBody contains issues.");
+			logger.error(e.getMessage());
+			logger.error(f);
+			return null;
+		}
+		
+		return name;
+	}
+
+}

+ 38 - 0
src/r2rml/model/FunctionCall.java

@@ -0,0 +1,38 @@
+package r2rml.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * FunctionCall Class.
+ * 
+ * @author Christophe Debruyne
+ * @version 0.1
+ *
+ */
+public class FunctionCall {
+	
+	private List<TermMap> termMaps = new ArrayList<TermMap>();
+	private String functionName;
+	
+	public FunctionCall(String functionName) {
+		this.setFunctionName(functionName);
+	}
+
+	public List<TermMap> getTermMaps() {
+		return termMaps;
+	}
+
+	public String getFunctionName() {
+		return functionName;
+	}
+
+	public void setFunctionName(String functionName) {
+		this.functionName = functionName;
+	}
+
+	public void addParameter(TermMap termMap) {
+		termMaps.add(termMap);
+	}
+
+}

+ 5 - 1
src/r2rml/model/ObjectMap.java

@@ -15,7 +15,7 @@ import r2rml.engine.R2RML;
  * TODO: Implement inferring datatypes
  * 
  * @author Christophe Debruyne
- * @version 0.1
+ * @version 0.2
  *
  */
 public class ObjectMap extends TermMap {
@@ -128,6 +128,10 @@ public class ObjectMap extends TermMap {
 
 		if(isColumnValuedTermMap() || datatypes.size() > 0 || languages.size() > 0) 
 			return R2RML.LITERAL;
+		
+		/* We assume that functions are also by default literals */
+		if(isFunctionValuedTermMap())
+			return R2RML.LITERAL;
 
 		return R2RML.IRI;
 	}

+ 4 - 2
src/r2rml/model/PredicateObjectMap.java

@@ -8,12 +8,13 @@ import org.apache.jena.rdf.model.Statement;
 import org.apache.log4j.Logger;
 
 import r2rml.engine.R2RML;
+import r2rml.engine.RRF;
 
 /**
  * PredicateObjectMap Class.
  * 
  * @author Christophe Debruyne
- * @version 0.1
+ * @version 0.2
  *
  */
 public class PredicateObjectMap extends R2RMLResource {
@@ -117,7 +118,8 @@ public class PredicateObjectMap extends R2RMLResource {
 			 */
 			boolean isOM = r.hasProperty(R2RML.column) 
 					|| r.hasProperty(R2RML.constant) 
-					|| r.hasProperty(R2RML.template);
+					|| r.hasProperty(R2RML.template)
+					|| r.hasProperty(RRF.functionCall);
 			boolean isROM = r.hasProperty(R2RML.joinCondition);
 
 			// If it plays the roles of a OM, create OM

+ 106 - 3
src/r2rml/model/TermMap.java

@@ -1,6 +1,7 @@
 package r2rml.model;
 
 import java.net.MalformedURLException;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -9,14 +10,19 @@ import java.util.Set;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import javax.script.ScriptException;
+
 import org.apache.commons.lang3.StringEscapeUtils;
 import org.apache.jena.datatypes.RDFDatatype;
+import org.apache.jena.enhanced.UnsupportedPolymorphismException;
 import org.apache.jena.iri.IRI;
 import org.apache.jena.iri.IRIFactory;
+import org.apache.jena.rdf.model.RDFList;
 import org.apache.jena.rdf.model.RDFNode;
 import org.apache.jena.rdf.model.Resource;
 import org.apache.jena.rdf.model.ResourceFactory;
 import org.apache.jena.rdf.model.Statement;
+import org.apache.jena.util.iterator.ExtendedIterator;
 import org.apache.jena.vocabulary.XSD;
 import org.apache.log4j.Logger;
 
@@ -24,12 +30,14 @@ import r2rml.database.Row;
 import r2rml.engine.R2RML;
 import r2rml.engine.R2RMLException;
 import r2rml.engine.R2RMLTypeMapper;
+import r2rml.engine.RRF;
+import r2rml.function.JSEnv;
 
 /**
  * TermMap Class.
  * 
  * @author Christophe Debruyne
- * @version 0.1
+ * @version 0.2
  *
  */
 public abstract class TermMap extends R2RMLResource {
@@ -52,6 +60,7 @@ public abstract class TermMap extends R2RMLResource {
 	private String template;
 	private RDFNode constant;
 	private String column;
+	private FunctionCall functionCall;
 
 	protected String language = null;
 	protected Resource datatype = null;
@@ -69,9 +78,10 @@ public abstract class TermMap extends R2RMLResource {
 		List<Statement> templates = description.listProperties(R2RML.template).toList();
 		List<Statement> constants = description.listProperties(R2RML.constant).toList();
 		List<Statement> columns = description.listProperties(R2RML.column).toList();
+		List<Statement> functions = description.listProperties(RRF.functionCall).toList();
 
 		// Having exactly one of rr:constant, rr:column, rr:template
-		if(templates.size() + constants.size() + columns.size() != 1) {
+		if(templates.size() + constants.size() + columns.size() + functions.size() != 1) {
 			logger.error("TermMap must have exactly one of rr:constant, rr:column, and rr:template.");
 			logger.error(description);
 			return false;
@@ -108,6 +118,19 @@ public abstract class TermMap extends R2RMLResource {
 			constant = distillConstant(constants.get(0).getObject());
 			if(constant == null)
 				return false;
+		} else if(functions.size() == 1) {
+			functionCall = distillFunction(functions.get(0).getObject());
+			if(functionCall == null)
+				return false;
+			
+			// Check whether the referenced column names are valid
+			for(String columnName : getReferencedColumns()) {
+				if(!R2RMLUtil.isValidColumnName(columnName)) {
+					logger.error("Invalid column name in rrf:functionCall " + columnName);
+					logger.error(description);
+					return false;
+				}
+			}
 		}
 
 		// Validity of the termType is also local. 
@@ -137,6 +160,67 @@ public abstract class TermMap extends R2RMLResource {
 		return true;
 	}
 
+	private FunctionCall distillFunction(RDFNode node) {
+		if(node.isLiteral())
+			return null;
+		
+		// fcn stands for Function Call Node
+		Resource fcn = node.asResource();
+		
+		List<Statement> functions = fcn.listProperties(RRF.function).toList();
+		if(functions.size() != 1) {
+			logger.error("Function valued TermMap must have exactly one rrf:function.");
+			logger.error(description);
+			return null;
+		}
+		
+		// Process the function, get the function name and then the parameters
+		RDFNode f = functions.get(0).getObject();
+		String functionname = JSEnv.registerFunction(f);
+		if(functionname == null) {
+			// Something went wrong, reported by the function. 
+			return null;
+		}
+		
+		List<Statement> pbindings = fcn.listProperties(RRF.parameterBindings).toList();
+		if(pbindings.size() != 1) {
+			logger.error("Function valued TermMap must have exactly one rrf:parameterBindings.");
+			logger.error(description);
+			return null;
+		}
+		
+		RDFList list = null;
+		try {
+			list = pbindings.get(0).getObject().as(RDFList.class);
+		} catch(UnsupportedPolymorphismException e) {
+			logger.error("rrf:parameterBindings must be an RDF collection.");
+			logger.error(description);
+			return null;
+		}
+		
+		functionCall = new FunctionCall(functionname);
+		
+		ExtendedIterator<RDFNode> iter = list.iterator();
+		while(iter.hasNext()) {
+			RDFNode param = iter.next();
+			if(!param.isResource()) {
+				logger.error("Parameters in rrf:parameterBindings have to be resources.");
+				logger.error(description);
+				return null;
+			}
+			ObjectMap om = new ObjectMap(param.asResource(), baseIRI);
+			if(om.preProcessAndValidate()) {
+				functionCall.addParameter(om);
+			} else {
+				logger.error("Something went wrong processing parameter.");
+				logger.error(description);
+				return null;
+			}
+		}
+		
+		return functionCall;
+	}
+
 	/**
 	 * Infer "default" termtype.
 	 * @return
@@ -177,6 +261,10 @@ public abstract class TermMap extends R2RMLResource {
 			while(m.find()) {
 				set.add(template.substring(m.start(1), m.end(1)));
 			}
+		} else if(isFunctionValuedTermMap()) {
+			for(TermMap tm : functionCall.getTermMaps()) {
+				set.addAll(tm.getReferencedColumns());
+			}
 		} // else constant and thus empty set.
 		return set;
 	}
@@ -203,6 +291,10 @@ public abstract class TermMap extends R2RMLResource {
 	public boolean isConstantValuedTermMap() {
 		return constant != null;
 	}
+	
+	public boolean isFunctionValuedTermMap() {
+		return functionCall != null;
+	}
 
 	public Resource getTermType() {
 		return termType;
@@ -299,7 +391,7 @@ public abstract class TermMap extends R2RMLResource {
 	 * ~A_17.1-2			-> ~A_17.1-2				OK
 	 * 葉篤正					-> 葉篤正						NOK!
 	 * 
-	 * TODO: Better complient safe IRI conversion.
+	 * TODO: Better compliant safe IRI conversion.
 	 * 
 	 * @param iri
 	 * @return
@@ -330,6 +422,17 @@ public abstract class TermMap extends R2RMLResource {
 			// Unescape all the values!
 			value = StringEscapeUtils.unescapeJava(value);
 			return value;
+		} else if (isFunctionValuedTermMap()) {
+			List<Object> arguments = new ArrayList<>();
+			for(TermMap tm : functionCall.getTermMaps()) {
+				Object argument = tm.getValueForRDFTerm(row);
+				arguments.add(argument);
+			}
+			try {
+				return JSEnv.invoke(functionCall.getFunctionName(), arguments.toArray());
+			} catch (NoSuchMethodException | ScriptException e) {
+				throw new R2RMLException("Error invoking function.", e);
+			}
 		}
 		return null;
 	}

+ 39 - 0
test/resources/F01.mapping.ttl

@@ -0,0 +1,39 @@
+@prefix rr: <http://www.w3.org/ns/r2rml#> .
+@prefix ex: <http://example.com/ns#> .
+@prefix rrf: <http://kdeg.scss.tcd.ie/ns/rrf#>
+
+<#TriplesMap1>
+    rr:logicalTable [ rr:tableName "EMP" ];
+    rr:subjectMap [
+        rr:template "http://data.example.com/employee/{EMPNO}";
+        rr:class ex:Employee;
+    ];
+    rr:predicateObjectMap [
+        rr:predicate ex:name;
+        rr:objectMap [ rr:column "ENAME" ];
+    ];
+    rr:predicateObjectMap [
+        rr:predicate ex:test;
+        rr:objectMap [
+	        rrf:functionCall [
+	 			rrf:function <#Concat> ;
+	 			rrf:parameterBindings (
+	 				[ rr:column "ENAME" ]
+	 				[ rr:column "EMPNO" ]
+	 			) ;
+	 		] ; 
+	 	]
+    ]    
+    .
+    
+<#Concat>
+	rrf:functionName "concat" ;
+	rrf:functionBody """
+		function concat(var1, var2) {
+		return var1 + " " + var2 ;
+	}
+	""" ;
+.
+	
+<#a> rr:objectMap <#b> .
+<#b> rr:column "ENAME" .

+ 5 - 0
test/resources/F01.output.ttl

@@ -0,0 +1,5 @@
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix ex: <http://example.com/ns#> .
+<http://data.example.com/employee/7369> rdf:type ex:Employee.
+<http://data.example.com/employee/7369> ex:name "SMITH".
+<http://data.example.com/employee/7369> ex:test "SMITH 7369".

+ 111 - 0
test/test/TestR2RMLF.java

@@ -0,0 +1,111 @@
+package test;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLNonTransientConnectionException;
+import java.sql.Statement;
+
+import org.apache.jena.rdf.model.Model;
+import org.apache.jena.rdf.model.ModelFactory;
+import org.apache.log4j.BasicConfigurator;
+import org.apache.log4j.Logger;
+import org.junit.BeforeClass;
+
+import junit.framework.TestCase;
+import r2rml.engine.Configuration;
+import r2rml.engine.R2RMLProcessor;
+
+/**
+ * Unit test for testing the functionality of this implementation using an
+ * in memory database.
+ * 
+ * @author Christophe Debruyne
+ *
+ */
+public class TestR2RMLF extends TestCase {
+
+	private static Logger logger = Logger.getLogger(TestR2RMLF.class.getName());
+	private static String connectionURL = "jdbc:derby:memory:testing";
+
+	public TestR2RMLF(String testName) {
+		super(testName);
+	}
+
+	@BeforeClass
+	public static void init() throws Exception {
+		// Log4J junit configuration.
+		BasicConfigurator.configure();
+	}
+
+	@Override
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		try {
+			logger.info("Starting in-memory database for unit tests");
+			Class.forName("org.apache.derby.jdbc.EmbeddedDriver");
+			DriverManager.getConnection(connectionURL + ";create=true").close();
+		} catch (Exception ex) {
+			ex.printStackTrace();
+			fail("Exception during database startup.");
+		}
+		try {
+			Connection connection = DriverManager.getConnection(connectionURL);
+			Statement statement = connection.createStatement();
+			statement.execute("CREATE TABLE DEPT(DEPTNO INT PRIMARY KEY, DNAME VARCHAR(30), LOC VARCHAR(30))");
+			statement.execute("CREATE TABLE EMP(EMPNO INT PRIMARY KEY, ENAME VARCHAR(100), JOB VARCHAR(20), DEPTNO INT, FOREIGN KEY (DEPTNO) REFERENCES DEPT(DEPTNO))");
+			statement.execute("INSERT INTO DEPT VALUES (10, 'APPSERVER', 'NEW YORK')");
+			statement.execute("INSERT INTO EMP VALUES (7369, 'SMITH', 'CLERK', 10)");
+
+			statement.execute("CREATE TABLE DEPT2(DEPTNO INT, DNAME VARCHAR(30), LOC VARCHAR(30))");
+			statement.execute("CREATE TABLE EMP2(EMPNO INT, ENAME VARCHAR(100), JOB VARCHAR(20))");
+			statement.execute("CREATE TABLE EMP2DEPT(EMPNO INT, DEPTNO INT)");
+			statement.execute("INSERT INTO DEPT2 VALUES (10, 'APPSERVER', 'NEW YORK')");
+			statement.execute("INSERT INTO DEPT2 VALUES (20, 'RESEARCH', 'BOSTON')");
+			statement.execute("INSERT INTO EMP2 VALUES (7369, 'SMITH', 'CLERK')");
+			statement.execute("INSERT INTO EMP2 VALUES (7369, 'SMITH', 'NIGHTGUARD')");
+			statement.execute("INSERT INTO EMP2 VALUES (7400, 'JONES', 'ENGINEER')");
+			statement.execute("INSERT INTO EMP2DEPT VALUES (7369, 10)");
+			statement.execute("INSERT INTO EMP2DEPT VALUES (7369, 20)");
+			statement.execute("INSERT INTO EMP2DEPT VALUES (7400, 10)");
+			statement.close();
+			connection.close();
+
+		} catch (Exception ex) {
+			ex.printStackTrace();
+			fail("Failure setting up the database.");
+		}
+	}
+
+	@Override
+	protected void tearDown() throws Exception {
+		super.tearDown();
+		logger.info("Stopping in-memory database.");
+		try {
+			DriverManager.getConnection(connectionURL + ";drop=true").close();
+		} catch (SQLNonTransientConnectionException ex) {
+			if (ex.getErrorCode() != 45000) {
+				throw ex;
+			}
+			// Shutdown success
+		}
+	}
+
+	/**
+	 * 2.3 Example: Mapping a Simple Table
+	 * https://www.w3.org/TR/r2rml/#example-simple
+	 */
+	public void testExampleF01() {
+		Configuration configuration = new Configuration();
+		configuration.setMappingFile("./test/resources/F01.mapping.ttl");
+		configuration.setConnectionURL(connectionURL);
+		R2RMLProcessor engine = new R2RMLProcessor(configuration);
+		engine.execute();
+		Model model = engine.getDataset().getDefaultModel();
+		Model target = ModelFactory.createDefaultModel();
+		target.read("./test/resources/F01.output.ttl");
+		assertEquals(true, model.difference(target).isEmpty());
+		assertEquals(true, target.difference(model).isEmpty());	
+	}
+
+}