This is an automated email from the ASF dual-hosted git repository.
zjffdu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/zeppelin.git
The following commit(s) were added to refs/heads/master by this push:
new 660c476 [ZEPPELIN-3644] SPARQL-Interpreter support via Apache Jena ARQ
660c476 is described below
commit 660c476ca3fe622e820218eb4ddd114789bf277d
Author: hsteinmueller <[email protected]>
AuthorDate: Tue Nov 5 11:12:29 2019 +0000
[ZEPPELIN-3644] SPARQL-Interpreter support via Apache Jena ARQ
### What is this PR for?
Interpreter for SPARQL-queries on any SPARQL-endpoint via [Apache Jena
ARQ](https://jena.apache.org/documentation/query/).
[SPARQL](https://www.w3.org/TR/sparql11-query/) is an RDF query language
able to retrieve and manipulate data stored in Resource Description Framework
(RDF) format.
### What type of PR is it?
[Feature]
### Todos
* [ ] - Task
### What is the Jira issue?
* https://issues.apache.org/jira/browse/ZEPPELIN-3644
### How should this be tested?
* Unit tests are provided
* https://travis-ci.org/hsteinmueller/zeppelin
### Screenshots (if appropriate)

### Questions:
* Does the licenses files need update? Yes, added in PR
* Is there breaking changes for older versions? No
* Does this needs documentation? Yes, added in PR
Author: hsteinmueller <[email protected]>
Author: Harald Steinmueller <[email protected]>
Closes #3480 from hsteinmueller/sparql-interpreter and squashes the
following commits:
44f14bc50 [hsteinmueller] added support for different implementations
720f7144f [Harald Steinmueller] Merge branch 'master' into
sparql-interpreter
475a94163 [hsteinmueller] renamed package from fuseki to sparql
594a033ae [hsteinmueller] added support for cancel
3e2120f48 [hsteinmueller] moved variable initialization to open()-method
94dffd93e [hsteinmueller] Merge branch 'sparql-interpreter' of
https://github.com/hsteinmueller/zeppelin into sparql-interpreter
c5ea82750 [hsteinmueller] added removeDatatypes option
5dbd27325 [Harald Steinmueller] Merge branch 'master' into
sparql-interpreter
85f48e92a [hsteinmueller] SPARQL-Interpreter for Apache Zeppelin
---
.travis.yml | 2 +-
conf/interpreter-list | 1 +
docs/_includes/themes/zeppelin/_navigation.html | 1 +
.../zeppelin/img/docs-img/sparql-example.png | Bin 0 -> 68474 bytes
docs/index.md | 1 +
docs/interpreter/sparql.md | 55 ++++++
docs/usage/interpreter/installation.md | 5 +
pom.xml | 1 +
sparql/pom.xml | 80 +++++++++
.../apache/zeppelin/sparql/JenaInterpreter.java | 143 +++++++++++++++
.../org/apache/zeppelin/sparql/SparqlEngine.java | 32 ++++
.../apache/zeppelin/sparql/SparqlInterpreter.java | 107 +++++++++++
sparql/src/main/resources/interpreter-setting.json | 42 +++++
.../zeppelin/sparql/SparqlJenaEngineTest.java | 198 +++++++++++++++++++++
sparql/src/test/resources/data.ttl | 18 ++
zeppelin-distribution/src/bin_license/LICENSE | 1 +
zeppelin-web/bower.json | 1 +
zeppelin-web/karma.conf.js | 1 +
zeppelin-web/src/index.html | 1 +
19 files changed, 689 insertions(+), 1 deletion(-)
diff --git a/.travis.yml b/.travis.yml
index 95fe8dd..8c48d3c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -51,7 +51,7 @@ services:
env:
global:
# Interpreters does not required by zeppelin-server integration tests
- -
INTERPRETERS='!beam,!hbase,!pig,!jdbc,!file,!flink,!ignite,!kylin,!lens,!cassandra,!elasticsearch,!bigquery,!alluxio,!scio,!livy,!groovy,!sap,!java,!geode,!neo4j,!hazelcastjet,!submarine'
+ -
INTERPRETERS='!beam,!hbase,!pig,!jdbc,!file,!flink,!ignite,!kylin,!lens,!cassandra,!elasticsearch,!bigquery,!alluxio,!scio,!livy,!groovy,!sap,!java,!geode,!neo4j,!hazelcastjet,!submarine,!sparql'
matrix:
include:
diff --git a/conf/interpreter-list b/conf/interpreter-list
index 2e19ad6..05cb9ff 100644
--- a/conf/interpreter-list
+++ b/conf/interpreter-list
@@ -43,4 +43,5 @@ sap org.apache.zeppelin:zeppelin-sap:0.9.0
SAP Supp
scalding org.apache.zeppelin:zeppelin-scalding_2.0.10:0.9.0
Scalding interpreter
scio org.apache.zeppelin:zeppelin-scio:0.9.0 Scio
interpreter
shell org.apache.zeppelin:zeppelin-shell:0.9.0 Shell
command
+sparql org.apache.zeppelin:zeppelin-sparql:0.9.0 Sparql
interpreter
submarine org.apache.zeppelin:zeppelin-submarine:0.9.0
Submarine interpreter
diff --git a/docs/_includes/themes/zeppelin/_navigation.html
b/docs/_includes/themes/zeppelin/_navigation.html
index 4858691..9b83369 100644
--- a/docs/_includes/themes/zeppelin/_navigation.html
+++ b/docs/_includes/themes/zeppelin/_navigation.html
@@ -153,6 +153,7 @@
<li><a
href="{{BASE_PATH}}/interpreter/scalding.html">Scalding</a></li>
<li><a href="{{BASE_PATH}}/interpreter/scio.html">Scio</a></li>
<li><a
href="{{BASE_PATH}}/interpreter/shell.html">Shell</a></li>
+ <li><a
href="{{BASE_PATH}}/interpreter/sparql.html">Sparql</a></li>
<li><a
href="{{BASE_PATH}}/interpreter/submarine.html">Submarine</a></li>
</ul>
</li>
diff --git a/docs/assets/themes/zeppelin/img/docs-img/sparql-example.png
b/docs/assets/themes/zeppelin/img/docs-img/sparql-example.png
new file mode 100644
index 0000000..de39570
Binary files /dev/null and
b/docs/assets/themes/zeppelin/img/docs-img/sparql-example.png differ
diff --git a/docs/index.md b/docs/index.md
index 5481fb4..ad53721 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -158,6 +158,7 @@ limitations under the License.
* [Scio](./interpreter/scio.html)
* [Shell](./interpreter/shell.html)
* [Spark](./interpreter/spark.html)
+ * [Sparql](./interpreter/sparql.html)
* [Submarine](./interpreter/submarine.html)
#### External Resources
diff --git a/docs/interpreter/sparql.md b/docs/interpreter/sparql.md
new file mode 100644
index 0000000..542853c
--- /dev/null
+++ b/docs/interpreter/sparql.md
@@ -0,0 +1,55 @@
+---
+layout: page
+title: "SPARQL Interpreter for Apache Zeppelin"
+description: "SPARQL is an RDF query language able to retrieve and manipulate
data stored in Resource Description Framework (RDF) format. Apache Zeppelin
uses Apache Jena"
+group: interpreter
+---
+<!--
+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.
+-->
+{% include JB/setup %}
+
+# SPARQL Interpreter for Apache Zeppelin
+
+<div id="toc"></div>
+
+## Overview
+[SPARQL](https://www.w3.org/TR/sparql11-query/) is an RDF query language able
to retrieve and manipulate data stored in Resource Description Framework (RDF)
format.
+Apache Zeppelin uses [Apache Jena](https://jena.apache.org/) to query
SPARQL-Endpoints.
+
+To query your endpoint configure it in the Interpreter-Settings and use the
**%sparql** interpreter.
+Then write your query in the paragraph.
+If you want the prefixes to replace the URI's, set the replaceURIs setting.
+
+## Configuration
+<table class="table-configuration">
+ <tr>
+ <th>Name</th>
+ <th>Default Value</th>
+ <th>Description</th>
+ </tr>
+ <tr>
+ <td>sparql.endpoint</td>
+ <td>http://dbpedia.org/sparql</td>
+ <td>Complete URL of the endpoint</td>
+ </tr>
+ <tr>
+ <td>sparql.replaceURIs</td>
+ <td>true</td>
+ <td>Replace the URIs in the result with the prefixes</td>
+ </tr>
+</table>
+
+## Example
+
+<img
src="{{BASE_PATH}}/assets/themes/zeppelin/img/docs-img/sparql-example.png"
width="100%"/>
\ No newline at end of file
diff --git a/docs/usage/interpreter/installation.md
b/docs/usage/interpreter/installation.md
index 1ee708d..83c2094 100644
--- a/docs/usage/interpreter/installation.md
+++ b/docs/usage/interpreter/installation.md
@@ -244,6 +244,11 @@ You can also find the below community managed interpreter
list in `conf/interpre
<td>Shell command</td>
</tr>
<tr>
+ <td>sparql</td>
+ <td>org.apache.zeppelin:zeppelin-sparql:0.9.0</td>
+ <td>Sparql interpreter</td>
+ </tr>
+ <tr>
<td>submarine</td>
<td>org.apache.zeppelin:zeppelin-submarine:0.9.0</td>
<td>Submarine interpreter</td>
diff --git a/pom.xml b/pom.xml
index ffe1802..2ec7755 100644
--- a/pom.xml
+++ b/pom.xml
@@ -86,6 +86,7 @@
<module>hazelcastjet</module>
<module>geode</module>
<module>ksql</module>
+ <module>sparql</module>
<module>zeppelin-web</module>
<module>zeppelin-server</module>
<module>zeppelin-jupyter</module>
diff --git a/sparql/pom.xml b/sparql/pom.xml
new file mode 100644
index 0000000..abf6bc6
--- /dev/null
+++ b/sparql/pom.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one or more
+ ~ contributor license agreements. See the NOTICE file distributed with
+ ~ this work for additional information regarding copyright ownership.
+ ~ The ASF licenses this file to You under the Apache License, Version 2.0
+ ~ (the "License"); you may not use this file except in compliance with
+ ~ the License. You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <artifactId>zeppelin-interpreter-parent</artifactId>
+ <groupId>org.apache.zeppelin</groupId>
+ <version>0.9.0-SNAPSHOT</version>
+ <relativePath>../zeppelin-interpreter-parent/pom.xml</relativePath>
+ </parent>
+
+ <groupId>org.apache.zeppelin</groupId>
+ <artifactId>zeppelin-sparql</artifactId>
+ <packaging>jar</packaging>
+ <version>0.9.0-SNAPSHOT</version>
+ <name>Zeppelin: Sparql interpreter</name>
+ <description>Zeppelin sparql support</description>
+
+ <properties>
+ <interpreter.name>sparql</interpreter.name>
+ <jena.version>3.12.0</jena.version>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.jena</groupId>
+ <artifactId>jena-arq</artifactId>
+ <version>${jena.version}</version>
+ </dependency>
+
+ <!-- test libraries -->
+ <dependency>
+ <groupId>org.apache.jena</groupId>
+ <artifactId>jena-fuseki-main</artifactId>
+ <version>${jena.version}</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-enforcer-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <artifactId>maven-dependency-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <artifactId>maven-resources-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <artifactId>maven-shade-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-checkstyle-plugin</artifactId>
+ <configuration>
+ <skip>false</skip>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
diff --git
a/sparql/src/main/java/org/apache/zeppelin/sparql/JenaInterpreter.java
b/sparql/src/main/java/org/apache/zeppelin/sparql/JenaInterpreter.java
new file mode 100644
index 0000000..9faf869
--- /dev/null
+++ b/sparql/src/main/java/org/apache/zeppelin/sparql/JenaInterpreter.java
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.zeppelin.sparql;
+
+import org.apache.http.HttpStatus;
+
+import org.apache.jena.query.QueryExecution;
+import org.apache.jena.query.QueryExecutionFactory;
+import org.apache.jena.query.QueryParseException;
+import org.apache.jena.query.ResultSet;
+import org.apache.jena.query.ResultSetFormatter;
+import org.apache.jena.shared.PrefixMapping;
+import org.apache.jena.sparql.ARQException;
+import org.apache.jena.sparql.engine.http.QueryExceptionHTTP;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.ByteArrayOutputStream;
+import java.util.Map;
+
+import org.apache.zeppelin.interpreter.InterpreterResult;
+
+
+/**
+ * Interpreter for SPARQL-Query via Apache Jena ARQ.
+ */
+public class JenaInterpreter implements SparqlEngine {
+ private static final Logger LOGGER =
LoggerFactory.getLogger(JenaInterpreter.class);
+
+ private String serviceEndpoint;
+ private boolean replaceURIs;
+ private boolean removeDatatypes;
+
+ private QueryExecution queryExecution;
+
+ public JenaInterpreter(String serviceEndpoint, boolean replaceURIs, boolean
removeDatatypes) {
+ this.serviceEndpoint = serviceEndpoint;
+ this.replaceURIs = replaceURIs;
+ this.removeDatatypes = removeDatatypes;
+ }
+
+ @Override
+ public InterpreterResult query(String query) {
+ LOGGER.info("SPARQL: Run Query '" + query + "' against " +
serviceEndpoint);
+
+ try {
+ queryExecution = QueryExecutionFactory.sparqlService(serviceEndpoint,
query);
+ PrefixMapping prefixMapping =
queryExecution.getQuery().getPrefixMapping();
+
+ // execute query and get Results
+ ResultSet results = queryExecution.execSelect();
+
+ // transform ResultSet to TSV-String
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ ResultSetFormatter.outputAsTSV(outputStream, results);
+ String tsv = new String(outputStream.toByteArray());
+
+ if (replaceURIs) {
+ LOGGER.info("SPARQL: Replacing URIs");
+ tsv = replaceURIs(tsv, prefixMapping);
+ }
+
+ if (removeDatatypes) {
+ LOGGER.info("SPARQL: Removing datatypes");
+ tsv = removeDatatypes(tsv);
+ }
+
+ return new InterpreterResult(
+ InterpreterResult.Code.SUCCESS,
+ InterpreterResult.Type.TABLE,
+ tsv);
+ } catch (QueryParseException e) {
+ LOGGER.error(e.toString());
+ return new InterpreterResult(
+ InterpreterResult.Code.ERROR,
+ "Error: " + e.getMessage());
+ } catch (QueryExceptionHTTP e) {
+ LOGGER.error(e.toString());
+ int responseCode = e.getResponseCode();
+
+ if (responseCode == HttpStatus.SC_UNAUTHORIZED) {
+ return new InterpreterResult(
+ InterpreterResult.Code.ERROR,
+ "Unauthorized.");
+ } else if (responseCode == HttpStatus.SC_NOT_FOUND) {
+ return new InterpreterResult(
+ InterpreterResult.Code.ERROR,
+ "Endpoint not found, please check endpoint in the configuration.");
+ } else {
+ return new InterpreterResult(
+ InterpreterResult.Code.ERROR,
+ "Error: " + e.getMessage());
+ }
+ } catch (ARQException e) {
+ return new InterpreterResult(
+ InterpreterResult.Code.INCOMPLETE,
+ "Query cancelled.");
+ }
+ }
+
+ @Override
+ public void cancel() {
+ if (queryExecution != null) {
+ queryExecution.abort();
+ }
+ }
+
+ @Override
+ public void close() {
+ if (queryExecution != null) {
+ queryExecution.close();
+ }
+ }
+
+ private String replaceURIs(String tsv, PrefixMapping prefixMapping) {
+ Map<String, String> pmap = prefixMapping.getNsPrefixMap();
+ for (Map.Entry<String, String> entry : pmap.entrySet()) {
+ tsv = tsv.replaceAll(entry.getValue(), entry.getKey() + ":");
+ }
+ return tsv;
+ }
+
+ private String removeDatatypes(String tsv) {
+ // capture group: "($1)"^^<.+?>
+ return tsv.replaceAll("\"(.+?)\"\\^\\^\\<.+?\\>", "$1");
+ }
+}
diff --git a/sparql/src/main/java/org/apache/zeppelin/sparql/SparqlEngine.java
b/sparql/src/main/java/org/apache/zeppelin/sparql/SparqlEngine.java
new file mode 100644
index 0000000..af1863e
--- /dev/null
+++ b/sparql/src/main/java/org/apache/zeppelin/sparql/SparqlEngine.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.zeppelin.sparql;
+
+import org.apache.zeppelin.interpreter.InterpreterResult;
+
+
+/**
+ * Interpreter for SPARQL-Query via Apache Jena ARQ.
+ */
+public interface SparqlEngine {
+ InterpreterResult query(String query);
+
+ void cancel();
+
+ void close();
+}
diff --git
a/sparql/src/main/java/org/apache/zeppelin/sparql/SparqlInterpreter.java
b/sparql/src/main/java/org/apache/zeppelin/sparql/SparqlInterpreter.java
new file mode 100644
index 0000000..eeb9955
--- /dev/null
+++ b/sparql/src/main/java/org/apache/zeppelin/sparql/SparqlInterpreter.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.zeppelin.sparql;
+
+import org.apache.commons.lang3.StringUtils;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Properties;
+
+import org.apache.zeppelin.interpreter.Interpreter;
+import org.apache.zeppelin.interpreter.InterpreterContext;
+import org.apache.zeppelin.interpreter.InterpreterResult;
+
+
+/**
+ * Interpreter for SPARQL-Query via Apache Jena ARQ.
+ */
+public class SparqlInterpreter extends Interpreter {
+ private static final Logger LOGGER =
LoggerFactory.getLogger(SparqlInterpreter.class);
+
+ public static final String SPARQL_ENGINE_TYPE = "sparql.engine";
+ public static final String SPARQL_SERVICE_ENDPOINT = "sparql.endpoint";
+ public static final String SPARQL_REPLACE_URIS = "sparql.replaceURIs";
+ public static final String SPARQL_REMOVE_DATATYPES =
"sparql.removeDatatypes";
+
+ public static final String ENGINE_TYPE_JENA = "jena";
+
+ public SparqlEngine engine;
+
+ /**
+ * Sparql Engine Type.
+ */
+ public enum SparqlEngineType {
+ JENA {
+ @Override
+ public String toString() {
+ return ENGINE_TYPE_JENA;
+ }
+ }
+ }
+
+ public SparqlInterpreter(Properties properties) {
+ super(properties);
+ }
+
+ @Override
+ public void open() {
+ LOGGER.info("Properties: {}", getProperties());
+
+ String serviceEndpoint = getProperty(SPARQL_SERVICE_ENDPOINT);
+ boolean replaceURIs = getProperty(SPARQL_REPLACE_URIS) != null
+ && getProperty(SPARQL_REPLACE_URIS).equals("true");
+ boolean removeDatatypes = getProperty(SPARQL_REMOVE_DATATYPES) != null
+ && getProperty(SPARQL_REMOVE_DATATYPES).equals("true");
+ String engineType = getProperty(SPARQL_ENGINE_TYPE);
+
+ if (SparqlEngineType.JENA.toString().equals(engineType)) {
+ engine = new JenaInterpreter(serviceEndpoint, replaceURIs,
removeDatatypes);
+ }
+ }
+
+ @Override
+ public void close() {
+ engine.close();
+ }
+
+ @Override
+ public InterpreterResult interpret(String query, InterpreterContext context)
{
+ if (StringUtils.isEmpty(query) || StringUtils.isEmpty(query.trim())) {
+ return new InterpreterResult(InterpreterResult.Code.SUCCESS);
+ }
+
+ return engine.query(query);
+ }
+
+ @Override
+ public void cancel(InterpreterContext context) {
+ engine.cancel();
+ }
+
+ @Override
+ public FormType getFormType() {
+ return FormType.SIMPLE;
+ }
+
+ @Override
+ public int getProgress(InterpreterContext context) {
+ return 0;
+ }
+}
diff --git a/sparql/src/main/resources/interpreter-setting.json
b/sparql/src/main/resources/interpreter-setting.json
new file mode 100644
index 0000000..77bdbfb
--- /dev/null
+++ b/sparql/src/main/resources/interpreter-setting.json
@@ -0,0 +1,42 @@
+[
+ {
+ "group": "sparql",
+ "name": "sparql",
+ "className": "org.apache.zeppelin.sparql.SparqlInterpreter",
+ "properties": {
+ "sparql.engine": {
+ "envName": "SPARQL_ENGINE",
+ "propertyName": "sparql.engine",
+ "defaultValue": "jena",
+ "description": "The sparql engine to use for the queries. Default:
jena",
+ "type": "string"
+ },
+ "sparql.endpoint": {
+ "envName": "SPARQL_SERVICE_ENDPOINT",
+ "propertyName": "sparql.endpoint",
+ "defaultValue": "http://dbpedia.org/sparql",
+ "description": "Complete URL of the endpoint. Default:
http://dbpedia.org/sparql",
+ "type": "string"
+ },
+ "sparql.replaceURIs": {
+ "envName": "SPARQL_REPLACE_URI",
+ "propertyName": "sparql.replaceURIs",
+ "defaultValue": true,
+ "description": "Replace the URIs in the result with the prefixes.
Default: true",
+ "type": "checkbox"
+ },
+ "sparql.removeDatatypes": {
+ "envName": "SPARQL_REMOVE_DATATYPES",
+ "propertyName": "sparql.removeDatatypes",
+ "defaultValue": true,
+ "description": "Remove the datatypes from Literals so Zeppelin can use
the values. Default: true",
+ "type": "checkbox"
+ }
+ },
+ "editor": {
+ "language": "sparql",
+ "editOnDblClick": false,
+ "completionKey": "TAB"
+ }
+ }
+]
diff --git
a/sparql/src/test/java/org/apache/zeppelin/sparql/SparqlJenaEngineTest.java
b/sparql/src/test/java/org/apache/zeppelin/sparql/SparqlJenaEngineTest.java
new file mode 100644
index 0000000..15c8880
--- /dev/null
+++ b/sparql/src/test/java/org/apache/zeppelin/sparql/SparqlJenaEngineTest.java
@@ -0,0 +1,198 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.zeppelin.sparql;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.apache.jena.fuseki.Fuseki;
+import org.apache.jena.fuseki.main.FusekiServer;
+import org.apache.jena.fuseki.server.DataAccessPointRegistry;
+import org.apache.jena.query.Dataset;
+import org.apache.jena.query.DatasetFactory;
+import org.apache.jena.rdf.model.Model;
+import org.apache.jena.rdf.model.ModelFactory;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.Properties;
+
+import org.apache.zeppelin.interpreter.InterpreterResult;
+import org.apache.zeppelin.interpreter.InterpreterResult.Code;
+
+
+public class SparqlJenaEngineTest {
+ private static int port;
+
+ private static FusekiServer server;
+
+ private static final String ENGINE = "jena";
+ private static final String DATASET = "/dataset";
+ private static final String HOST = "http://localhost";
+
+ private static Properties properties;
+
+ private static final String DATA_FILE = "data.ttl";
+
+ @BeforeClass
+ public static void setUp() {
+ port = Fuseki.choosePort();
+
+ Model model = ModelFactory.createDefaultModel();
+ model.read(DATA_FILE);
+ Dataset ds = DatasetFactory.create(model);
+
+ server = FusekiServer
+ .create()
+ .port(port)
+ .add(DATASET, ds)
+ .build();
+ DataAccessPointRegistry registry = server.getDataAccessPointRegistry();
+ assertTrue(registry.isRegistered(DATASET));
+ assertEquals(1, registry.size());
+ server.start();
+ }
+
+ @AfterClass
+ public static void tearDown() {
+ if (server != null) {
+ server.stop();
+ }
+ }
+
+ @Before
+ public void setUpProperties() {
+ properties = new Properties();
+ properties.put(SparqlInterpreter.SPARQL_ENGINE_TYPE, ENGINE);
+ properties.put(SparqlInterpreter.SPARQL_SERVICE_ENDPOINT, HOST + ":" +
port + DATASET);
+ }
+
+ @Test
+ public void testWrongQuery() {
+ SparqlInterpreter interpreter = new SparqlInterpreter(properties);
+ interpreter.open();
+
+ final InterpreterResult result = interpreter.interpret("SELECT * WHER {",
null);
+ assertEquals(Code.ERROR, result.code());
+ }
+
+ @Test
+ public void testSuccessfulRawQuery() {
+ properties.put(SparqlInterpreter.SPARQL_REPLACE_URIS, "false");
+ properties.put(SparqlInterpreter.SPARQL_REMOVE_DATATYPES, "false");
+ SparqlInterpreter interpreter = new SparqlInterpreter(properties);
+ interpreter.open();
+
+ final InterpreterResult result = interpreter.interpret(
+ "PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>" +
+ "PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>" +
+ "PREFIX foaf: <http://xmlns.com/foaf/0.1/>" +
+ "PREFIX rel: <http://www.perceive.net/schemas/relationship/>" +
+ "SELECT ?subject ?predicate ?object WHERE { ?subject ?predicate
?object }", null);
+ assertEquals(Code.SUCCESS, result.code());
+
+ final String expected = "?subject\t?predicate\t?object\n" +
+ "<http://example.org/#spiderman>\t<http://xmlns.com/foaf/0.1/name>" +
+ "\t\"Человек-паук\"@ru\n<http://example.org/#spiderman>\t" +
+ "<http://xmlns.com/foaf/0.1/name>\t\"Spiderman\"\n" +
+
"<http://example.org/#spiderman>\t<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>"
+
+
"\t<http://xmlns.com/foaf/0.1/Person>\n<http://example.org/#spiderman>\t" +
+ "<http://www.perceive.net/schemas/relationship/enemyOf>\t" +
+
"<http://example.org/#green-goblin>\n<http://example.org/#spiderman>\t" +
+ "<http://example.org/stats#born>\t\"1962-10-15T14:00.00\"^^" +
+
"<http://www.w3.org/2001/XMLSchema#dateTime>\n<http://example.org/#green-goblin>\t"
+
+ "<http://xmlns.com/foaf/0.1/name>\t\"Green Goblin\"\n" +
+ "<http://example.org/#green-goblin>\t" +
+ "<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>" +
+
"\t<http://xmlns.com/foaf/0.1/Person>\n<http://example.org/#green-goblin>\t" +
+
"<http://www.perceive.net/schemas/relationship/enemyOf>\t<http://example.org/#spiderman>\n";
+ assertEquals(expected, result.message().get(0).getData());
+ }
+
+ @Test
+ public void testSuccessfulReplaceRemoveQuery() {
+ properties.put(SparqlInterpreter.SPARQL_REPLACE_URIS, "true");
+ properties.put(SparqlInterpreter.SPARQL_REMOVE_DATATYPES, "true");
+ SparqlInterpreter interpreter = new SparqlInterpreter(properties);
+ interpreter.open();
+
+ final InterpreterResult result = interpreter.interpret(
+ "PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>" +
+ "PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>" +
+ "PREFIX foaf: <http://xmlns.com/foaf/0.1/>" +
+ "PREFIX rel: <http://www.perceive.net/schemas/relationship/>" +
+ "SELECT ?subject ?predicate ?object WHERE { ?subject ?predicate
?object }", null);
+ assertEquals(Code.SUCCESS, result.code());
+
+ final String expected = "?subject\t?predicate\t?object\n" +
+ "<http://example.org/#spiderman>\t<foaf:name>" +
+ "\t\"Человек-паук\"@ru\n<http://example.org/#spiderman>\t" +
+ "<foaf:name>\t\"Spiderman\"\n" +
+ "<http://example.org/#spiderman>\t<rdf:type>" +
+ "\t<foaf:Person>\n<http://example.org/#spiderman>\t" +
+ "<rel:enemyOf>\t" +
+
"<http://example.org/#green-goblin>\n<http://example.org/#spiderman>\t" +
+ "<http://example.org/stats#born>\t1962-10-15T14:00.00\n" +
+ "<http://example.org/#green-goblin>\t<foaf:name>\t\"Green Goblin\"\n" +
+ "<http://example.org/#green-goblin>\t" +
+ "<rdf:type>" +
+ "\t<foaf:Person>\n<http://example.org/#green-goblin>\t" +
+ "<rel:enemyOf>\t<http://example.org/#spiderman>\n";
+
+ assertEquals(expected, result.message().get(0).getData());
+ }
+
+ @Test
+ public void testRemoteEndpoint() {
+ properties.put(SparqlInterpreter.SPARQL_SERVICE_ENDPOINT,
"http://dbpedia.org/sparql");
+ SparqlInterpreter interpreter = new SparqlInterpreter(properties);
+ interpreter.open();
+
+ final InterpreterResult result = interpreter.interpret(
+ "SELECT DISTINCT ?Concept WHERE {[] a ?Concept} LIMIT 1", null);
+ assertEquals(Code.SUCCESS, result.code());
+
+ final String expected =
"?Concept\n<http://www.openlinksw.com/schemas/virtrdf#QuadMapFormat>\n";
+ assertEquals(expected, result.message().get(0).getData());
+ }
+
+ @Test
+ public void testEndpointMalformed() {
+ properties.put(SparqlInterpreter.SPARQL_SERVICE_ENDPOINT, "tsohlacol");
+ SparqlInterpreter interpreter = new SparqlInterpreter(properties);
+ interpreter.open();
+
+ final InterpreterResult result = interpreter.interpret(
+ "SELECT ?subject ?predicate ?object WHERE { ?subject ?predicate
?object }", null);
+ assertEquals(Code.ERROR, result.code());
+ }
+
+ @Test
+ public void testEndpointNotFound() {
+ properties.put(SparqlInterpreter.SPARQL_SERVICE_ENDPOINT,
"http://tsohlacol/");
+ SparqlInterpreter interpreter = new SparqlInterpreter(properties);
+ interpreter.open();
+
+ final InterpreterResult result = interpreter.interpret(
+ "SELECT ?subject ?predicate ?object WHERE { ?subject ?predicate
?object }", null);
+ assertEquals(Code.ERROR, result.code());
+ }
+}
diff --git a/sparql/src/test/resources/data.ttl
b/sparql/src/test/resources/data.ttl
new file mode 100644
index 0000000..984aaee
--- /dev/null
+++ b/sparql/src/test/resources/data.ttl
@@ -0,0 +1,18 @@
+@base <http://example.org/> .
+@prefix : <http://example.org/stats#> .
+@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
+@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
+@prefix foaf: <http://xmlns.com/foaf/0.1/> .
+@prefix rel: <http://www.perceive.net/schemas/relationship/> .
+@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
+
+<#green-goblin>
+ rel:enemyOf <#spiderman> ;
+ a foaf:Person ;
+ foaf:name "Green Goblin" .
+
+<#spiderman>
+ :born "1962-10-15T14:00.00"^^xsd:dateTime ;
+ rel:enemyOf <#green-goblin> ;
+ a foaf:Person ;
+ foaf:name "Spiderman", "Человек-паук"@ru .
\ No newline at end of file
diff --git a/zeppelin-distribution/src/bin_license/LICENSE
b/zeppelin-distribution/src/bin_license/LICENSE
index c115e9f..24880ff 100644
--- a/zeppelin-distribution/src/bin_license/LICENSE
+++ b/zeppelin-distribution/src/bin_license/LICENSE
@@ -37,6 +37,7 @@ The following components are provided under Apache License.
(Apache 2.0) Apache Kylin (http://kylin.apache.org/)
(Apache 2.0) Apache Lens (http://lens.apache.org/)
(Apache 2.0) Apache Flink (http://flink.apache.org/)
+ (Apache 2.0) Apache Jena (https://jena.apache.org/)
(Apache 2.0) Apache Beam (http://beam.apache.org/)
(Apache 2.0) Apache Thrift 0.12.0 (org.apache.thrift:libthrift:0.12.0 -
http://thrift.apache.org/)
(Apache 2.0) Apache Lucene (https://lucene.apache.org/)
diff --git a/zeppelin-web/bower.json b/zeppelin-web/bower.json
index 1e9c480..b2cee2a 100644
--- a/zeppelin-web/bower.json
+++ b/zeppelin-web/bower.json
@@ -53,6 +53,7 @@
"src-noconflict/mode-pig.js",
"src-noconflict/mode-sh.js",
"src-noconflict/mode-r.js",
+ "src-noconflict/mode-sparql.js",
"src-noconflict/keybinding-emacs.js",
"src-noconflict/ext-language_tools.js",
"src-noconflict/theme-chrome.js"
diff --git a/zeppelin-web/karma.conf.js b/zeppelin-web/karma.conf.js
index 5daceb9..6ceef8d 100644
--- a/zeppelin-web/karma.conf.js
+++ b/zeppelin-web/karma.conf.js
@@ -62,6 +62,7 @@ module.exports = function(config) {
'bower_components/ace-builds/src-noconflict/mode-pig.js',
'bower_components/ace-builds/src-noconflict/mode-sh.js',
'bower_components/ace-builds/src-noconflict/mode-r.js',
+ 'bower_components/ace-builds/src-noconflict/mode-sparql.js',
'bower_components/ace-builds/src-noconflict/keybinding-emacs.js',
'bower_components/ace-builds/src-noconflict/ext-language_tools.js',
'bower_components/ace-builds/src-noconflict/theme-chrome.js',
diff --git a/zeppelin-web/src/index.html b/zeppelin-web/src/index.html
index fb831c8..6ddcdbb 100644
--- a/zeppelin-web/src/index.html
+++ b/zeppelin-web/src/index.html
@@ -186,6 +186,7 @@ limitations under the License.
<script
src="bower_components/ace-builds/src-noconflict/mode-pig.js"></script>
<script
src="bower_components/ace-builds/src-noconflict/mode-sh.js"></script>
<script
src="bower_components/ace-builds/src-noconflict/mode-r.js"></script>
+ <script
src="bower_components/ace-builds/src-noconflict/mode-sparql.js"></script>
<script
src="bower_components/ace-builds/src-noconflict/keybinding-emacs.js"></script>
<script
src="bower_components/ace-builds/src-noconflict/ext-language_tools.js"></script>
<script
src="bower_components/ace-builds/src-noconflict/theme-chrome.js"></script>