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();
+        }
 }