You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by an...@apache.org on 2018/02/27 23:10:32 UTC
[12/24] jena git commit: RDFConnectionFactory functions and tests for
RDFConnectionFuseki
RDFConnectionFactory functions and tests for RDFConnectionFuseki
Project: http://git-wip-us.apache.org/repos/asf/jena/repo
Commit: http://git-wip-us.apache.org/repos/asf/jena/commit/6b2dcba8
Tree: http://git-wip-us.apache.org/repos/asf/jena/tree/6b2dcba8
Diff: http://git-wip-us.apache.org/repos/asf/jena/diff/6b2dcba8
Branch: refs/heads/master
Commit: 6b2dcba8a4b70295863a666af3eb56090abaafa0
Parents: da1ccbc
Author: Andy Seaborne <an...@apache.org>
Authored: Thu Feb 22 20:00:58 2018 +0000
Committer: Andy Seaborne <an...@apache.org>
Committed: Thu Feb 22 20:00:58 2018 +0000
----------------------------------------------------------------------
.../java/org/apache/jena/fuseki/FusekiLib.java | 45 +++++
.../test/rdfconnection/TS_RDFConnection2.java | 3 +-
.../test/rdfconnection/TestBlankNodeBinary.java | 192 +++++++++++++++++++
.../rdfconnection/RDFConnectionFactory.java | 50 ++++-
4 files changed, 288 insertions(+), 2 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/jena/blob/6b2dcba8/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/FusekiLib.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/FusekiLib.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/FusekiLib.java
index 823593a..dcfa590 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/FusekiLib.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/FusekiLib.java
@@ -24,7 +24,14 @@ import java.util.Iterator ;
import javax.servlet.http.HttpServletRequest ;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpOptions;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.protocol.HttpContext;
+import org.apache.jena.atlas.logging.Log;
import org.apache.jena.atlas.web.ContentType ;
+import org.apache.jena.atlas.web.HttpException;
import org.apache.jena.ext.com.google.common.collect.ArrayListMultimap;
import org.apache.jena.ext.com.google.common.collect.Multimap;
import org.apache.jena.fuseki.server.SystemState ;
@@ -37,8 +44,10 @@ import org.apache.jena.rdf.model.Literal ;
import org.apache.jena.rdf.model.Model ;
import org.apache.jena.rdf.model.RDFNode ;
import org.apache.jena.rdf.model.Resource ;
+import org.apache.jena.rdfconnection.RDFConnectionRemote;
import org.apache.jena.riot.Lang ;
import org.apache.jena.riot.RDFLanguages ;
+import org.apache.jena.riot.web.HttpOp;
import org.apache.jena.shared.PrefixMapping ;
import org.apache.jena.sparql.core.DatasetGraph ;
import org.apache.jena.sparql.core.Quad ;
@@ -265,4 +274,40 @@ public class FusekiLib {
throw new FusekiException("Failed to find a port");
}
}
+
+ /** Test whether a URL identifies a Fuseki server */
+ public static boolean isFuseki(String datasetURL) {
+ HttpOptions request = new HttpOptions(datasetURL);
+ HttpClient httpClient = HttpOp.getDefaultHttpClient();
+ if ( httpClient == null )
+ httpClient = HttpClients.createSystem();
+ return isFuseki(request, httpClient, null);
+ }
+
+ /** Test whether a {@link RDFConnectionRemote} connects to a Fuseki server */
+ public static boolean isFuseki(RDFConnectionRemote connection) {
+ HttpOptions request = new HttpOptions(connection.getDestination());
+ HttpClient httpClient = connection.getHttpClient();
+ if ( httpClient == null )
+ httpClient = HttpClients.createSystem();
+ HttpContext httpContext = connection.getHttpContext();
+ return isFuseki(request, httpClient, httpContext);
+ }
+
+ private static boolean isFuseki(HttpOptions request, HttpClient httpClient, HttpContext httpContext) {
+ try {
+ HttpResponse response = httpClient.execute(request);
+ // Fuseki-Request-ID:
+ //String reqId = response.getFirstHeader("Fuseki-Request-ID").getValue();
+ // Server:
+ String serverIdent = response.getFirstHeader("Server").getValue();
+ Log.debug(ARQ.getHttpRequestLogger(), "Server: "+serverIdent);
+ boolean isFuseki = serverIdent.startsWith("Apache Jena Fuseki");
+ if ( !isFuseki )
+ isFuseki = serverIdent.toLowerCase().contains("fuseki");
+ return isFuseki; // Maybe
+ } catch (IOException ex) {
+ throw new HttpException("Failed to check for a Fuseki server", ex);
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/jena/blob/6b2dcba8/jena-integration-tests/src/test/java/org/apache/jena/test/rdfconnection/TS_RDFConnection2.java
----------------------------------------------------------------------
diff --git a/jena-integration-tests/src/test/java/org/apache/jena/test/rdfconnection/TS_RDFConnection2.java b/jena-integration-tests/src/test/java/org/apache/jena/test/rdfconnection/TS_RDFConnection2.java
index 55a0dff..ea37a51 100644
--- a/jena-integration-tests/src/test/java/org/apache/jena/test/rdfconnection/TS_RDFConnection2.java
+++ b/jena-integration-tests/src/test/java/org/apache/jena/test/rdfconnection/TS_RDFConnection2.java
@@ -29,7 +29,8 @@ import org.junit.runners.Suite ;
// Addition tests added here.
TestRDFConnectionLocalTDB.class ,
- TestRDFConnectionRemote.class
+ TestRDFConnectionRemote.class,
+ TestBlankNodeBinary.class
})
public class TS_RDFConnection2 {}
http://git-wip-us.apache.org/repos/asf/jena/blob/6b2dcba8/jena-integration-tests/src/test/java/org/apache/jena/test/rdfconnection/TestBlankNodeBinary.java
----------------------------------------------------------------------
diff --git a/jena-integration-tests/src/test/java/org/apache/jena/test/rdfconnection/TestBlankNodeBinary.java b/jena-integration-tests/src/test/java/org/apache/jena/test/rdfconnection/TestBlankNodeBinary.java
new file mode 100644
index 0000000..a3eee4d
--- /dev/null
+++ b/jena-integration-tests/src/test/java/org/apache/jena/test/rdfconnection/TestBlankNodeBinary.java
@@ -0,0 +1,192 @@
+/*
+ * 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.jena.test.rdfconnection;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+
+import org.apache.jena.fuseki.FusekiLib;
+import org.apache.jena.fuseki.embedded.FusekiServer;
+import org.apache.jena.graph.*;
+import org.apache.jena.query.Query;
+import org.apache.jena.query.QueryExecution;
+import org.apache.jena.query.QueryFactory;
+import org.apache.jena.rdf.model.Model;
+import org.apache.jena.rdf.model.ModelFactory;
+import org.apache.jena.rdfconnection.RDFConnectionFuseki;
+import org.apache.jena.rdfconnection.RDFConnectionRemoteBuilder;
+import org.apache.jena.riot.Lang;
+import org.apache.jena.riot.RDFDataMgr;
+import org.apache.jena.sparql.algebra.Algebra;
+import org.apache.jena.sparql.algebra.Op;
+import org.apache.jena.sparql.algebra.op.OpBGP;
+import org.apache.jena.sparql.core.BasicPattern;
+import org.apache.jena.sparql.core.DatasetGraph;
+import org.apache.jena.sparql.core.DatasetGraphFactory;
+import org.apache.jena.sparql.core.TriplePath;
+import org.apache.jena.sparql.modify.request.UpdateDataInsert;
+import org.apache.jena.sparql.sse.SSE;
+import org.apache.jena.sparql.syntax.Element;
+import org.apache.jena.sparql.syntax.ElementGroup;
+import org.apache.jena.sparql.syntax.ElementPathBlock;
+import org.apache.jena.update.Update;
+import org.apache.jena.update.UpdateFactory;
+import org.apache.jena.update.UpdateRequest;
+import org.junit.Test;
+
+/* Tests that blanknodes work over RDFConnectionFuseki
+ * This consists of testing each of the necessary components,
+ * and then a test of a connection itself.
+ */
+
+public class TestBlankNodeBinary {
+ private static Node n(String str) { return SSE.parseNode(str) ; }
+
+ // Check RDF Thrift round-trips blank nodes.
+ @Test public void binaryThrift() {
+ Triple t = Triple.create(n(":s"), n(":p"), NodeFactory.createBlankNode("ABCD"));
+ Node obj = t.getObject();
+ Graph graph = Factory.createDefaultGraph();
+ graph.add(t);
+
+ ByteArrayOutputStream bout = new ByteArrayOutputStream();
+ RDFDataMgr.write(bout, graph, Lang.RDFTHRIFT);
+
+ ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
+ Graph graph1 = Factory.createDefaultGraph();
+ RDFDataMgr.read(graph1, bin, Lang.RDFTHRIFT);
+
+ Node obj1 = graph1.find().next().getObject();
+ assertEquals(obj, obj1);
+ assertTrue(obj1.isBlank());
+ assertEquals(obj.getBlankNodeLabel(), obj1.getBlankNodeLabel());
+ }
+
+ // Check SPARQL parsing.
+ @Test public void bNodeSPARQL_Query_1() {
+ String qs = "SELECT * { ?s ?p <_:ABC>}";
+ Query query = QueryFactory.create(qs);
+ Element el = ((ElementGroup)query.getQueryPattern()).get(0);
+ ElementPathBlock epb = (ElementPathBlock)el;
+ TriplePath tp = epb.getPattern().get(0);
+ Triple t = tp.asTriple();
+ assertEquals("ABC", t.getObject().getBlankNodeLabel());
+ }
+
+ @Test public void bNodeSPARQL_Query_2() {
+ String qs = "SELECT * { ?s ?p <_:BCD>}";
+ Query query = QueryFactory.create(qs);
+ Op op = Algebra.compile(query);
+ BasicPattern bp = ((OpBGP)op).getPattern();
+ Triple t = bp.get(0);
+ assertEquals("BCD", t.getObject().getBlankNodeLabel());
+ }
+
+ @Test public void bNodeSPARQL_Update_1() {
+ String str = "INSERT DATA { <x:s> <x:p> <_:789> }";
+ UpdateRequest req = UpdateFactory.create(str);
+ Update update = req.getOperations().get(0);
+ UpdateDataInsert ins = (UpdateDataInsert)update;
+ Node obj = ins.getQuads().get(0).getObject();
+ assertEquals("789", obj.getBlankNodeLabel());
+ }
+
+ // RDFConnection level testing.
+
+ @Test public void rdfconnection_binary_1() {
+ // Tests on one connection.
+ Triple triple = SSE.parseTriple("(:s :p <_:b3456>)");
+ // Goes in as URI! (pre this PR)
+ Model model = ModelFactory.createDefaultModel();
+ model.getGraph().add(triple);
+
+ int PORT = FusekiLib.choosePort();
+ FusekiServer server = createFusekiServer(PORT).build().start();
+ try {
+ String dsURL = "http://localhost:"+PORT+"/ds" ;
+ assertTrue(FusekiLib.isFuseki(dsURL));
+
+ RDFConnectionRemoteBuilder builder = RDFConnectionFuseki.create().destination(dsURL);
+
+ try (RDFConnectionFuseki conn = (RDFConnectionFuseki)builder.build()) {
+ assertTrue(FusekiLib.isFuseki(conn));
+ // GSP
+ conn.put(model);
+ checkModel(conn, "b3456");
+
+ // Query forms.
+ conn.querySelect("SELECT * {?s ?p ?o}", x-> {
+ Node obj = x.get("o").asNode();
+ assertTrue(obj.isBlank());
+ assertEquals("b3456", obj.getBlankNodeLabel());
+ });
+
+ try(QueryExecution qExec = conn.query("ASK {?s ?p <_:b3456>}")){
+ boolean bool = qExec.execAsk();
+ assertTrue(bool);
+ }
+
+ try (QueryExecution qExec = conn.query("CONSTRUCT { ?s ?p ?o } WHERE { ?s ?p ?o . FILTER (sameTerm(?o, <_:b3456>)) }")){
+ Model model2 = qExec.execConstruct() ;
+ checkModel(model2, "b3456");
+ }
+
+ try(QueryExecution qExec = conn.query("DESCRIBE ?s WHERE { ?s ?p <_:b3456>}")){
+ Model model2 = qExec.execConstruct() ;
+ checkModel(model2, "b3456");
+ }
+
+ // Update
+ conn.update("CLEAR DEFAULT" );
+ conn.update("INSERT DATA { <x:s> <x:p> <_:b789> }" );
+ checkModel(conn, "b789");
+ conn.update("CLEAR DEFAULT" );
+ conn.update("INSERT DATA { <x:s> <x:p> <_:6789> }" );
+ checkModel(conn, "6789");
+ }
+ } finally { server.stop(); }
+ }
+
+ private void checkModel(RDFConnectionFuseki conn, String label) {
+ Model model2 = conn.fetch();
+ checkModel(model2, label);
+ }
+
+ private void checkModel(Model model2, String label) {
+ assertEquals(1, model2.size());
+ Node n = model2.listStatements().next().getObject().asNode();
+ assertTrue(n.isBlank());
+ assertEquals(label, n.getBlankNodeLabel());
+ }
+
+
+ private static FusekiServer.Builder createFusekiServer(int PORT) {
+ DatasetGraph dsg = DatasetGraphFactory.createTxnMem();
+ return
+ FusekiServer.create()
+ .setPort(PORT)
+ //.setStaticFileBase("/home/afs/ASF/jena-fuseki-cmds/sparqler")
+ .add("/ds", dsg)
+ //.setVerbose(true)
+ ;
+ }
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/6b2dcba8/jena-rdfconnection/src/main/java/org/apache/jena/rdfconnection/RDFConnectionFactory.java
----------------------------------------------------------------------
diff --git a/jena-rdfconnection/src/main/java/org/apache/jena/rdfconnection/RDFConnectionFactory.java b/jena-rdfconnection/src/main/java/org/apache/jena/rdfconnection/RDFConnectionFactory.java
index ef6371c..3049812 100644
--- a/jena-rdfconnection/src/main/java/org/apache/jena/rdfconnection/RDFConnectionFactory.java
+++ b/jena-rdfconnection/src/main/java/org/apache/jena/rdfconnection/RDFConnectionFactory.java
@@ -34,7 +34,7 @@ public class RDFConnectionFactory {
* <li>SPARQL Graph Store Protocol : "data"
* </ul>
* These are the default names in <a href="http://jena.apache.org/documentation/fuseki2">Fuseki</a>
- * Other names can be specificied using {@link #connect(String, String, String, String)}
+ * Other names can be specified using {@link #connect(String, String, String, String)}
*
* @param destination
* @return RDFConnection
@@ -117,4 +117,52 @@ public class RDFConnectionFactory {
public static RDFConnection connect(Dataset dataset, Isolation isolation) {
return new RDFConnectionLocal(dataset, isolation);
}
+
+ /** Create a connection to a remote Fuseki server by URL.
+ * This is the URL for the dataset.
+ * <p>
+ * A {@link RDFConnectionFuseki} is an {@link RDFConnection} that:
+ * <ul>
+ * <li>provides round-trip of blank nodes between this application and the server
+ * <li>uses the more efficient <a href="http://jena.apache.org/documentation/io/rdf-binary.html">RDF Thrift binary</a> format.
+ * </ul>
+ *
+ * This factory call assumes the names of services as:
+ * <ul>
+ * <li>SPARQL Query endpoint : "sparql"
+ * <li>SPARQL Update endpoint : "update"
+ * <li>SPARQL Graph Store Protocol : "data"
+ * </ul>
+ * These are the default names in <a href="http://jena.apache.org/documentation/fuseki2">Fuseki</a>
+ * Other names can be specified using {@link #connectFuseki(String, String, String, String)}.
+ *
+ * @param destination
+ * @return RDFConnectionFuseki
+ */
+ public static RDFConnectionFuseki connectFuseki(String destination) {
+ return (RDFConnectionFuseki)RDFConnectionFuseki.create().destination(destination).build();
+ }
+
+ /** Create a connection to a remote Fuseki server by URL.
+ * This is the URL for the dataset.
+ *
+ * Each service is then specified by a URL which is relative to the {@code datasetURL}.
+ *
+ * @param datasetURL
+ * @param queryServiceEndpoint
+ * @param updateServiceEndpoint
+ * @param graphStoreProtocolEndpoint
+ * @return RDFConnectionFuseki
+ s */
+ public static RDFConnectionFuseki connectFuseki(String datasetURL,
+ String queryServiceEndpoint,
+ String updateServiceEndpoint,
+ String graphStoreProtocolEndpoint) {
+ return (RDFConnectionFuseki)RDFConnectionFuseki.create()
+ .destination(datasetURL)
+ .queryEndpoint(queryServiceEndpoint)
+ .updateEndpoint(updateServiceEndpoint)
+ .gspEndpoint(graphStoreProtocolEndpoint)
+ .build();
+ }
}