http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/test/java/org/apache/jena/fuseki/http/TestDatasetAccessorHTTP.java ---------------------------------------------------------------------- diff --git a/jena-fuseki/src/test/java/org/apache/jena/fuseki/http/TestDatasetAccessorHTTP.java b/jena-fuseki/src/test/java/org/apache/jena/fuseki/http/TestDatasetAccessorHTTP.java new file mode 100644 index 0000000..f8cf8fd --- /dev/null +++ b/jena-fuseki/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(expected=HttpException.class) + public void test_ds_1() + { + // Can't 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/36855e1b/jena-fuseki/src/test/java/org/apache/jena/fuseki/http/TestDatasetGraphAccessorHTTP.java ---------------------------------------------------------------------- diff --git a/jena-fuseki/src/test/java/org/apache/jena/fuseki/http/TestDatasetGraphAccessorHTTP.java b/jena-fuseki/src/test/java/org/apache/jena/fuseki/http/TestDatasetGraphAccessorHTTP.java new file mode 100644 index 0000000..7687b2c --- /dev/null +++ b/jena-fuseki/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/36855e1b/jena-fuseki/src/test/java/org/apache/jena/fuseki/http/TestHttpOp.java ---------------------------------------------------------------------- diff --git a/jena-fuseki/src/test/java/org/apache/jena/fuseki/http/TestHttpOp.java b/jena-fuseki/src/test/java/org/apache/jena/fuseki/http/TestHttpOp.java new file mode 100644 index 0000000..1c5ccd5 --- /dev/null +++ b/jena-fuseki/src/test/java/org/apache/jena/fuseki/http/TestHttpOp.java @@ -0,0 +1,214 @@ +/** + * 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 java.io.IOException ; + +import org.apache.jena.atlas.io.IO ; +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 ; +import com.hp.hpl.jena.util.FileUtils ; + +// 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.txt" ; + @BeforeClass public static void beforeClass() { ServerTest.allocServer() ; } + @AfterClass public static void afterClass() { ServerTest.freeServer() ; } + + static String graphURL = ServerTest.serviceREST+"?default" ; + 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() { + TypedInputStream in = HttpOp.execHttpGet(pingURL) ; + IO.close(in) ; + } + + @Test(expected=HttpException.class) + public void httpGet_02() { + try { + TypedInputStream in = HttpOp.execHttpGet(ServerTest.urlRoot+"does-not-exist") ; + IO.close(in) ; + } catch(HttpException ex) { + assertEquals(HttpSC.NOT_FOUND_404, ex.getResponseCode()) ; + throw ex ; + } + } + + @Test public void httpGet_03() throws IOException { + String x = HttpOp.execHttpGetString(pingURL) ; + String y = FileUtils.readWholeFileAsUTF8("pages/ping.txt") ; + assertEquals(y,x) ; + } + + @Test public void httpGet_04() { + String x = HttpOp.execHttpGetString(ServerTest.urlRoot+"does-not-exist") ; + assertNull(x) ; + } + + @Test public void httpGet_05() { + TypedInputStream in = HttpOp.execHttpGet(simpleQuery) ; + IO.close(in) ; + } + + // SPARQL Query + + @Test public void queryGet_01() { + TypedInputStream in = HttpOp.execHttpGet(simpleQuery) ; + IO.close(in) ; + } + + @Test(expected=HttpException.class) + public void queryGet_02() { + try { + // No query. + TypedInputStream in = HttpOp.execHttpGet(queryURL+"?query=") ; + IO.close(in) ; + } catch (HttpException ex) { + assertEquals(ex.getResponseCode(), HttpSC.BAD_REQUEST_400) ; + throw ex ; + } + } + +// @Test(expected=HttpException.class) +// public void queryGet_03() { +// try { +// // Service description. +// TypedInputStream in = HttpOp.execHttpGet(queryURL) ; +// IO.close(in) ; +// } catch (HttpException ex) { +// assertEquals(ex.getResponseCode(), HttpSC.NOT_FOUND_404) ; +// 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{}") ; + TypedInputStream in = HttpOp.execHttpPostFormStream(queryURL, params, WebContent.contentTypeResultsJSON) ; + IO.close(in) ; + } + + @Test(expected=HttpException.class) + public void httpPost_05() { + Params params = new Params() ; + params.addParam("query", "ASK{}") ; + TypedInputStream in = null ; + try { + // Query to Update + in = HttpOp.execHttpPostFormStream(updateURL, params, WebContent.contentTypeResultsJSON) ; + } catch (HttpException ex) { + assertEquals(ex.getResponseCode(), HttpSC.BAD_REQUEST_400) ; + throw ex ; + } + finally { IO.close(in) ; } + } + + @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(graphURL, "application/rdf+xml") ; + assertTrue(x.contains("</")) ; + assertTrue(x.contains(":RDF")) ; + } + + @Test public void gsp_02() { + String x = HttpOp.execHttpGetString(graphURL, "application/n-triples") ; + assertTrue(x.isEmpty()) ; + } + + static String graphString = "@prefix : <http://example/> . :s :p :o ." ; + + @Test public void gsp_03() { + HttpOp.execHttpPut(graphURL, WebContent.contentTypeTurtle, graphString) ; + } + + @Test public void gsp_04() { + HttpOp.execHttpPut(graphURL, WebContent.contentTypeTurtle, graphString) ; + String s1 = HttpOp.execHttpGetString(graphURL, WebContent.contentTypeNTriples) ; + assertFalse(s1.isEmpty()) ; + HttpOp.execHttpDelete(graphURL) ; + String s2 = HttpOp.execHttpGetString(graphURL, WebContent.contentTypeNTriples) ; + assertTrue(s2.isEmpty()) ; + } + + @Test public void gsp_05() { + HttpOp.execHttpDelete(graphURL) ; + + HttpOp.execHttpPost(graphURL, WebContent.contentTypeTurtle, graphString) ; + String s1 = HttpOp.execHttpGetString(graphURL, WebContent.contentTypeNTriples) ; + assertFalse(s1.isEmpty()) ; + HttpOp.execHttpDelete(graphURL) ; + String s2 = HttpOp.execHttpGetString(graphURL, WebContent.contentTypeNTriples) ; + assertTrue(s2.isEmpty()) ; + } +} + http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/test/resources/log4j.properties ---------------------------------------------------------------------- diff --git a/jena-fuseki/src/test/resources/log4j.properties b/jena-fuseki/src/test/resources/log4j.properties new file mode 100644 index 0000000..da8f386 --- /dev/null +++ b/jena-fuseki/src/test/resources/log4j.properties @@ -0,0 +1,20 @@ +log4j.rootLogger=INFO,stdlog + +log4j.appender.stdlog=org.apache.log4j.ConsoleAppender +log4j.appender.stdlog.layout=org.apache.log4j.PatternLayout +log4j.appender.stdlog.layout.ConversionPattern=%d{HH:mm:ss} %-5p %-25c{1} :: %m%n + +# Jena Dependency log levels +log4j.logger.com.hp.hpl.jena.arq.info=INFO +log4j.logger.com.hp.hpl.jena.arq.exec=INFO +log4j.logger.com.hp.hpl.jena=WARN +log4j.logger.org.apache.jena=WARN +log4j.logger.org.apache.jena.riot=INFO + +# Jetty - Fuseki catches Jetty errors and reports them. +log4j.logger.org.eclipse.jetty=FATAL + +# Fuseki +log4j.logger.org.apache.jena.fuseki.Server=WARN +log4j.logger.org.apache.jena.fuseki.Fuseki=WARN +log4j.logger.org.apache.jena.fuseki=WARN \ No newline at end of file http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/tdb.ttl ---------------------------------------------------------------------- diff --git a/jena-fuseki/tdb.ttl b/jena-fuseki/tdb.ttl new file mode 100644 index 0000000..9c42345 --- /dev/null +++ b/jena-fuseki/tdb.ttl @@ -0,0 +1,14 @@ +@prefix tdb: <http://jena.hpl.hp.com/2008/tdb#> . +@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#> . +@prefix tdb: <http://jena.hpl.hp.com/2008/tdb#> . + +[] ja:loadClass "com.hp.hpl.jena.tdb.TDB" . +tdb:DatasetTDB rdfs:subClassOf ja:RDFDataset . +tdb:GraphTDB rdfs:subClassOf ja:Model . + + <#dataset> rdf:type tdb:DatasetTDB ; + tdb:location "DB" ; +## tdb:unionDefaultGraph true ; + . http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki2/.gitignore ---------------------------------------------------------------------- diff --git a/jena-fuseki2/.gitignore b/jena-fuseki2/.gitignore new file mode 100644 index 0000000..ede8481 --- /dev/null +++ b/jena-fuseki2/.gitignore @@ -0,0 +1,3 @@ +run/ +fuseki.classpath + http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki2/D.trig ---------------------------------------------------------------------- diff --git a/jena-fuseki2/D.trig b/jena-fuseki2/D.trig new file mode 100644 index 0000000..e3b6dcf --- /dev/null +++ b/jena-fuseki2/D.trig @@ -0,0 +1,4 @@ +prefix : <http://example/> + +{ :s :p "quads" } +:g { :s :p 2 } http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki2/D.ttl ---------------------------------------------------------------------- diff --git a/jena-fuseki2/D.ttl b/jena-fuseki2/D.ttl new file mode 100644 index 0000000..c323f1e --- /dev/null +++ b/jena-fuseki2/D.ttl @@ -0,0 +1,3 @@ +prefix : <http://example/> + +[] :p "TTL" . http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki2/DEPENDENCIES ---------------------------------------------------------------------- diff --git a/jena-fuseki2/DEPENDENCIES b/jena-fuseki2/DEPENDENCIES new file mode 100644 index 0000000..0606adb --- /dev/null +++ b/jena-fuseki2/DEPENDENCIES @@ -0,0 +1,24 @@ +This file lists the dependences for Apache Jena Fuseki. + Version numbers are given in the POM file for a particular distribution. + +Apache Projects: +Apache Software License + Apache Jena, including the Jena IRI library + Apache Xerces-J + Apache log4j + Apache HttpComponents (HTTP Client) + Apache Commons Codec + Apache Commons FileUpload + Apache Lucene + Apache Solr + +SLF4J : http://www.slf4j.org/ + Copyright (c) 2004-2008 QOS.ch + MIT License + +JUnit : http://junit.org/ + Common Public License - v 1.0 + +Jetty: http://www.eclipse.org/jetty/ + Apache License 2.0 + (also avilable under Eclipse Public License 1.0) http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki2/Data/books.ttl ---------------------------------------------------------------------- diff --git a/jena-fuseki2/Data/books.ttl b/jena-fuseki2/Data/books.ttl new file mode 100644 index 0000000..032c805 --- /dev/null +++ b/jena-fuseki2/Data/books.ttl @@ -0,0 +1,46 @@ +@prefix dc: <http://purl.org/dc/elements/1.1/> . +@prefix vcard: <http://www.w3.org/2001/vcard-rdf/3.0#> . +@prefix ns: <http://example.org/ns#> . + +@prefix : <http://example.org/book/> . + +# A small dataset for usage examples of Joseki +# This data is intentionaly irregular (e.g. different ways to +# record the book creator) as if the information is either an +# aggregation or was created at different times. + +:book1 + dc:title "Harry Potter and the Philosopher's Stone" ; + dc:creator "J.K. Rowling" ; + . + +:book2 + dc:title "Harry Potter and the Chamber of Secrets" ; + dc:creator _:a . + +:book3 + dc:title "Harry Potter and the Prisoner Of Azkaban" ; + dc:creator _:a . + +:book4 + dc:title "Harry Potter and the Goblet of Fire" . + +:book5 + dc:title "Harry Potter and the Order of the Phoenix"; + dc:creator "J.K. Rowling" ; + . + +:book6 + dc:title "Harry Potter and the Half-Blood Prince"; + dc:creator "J.K. Rowling" . + +:book7 + dc:title "Harry Potter and the Deathly Hallows" ; + dc:creator "J.K. Rowling" . +_:a + vcard:FN "J.K. Rowling" ; + vcard:N + [ vcard:Family "Rowling" ; + vcard:Given "Joanna" + ] + . http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki2/Data/test_abox.ttl ---------------------------------------------------------------------- diff --git a/jena-fuseki2/Data/test_abox.ttl b/jena-fuseki2/Data/test_abox.ttl new file mode 100644 index 0000000..18b907f --- /dev/null +++ b/jena-fuseki2/Data/test_abox.ttl @@ -0,0 +1,5 @@ +@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . +@prefix : <http://example/ns#> . + +:x rdf:type :A . + http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki2/Data/test_data_rdfs.ttl ---------------------------------------------------------------------- diff --git a/jena-fuseki2/Data/test_data_rdfs.ttl b/jena-fuseki2/Data/test_data_rdfs.ttl new file mode 100644 index 0000000..d1dbe43 --- /dev/null +++ b/jena-fuseki2/Data/test_data_rdfs.ttl @@ -0,0 +1,12 @@ +@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . +@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . + +@prefix : <http://example/ns#> . + +:A a rdfs:Class . +:B a rdfs:Class . + +:A rdfs:subClassOf :B . + +:x rdf:type :A . + http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki2/Data/test_tbox.ttl ---------------------------------------------------------------------- diff --git a/jena-fuseki2/Data/test_tbox.ttl b/jena-fuseki2/Data/test_tbox.ttl new file mode 100644 index 0000000..7c6e456 --- /dev/null +++ b/jena-fuseki2/Data/test_tbox.ttl @@ -0,0 +1,9 @@ +@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . +@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . + +@prefix : <http://example/ns#> . + +:A a rdfs:Class . +:B a rdfs:Class . + +:A rdfs:subClassOf :B . http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki2/LICENSE ---------------------------------------------------------------------- diff --git a/jena-fuseki2/LICENSE b/jena-fuseki2/LICENSE new file mode 100644 index 0000000..4eea17d --- /dev/null +++ b/jena-fuseki2/LICENSE @@ -0,0 +1,257 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +- - - - - - - - - - - - - - - - - - - - - - - + +============================================================== + Jetty Web Container + Copyright 1995-2012 Mort Bay Consulting Pty Ltd. +============================================================== + +The Jetty Web Container is Copyright Mort Bay Consulting Pty Ltd +unless otherwise noted. + +Jetty is dual licensed under both + + * The Apache 2.0 License + http://www.apache.org/licenses/LICENSE-2.0.html + + and + + * The Eclipse Public 1.0 License + http://www.eclipse.org/legal/epl-v10.html + +Jetty may be distributed under either license. + +- - - - - - - - - - - - - - - - - - - - - - - + +This product bundles "Bootstrap", which is available under an +MIT license. See: https://github.com/twbs/bootstrap/blob/master/LICENSE + +This product bundles "codemirror", which is available under an +MIT license. See http://codemirror.net/LICENSE + +This product bundles "jquery", which is available under an +MIT license. See https://jquery.org/license/ + +This product bundles "DataTables", which is available under a +"BSD 3-clause" license. See http://datatables.net/license_bsd + +This product bundles "jquery.form", which is available under an +MIT license. See http://malsup.github.io/mit-license.txt + +This product bundles "jquery.xdomainrequest", which is available under an +MIT license. See https://github.com/MoonScript/jQuery-ajaxTransport-XDomainRequest/blob/master/LICENSE.txt + +This product bundles "backbone.js", which is available under an +MIT license. See https://github.com/jashkenas/backbone/blob/master/LICENSE + +This product bundles "backbone.marionette", which is available under an +MIT license. See http://mutedsolutions.mit-license.org/ + "backbone.marionette" includes "Backbone.BabySitter" and + "Backbone.Wreqr" also available under the same MIT license. + +This product bundles "html5shiv", which is available under an +MIT license. See https://code.google.com/p/html5shiv/ + +This product bundles "RequireJS", which is available under an +MIT license. +https://github.com/jrburke/requirejs/blob/master/LICENSE + "RequireJS" is also available with a "30clause BSD license" + +This product bundles "Respond", which is available under an +MIT license. See https://github.com/scottjehl/Respond/blob/master/LICENSE-MIT + +This product bundles "sprintf.js", which is available under a +"3 clause BSD" license. + https://github.com/alexei/sprintf.js/blob/master/LICENSE + +This product bundles "underscore", which is available under an +MIT license. See https://github.com/jashkenas/underscore/blob/master/LICENSE + +This product bundles "FontAwesome" + "Font Awesome by Dave Gandy - http://fontawesome.io" +The font is available under an SIL Open Font License 1.1 +and the CSS files under an MIT License. +See http://fontawesome.io/license/ + +This product bundles "jQuery File Upload Plugin" +which is available under an MIT License. +See https://github.com/blueimp/jQuery-File-Upload + +This product bundles "Bootstrap-select" +which is available under an MIT License. +See https://github.com/silviomoreto/bootstrap-select/blob/master/LICENSE http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki2/NOTICE ---------------------------------------------------------------------- diff --git a/jena-fuseki2/NOTICE b/jena-fuseki2/NOTICE new file mode 100644 index 0000000..6c51c3c --- /dev/null +++ b/jena-fuseki2/NOTICE @@ -0,0 +1,16 @@ +Apache Jena - module Fuseki +Copyright 2011-2014 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). + +Portions of this software were originally based on the following: + - Copyright 2005, 2006, 2007, 2008, 2009 Hewlett-Packard Development Company, LP + - Copyright 2010, 2011 Epimorphics Ltd. + - Copyright 2010, 2011 Talis Systems Ltd. +These have been licensed to the Apache Software Foundation under a software grant. + +- - - - - - - - - - - - - - - - - - - - - - - + +Portions of this software include software from Mort Bay Consulting Pty. Ltd. + - Copyright (c) 2004-2009 Mort Bay Consulting Pty. Ltd. http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki2/ReleaseNotes.txt ---------------------------------------------------------------------- diff --git a/jena-fuseki2/ReleaseNotes.txt b/jena-fuseki2/ReleaseNotes.txt new file mode 100644 index 0000000..da3d1a5 --- /dev/null +++ b/jena-fuseki2/ReleaseNotes.txt @@ -0,0 +1,19 @@ +==== Fuseki + +== Fuseki 2.0.0 + +Fuseki 2.0.0 is a major change to Fuseki. Se the documentation for details. + +* Security, provided by Apache Shiro. +* New admin API +* New UI + +Migration: + +Existing installations of Fuseki should work unchanged +but some new functionality if not available to datasets +defined in the Fuseki config.ttl file. + ++ Service assemblers should be placed configuration/ + one service per file. Only this way will all features, + particularly making a dataset offline, be available. http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki2/assembly-dist.xml ---------------------------------------------------------------------- diff --git a/jena-fuseki2/assembly-dist.xml b/jena-fuseki2/assembly-dist.xml new file mode 100644 index 0000000..f1162ce --- /dev/null +++ b/jena-fuseki2/assembly-dist.xml @@ -0,0 +1,88 @@ +<?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> + + <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-dist</source> + <destName>LICENSE</destName> + </file> + <file> + <source>dist/NOTICE-dist</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>src/main/webapp</directory> + <outputDirectory>webapp</outputDirectory> + </fileSet> + + </fileSets> +</assembly> http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki2/backup ---------------------------------------------------------------------- diff --git a/jena-fuseki2/backup b/jena-fuseki2/backup new file mode 100755 index 0000000..48d5252 --- /dev/null +++ b/jena-fuseki2/backup @@ -0,0 +1,7 @@ +#!/bin/bash + +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/36855e1b/jena-fuseki2/bin/s-delete ---------------------------------------------------------------------- diff --git a/jena-fuseki2/bin/s-delete b/jena-fuseki2/bin/s-delete new file mode 100755 index 0000000..0e3a807 --- /dev/null +++ b/jena-fuseki2/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/36855e1b/jena-fuseki2/bin/s-get ---------------------------------------------------------------------- diff --git a/jena-fuseki2/bin/s-get b/jena-fuseki2/bin/s-get new file mode 100755 index 0000000..0e3a807 --- /dev/null +++ b/jena-fuseki2/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
