http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/TestSPARQLProtocol.java ---------------------------------------------------------------------- diff --git a/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/TestSPARQLProtocol.java b/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/TestSPARQLProtocol.java new file mode 100644 index 0000000..3b86e17 --- /dev/null +++ b/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/TestSPARQLProtocol.java @@ -0,0 +1,95 @@ +/* + * 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.fuseki; + +import static org.apache.jena.fuseki.ServerTest.* ; +import org.apache.jena.atlas.junit.BaseTest ; +import org.apache.jena.riot.WebContent ; +import org.junit.AfterClass ; +import org.junit.BeforeClass ; +import org.junit.Test ; + +import com.hp.hpl.jena.query.* ; +import com.hp.hpl.jena.sparql.engine.http.QueryEngineHTTP ; +import com.hp.hpl.jena.sparql.util.Convert ; +import com.hp.hpl.jena.update.UpdateExecutionFactory ; +import com.hp.hpl.jena.update.UpdateFactory ; +import com.hp.hpl.jena.update.UpdateProcessor ; +import com.hp.hpl.jena.update.UpdateRequest ; +// Generally poke the server using Jena APIs +// SPARQL Query +// SPARQL Update +// GSP is done in TestDatasetAccessorHTTP + +public class TestSPARQLProtocol extends BaseTest +{ + @BeforeClass public static void beforeClass() + { + ServerTest.allocServer() ; + ServerTest.resetServer() ; + // Load some data. + DatasetAccessor du = DatasetAccessorFactory.createHTTP(serviceREST) ; + du.putModel(model1) ; + du.putModel(gn1, model2) ; + } + + @AfterClass public static void afterClass() + { + ServerTest.resetServer() ; + ServerTest.freeServer() ; + } + + static String query(String base, String queryString) + { + return base+"?query="+Convert.encWWWForm(queryString) ; + } + + @Test public void query_01() + { + Query query = QueryFactory.create("SELECT * { ?s ?p ?o }") ; + QueryExecution qexec = QueryExecutionFactory.sparqlService(serviceQuery, query) ; + ResultSet rs = qexec.execSelect() ; + int x = ResultSetFormatter.consume(rs) ; + assertTrue( x != 0 ) ; + } + + @Test public void query_02() + { + Query query = QueryFactory.create("SELECT * { ?s ?p ?o }") ; + QueryEngineHTTP engine = QueryExecutionFactory.createServiceRequest(serviceQuery, query) ; + engine.setSelectContentType(WebContent.contentTypeResultsJSON) ; + ResultSet rs = engine.execSelect() ; + int x = ResultSetFormatter.consume(rs) ; + assertTrue( x != 0 ) ; + } + + @Test public void update_01() + { + UpdateRequest update = UpdateFactory.create("INSERT DATA {}") ; + UpdateProcessor proc = UpdateExecutionFactory.createRemote(update, serviceUpdate) ; + proc.execute() ; + } + + @Test public void update_02() + { + UpdateRequest update = UpdateFactory.create("INSERT DATA {}") ; + UpdateProcessor proc = UpdateExecutionFactory.createRemoteForm(update, serviceUpdate) ; + proc.execute() ; + } +}
http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/http/TestDatasetAccessorHTTP.java ---------------------------------------------------------------------- diff --git a/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/http/TestDatasetAccessorHTTP.java b/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/http/TestDatasetAccessorHTTP.java new file mode 100644 index 0000000..1314a2b --- /dev/null +++ b/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/http/TestDatasetAccessorHTTP.java @@ -0,0 +1,261 @@ +/* + * 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.fuseki.http; + +import static org.apache.jena.fuseki.ServerTest.* ; +import org.apache.jena.atlas.junit.BaseTest ; +import org.apache.jena.atlas.web.HttpException ; +import org.apache.jena.fuseki.ServerTest ; +import org.apache.jena.riot.web.HttpOp ; +import org.apache.jena.web.HttpSC ; +import org.junit.AfterClass ; +import org.junit.Before ; +import org.junit.BeforeClass ; +import org.junit.Test ; + +import com.hp.hpl.jena.query.DatasetAccessor ; +import com.hp.hpl.jena.query.DatasetAccessorFactory ; +import com.hp.hpl.jena.rdf.model.Model ; +import com.hp.hpl.jena.rdf.model.ModelFactory ; + + +public class TestDatasetAccessorHTTP extends BaseTest +{ + //Model level testing. + + static final String datasetURI_not_1 = "http://localhost:"+port+"/junk" ; + static final String datasetURI_not_2 = serviceREST+"/not" ; + static final String datasetURI_not_3 = "http://localhost:"+port+datasetPath+"/not/data" ; + + @BeforeClass public static void beforeClass() { ServerTest.allocServer() ; } + @AfterClass public static void afterClass() { ServerTest.freeServer() ; } + @Before public void before() { ServerTest.resetServer() ; } + + @Test + public void test_ds_1() + { + // Can GET the dataset service. + try { + HttpOp.execHttpGet(serviceREST) ; + } catch (HttpException ex) { + assertTrue(HttpSC.isClientError(ex.getResponseCode())) ; + throw ex ; + } + } + + @Test(expected=HttpException.class) + public void test_ds_2() + { + try { + HttpOp.execHttpGet(datasetURI_not_1) ; + } catch (HttpException ex) { + assertEquals(HttpSC.NOT_FOUND_404, ex.getResponseCode()) ; + throw ex ; + } + } + + @Test(expected=HttpException.class) + public void test_ds_3() + { + try { + HttpOp.execHttpGet(datasetURI_not_2) ; + } catch (HttpException ex) { + assertEquals(HttpSC.NOT_FOUND_404, ex.getResponseCode()) ; + throw ex ; + } + } + + @Test + public void test_404_1() + { + // Not the right service. + DatasetAccessor du = DatasetAccessorFactory.createHTTP(datasetURI_not_1) ; + Model graph = du.getModel(gn99) ; + assertNull(graph) ; + } + + @Test + public void test_404_2() + { + DatasetAccessor du = DatasetAccessorFactory.createHTTP(datasetURI_not_2) ; + Model graph = du.getModel(gn99) ; + assertNull(graph) ; + } + + @Test + public void test_404_3() + { + // Right service, wrong graph + DatasetAccessor du = DatasetAccessorFactory.createHTTP(serviceREST) ; + Model graph = du.getModel(gn99) ; + assertNull(graph) ; + } + + @Test public void head_01() + { + DatasetAccessor du = create() ; + boolean b = du.containsModel(gn1) ; + assertFalse("Blank remote dataset as a named graph", b) ; + } + + @Test public void head_02() + { + DatasetAccessor du = create() ; + du.putModel(gn1, model1) ; + boolean exists = du.containsModel(gn1) ; + assertTrue(exists) ; + exists = du.containsModel(gn2) ; + assertFalse("Expected gn2 not to exist (1)", exists) ; + + exists = du.containsModel(gn2) ; + assertFalse("Expected gn2 not to exist (2)", exists) ; + // Clearup + du.deleteModel(gn1) ; + } + + @Test public void get_01() + { + DatasetAccessor du = create() ; + Model graph = du.getModel() ; + assertTrue(graph.isEmpty()) ; + } + + @Test public void get_02() + { + DatasetAccessor du = create() ; + Model graph = du.getModel(gn1) ; + assertNull(graph) ; + } + + @Test public void delete_01() + { + DatasetAccessor du = create() ; + du.deleteDefault() ; + } + + @Test public void delete_02() + { + DatasetAccessor du = create() ; + du.deleteModel(gn1) ; + boolean exists = du.containsModel(gn1) ; + assertFalse("Expected gn1 not to exist", exists) ; + } + + @Test public void put_01() + { + DatasetAccessor du = create() ; + du.putModel(model1) ; + Model graph = du.getModel() ; + assertTrue(graph.isIsomorphicWith(model1)) ; + // Empty it. + du.deleteDefault() ; + graph = du.getModel() ; + assertTrue(graph.isEmpty()) ; + } + + @Test public void put_02() + { + DatasetAccessor du = create() ; + du.putModel(gn1, model1) ; + boolean exists = du.containsModel(gn1) ; + assertTrue(exists) ; + exists = du.containsModel(gn2) ; + assertFalse("Expected gn2 not to exist", exists) ; + + Model graph = du.getModel() ; + assertTrue(graph.isEmpty()) ; + graph = du.getModel(gn1) ; + assertTrue(graph.isIsomorphicWith(model1)) ; + + du.deleteModel(gn1) ; + exists = du.containsModel(gn1) ; + assertFalse("Expected gn1 not to exist", exists) ; + + graph = du.getModel(gn1) ; + assertNull(graph) ; + } + + @Test public void put_03() + { + DatasetAccessor du = create() ; + du.putModel(model1) ; + du.putModel(model2) ; // PUT overwrites + Model graph = du.getModel() ; + assertFalse(graph.isIsomorphicWith(model1)) ; + assertTrue(graph.isIsomorphicWith(model2)) ; + // Empty it. + du.deleteDefault() ; + graph = du.getModel() ; + assertTrue(graph.isEmpty()) ; + } + + @Test public void post_01() + { + DatasetAccessor du = create() ; + du.putModel(model1) ; + du.add(model2) ; // POST appends + Model graph = du.getModel() ; + + Model graph3 = ModelFactory.createDefaultModel() ; + graph3.add(model1) ; + graph3.add(model2) ; + + assertFalse(graph.isIsomorphicWith(model1)) ; + assertFalse(graph.isIsomorphicWith(model2)) ; + assertTrue(graph.isIsomorphicWith(graph3)) ; + // Empty it. + du.deleteDefault() ; + graph = du.getModel() ; + assertTrue(graph.isEmpty()) ; + } + + @Test public void post_02() + { + DatasetAccessor du = create() ; + du.add(model1) ; + du.add(model2) ; + Model graph = du.getModel() ; + + Model graph3 = ModelFactory.createDefaultModel() ; + graph3.add(model1) ; + graph3.add(model2) ; + + assertFalse(graph.isIsomorphicWith(model1)) ; + assertFalse(graph.isIsomorphicWith(model2)) ; + assertTrue(graph.isIsomorphicWith(graph3)) ; + // Empty it. + du.deleteDefault() ; + graph = du.getModel() ; + assertTrue(graph.isEmpty()) ; + } + + @Test public void clearup_1() + { + DatasetAccessor du = create() ; + du.deleteDefault() ; + du.deleteModel(gn1) ; + du.deleteModel(gn2) ; + du.deleteModel(gn99) ; + } + + static DatasetAccessor create() + { + return DatasetAccessorFactory.createHTTP(ServerTest.serviceREST) ; + } +} http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/http/TestDatasetGraphAccessorHTTP.java ---------------------------------------------------------------------- diff --git a/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/http/TestDatasetGraphAccessorHTTP.java b/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/http/TestDatasetGraphAccessorHTTP.java new file mode 100644 index 0000000..7687b2c --- /dev/null +++ b/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/http/TestDatasetGraphAccessorHTTP.java @@ -0,0 +1,43 @@ +/* + * 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.fuseki.http; + +import org.apache.jena.fuseki.ServerTest ; +import org.apache.jena.web.AbstractTestDatasetGraphAccessor ; +import org.apache.jena.web.DatasetGraphAccessor ; +import org.apache.jena.web.DatasetGraphAccessorHTTP ; +import org.junit.AfterClass ; +import org.junit.Before ; +import org.junit.BeforeClass ; + +public class TestDatasetGraphAccessorHTTP extends AbstractTestDatasetGraphAccessor +{ + @BeforeClass public static void beforeClass() { ServerTest.allocServer() ; } + @AfterClass public static void afterClass() { ServerTest.freeServer() ; } + @Before public void before() { + ServerTest.resetServer() ; + } + + + @Override + protected DatasetGraphAccessor getDatasetUpdater() + { + return new DatasetGraphAccessorHTTP(ServerTest.serviceREST) ; + } +} http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/http/TestHttpOp.java ---------------------------------------------------------------------- diff --git a/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/http/TestHttpOp.java b/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/http/TestHttpOp.java new file mode 100644 index 0000000..9dc8713 --- /dev/null +++ b/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/http/TestHttpOp.java @@ -0,0 +1,233 @@ +/** + * 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.fuseki.http; + +import org.apache.jena.atlas.junit.BaseTest ; +import org.apache.jena.atlas.web.HttpException ; +import org.apache.jena.atlas.web.TypedInputStream ; +import org.apache.jena.fuseki.ServerTest ; +import org.apache.jena.riot.WebContent ; +import org.apache.jena.riot.system.IRILib ; +import org.apache.jena.riot.web.HttpOp ; +import org.apache.jena.web.HttpSC ; +import org.junit.AfterClass ; +import org.junit.BeforeClass ; +import org.junit.Test ; + +import com.hp.hpl.jena.sparql.engine.http.Params ; + +// This a mixture of testing HttpOp and testing basic operation of the SPARQL server +// especially error cases abnd unusual usage that the higher level APIs don't use. +public class TestHttpOp extends BaseTest { + + static String pingURL = ServerTest.urlRoot+"$/ping" ; + @BeforeClass public static void beforeClass() { ServerTest.allocServer() ; } + @AfterClass public static void afterClass() { ServerTest.freeServer() ; } + + static String gspServiceURL = ServerTest.serviceREST ; + static String defaultGraphURL = ServerTest.serviceREST+"?default" ; + static String namedGraphURL = ServerTest.serviceREST+"?graph=http://example/g" ; + static String queryURL = ServerTest.serviceQuery ; + static String updateURL = ServerTest.serviceUpdate ; + + static String simpleQuery = queryURL+"?query="+IRILib.encodeUriComponent("ASK{}") ; + + // Basic operations + + @Test public void httpGet_01() { + try ( TypedInputStream in = HttpOp.execHttpGet(pingURL) ) {} + } + + @Test(expected=HttpException.class) + public void httpGet_02() { + try ( TypedInputStream in = HttpOp.execHttpGet(ServerTest.urlRoot+"does-not-exist") ) { } + catch(HttpException ex) { + assertEquals(HttpSC.NOT_FOUND_404, ex.getResponseCode()) ; + throw ex ; + } + } + + @Test public void httpGet_03() { + String x = HttpOp.execHttpGetString(pingURL) ; + } + + @Test public void httpGet_04() { + String x = HttpOp.execHttpGetString(ServerTest.urlRoot+"does-not-exist") ; + assertNull(x) ; + } + + @Test public void httpGet_05() { + try ( TypedInputStream in = HttpOp.execHttpGet(simpleQuery) ) {} + } + + // SPARQL Query + + @Test public void queryGet_01() { + try ( TypedInputStream in = HttpOp.execHttpGet(simpleQuery) ) {} + } + + @Test(expected=HttpException.class) + public void queryGet_02() { + // No query. + try ( TypedInputStream in = HttpOp.execHttpGet(queryURL+"?query=") ) {} + catch (HttpException ex) { + assertEquals(ex.getResponseCode(), HttpSC.BAD_REQUEST_400) ; + throw ex ; + } + } + + @Test(expected=HttpException.class) + public void httpPost_01() { + try { + HttpOp.execHttpPost(queryURL, "ASK{}", "text/plain") ; + } catch (HttpException ex) { + assertEquals(ex.getResponseCode(), HttpSC.UNSUPPORTED_MEDIA_TYPE_415) ; + throw ex ; + } + } + + @Test(expected=HttpException.class) + public void httpPost_02() { + try { + HttpOp.execHttpPost(queryURL, "ASK{}", WebContent.contentTypeSPARQLQuery) ; + } catch (HttpException ex) { + assertEquals(ex.getResponseCode(), HttpSC.UNSUPPORTED_MEDIA_TYPE_415) ; + throw ex ; + } + } + + @Test(expected=HttpException.class) + public void httpPost_03() { + try { + HttpOp.execHttpPost(queryURL, "ASK{}", WebContent.contentTypeOctets) ; + } catch (HttpException ex) { + assertEquals(ex.getResponseCode(), HttpSC.UNSUPPORTED_MEDIA_TYPE_415) ; + throw ex ; + } + } + + @Test public void httpPost_04() { + Params params = new Params() ; + params.addParam("query", "ASK{}") ; + try ( TypedInputStream in = HttpOp.execHttpPostFormStream(queryURL, params, WebContent.contentTypeResultsJSON) ) {} + } + + @Test(expected=HttpException.class) + public void httpPost_05() { + Params params = new Params() ; + params.addParam("query", "ASK{}") ; + // Query to Update + try ( TypedInputStream in = HttpOp.execHttpPostFormStream(updateURL, params, WebContent.contentTypeResultsJSON) ) { } + catch (HttpException ex) { + assertEquals(ex.getResponseCode(), HttpSC.BAD_REQUEST_400) ; + throw ex ; + } + } + + @Test public void httpPost_06() { + Params params = new Params() ; + params.addParam("request", "CLEAR ALL") ; + HttpOp.execHttpPostForm(updateURL, params) ; + } + + // GSP + @Test public void gsp_01() { + String x = HttpOp.execHttpGetString(defaultGraphURL, "application/rdf+xml") ; + assertTrue(x.contains("</")) ; + assertTrue(x.contains(":RDF")) ; + } + + @Test public void gsp_02() { + String x = HttpOp.execHttpGetString(defaultGraphURL, "application/n-triples") ; + assertTrue(x.isEmpty()) ; + } + + static String graphString = "@prefix : <http://example/> . :s :p :o ." ; + static String datasetString = "@prefix : <http://example/> . :s :p :o . :g { :sg :pg :og }" ; + + @Test public void gsp_03() { + HttpOp.execHttpPut(defaultGraphURL, WebContent.contentTypeTurtle, graphString) ; + } + + @Test public void gsp_04() { + HttpOp.execHttpPut(defaultGraphURL, WebContent.contentTypeTurtle, graphString) ; + String s1 = HttpOp.execHttpGetString(defaultGraphURL, WebContent.contentTypeNTriples) ; + assertFalse(s1.isEmpty()) ; + HttpOp.execHttpDelete(defaultGraphURL) ; + String s2 = HttpOp.execHttpGetString(defaultGraphURL, WebContent.contentTypeNTriples) ; + assertTrue(s2.isEmpty()) ; + } + + @Test public void gsp_05() { + HttpOp.execHttpDelete(defaultGraphURL) ; + + HttpOp.execHttpPost(defaultGraphURL, WebContent.contentTypeTurtle, graphString) ; + String s1 = HttpOp.execHttpGetString(defaultGraphURL, WebContent.contentTypeNTriples) ; + assertFalse(s1.isEmpty()) ; + HttpOp.execHttpDelete(defaultGraphURL) ; + String s2 = HttpOp.execHttpGetString(defaultGraphURL, WebContent.contentTypeNTriples) ; + assertTrue(s2.isEmpty()) ; + } + + @Test public void gsp_06() { + //HttpOp.execHttpDelete(namedGraphURL) ; -- woudl be 404. + + HttpOp.execHttpPost(namedGraphURL, WebContent.contentTypeTurtle, graphString) ; + String s1 = HttpOp.execHttpGetString(namedGraphURL, WebContent.contentTypeNTriples) ; + assertFalse(s1.isEmpty()) ; + String s2 = HttpOp.execHttpGetString(defaultGraphURL, WebContent.contentTypeNTriples) ; + assertTrue(s2.isEmpty()) ; + HttpOp.execHttpDelete(namedGraphURL) ; + String s3 = HttpOp.execHttpGetString(defaultGraphURL, WebContent.contentTypeNTriples) ; + assertTrue(s3.isEmpty()) ; + + try { + HttpOp.execHttpDelete(namedGraphURL) ; + fail("Expected 404") ; + } catch (HttpException ex) { + assertEquals(ex.getResponseCode(), HttpSC.NOT_FOUND_404) ; + } + + } + + // Extended GSP - no ?default, no ?graph acts on the datasets as a whole. + + @Test public void gsp_10() { + try { + HttpOp.execHttpDelete(gspServiceURL) ; + fail("Expected 405") ; + } catch (HttpException ex) { + assertEquals(ex.getResponseCode(), HttpSC.METHOD_NOT_ALLOWED_405) ; + } + } + + @Test public void gsp_11() { + + String s1 = HttpOp.execHttpGetString(gspServiceURL, WebContent.contentTypeNQuads) ; + assertTrue(s1.isEmpty()) ; + + HttpOp.execHttpPost(gspServiceURL, WebContent.contentTypeTriG, datasetString) ; + String s2 = HttpOp.execHttpGetString(gspServiceURL, WebContent.contentTypeNQuads) ; + assertFalse(s2.isEmpty()) ; + + String s4 = HttpOp.execHttpGetString(defaultGraphURL, WebContent.contentTypeNTriples) ; + assertFalse(s4.isEmpty()) ; + } +} + http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-core/testing/config-ds-1.ttl ---------------------------------------------------------------------- diff --git a/jena-fuseki2/jena-fuseki-core/testing/config-ds-1.ttl b/jena-fuseki2/jena-fuseki-core/testing/config-ds-1.ttl new file mode 100644 index 0000000..8e7a57b --- /dev/null +++ b/jena-fuseki2/jena-fuseki-core/testing/config-ds-1.ttl @@ -0,0 +1,15 @@ +@prefix : <#> . +@prefix fuseki: <http://jena.apache.org/fuseki#> . +@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . + +@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . +@prefix ja: <http://jena.hpl.hp.com/2005/11/Assembler#> . + +<#service1> rdf:type fuseki:Service ; + # URI of the dataset -- http://host:port/ds + fuseki:name "test-ds2" ; + fuseki:serviceQuery "sparql" ; + fuseki:dataset <#emptyDataset> ; + . + +<#emptyDataset> rdf:type ja:RDFDataset . http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-dist/assembly-dist.xml ---------------------------------------------------------------------- diff --git a/jena-fuseki2/jena-fuseki-dist/assembly-dist.xml b/jena-fuseki2/jena-fuseki-dist/assembly-dist.xml new file mode 100644 index 0000000..e7ae2d5 --- /dev/null +++ b/jena-fuseki2/jena-fuseki-dist/assembly-dist.xml @@ -0,0 +1,100 @@ +<?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. +--> + +<!-- +The distribution. +Assumes jar made and onejar has been assembled. +--> + +<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd"> + + <id>distribution</id> + + <formats> + <format>zip</format> + <format>tar.gz</format> + </formats> + + <baseDirectory>${project.artifactId}-${project.version}</baseDirectory> + + <dependencySets> + <dependencySet> + <useProjectArtifact>false</useProjectArtifact> + <includes> + <include>org.apache.jena:jena-fuseki-server:jar</include> + <include>org.apache.jena:jena-fuseki-war:war</include> + </includes> + </dependencySet> + </dependencySets> + + <files> + + <!-- + <file> + <source>${project.build.directory}/${server.jar.name}.jar</source> + <outputDirectory></outputDirectory> + <destName>fuseki-server.jar</destName> + </file> + <file> + <source>${project.build.directory}/${project.build.finalName}.war</source> + <outputDirectory></outputDirectory> + <destName>fuseki-server.war</destName> + </file> + --> + <file> + <source>dist/LICENSE</source> + <destName>LICENSE</destName> + </file> + <file> + <source>dist/NOTICE</source> + <destName>NOTICE</destName> + </file> + </files> + + <fileSets> + <fileSet> + <outputDirectory></outputDirectory> + <includes> + <include>templates/*</include> + <include>README*</include> + <include>DEPENDENCIES*</include> + <include>ReleaseNotes.txt</include> + </includes> + </fileSet> + + <fileSet> + <outputDirectory></outputDirectory> + <includes> + <include>log4j.properties</include> + <!--<include>examples/**</include>--> + <include>fuseki</include> + <include>fuseki-server</include> + <include>fuseki-server.bat</include> + <!--<include>s-*</include>--> + </includes> + </fileSet> + + <fileSet> + <directory>../jena-fuseki-core/src/main/webapp</directory> + <outputDirectory>webapp</outputDirectory> + </fileSet> + + </fileSets> +</assembly> http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-dist/backup ---------------------------------------------------------------------- diff --git a/jena-fuseki2/jena-fuseki-dist/backup b/jena-fuseki2/jena-fuseki-dist/backup new file mode 100755 index 0000000..a3cb0b1 --- /dev/null +++ b/jena-fuseki2/jena-fuseki-dist/backup @@ -0,0 +1,22 @@ +#!/bin/bash +# 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. + +DB="ds" + +curl -v -XPOST 'http://localhost:3030/$/sleep?interval=10000' + +curl -v 'http://localhost:3030/$/tasks' \ No newline at end of file http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-dist/bin/s-delete ---------------------------------------------------------------------- diff --git a/jena-fuseki2/jena-fuseki-dist/bin/s-delete b/jena-fuseki2/jena-fuseki-dist/bin/s-delete new file mode 100755 index 0000000..0e3a807 --- /dev/null +++ b/jena-fuseki2/jena-fuseki-dist/bin/s-delete @@ -0,0 +1,707 @@ +#!/usr/bin/env ruby +# -*- coding: 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. + +# SPARQL HTTP Update, client. + +require 'optparse' +require 'net/http' +require 'uri' +require 'cgi' +require 'pp' +require 'ostruct' + +# ToDo +# Allow a choice of media type for GET +# --accept "content-type" (and abbreviations) +# --header "Add:this" +# --user, --password +# Basic authentication: request.basic_auth("username", "password") +# Follow redirects => 301: puts response["location"] # All headers are lowercase? + +SOH_NAME="SOH" +SOH_VERSION="0.0.0" + +$proxy = ENV['http_proxy'] ? URI.parse(ENV['http_proxy']) : OpenStruct.new + +# What about direct naming? + +# Names +$mtTurtle = 'text/turtle;charset=utf-8' +$mtRDF = 'application/rdf+xml' +$mtText = 'text/plain' +$mtNQuads = 'text/n-quads' +$mtTriG = 'application/trig' +$mtSparqlResultsX = 'application/sparql-results+xml' +$mtSparqlResultsJ = 'application/sparql-results+json' +$mtAppJSON = 'application/json' +$mtAppXML = 'application/xml' +$mtSparqlResultsTSV = 'application/sparql-results+tsv' +$mtSparqlResultsCSV = 'application/sparql-results+csv' +$mtSparqlUpdate = 'application/sparql-update' +$mtWWWForm = 'application/x-www-form-urlencoded' +$mtSparqlQuery = "application/sparql-query" ; + +# Global media type table. +$fileMediaTypes = {} +$fileMediaTypes['ttl'] = $mtTurtle +$fileMediaTypes['n3'] = 'text/n3; charset=utf-8' +$fileMediaTypes['nt'] = $mtText +$fileMediaTypes['rdf'] = $mtRDF +$fileMediaTypes['owl'] = $mtRDF +$fileMediaTypes['nq'] = $mtNQuads +$fileMediaTypes['trig'] = $mtTriG + +# Global charset : no entry means "don't set" +$charsetUTF8 = 'utf-8' +$charset = {} +$charset[$mtTurtle] = 'utf-8' +$charset[$mtText] = 'ascii' +$charset[$mtTriG] = 'utf-8' +$charset[$mtNQuads] = 'ascii' + +# Headers + +$hContentType = 'Content-Type' +# $hContentEncoding = 'Content-Encoding' +$hContentLength = 'Content-Length' +# $hContentLocation = 'Content-Location' +# $hContentRange = 'Content-Range' + +$hAccept = 'Accept' +$hAcceptCharset = 'Accept-Charset' +$hAcceptEncoding = 'Accept-Encoding' +$hAcceptRanges = 'Accept-Ranges' + +$headers = { "User-Agent" => "#{SOH_NAME}/Fuseki #{SOH_VERSION}"} +$print_http = false + +# Default for GET +# At least allow anythign (and hope!) +$accept_rdf="#{$mtRDF};q=0.9 , #{$mtTurtle}" +# For SPARQL query +$accept_results="#{$mtSparqlResultsJ} , #{$mtSparqlResultsX};q=0.9 , #{$accept_rdf}" + +# Accept any in case of trouble. +$accept_rdf="#{$accept_rdf} , */*;q=0.1" +$accept_results="#{$accept_results} , */*;q=0.1" + +# The media type usually forces the charset. +$accept_charset=nil + +## Who we are. +## Two styles: +## s-query ..... +## soh query ..... + +$cmd = File.basename($0) +if $cmd == 'soh' +then + $cmd = (ARGV.size == 0) ? 'soh' : ARGV.shift +end + +if ! $cmd.start_with?('s-') && $cmd != 'soh' + $cmd = 's-'+$cmd +end + +## -------- + +def GET(dataset, graph) + print "GET #{dataset} #{graph}\n" if $verbose + requestURI = target(dataset, graph) + headers = {} + headers.merge!($headers) + headers[$hAccept] = $accept_rdf + headers[$hAcceptCharset] = $accept_charset unless $accept_charset.nil? + get_worker(requestURI, headers) +end + +def get_worker(requestURI, headers) + uri = URI.parse(requestURI) + request = Net::HTTP::Get.new(uri.request_uri) + request.initialize_http_header(headers) + print_http_request(uri, request) + response_print_body(uri, request) +end + +def HEAD(dataset, graph) + print "HEAD #{dataset} #{graph}\n" if $verbose + requestURI = target(dataset, graph) + headers = {} + headers.merge!($headers) + headers[$hAccept] = $accept_rdf + headers[$hAcceptCharset] = $accept_charset unless $accept_charset.nil? + uri = URI.parse(requestURI) + request = Net::HTTP::Head.new(uri.request_uri) + request.initialize_http_header(headers) + print_http_request(uri, request) + response_no_body(uri, request) +end + +def PUT(dataset, graph, file) + print "PUT #{dataset} #{graph} #{file}\n" if $verbose + send_body(dataset, graph, file, Net::HTTP::Put) +end + +def POST(dataset, graph, file) + print "POST #{dataset} #{graph} #{file}\n" if $verbose + send_body(dataset, graph, file, Net::HTTP::Post) +end + +def DELETE(dataset, graph) + print "DELETE #{dataset} #{graph}\n" if $verbose + requestURI = target(dataset, graph) + uri = URI.parse(requestURI) + request = Net::HTTP::Delete.new(uri.request_uri) + headers = {} + headers.merge!($headers) + request.initialize_http_header(headers) + print_http_request(uri, request) + response_no_body(uri, request) +end + +def uri_escape(string) + CGI.escape(string) +end + +def target(dataset, graph) + return dataset+"?default" if graph == "default" + return dataset+"?graph="+uri_escape(graph) +end + +def send_body(dataset, graph, file, method) + mt = content_type(file) + headers = {} + headers.merge!($headers) + headers[$hContentType] = mt + headers[$hContentLength] = File.size(file).to_s + ## p headers + + requestURI = target(dataset, graph) + uri = URI.parse(requestURI) + + request = method.new(uri.request_uri) + request.initialize_http_header(headers) + print_http_request(uri, request) + request.body_stream = File.open(file) + response_no_body(uri, request) +end + +def response_no_body(uri, request) + http = Net::HTTP::Proxy($proxy.host,$proxy.port).new(uri.host, uri.port) + http.read_timeout = nil + # check we can connect. + begin http.start + rescue Exception => e + # puts e.message + #puts e.backtrace.inspect + warn_exit "Failed to connect: #{uri.host}:#{uri.port}: #{e.message}", 3 + end + response = http.request(request) + print_http_response(response) + case response + when Net::HTTPSuccess, Net::HTTPRedirection + # OK + when Net::HTTPNotFound + warn_exit "404 Not found: #{uri}", 9 + #print response.body + else + warn_exit "#{response.code} #{response.message} #{uri}", 9 + # Unreachable + response.error! + end + # NO BODY IN RESPONSE +end + +def response_print_body(uri, request) + http = Net::HTTP::Proxy($proxy.host,$proxy.port).new(uri.host, uri.port) + http.read_timeout = nil + # check we can connect. + begin http.start + rescue => e + #puts e.backtrace.inspect + #print e.class + warn_exit "Failed to connect: #{uri.host}:#{uri.port}: #{e.message}", 3 + end + + # Add a blank line if headers were output. + print "\n" if $http_print ; + + begin + response = http.request(request) { |res| + print_http_response(res) + #puts res.code + res.read_body do |segment| + print segment + end + } + case response + when Net::HTTPSuccess, Net::HTTPRedirection + # OK + when Net::HTTPNotFound + warn_exit "404 Not found: #{uri}", 9 + #print response.body + else + warn_exit "#{response.code}: #{uri}", 9 + # Unreachable + response.error! + end + rescue EOFError => e + warn_exit "IO Error: "+e.message, 3 + end +end + +def print_http_request(uri, request) + return unless $print_http + #print "Request\n" + print request.method," ",uri, "\n" + print_headers(" ",request) +end + +def print_http_response(response) + return unless $print_http + #print "Response\n" + print response.code, " ", response.message, "\n" + print_headers(" ",response) +end + +def print_headers(marker, headers) + headers.each do |k,v| + k = k.split('-').map{|w| w.capitalize}.join('-')+':' + printf "%s%-20s %s\n",marker,k,v + end +end + +def content_type(file) + file =~ /\.([^.]*)$/ + ext = $1 + mt = $fileMediaTypes[ext] + cs = $charset[mt] + mt = mt+';charset='+cs if ! cs.nil? + return mt +end + +def charset(content_type) + return $charset[content_type] +end + +def warn_exit(msg, rc) + warn msg + exit rc ; +end + +def parseURI(uri_string) + begin + return URI.parse(uri_string).to_s + rescue URI::InvalidURIError => err + warn_exit "Bad URI: <#{uri_string}>", 2 + end +end + +## ---- Command + +def cmd_soh(command=nil) + ## Command line + options = {} + optparse = OptionParser.new do |opts| + # Set a banner, displayed at the top + # of the help screen. + case $cmd + when "s-http", "sparql-http", "soh" + banner="$cmd [get|post|put|delete] datasetURI graph [file]" + when "s-get", "s-head", "s-delete" + banner="$cmd datasetURI graph" + end + + opts.banner = $banner + # Define the options, and what they do + + options[:verbose] = false + opts.on( '-v', '--verbose', 'Verbose' ) do + options[:verbose] = true + end + + options[:version] = false + opts.on( '--version', 'Print version and exit' ) do + print "#{SOH_NAME} #{SOH_VERSION}\n" + exit + end + + # This displays the help screen, all programs are + # assumed to have this option. + opts.on( '-h', '--help', 'Display this screen and exit' ) do + puts opts + exit + end + end + + begin optparse.parse! + rescue OptionParser::InvalidArgument => e + warn e + exit + end + + $verbose = options[:verbose] + $print_http = $verbose + + if command.nil? + if ARGV.size == 0 + warn "No command given: expected one of 'get', 'put', 'post', 'delete', 'query' or 'update'" + exit 1 + end + cmdPrint=ARGV.shift + command=cmdPrint.upcase + else + cmdPrint=command + end + + case command + when "HEAD", "GET", "DELETE" + requiredFile=false + when "PUT", "POST" + requiredFile=true + when "QUERY" + cmd_sparql_query + when "UPDATE" + cmd_sparql_update + else + warn_exit "Unknown command: #{command}", 2 + end + + if requiredFile + then + if ARGV.size != 3 + warn_exit "Required: dataset URI, graph URI (or 'default') and file", 1 + end + else + if ARGV.size != 2 + warn_exit "Required: dataset URI and graph URI (or 'default')", 1 + end + end + + dataset=parseURI(ARGV.shift) + # Relative URI? + graph=parseURI(ARGV.shift) + file="" + if requiredFile + then + file = ARGV.shift if requiredFile + if ! File.exist?(file) + warn_exit "No such file: "+file, 3 + end + if File.directory?(file) + warn_exit "File is a directory: "+file, 3 + end + end + + case command + when "GET" + GET(dataset, graph) + when "HEAD" + HEAD(dataset, graph) + when "PUT" + PUT(dataset, graph, file) + when "DELETE" + DELETE(dataset, graph) + when "POST" + POST(dataset, graph, file) + else + warn_exit "Internal error: Unknown command: #{cmd}", 2 + end + exit 0 +end + +## -------- +def string_or_file(arg) + return arg if ! arg.match(/^@/) + a=(arg[1..-1]) + open(a, 'rb'){|f| f.read} +end + +## -------- SPARQL Query + +## Choose method +def SPARQL_query(service, query, query_file, forcePOST=false, args2={}) + if ! query_file.nil? + query = open(query_file, 'rb'){|f| f.read} + end + if forcePOST || query.length >= 2*1024 + SPARQL_query_POST(service, query, args2) + else + SPARQL_query_GET(service, query, args2) + end +end + +## By GET + +def SPARQL_query_GET(service, query, args2) + args = { "query" => query } + args.merge!(args2) + qs=args.collect { |k,v| "#{k}=#{uri_escape(v)}" }.join('&') + action="#{service}?#{qs}" + headers={} + headers.merge!($headers) + headers[$hAccept]=$accept_results + get_worker(action, headers) +end + +## By POST + +def SPARQL_query_POST(service, query, args2) + # DRY - body/no body for each of request and response. + post_params={ "query" => query } + post_params.merge!(args2) + uri = URI.parse(service) + headers={} + headers.merge!($headers) + headers[$hAccept]=$accept_results + execute_post_form_body(uri, headers, post_params) +end + +def execute_post_form_body(uri, headers, post_params) + request = Net::HTTP::Post.new(uri.request_uri) + qs=post_params.collect { |k,v| "#{k}=#{uri_escape(v)}" }.join('&') + headers[$hContentType] = $mtWWWForm + headers[$hContentLength] = qs.length.to_s + request.initialize_http_header(headers) + request.body = qs + print_http_request(uri, request) + response_print_body(uri, request) +end + +# Usage: -v --help --file= --query= +def cmd_sparql_query + options={} + optparse = OptionParser.new do |opts| + opts.banner = "Usage: #{$cmd} [--query QUERY] [--service URI] [--post] 'query' | @file" + opts.on('--service=URI', '--server=URI', 'SPARQL endpoint') do |uri| + options[:service]=uri + end + opts.on('--query=FILE','--file=FILE', 'Take query from a file') do |file| + options[:file]=file + end + opts.on('--output=TYPE', [:json,:xml,:text,:csv,:tsv], + 'Set the output argument') do |type| + options[:output]=type + end + opts.on('--accept=TYPE', [:json,:xml,:text,:csv,:tsv], + 'Set the accept header type') do |type| + options[:accept]=type + end + options[:verbose] = false + opts.on( '--post', 'Force use of POST' ) do + options[:post] = true + end + opts.on( '-v', '--verbose', 'Verbose' ) do + options[:verbose] = true + end + opts.on( '--version', 'Print version and exit' ) do + print "#{SOH_NAME} #{SOH_VERSION}\n" + exit + end + opts.on( '-h', '--help', 'Display this screen and exit' ) do + puts opts + exit + end + end + + begin optparse.parse! + rescue OptionParser::InvalidArgument, OptionParser::InvalidOption => e + warn e + exit 1 + end + + $verbose = options[:verbose] + $print_http = $verbose + usePOST = options[:post] + + service = options[:service] + warn_exit 'No service specified. Required --service=URI',1 if service.nil? + + # Query + query=nil + query_file=options[:file] + if query_file.nil? && ARGV.size == 0 + then + warn_exit 'No query specified.',1 + end + if query_file.nil? + query = ARGV.shift + if query.match(/^@/) + query_file = query[1..-1] + query = nil + end + end + + # --output ==> output= (non-standard) + args={} + case options[:output] + when nil + when "json","xml","text","csv","tsv" + args['output'] = options[:output] + when :json,:xml,:text,:csv,:tsv + args['output'] = options[:output].to_s + else + warn_exit "Unrecognized output type: "+options[:output],2 + end + + # --accept + # options[:accept] + + print "SPARQL #{service}\n" if $verbose + #args={"output"=>"text"} + SPARQL_query(service, query, query_file, usePOST, args) + exit(0) +end + +## -------- SPARQL Update + +# Update sent as a WWW form. +def SPARQL_update_by_form(service, update, args2={}) + args = {} + args.merge!(args2) + headers={} + headers.merge!($headers) + # args? encode? + body="update="+uri_escape(update) + headers[$hContentType] = $mtWWWForm + headers[$hContentLength] = body.length.to_s + uri = URI.parse(service) + execute_post_form(uri, headers, body) +end + +# DRY - query form. +def execute_post_form(uri, headers, body) + request = Net::HTTP::Post.new(uri.request_uri) + request.initialize_http_header(headers) + request.body = body + print_http_request(uri, request) + response_no_body(uri, request) +end + +def SPARQL_update(service, update, args2={}) + args = {} + args.merge!(args2) + headers={} + headers.merge!($headers) + headers[$hContentType] = $mtSparqlUpdate + uri = URI.parse(service) + request = Net::HTTP::Post.new(uri.request_uri) + request.initialize_http_header(headers) + request.body = update + print_http_request(uri, request) + response_no_body(uri, request) +end + +def cmd_sparql_update(by_raw_post=true) + # Share with cmd_sparql_query + options={} + optparse = OptionParser.new do |opts| + opts.banner = "Usage: #{$cmd} [--file REQUEST] [--service URI] 'request' | @file" + opts.on('--service=URI', '--server=URI', 'SPARQL endpoint') do |uri| + options[:service]=uri + end + opts.on('--update=FILE', '--file=FILE', 'Take update from a file') do |file| + options[:file]=file + end + options[:verbose] = false + opts.on( '-v', '--verbose', 'Verbose' ) do + options[:verbose] = true + end + opts.on( '--version', 'Print version and exit' ) do + print "#{SOH_NAME} #{SOH_VERSION}\n" + exit + end + opts.on( '-h', '--help', 'Display this screen and exit' ) do + puts opts + exit + end + end + + begin optparse.parse! + rescue OptionParser::InvalidArgument => e + warn e + exit + end + + $verbose = options[:verbose] + $print_http = $verbose + + service = options[:service] + warn_exit 'No service specified. Required --service=URI',1 if service.nil? + + update=nil + update_file=options[:file] + + if update_file.nil? && ARGV.size == 0 + then + warn_exit 'No update specified.',1 + end + if update_file.nil? + update = ARGV.shift + if update.match(/^@/) + update_file = update[1..-1] + update = nil + end + end + + print "SPARQL-Update #{service}\n" if $verbose + args={} + + # Reads in the file :-( + if update.nil? + then + update = open(update_file, 'rb'){|f| f.read} + else + update = string_or_file(update) + end + + if by_raw_post + SPARQL_update(service, update, args) + else + SPARQL_update_by_form(service, update, args) + end + exit(0) +end + +## ------- + +case $cmd +when "s-http", "sparql-http", "soh" + $banner="#{$cmd} [get|post|put|delete] datasetURI graph [file]" + cmd_soh +when "s-get", "s-head", "s-put", "s-delete", "s-post" + + case $cmd + when "s-get", "s-head", "s-delete" + $banner="#{$cmd} datasetURI graph" + when "s-put", "s-post" + $banner="#{$cmd} datasetURI graph file" + end + cmd2 = $cmd.sub(/^s-/, '').upcase + cmd_soh cmd2 + +when "s-query", "sparql-query" + cmd_sparql_query +when "s-update", "sparql-update" + cmd_sparql_update true +when "s-update-form", "sparql-update-form" + cmd_sparql_update false +else + warn_exit "Unknown: "+$cmd, 1 +end http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-dist/bin/s-get ---------------------------------------------------------------------- diff --git a/jena-fuseki2/jena-fuseki-dist/bin/s-get b/jena-fuseki2/jena-fuseki-dist/bin/s-get new file mode 100755 index 0000000..0e3a807 --- /dev/null +++ b/jena-fuseki2/jena-fuseki-dist/bin/s-get @@ -0,0 +1,707 @@ +#!/usr/bin/env ruby +# -*- coding: 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. + +# SPARQL HTTP Update, client. + +require 'optparse' +require 'net/http' +require 'uri' +require 'cgi' +require 'pp' +require 'ostruct' + +# ToDo +# Allow a choice of media type for GET +# --accept "content-type" (and abbreviations) +# --header "Add:this" +# --user, --password +# Basic authentication: request.basic_auth("username", "password") +# Follow redirects => 301: puts response["location"] # All headers are lowercase? + +SOH_NAME="SOH" +SOH_VERSION="0.0.0" + +$proxy = ENV['http_proxy'] ? URI.parse(ENV['http_proxy']) : OpenStruct.new + +# What about direct naming? + +# Names +$mtTurtle = 'text/turtle;charset=utf-8' +$mtRDF = 'application/rdf+xml' +$mtText = 'text/plain' +$mtNQuads = 'text/n-quads' +$mtTriG = 'application/trig' +$mtSparqlResultsX = 'application/sparql-results+xml' +$mtSparqlResultsJ = 'application/sparql-results+json' +$mtAppJSON = 'application/json' +$mtAppXML = 'application/xml' +$mtSparqlResultsTSV = 'application/sparql-results+tsv' +$mtSparqlResultsCSV = 'application/sparql-results+csv' +$mtSparqlUpdate = 'application/sparql-update' +$mtWWWForm = 'application/x-www-form-urlencoded' +$mtSparqlQuery = "application/sparql-query" ; + +# Global media type table. +$fileMediaTypes = {} +$fileMediaTypes['ttl'] = $mtTurtle +$fileMediaTypes['n3'] = 'text/n3; charset=utf-8' +$fileMediaTypes['nt'] = $mtText +$fileMediaTypes['rdf'] = $mtRDF +$fileMediaTypes['owl'] = $mtRDF +$fileMediaTypes['nq'] = $mtNQuads +$fileMediaTypes['trig'] = $mtTriG + +# Global charset : no entry means "don't set" +$charsetUTF8 = 'utf-8' +$charset = {} +$charset[$mtTurtle] = 'utf-8' +$charset[$mtText] = 'ascii' +$charset[$mtTriG] = 'utf-8' +$charset[$mtNQuads] = 'ascii' + +# Headers + +$hContentType = 'Content-Type' +# $hContentEncoding = 'Content-Encoding' +$hContentLength = 'Content-Length' +# $hContentLocation = 'Content-Location' +# $hContentRange = 'Content-Range' + +$hAccept = 'Accept' +$hAcceptCharset = 'Accept-Charset' +$hAcceptEncoding = 'Accept-Encoding' +$hAcceptRanges = 'Accept-Ranges' + +$headers = { "User-Agent" => "#{SOH_NAME}/Fuseki #{SOH_VERSION}"} +$print_http = false + +# Default for GET +# At least allow anythign (and hope!) +$accept_rdf="#{$mtRDF};q=0.9 , #{$mtTurtle}" +# For SPARQL query +$accept_results="#{$mtSparqlResultsJ} , #{$mtSparqlResultsX};q=0.9 , #{$accept_rdf}" + +# Accept any in case of trouble. +$accept_rdf="#{$accept_rdf} , */*;q=0.1" +$accept_results="#{$accept_results} , */*;q=0.1" + +# The media type usually forces the charset. +$accept_charset=nil + +## Who we are. +## Two styles: +## s-query ..... +## soh query ..... + +$cmd = File.basename($0) +if $cmd == 'soh' +then + $cmd = (ARGV.size == 0) ? 'soh' : ARGV.shift +end + +if ! $cmd.start_with?('s-') && $cmd != 'soh' + $cmd = 's-'+$cmd +end + +## -------- + +def GET(dataset, graph) + print "GET #{dataset} #{graph}\n" if $verbose + requestURI = target(dataset, graph) + headers = {} + headers.merge!($headers) + headers[$hAccept] = $accept_rdf + headers[$hAcceptCharset] = $accept_charset unless $accept_charset.nil? + get_worker(requestURI, headers) +end + +def get_worker(requestURI, headers) + uri = URI.parse(requestURI) + request = Net::HTTP::Get.new(uri.request_uri) + request.initialize_http_header(headers) + print_http_request(uri, request) + response_print_body(uri, request) +end + +def HEAD(dataset, graph) + print "HEAD #{dataset} #{graph}\n" if $verbose + requestURI = target(dataset, graph) + headers = {} + headers.merge!($headers) + headers[$hAccept] = $accept_rdf + headers[$hAcceptCharset] = $accept_charset unless $accept_charset.nil? + uri = URI.parse(requestURI) + request = Net::HTTP::Head.new(uri.request_uri) + request.initialize_http_header(headers) + print_http_request(uri, request) + response_no_body(uri, request) +end + +def PUT(dataset, graph, file) + print "PUT #{dataset} #{graph} #{file}\n" if $verbose + send_body(dataset, graph, file, Net::HTTP::Put) +end + +def POST(dataset, graph, file) + print "POST #{dataset} #{graph} #{file}\n" if $verbose + send_body(dataset, graph, file, Net::HTTP::Post) +end + +def DELETE(dataset, graph) + print "DELETE #{dataset} #{graph}\n" if $verbose + requestURI = target(dataset, graph) + uri = URI.parse(requestURI) + request = Net::HTTP::Delete.new(uri.request_uri) + headers = {} + headers.merge!($headers) + request.initialize_http_header(headers) + print_http_request(uri, request) + response_no_body(uri, request) +end + +def uri_escape(string) + CGI.escape(string) +end + +def target(dataset, graph) + return dataset+"?default" if graph == "default" + return dataset+"?graph="+uri_escape(graph) +end + +def send_body(dataset, graph, file, method) + mt = content_type(file) + headers = {} + headers.merge!($headers) + headers[$hContentType] = mt + headers[$hContentLength] = File.size(file).to_s + ## p headers + + requestURI = target(dataset, graph) + uri = URI.parse(requestURI) + + request = method.new(uri.request_uri) + request.initialize_http_header(headers) + print_http_request(uri, request) + request.body_stream = File.open(file) + response_no_body(uri, request) +end + +def response_no_body(uri, request) + http = Net::HTTP::Proxy($proxy.host,$proxy.port).new(uri.host, uri.port) + http.read_timeout = nil + # check we can connect. + begin http.start + rescue Exception => e + # puts e.message + #puts e.backtrace.inspect + warn_exit "Failed to connect: #{uri.host}:#{uri.port}: #{e.message}", 3 + end + response = http.request(request) + print_http_response(response) + case response + when Net::HTTPSuccess, Net::HTTPRedirection + # OK + when Net::HTTPNotFound + warn_exit "404 Not found: #{uri}", 9 + #print response.body + else + warn_exit "#{response.code} #{response.message} #{uri}", 9 + # Unreachable + response.error! + end + # NO BODY IN RESPONSE +end + +def response_print_body(uri, request) + http = Net::HTTP::Proxy($proxy.host,$proxy.port).new(uri.host, uri.port) + http.read_timeout = nil + # check we can connect. + begin http.start + rescue => e + #puts e.backtrace.inspect + #print e.class + warn_exit "Failed to connect: #{uri.host}:#{uri.port}: #{e.message}", 3 + end + + # Add a blank line if headers were output. + print "\n" if $http_print ; + + begin + response = http.request(request) { |res| + print_http_response(res) + #puts res.code + res.read_body do |segment| + print segment + end + } + case response + when Net::HTTPSuccess, Net::HTTPRedirection + # OK + when Net::HTTPNotFound + warn_exit "404 Not found: #{uri}", 9 + #print response.body + else + warn_exit "#{response.code}: #{uri}", 9 + # Unreachable + response.error! + end + rescue EOFError => e + warn_exit "IO Error: "+e.message, 3 + end +end + +def print_http_request(uri, request) + return unless $print_http + #print "Request\n" + print request.method," ",uri, "\n" + print_headers(" ",request) +end + +def print_http_response(response) + return unless $print_http + #print "Response\n" + print response.code, " ", response.message, "\n" + print_headers(" ",response) +end + +def print_headers(marker, headers) + headers.each do |k,v| + k = k.split('-').map{|w| w.capitalize}.join('-')+':' + printf "%s%-20s %s\n",marker,k,v + end +end + +def content_type(file) + file =~ /\.([^.]*)$/ + ext = $1 + mt = $fileMediaTypes[ext] + cs = $charset[mt] + mt = mt+';charset='+cs if ! cs.nil? + return mt +end + +def charset(content_type) + return $charset[content_type] +end + +def warn_exit(msg, rc) + warn msg + exit rc ; +end + +def parseURI(uri_string) + begin + return URI.parse(uri_string).to_s + rescue URI::InvalidURIError => err + warn_exit "Bad URI: <#{uri_string}>", 2 + end +end + +## ---- Command + +def cmd_soh(command=nil) + ## Command line + options = {} + optparse = OptionParser.new do |opts| + # Set a banner, displayed at the top + # of the help screen. + case $cmd + when "s-http", "sparql-http", "soh" + banner="$cmd [get|post|put|delete] datasetURI graph [file]" + when "s-get", "s-head", "s-delete" + banner="$cmd datasetURI graph" + end + + opts.banner = $banner + # Define the options, and what they do + + options[:verbose] = false + opts.on( '-v', '--verbose', 'Verbose' ) do + options[:verbose] = true + end + + options[:version] = false + opts.on( '--version', 'Print version and exit' ) do + print "#{SOH_NAME} #{SOH_VERSION}\n" + exit + end + + # This displays the help screen, all programs are + # assumed to have this option. + opts.on( '-h', '--help', 'Display this screen and exit' ) do + puts opts + exit + end + end + + begin optparse.parse! + rescue OptionParser::InvalidArgument => e + warn e + exit + end + + $verbose = options[:verbose] + $print_http = $verbose + + if command.nil? + if ARGV.size == 0 + warn "No command given: expected one of 'get', 'put', 'post', 'delete', 'query' or 'update'" + exit 1 + end + cmdPrint=ARGV.shift + command=cmdPrint.upcase + else + cmdPrint=command + end + + case command + when "HEAD", "GET", "DELETE" + requiredFile=false + when "PUT", "POST" + requiredFile=true + when "QUERY" + cmd_sparql_query + when "UPDATE" + cmd_sparql_update + else + warn_exit "Unknown command: #{command}", 2 + end + + if requiredFile + then + if ARGV.size != 3 + warn_exit "Required: dataset URI, graph URI (or 'default') and file", 1 + end + else + if ARGV.size != 2 + warn_exit "Required: dataset URI and graph URI (or 'default')", 1 + end + end + + dataset=parseURI(ARGV.shift) + # Relative URI? + graph=parseURI(ARGV.shift) + file="" + if requiredFile + then + file = ARGV.shift if requiredFile + if ! File.exist?(file) + warn_exit "No such file: "+file, 3 + end + if File.directory?(file) + warn_exit "File is a directory: "+file, 3 + end + end + + case command + when "GET" + GET(dataset, graph) + when "HEAD" + HEAD(dataset, graph) + when "PUT" + PUT(dataset, graph, file) + when "DELETE" + DELETE(dataset, graph) + when "POST" + POST(dataset, graph, file) + else + warn_exit "Internal error: Unknown command: #{cmd}", 2 + end + exit 0 +end + +## -------- +def string_or_file(arg) + return arg if ! arg.match(/^@/) + a=(arg[1..-1]) + open(a, 'rb'){|f| f.read} +end + +## -------- SPARQL Query + +## Choose method +def SPARQL_query(service, query, query_file, forcePOST=false, args2={}) + if ! query_file.nil? + query = open(query_file, 'rb'){|f| f.read} + end + if forcePOST || query.length >= 2*1024 + SPARQL_query_POST(service, query, args2) + else + SPARQL_query_GET(service, query, args2) + end +end + +## By GET + +def SPARQL_query_GET(service, query, args2) + args = { "query" => query } + args.merge!(args2) + qs=args.collect { |k,v| "#{k}=#{uri_escape(v)}" }.join('&') + action="#{service}?#{qs}" + headers={} + headers.merge!($headers) + headers[$hAccept]=$accept_results + get_worker(action, headers) +end + +## By POST + +def SPARQL_query_POST(service, query, args2) + # DRY - body/no body for each of request and response. + post_params={ "query" => query } + post_params.merge!(args2) + uri = URI.parse(service) + headers={} + headers.merge!($headers) + headers[$hAccept]=$accept_results + execute_post_form_body(uri, headers, post_params) +end + +def execute_post_form_body(uri, headers, post_params) + request = Net::HTTP::Post.new(uri.request_uri) + qs=post_params.collect { |k,v| "#{k}=#{uri_escape(v)}" }.join('&') + headers[$hContentType] = $mtWWWForm + headers[$hContentLength] = qs.length.to_s + request.initialize_http_header(headers) + request.body = qs + print_http_request(uri, request) + response_print_body(uri, request) +end + +# Usage: -v --help --file= --query= +def cmd_sparql_query + options={} + optparse = OptionParser.new do |opts| + opts.banner = "Usage: #{$cmd} [--query QUERY] [--service URI] [--post] 'query' | @file" + opts.on('--service=URI', '--server=URI', 'SPARQL endpoint') do |uri| + options[:service]=uri + end + opts.on('--query=FILE','--file=FILE', 'Take query from a file') do |file| + options[:file]=file + end + opts.on('--output=TYPE', [:json,:xml,:text,:csv,:tsv], + 'Set the output argument') do |type| + options[:output]=type + end + opts.on('--accept=TYPE', [:json,:xml,:text,:csv,:tsv], + 'Set the accept header type') do |type| + options[:accept]=type + end + options[:verbose] = false + opts.on( '--post', 'Force use of POST' ) do + options[:post] = true + end + opts.on( '-v', '--verbose', 'Verbose' ) do + options[:verbose] = true + end + opts.on( '--version', 'Print version and exit' ) do + print "#{SOH_NAME} #{SOH_VERSION}\n" + exit + end + opts.on( '-h', '--help', 'Display this screen and exit' ) do + puts opts + exit + end + end + + begin optparse.parse! + rescue OptionParser::InvalidArgument, OptionParser::InvalidOption => e + warn e + exit 1 + end + + $verbose = options[:verbose] + $print_http = $verbose + usePOST = options[:post] + + service = options[:service] + warn_exit 'No service specified. Required --service=URI',1 if service.nil? + + # Query + query=nil + query_file=options[:file] + if query_file.nil? && ARGV.size == 0 + then + warn_exit 'No query specified.',1 + end + if query_file.nil? + query = ARGV.shift + if query.match(/^@/) + query_file = query[1..-1] + query = nil + end + end + + # --output ==> output= (non-standard) + args={} + case options[:output] + when nil + when "json","xml","text","csv","tsv" + args['output'] = options[:output] + when :json,:xml,:text,:csv,:tsv + args['output'] = options[:output].to_s + else + warn_exit "Unrecognized output type: "+options[:output],2 + end + + # --accept + # options[:accept] + + print "SPARQL #{service}\n" if $verbose + #args={"output"=>"text"} + SPARQL_query(service, query, query_file, usePOST, args) + exit(0) +end + +## -------- SPARQL Update + +# Update sent as a WWW form. +def SPARQL_update_by_form(service, update, args2={}) + args = {} + args.merge!(args2) + headers={} + headers.merge!($headers) + # args? encode? + body="update="+uri_escape(update) + headers[$hContentType] = $mtWWWForm + headers[$hContentLength] = body.length.to_s + uri = URI.parse(service) + execute_post_form(uri, headers, body) +end + +# DRY - query form. +def execute_post_form(uri, headers, body) + request = Net::HTTP::Post.new(uri.request_uri) + request.initialize_http_header(headers) + request.body = body + print_http_request(uri, request) + response_no_body(uri, request) +end + +def SPARQL_update(service, update, args2={}) + args = {} + args.merge!(args2) + headers={} + headers.merge!($headers) + headers[$hContentType] = $mtSparqlUpdate + uri = URI.parse(service) + request = Net::HTTP::Post.new(uri.request_uri) + request.initialize_http_header(headers) + request.body = update + print_http_request(uri, request) + response_no_body(uri, request) +end + +def cmd_sparql_update(by_raw_post=true) + # Share with cmd_sparql_query + options={} + optparse = OptionParser.new do |opts| + opts.banner = "Usage: #{$cmd} [--file REQUEST] [--service URI] 'request' | @file" + opts.on('--service=URI', '--server=URI', 'SPARQL endpoint') do |uri| + options[:service]=uri + end + opts.on('--update=FILE', '--file=FILE', 'Take update from a file') do |file| + options[:file]=file + end + options[:verbose] = false + opts.on( '-v', '--verbose', 'Verbose' ) do + options[:verbose] = true + end + opts.on( '--version', 'Print version and exit' ) do + print "#{SOH_NAME} #{SOH_VERSION}\n" + exit + end + opts.on( '-h', '--help', 'Display this screen and exit' ) do + puts opts + exit + end + end + + begin optparse.parse! + rescue OptionParser::InvalidArgument => e + warn e + exit + end + + $verbose = options[:verbose] + $print_http = $verbose + + service = options[:service] + warn_exit 'No service specified. Required --service=URI',1 if service.nil? + + update=nil + update_file=options[:file] + + if update_file.nil? && ARGV.size == 0 + then + warn_exit 'No update specified.',1 + end + if update_file.nil? + update = ARGV.shift + if update.match(/^@/) + update_file = update[1..-1] + update = nil + end + end + + print "SPARQL-Update #{service}\n" if $verbose + args={} + + # Reads in the file :-( + if update.nil? + then + update = open(update_file, 'rb'){|f| f.read} + else + update = string_or_file(update) + end + + if by_raw_post + SPARQL_update(service, update, args) + else + SPARQL_update_by_form(service, update, args) + end + exit(0) +end + +## ------- + +case $cmd +when "s-http", "sparql-http", "soh" + $banner="#{$cmd} [get|post|put|delete] datasetURI graph [file]" + cmd_soh +when "s-get", "s-head", "s-put", "s-delete", "s-post" + + case $cmd + when "s-get", "s-head", "s-delete" + $banner="#{$cmd} datasetURI graph" + when "s-put", "s-post" + $banner="#{$cmd} datasetURI graph file" + end + cmd2 = $cmd.sub(/^s-/, '').upcase + cmd_soh cmd2 + +when "s-query", "sparql-query" + cmd_sparql_query +when "s-update", "sparql-update" + cmd_sparql_update true +when "s-update-form", "sparql-update-form" + cmd_sparql_update false +else + warn_exit "Unknown: "+$cmd, 1 +end http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-dist/bin/s-head ---------------------------------------------------------------------- diff --git a/jena-fuseki2/jena-fuseki-dist/bin/s-head b/jena-fuseki2/jena-fuseki-dist/bin/s-head new file mode 100755 index 0000000..0e3a807 --- /dev/null +++ b/jena-fuseki2/jena-fuseki-dist/bin/s-head @@ -0,0 +1,707 @@ +#!/usr/bin/env ruby +# -*- coding: 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. + +# SPARQL HTTP Update, client. + +require 'optparse' +require 'net/http' +require 'uri' +require 'cgi' +require 'pp' +require 'ostruct' + +# ToDo +# Allow a choice of media type for GET +# --accept "content-type" (and abbreviations) +# --header "Add:this" +# --user, --password +# Basic authentication: request.basic_auth("username", "password") +# Follow redirects => 301: puts response["location"] # All headers are lowercase? + +SOH_NAME="SOH" +SOH_VERSION="0.0.0" + +$proxy = ENV['http_proxy'] ? URI.parse(ENV['http_proxy']) : OpenStruct.new + +# What about direct naming? + +# Names +$mtTurtle = 'text/turtle;charset=utf-8' +$mtRDF = 'application/rdf+xml' +$mtText = 'text/plain' +$mtNQuads = 'text/n-quads' +$mtTriG = 'application/trig' +$mtSparqlResultsX = 'application/sparql-results+xml' +$mtSparqlResultsJ = 'application/sparql-results+json' +$mtAppJSON = 'application/json' +$mtAppXML = 'application/xml' +$mtSparqlResultsTSV = 'application/sparql-results+tsv' +$mtSparqlResultsCSV = 'application/sparql-results+csv' +$mtSparqlUpdate = 'application/sparql-update' +$mtWWWForm = 'application/x-www-form-urlencoded' +$mtSparqlQuery = "application/sparql-query" ; + +# Global media type table. +$fileMediaTypes = {} +$fileMediaTypes['ttl'] = $mtTurtle +$fileMediaTypes['n3'] = 'text/n3; charset=utf-8' +$fileMediaTypes['nt'] = $mtText +$fileMediaTypes['rdf'] = $mtRDF +$fileMediaTypes['owl'] = $mtRDF +$fileMediaTypes['nq'] = $mtNQuads +$fileMediaTypes['trig'] = $mtTriG + +# Global charset : no entry means "don't set" +$charsetUTF8 = 'utf-8' +$charset = {} +$charset[$mtTurtle] = 'utf-8' +$charset[$mtText] = 'ascii' +$charset[$mtTriG] = 'utf-8' +$charset[$mtNQuads] = 'ascii' + +# Headers + +$hContentType = 'Content-Type' +# $hContentEncoding = 'Content-Encoding' +$hContentLength = 'Content-Length' +# $hContentLocation = 'Content-Location' +# $hContentRange = 'Content-Range' + +$hAccept = 'Accept' +$hAcceptCharset = 'Accept-Charset' +$hAcceptEncoding = 'Accept-Encoding' +$hAcceptRanges = 'Accept-Ranges' + +$headers = { "User-Agent" => "#{SOH_NAME}/Fuseki #{SOH_VERSION}"} +$print_http = false + +# Default for GET +# At least allow anythign (and hope!) +$accept_rdf="#{$mtRDF};q=0.9 , #{$mtTurtle}" +# For SPARQL query +$accept_results="#{$mtSparqlResultsJ} , #{$mtSparqlResultsX};q=0.9 , #{$accept_rdf}" + +# Accept any in case of trouble. +$accept_rdf="#{$accept_rdf} , */*;q=0.1" +$accept_results="#{$accept_results} , */*;q=0.1" + +# The media type usually forces the charset. +$accept_charset=nil + +## Who we are. +## Two styles: +## s-query ..... +## soh query ..... + +$cmd = File.basename($0) +if $cmd == 'soh' +then + $cmd = (ARGV.size == 0) ? 'soh' : ARGV.shift +end + +if ! $cmd.start_with?('s-') && $cmd != 'soh' + $cmd = 's-'+$cmd +end + +## -------- + +def GET(dataset, graph) + print "GET #{dataset} #{graph}\n" if $verbose + requestURI = target(dataset, graph) + headers = {} + headers.merge!($headers) + headers[$hAccept] = $accept_rdf + headers[$hAcceptCharset] = $accept_charset unless $accept_charset.nil? + get_worker(requestURI, headers) +end + +def get_worker(requestURI, headers) + uri = URI.parse(requestURI) + request = Net::HTTP::Get.new(uri.request_uri) + request.initialize_http_header(headers) + print_http_request(uri, request) + response_print_body(uri, request) +end + +def HEAD(dataset, graph) + print "HEAD #{dataset} #{graph}\n" if $verbose + requestURI = target(dataset, graph) + headers = {} + headers.merge!($headers) + headers[$hAccept] = $accept_rdf + headers[$hAcceptCharset] = $accept_charset unless $accept_charset.nil? + uri = URI.parse(requestURI) + request = Net::HTTP::Head.new(uri.request_uri) + request.initialize_http_header(headers) + print_http_request(uri, request) + response_no_body(uri, request) +end + +def PUT(dataset, graph, file) + print "PUT #{dataset} #{graph} #{file}\n" if $verbose + send_body(dataset, graph, file, Net::HTTP::Put) +end + +def POST(dataset, graph, file) + print "POST #{dataset} #{graph} #{file}\n" if $verbose + send_body(dataset, graph, file, Net::HTTP::Post) +end + +def DELETE(dataset, graph) + print "DELETE #{dataset} #{graph}\n" if $verbose + requestURI = target(dataset, graph) + uri = URI.parse(requestURI) + request = Net::HTTP::Delete.new(uri.request_uri) + headers = {} + headers.merge!($headers) + request.initialize_http_header(headers) + print_http_request(uri, request) + response_no_body(uri, request) +end + +def uri_escape(string) + CGI.escape(string) +end + +def target(dataset, graph) + return dataset+"?default" if graph == "default" + return dataset+"?graph="+uri_escape(graph) +end + +def send_body(dataset, graph, file, method) + mt = content_type(file) + headers = {} + headers.merge!($headers) + headers[$hContentType] = mt + headers[$hContentLength] = File.size(file).to_s + ## p headers + + requestURI = target(dataset, graph) + uri = URI.parse(requestURI) + + request = method.new(uri.request_uri) + request.initialize_http_header(headers) + print_http_request(uri, request) + request.body_stream = File.open(file) + response_no_body(uri, request) +end + +def response_no_body(uri, request) + http = Net::HTTP::Proxy($proxy.host,$proxy.port).new(uri.host, uri.port) + http.read_timeout = nil + # check we can connect. + begin http.start + rescue Exception => e + # puts e.message + #puts e.backtrace.inspect + warn_exit "Failed to connect: #{uri.host}:#{uri.port}: #{e.message}", 3 + end + response = http.request(request) + print_http_response(response) + case response + when Net::HTTPSuccess, Net::HTTPRedirection + # OK + when Net::HTTPNotFound + warn_exit "404 Not found: #{uri}", 9 + #print response.body + else + warn_exit "#{response.code} #{response.message} #{uri}", 9 + # Unreachable + response.error! + end + # NO BODY IN RESPONSE +end + +def response_print_body(uri, request) + http = Net::HTTP::Proxy($proxy.host,$proxy.port).new(uri.host, uri.port) + http.read_timeout = nil + # check we can connect. + begin http.start + rescue => e + #puts e.backtrace.inspect + #print e.class + warn_exit "Failed to connect: #{uri.host}:#{uri.port}: #{e.message}", 3 + end + + # Add a blank line if headers were output. + print "\n" if $http_print ; + + begin + response = http.request(request) { |res| + print_http_response(res) + #puts res.code + res.read_body do |segment| + print segment + end + } + case response + when Net::HTTPSuccess, Net::HTTPRedirection + # OK + when Net::HTTPNotFound + warn_exit "404 Not found: #{uri}", 9 + #print response.body + else + warn_exit "#{response.code}: #{uri}", 9 + # Unreachable + response.error! + end + rescue EOFError => e + warn_exit "IO Error: "+e.message, 3 + end +end + +def print_http_request(uri, request) + return unless $print_http + #print "Request\n" + print request.method," ",uri, "\n" + print_headers(" ",request) +end + +def print_http_response(response) + return unless $print_http + #print "Response\n" + print response.code, " ", response.message, "\n" + print_headers(" ",response) +end + +def print_headers(marker, headers) + headers.each do |k,v| + k = k.split('-').map{|w| w.capitalize}.join('-')+':' + printf "%s%-20s %s\n",marker,k,v + end +end + +def content_type(file) + file =~ /\.([^.]*)$/ + ext = $1 + mt = $fileMediaTypes[ext] + cs = $charset[mt] + mt = mt+';charset='+cs if ! cs.nil? + return mt +end + +def charset(content_type) + return $charset[content_type] +end + +def warn_exit(msg, rc) + warn msg + exit rc ; +end + +def parseURI(uri_string) + begin + return URI.parse(uri_string).to_s + rescue URI::InvalidURIError => err + warn_exit "Bad URI: <#{uri_string}>", 2 + end +end + +## ---- Command + +def cmd_soh(command=nil) + ## Command line + options = {} + optparse = OptionParser.new do |opts| + # Set a banner, displayed at the top + # of the help screen. + case $cmd + when "s-http", "sparql-http", "soh" + banner="$cmd [get|post|put|delete] datasetURI graph [file]" + when "s-get", "s-head", "s-delete" + banner="$cmd datasetURI graph" + end + + opts.banner = $banner + # Define the options, and what they do + + options[:verbose] = false + opts.on( '-v', '--verbose', 'Verbose' ) do + options[:verbose] = true + end + + options[:version] = false + opts.on( '--version', 'Print version and exit' ) do + print "#{SOH_NAME} #{SOH_VERSION}\n" + exit + end + + # This displays the help screen, all programs are + # assumed to have this option. + opts.on( '-h', '--help', 'Display this screen and exit' ) do + puts opts + exit + end + end + + begin optparse.parse! + rescue OptionParser::InvalidArgument => e + warn e + exit + end + + $verbose = options[:verbose] + $print_http = $verbose + + if command.nil? + if ARGV.size == 0 + warn "No command given: expected one of 'get', 'put', 'post', 'delete', 'query' or 'update'" + exit 1 + end + cmdPrint=ARGV.shift + command=cmdPrint.upcase + else + cmdPrint=command + end + + case command + when "HEAD", "GET", "DELETE" + requiredFile=false + when "PUT", "POST" + requiredFile=true + when "QUERY" + cmd_sparql_query + when "UPDATE" + cmd_sparql_update + else + warn_exit "Unknown command: #{command}", 2 + end + + if requiredFile + then + if ARGV.size != 3 + warn_exit "Required: dataset URI, graph URI (or 'default') and file", 1 + end + else + if ARGV.size != 2 + warn_exit "Required: dataset URI and graph URI (or 'default')", 1 + end + end + + dataset=parseURI(ARGV.shift) + # Relative URI? + graph=parseURI(ARGV.shift) + file="" + if requiredFile + then + file = ARGV.shift if requiredFile + if ! File.exist?(file) + warn_exit "No such file: "+file, 3 + end + if File.directory?(file) + warn_exit "File is a directory: "+file, 3 + end + end + + case command + when "GET" + GET(dataset, graph) + when "HEAD" + HEAD(dataset, graph) + when "PUT" + PUT(dataset, graph, file) + when "DELETE" + DELETE(dataset, graph) + when "POST" + POST(dataset, graph, file) + else + warn_exit "Internal error: Unknown command: #{cmd}", 2 + end + exit 0 +end + +## -------- +def string_or_file(arg) + return arg if ! arg.match(/^@/) + a=(arg[1..-1]) + open(a, 'rb'){|f| f.read} +end + +## -------- SPARQL Query + +## Choose method +def SPARQL_query(service, query, query_file, forcePOST=false, args2={}) + if ! query_file.nil? + query = open(query_file, 'rb'){|f| f.read} + end + if forcePOST || query.length >= 2*1024 + SPARQL_query_POST(service, query, args2) + else + SPARQL_query_GET(service, query, args2) + end +end + +## By GET + +def SPARQL_query_GET(service, query, args2) + args = { "query" => query } + args.merge!(args2) + qs=args.collect { |k,v| "#{k}=#{uri_escape(v)}" }.join('&') + action="#{service}?#{qs}" + headers={} + headers.merge!($headers) + headers[$hAccept]=$accept_results + get_worker(action, headers) +end + +## By POST + +def SPARQL_query_POST(service, query, args2) + # DRY - body/no body for each of request and response. + post_params={ "query" => query } + post_params.merge!(args2) + uri = URI.parse(service) + headers={} + headers.merge!($headers) + headers[$hAccept]=$accept_results + execute_post_form_body(uri, headers, post_params) +end + +def execute_post_form_body(uri, headers, post_params) + request = Net::HTTP::Post.new(uri.request_uri) + qs=post_params.collect { |k,v| "#{k}=#{uri_escape(v)}" }.join('&') + headers[$hContentType] = $mtWWWForm + headers[$hContentLength] = qs.length.to_s + request.initialize_http_header(headers) + request.body = qs + print_http_request(uri, request) + response_print_body(uri, request) +end + +# Usage: -v --help --file= --query= +def cmd_sparql_query + options={} + optparse = OptionParser.new do |opts| + opts.banner = "Usage: #{$cmd} [--query QUERY] [--service URI] [--post] 'query' | @file" + opts.on('--service=URI', '--server=URI', 'SPARQL endpoint') do |uri| + options[:service]=uri + end + opts.on('--query=FILE','--file=FILE', 'Take query from a file') do |file| + options[:file]=file + end + opts.on('--output=TYPE', [:json,:xml,:text,:csv,:tsv], + 'Set the output argument') do |type| + options[:output]=type + end + opts.on('--accept=TYPE', [:json,:xml,:text,:csv,:tsv], + 'Set the accept header type') do |type| + options[:accept]=type + end + options[:verbose] = false + opts.on( '--post', 'Force use of POST' ) do + options[:post] = true + end + opts.on( '-v', '--verbose', 'Verbose' ) do + options[:verbose] = true + end + opts.on( '--version', 'Print version and exit' ) do + print "#{SOH_NAME} #{SOH_VERSION}\n" + exit + end + opts.on( '-h', '--help', 'Display this screen and exit' ) do + puts opts + exit + end + end + + begin optparse.parse! + rescue OptionParser::InvalidArgument, OptionParser::InvalidOption => e + warn e + exit 1 + end + + $verbose = options[:verbose] + $print_http = $verbose + usePOST = options[:post] + + service = options[:service] + warn_exit 'No service specified. Required --service=URI',1 if service.nil? + + # Query + query=nil + query_file=options[:file] + if query_file.nil? && ARGV.size == 0 + then + warn_exit 'No query specified.',1 + end + if query_file.nil? + query = ARGV.shift + if query.match(/^@/) + query_file = query[1..-1] + query = nil + end + end + + # --output ==> output= (non-standard) + args={} + case options[:output] + when nil + when "json","xml","text","csv","tsv" + args['output'] = options[:output] + when :json,:xml,:text,:csv,:tsv + args['output'] = options[:output].to_s + else + warn_exit "Unrecognized output type: "+options[:output],2 + end + + # --accept + # options[:accept] + + print "SPARQL #{service}\n" if $verbose + #args={"output"=>"text"} + SPARQL_query(service, query, query_file, usePOST, args) + exit(0) +end + +## -------- SPARQL Update + +# Update sent as a WWW form. +def SPARQL_update_by_form(service, update, args2={}) + args = {} + args.merge!(args2) + headers={} + headers.merge!($headers) + # args? encode? + body="update="+uri_escape(update) + headers[$hContentType] = $mtWWWForm + headers[$hContentLength] = body.length.to_s + uri = URI.parse(service) + execute_post_form(uri, headers, body) +end + +# DRY - query form. +def execute_post_form(uri, headers, body) + request = Net::HTTP::Post.new(uri.request_uri) + request.initialize_http_header(headers) + request.body = body + print_http_request(uri, request) + response_no_body(uri, request) +end + +def SPARQL_update(service, update, args2={}) + args = {} + args.merge!(args2) + headers={} + headers.merge!($headers) + headers[$hContentType] = $mtSparqlUpdate + uri = URI.parse(service) + request = Net::HTTP::Post.new(uri.request_uri) + request.initialize_http_header(headers) + request.body = update + print_http_request(uri, request) + response_no_body(uri, request) +end + +def cmd_sparql_update(by_raw_post=true) + # Share with cmd_sparql_query + options={} + optparse = OptionParser.new do |opts| + opts.banner = "Usage: #{$cmd} [--file REQUEST] [--service URI] 'request' | @file" + opts.on('--service=URI', '--server=URI', 'SPARQL endpoint') do |uri| + options[:service]=uri + end + opts.on('--update=FILE', '--file=FILE', 'Take update from a file') do |file| + options[:file]=file + end + options[:verbose] = false + opts.on( '-v', '--verbose', 'Verbose' ) do + options[:verbose] = true + end + opts.on( '--version', 'Print version and exit' ) do + print "#{SOH_NAME} #{SOH_VERSION}\n" + exit + end + opts.on( '-h', '--help', 'Display this screen and exit' ) do + puts opts + exit + end + end + + begin optparse.parse! + rescue OptionParser::InvalidArgument => e + warn e + exit + end + + $verbose = options[:verbose] + $print_http = $verbose + + service = options[:service] + warn_exit 'No service specified. Required --service=URI',1 if service.nil? + + update=nil + update_file=options[:file] + + if update_file.nil? && ARGV.size == 0 + then + warn_exit 'No update specified.',1 + end + if update_file.nil? + update = ARGV.shift + if update.match(/^@/) + update_file = update[1..-1] + update = nil + end + end + + print "SPARQL-Update #{service}\n" if $verbose + args={} + + # Reads in the file :-( + if update.nil? + then + update = open(update_file, 'rb'){|f| f.read} + else + update = string_or_file(update) + end + + if by_raw_post + SPARQL_update(service, update, args) + else + SPARQL_update_by_form(service, update, args) + end + exit(0) +end + +## ------- + +case $cmd +when "s-http", "sparql-http", "soh" + $banner="#{$cmd} [get|post|put|delete] datasetURI graph [file]" + cmd_soh +when "s-get", "s-head", "s-put", "s-delete", "s-post" + + case $cmd + when "s-get", "s-head", "s-delete" + $banner="#{$cmd} datasetURI graph" + when "s-put", "s-post" + $banner="#{$cmd} datasetURI graph file" + end + cmd2 = $cmd.sub(/^s-/, '').upcase + cmd_soh cmd2 + +when "s-query", "sparql-query" + cmd_sparql_query +when "s-update", "sparql-update" + cmd_sparql_update true +when "s-update-form", "sparql-update-form" + cmd_sparql_update false +else + warn_exit "Unknown: "+$cmd, 1 +end
