http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/REST_Quads.java ---------------------------------------------------------------------- diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/REST_Quads.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/REST_Quads.java new file mode 100644 index 0000000..578447e --- /dev/null +++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/REST_Quads.java @@ -0,0 +1,68 @@ +/** + * 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.servlets ; + + +/** + * Servlet for operations directly on a dataset - REST(ish) behaviour on the + * dataset URI. + */ + +public abstract class REST_Quads extends SPARQL_GSP { + // Not supported: GSP direct naming. + + public REST_Quads() { + super() ; + } + + @Override + protected void validate(HttpAction action) { + // Check in the operations itself. + } + + @Override + protected void doOptions(HttpAction action) { + ServletOps.errorMethodNotAllowed("OPTIONS") ; + } + + @Override + protected void doHead(HttpAction action) { + ServletOps.errorMethodNotAllowed("HEAD") ; + } + + @Override + protected void doPost(HttpAction action) { + ServletOps.errorMethodNotAllowed("POST") ; + } + + @Override + protected void doPut(HttpAction action) { + ServletOps.errorMethodNotAllowed("PUT") ; + } + + @Override + protected void doDelete(HttpAction action) { + ServletOps.errorMethodNotAllowed("DELETE") ; + } + + @Override + protected void doPatch(HttpAction action) { + ServletOps.errorMethodNotAllowed("PATCH") ; + } +}
http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/REST_Quads_R.java ---------------------------------------------------------------------- diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/REST_Quads_R.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/REST_Quads_R.java new file mode 100644 index 0000000..ca25b06 --- /dev/null +++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/REST_Quads_R.java @@ -0,0 +1,99 @@ +/** + * 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.servlets ; + +import static java.lang.String.format ; + +import java.io.IOException ; + +import javax.servlet.ServletOutputStream ; + +import org.apache.jena.atlas.web.MediaType ; +import org.apache.jena.atlas.web.TypedOutputStream ; +import org.apache.jena.riot.web.HttpNames ; +import org.apache.jena.riot.* ; + +import com.hp.hpl.jena.sparql.core.DatasetGraph ; + +/** + * Servlet for operations directly on a dataset - REST(ish) behaviour on the + * dataset URI. + */ + +public class REST_Quads_R extends REST_Quads { + public REST_Quads_R() { + super() ; + } + + @Override + protected void validate(HttpAction action) { } + + @Override + protected void doGet(HttpAction action) { + MediaType mediaType = ActionLib.contentNegotationQuads(action) ; + ServletOutputStream output ; + try { + output = action.response.getOutputStream() ; + } catch (IOException ex) { + ServletOps.errorOccurred(ex) ; + output = null ; + } + + TypedOutputStream out = new TypedOutputStream(output, mediaType) ; + Lang lang = RDFLanguages.contentTypeToLang(mediaType.getContentType()) ; + if ( lang == null ) + lang = RDFLanguages.TRIG ; + + if ( action.verbose ) + action.log.info(format("[%d] Get: Content-Type=%s, Charset=%s => %s", action.id, + mediaType.getContentType(), mediaType.getCharset(), lang.getName())) ; + if ( !RDFLanguages.isQuads(lang) ) + ServletOps.errorBadRequest("Not a quads format: " + mediaType) ; + + action.beginRead() ; + try { + DatasetGraph dsg = action.getActiveDSG() ; + action.response.setHeader("Content-type", lang.getContentType().toHeaderString()); + RDFFormat fmt = + ( lang == Lang.RDFXML ) ? RDFFormat.RDFXML_PLAIN : RDFWriterRegistry.defaultSerialization(lang) ; + RDFDataMgr.write(out, dsg, fmt) ; + ServletOps.success(action) ; + } finally { + action.endRead() ; + } + } + + @Override + protected void doOptions(HttpAction action) { + action.response.setHeader(HttpNames.hAllow, "GET, HEAD, OPTIONS") ; + action.response.setHeader(HttpNames.hContentLengh, "0") ; + ServletOps.success(action) ; + } + + @Override + protected void doHead(HttpAction action) { + action.beginRead() ; + try { + MediaType mediaType = ActionLib.contentNegotationQuads(action) ; + ServletOps.success(action) ; + } finally { + action.endRead() ; + } + } +} http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/REST_Quads_RW.java ---------------------------------------------------------------------- diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/REST_Quads_RW.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/REST_Quads_RW.java new file mode 100644 index 0000000..dfc87ab --- /dev/null +++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/REST_Quads_RW.java @@ -0,0 +1,136 @@ +/** + * 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.servlets ; + +import org.apache.jena.fuseki.FusekiLib ; +import org.apache.jena.riot.RiotException ; +import org.apache.jena.riot.system.StreamRDF ; +import org.apache.jena.riot.system.StreamRDFLib ; + +import com.hp.hpl.jena.sparql.core.DatasetGraph ; +import com.hp.hpl.jena.sparql.core.DatasetGraphFactory ; + +/** + * Servlet for operations directly on a dataset - REST(ish) behaviour on the + * dataset URI. + */ + +public class REST_Quads_RW extends REST_Quads_R { + + public REST_Quads_RW() { + super() ; + } + + @Override + protected void validate(HttpAction action) { } + + @Override + protected void doPost(HttpAction action) { + if ( !action.getDataService().allowUpdate() ) + ServletOps.errorMethodNotAllowed("POST") ; + + if ( action.isTransactional() ) + doPutPostTxn(action, false) ; + else + doPutPostNonTxn(action, false) ; + } + + @Override + protected void doPut(HttpAction action) { + if ( !action.getDataService().allowUpdate() ) + ServletOps.errorMethodNotAllowed("POST") ; + + if ( action.isTransactional() ) + doPutPostTxn(action, true) ; + else + doPutPostNonTxn(action, true) ; + } + + // These are very similar to SPARQL_REST_RW.addDataIntoTxn/nonTxn + // Maybe can be usually DRYed. + + @Override + protected void doDelete(HttpAction action) { + ServletOps.errorMethodNotAllowed("DELETE") ; + } + + @Override + protected void doPatch(HttpAction action) { + ServletOps.errorMethodNotAllowed("PATCH") ; + } + + private void doPutPostTxn(HttpAction action, boolean clearFirst) { + UploadDetails details = null ; + action.beginWrite() ; + try { + DatasetGraph dsg = action.getActiveDSG() ; + if ( clearFirst ) + dsg.clear() ; + StreamRDF dest = StreamRDFLib.dataset(dsg) ; + details = Upload.incomingData(action, dest) ; + action.commit() ; + ServletOps.success(action) ; + } catch (RiotException ex) { + // Parse error + action.abort() ; + ServletOps.errorBadRequest(ex.getMessage()) ; + } catch (Exception ex) { + // Something else went wrong. Backout. + action.abort() ; + ServletOps.errorOccurred(ex.getMessage()) ; + } finally { + action.endWrite() ; + } + ServletOps.uploadResponse(action, details) ; + } + + private void doPutPostNonTxn(HttpAction action, boolean clearFirst) { + DatasetGraph dsgTmp = DatasetGraphFactory.createMem() ; + StreamRDF dest = StreamRDFLib.dataset(dsgTmp) ; + + UploadDetails details ; + try { + details = Upload.incomingData(action, dest) ; + } catch (RiotException ex) { + ServletOps.errorBadRequest(ex.getMessage()) ; + return ; + } + // Now insert into dataset + action.beginWrite() ; + try { + DatasetGraph dsg = action.getActiveDSG() ; + if ( clearFirst ) + dsg.clear() ; + FusekiLib.addDataInto(dsgTmp, dsg) ; + action.commit() ; + ServletOps.success(action) ; + } catch (Exception ex) { + // We're in the non-transactional branch, this probably will not + // work + // but it might and there is no harm safely trying. + try { + action.abort() ; + } catch (Exception ex2) {} + ServletOps.errorOccurred(ex.getMessage()) ; + } finally { + action.endWrite() ; + } + ServletOps.uploadResponse(action, details) ; + } +} http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ResponseCallback.java ---------------------------------------------------------------------- diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ResponseCallback.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ResponseCallback.java new file mode 100644 index 0000000..1a78627 --- /dev/null +++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ResponseCallback.java @@ -0,0 +1,24 @@ +/* + * 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.servlets ; + +public interface ResponseCallback +{ + public void callback(boolean successfulOperation) ; +} http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ResponseModel.java ---------------------------------------------------------------------- diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ResponseModel.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ResponseModel.java new file mode 100644 index 0000000..65eb1e5 --- /dev/null +++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ResponseModel.java @@ -0,0 +1,136 @@ +/* + * 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.servlets; + +import java.util.HashMap ; +import java.util.Map ; + +import javax.servlet.ServletOutputStream ; +import javax.servlet.http.HttpServletRequest ; +import javax.servlet.http.HttpServletResponse ; + +import org.apache.jena.atlas.web.MediaType ; +import org.apache.jena.fuseki.DEF ; +import org.apache.jena.fuseki.Fuseki ; +import org.apache.jena.fuseki.conneg.ConNeg ; +import org.apache.jena.fuseki.conneg.WebLib ; +import org.apache.jena.riot.Lang ; +import org.apache.jena.riot.RDFDataMgr ; +import org.apache.jena.riot.RDFLanguages ; +import static org.apache.jena.riot.WebContent.* ; +import org.apache.jena.web.HttpSC ; + +import com.hp.hpl.jena.rdf.model.Model ; + +public class ResponseModel +{ + // Short names for "output=" + private static final String contentOutputJSONLD = "json-ld" ; + private static final String contentOutputJSONRDF = "json-rdf" ; + private static final String contentOutputJSON = "json" ; + private static final String contentOutputXML = "xml" ; + private static final String contentOutputText = "text" ; + private static final String contentOutputTTL = "ttl" ; + private static final String contentOutputNT = "nt" ; + + public static Map<String,String> shortNamesModel = new HashMap<String, String>() ; + static { + + // Some short names. keys are lowercase. + ResponseOps.put(shortNamesModel, contentOutputJSONLD, contentTypeJSONLD) ; + ResponseOps.put(shortNamesModel, contentOutputJSONRDF, contentTypeRDFJSON) ; + ResponseOps.put(shortNamesModel, contentOutputJSON, contentTypeJSONLD) ; + ResponseOps.put(shortNamesModel, contentOutputXML, contentTypeRDFXML) ; + ResponseOps.put(shortNamesModel, contentOutputText, contentTypeTurtle) ; + ResponseOps.put(shortNamesModel, contentOutputTTL, contentTypeTurtle) ; + ResponseOps.put(shortNamesModel, contentOutputNT, contentTypeNTriples) ; + } + + public static void doResponseModel(HttpAction action, Model model) + { + HttpServletRequest request = action.request ; + HttpServletResponse response = action.response ; + + String mimeType = null ; // Header request type + + // TODO Use MediaType throughout. + MediaType i = ConNeg.chooseContentType(request, DEF.rdfOffer, DEF.acceptRDFXML) ; + if ( i != null ) + mimeType = i.getContentType() ; + + String outputField = ResponseOps.paramOutput(request, shortNamesModel) ; + if ( outputField != null ) + mimeType = outputField ; + + String writerMimeType = mimeType ; + + if ( mimeType == null ) + { + Fuseki.actionLog.warn("Can't find MIME type for response") ; + String x = WebLib.getAccept(request) ; + String msg ; + if ( x == null ) + msg = "No Accept: header" ; + else + msg = "Accept: "+x+" : Not understood" ; + ServletOps.error(HttpSC.NOT_ACCEPTABLE_406, msg) ; + } + + String contentType = mimeType ; + String charset = charsetUTF8 ; + + String forceAccept = ResponseOps.paramForceAccept(request) ; + if ( forceAccept != null ) + { + contentType = forceAccept ; + charset = charsetUTF8 ; + } + + Lang lang = RDFLanguages.contentTypeToLang(contentType) ; + if ( lang == null ) + ServletOps.errorBadRequest("Can't determine output content type: "+contentType) ; + +// if ( rdfw instanceof RDFXMLWriterI ) +// rdfw.setProperty("showXmlDeclaration", "true") ; + + // // Write locally to check it's possible. + // // Time/space tradeoff. + // try { + // OutputStream out = new NullOutputStream() ; + // RDFDataMgr.write(out, model, lang) ; + // IO.flush(out) ; + // } catch (JenaException ex) + // { + // SPARQL_ServletBase.errorOccurred(ex) ; + // } + + try { + ResponseResultSet.setHttpResponse(action, contentType, charset) ; + response.setStatus(HttpSC.OK_200) ; + ServletOutputStream out = response.getOutputStream() ; + RDFDataMgr.write(out, model, lang) ; + out.flush() ; + } + catch (Exception ex) { + action.log.info("Exception while writing the response model: "+ex.getMessage(), ex) ; + ServletOps.errorOccurred("Exception while writing the response model: "+ex.getMessage(), ex) ; + } + } +} + http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ResponseOps.java ---------------------------------------------------------------------- diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ResponseOps.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ResponseOps.java new file mode 100644 index 0000000..8a3a1c9 --- /dev/null +++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ResponseOps.java @@ -0,0 +1,94 @@ +/* + * 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.servlets; + +import java.io.IOException ; +import java.util.Locale ; +import java.util.Map ; + +import javax.servlet.http.HttpServletRequest ; + +import org.apache.jena.riot.web.HttpNames ; + +public class ResponseOps +{ + // Helpers + public static void put(Map<String, String> map, String key, String value) + { + map.put(key.toLowerCase(Locale.ROOT), value) ; + } + + public static boolean isEOFexception(IOException ioEx) + { + if ( ioEx.getClass().getName().equals("org.mortbay.jetty.EofException eofEx") ) + return true ; + if ( ioEx instanceof java.io.EOFException ) + return true ; + return false ; + } + + public static String paramForceAccept(HttpServletRequest request) + { + String x = fetchParam(request, HttpNames.paramForceAccept) ; + return x ; + } + + public static String paramStylesheet(HttpServletRequest request) + { return fetchParam(request, HttpNames.paramStyleSheet) ; } + + public static String paramOutput(HttpServletRequest request, Map<String,String> map) + { + // Two names. + String x = fetchParam(request, HttpNames.paramOutput1) ; + if ( x == null ) + x = fetchParam(request, HttpNames.paramOutput2) ; + return expandShortName(x, map) ; + } + + public static String expandShortName(String str, Map<String,String> map) + { + if ( str == null ) + return null ; + // Force keys to lower case. See put() above. + String key = str.toLowerCase(Locale.ROOT) ; + String str2 = map.get(key) ; + if ( str2 == null ) + return str ; + return str2 ; + } + + public static String paramCallback(HttpServletRequest request) + { + return fetchParam(request, HttpNames.paramCallback) ; + } + + public static String fetchParam(HttpServletRequest request, String parameterName) + { + String value = request.getParameter(parameterName) ; + if ( value != null ) + { + value = value.trim() ; + if ( value.length() == 0 ) + value = null ; + } + return value ; + } + +} + http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ResponseResultSet.java ---------------------------------------------------------------------- diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ResponseResultSet.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ResponseResultSet.java new file mode 100644 index 0000000..f87e56b --- /dev/null +++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ResponseResultSet.java @@ -0,0 +1,322 @@ +/* + * 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.servlets; + +import static java.lang.String.format ; +import static org.apache.jena.atlas.lib.Lib.equal ; +import static org.apache.jena.riot.WebContent.charsetUTF8 ; +import static org.apache.jena.riot.WebContent.contentTypeRDFXML ; +import static org.apache.jena.riot.WebContent.contentTypeResultsJSON ; +import static org.apache.jena.riot.WebContent.contentTypeResultsThrift ; +import static org.apache.jena.riot.WebContent.contentTypeResultsXML ; +import static org.apache.jena.riot.WebContent.contentTypeTextCSV ; +import static org.apache.jena.riot.WebContent.contentTypeTextPlain ; +import static org.apache.jena.riot.WebContent.contentTypeTextTSV ; +import static org.apache.jena.riot.WebContent.contentTypeXML ; + +import java.io.IOException ; +import java.util.HashMap ; +import java.util.Map ; + +import javax.servlet.ServletOutputStream ; +import javax.servlet.http.HttpServletRequest ; +import javax.servlet.http.HttpServletResponse ; + +import org.apache.jena.atlas.io.IO ; +import org.apache.jena.atlas.web.AcceptList ; +import org.apache.jena.atlas.web.MediaType ; +import org.apache.jena.fuseki.DEF ; +import org.apache.jena.fuseki.FusekiException ; +import org.apache.jena.fuseki.conneg.ConNeg ; +import org.apache.jena.riot.ResultSetMgr ; +import org.apache.jena.riot.WebContent ; +import org.apache.jena.riot.resultset.ResultSetLang ; +import org.apache.jena.web.HttpSC ; +import org.slf4j.Logger ; +import org.slf4j.LoggerFactory ; + +import com.hp.hpl.jena.query.QueryCancelledException ; +import com.hp.hpl.jena.query.ResultSet ; +import com.hp.hpl.jena.query.ResultSetFormatter ; +import com.hp.hpl.jena.sparql.core.Prologue ; + +/** This is the content negotiation for each kind of SPARQL query result */ +public class ResponseResultSet +{ + private static Logger xlog = LoggerFactory.getLogger(ResponseResultSet.class) ; + + // Short names for "output=" + private static final String contentOutputJSON = "json" ; + private static final String contentOutputXML = "xml" ; + private static final String contentOutputSPARQL = "sparql" ; + private static final String contentOutputText = "text" ; + private static final String contentOutputCSV = "csv" ; + private static final String contentOutputTSV = "tsv" ; + private static final String contentOutputThrift = "thrift" ; + + public static Map<String,String> shortNamesResultSet = new HashMap<>() ; + static { + // Some short names. keys are lowercase. + ResponseOps.put(shortNamesResultSet, contentOutputJSON, contentTypeResultsJSON) ; + ResponseOps.put(shortNamesResultSet, contentOutputSPARQL, contentTypeResultsXML) ; + ResponseOps.put(shortNamesResultSet, contentOutputXML, contentTypeResultsXML) ; + ResponseOps.put(shortNamesResultSet, contentOutputText, contentTypeTextPlain) ; + ResponseOps.put(shortNamesResultSet, contentOutputCSV, contentTypeTextCSV) ; + ResponseOps.put(shortNamesResultSet, contentOutputTSV, contentTypeTextTSV) ; + ResponseOps.put(shortNamesResultSet, contentOutputThrift, contentTypeResultsThrift) ; + } + + interface OutputContent { void output(ServletOutputStream out) ; } + + public static void doResponseResultSet(HttpAction action, Boolean booleanResult) + { + doResponseResultSet$(action, null, booleanResult, null, DEF.rsOfferBoolean) ; + } + + public static void doResponseResultSet(HttpAction action, ResultSet resultSet, Prologue qPrologue) + { + doResponseResultSet$(action, resultSet, null, qPrologue, DEF.rsOfferTable) ; + } + + // If we refactor the conneg into a single function, we can split boolean and result set handling. + + // One or the other argument must be null + private static void doResponseResultSet$(HttpAction action, + ResultSet resultSet, Boolean booleanResult, + Prologue qPrologue, + AcceptList contentTypeOffer) + { + HttpServletRequest request = action.request ; + HttpServletResponse response = action.response ; + long id = action.id ; + + if ( resultSet == null && booleanResult == null ) + { + xlog.warn("doResponseResult: Both result set and boolean result are null") ; + throw new FusekiException("Both result set and boolean result are null") ; + } + + if ( resultSet != null && booleanResult != null ) + { + xlog.warn("doResponseResult: Both result set and boolean result are set") ; + throw new FusekiException("Both result set and boolean result are set") ; + } + + String mimeType = null ; + MediaType i = ConNeg.chooseContentType(request, contentTypeOffer, DEF.acceptRSXML) ; + if ( i != null ) + mimeType = i.getContentType() ; + + // Override content type + // Does &output= override? + // Requested output type by the web form or &output= in the request. + String outputField = ResponseOps.paramOutput(request, shortNamesResultSet) ; // Expands short names + if ( outputField != null ) + mimeType = outputField ; + + String serializationType = mimeType ; // Choose the serializer based on this. + String contentType = mimeType ; // Set the HTTP respose header to this. + + // Stylesheet - change to application/xml. + final String stylesheetURL = ResponseOps.paramStylesheet(request) ; + if ( stylesheetURL != null && equal(serializationType,contentTypeResultsXML) ) + contentType = contentTypeXML ; + + // Force to text/plain? + String forceAccept = ResponseOps.paramForceAccept(request) ; + if ( forceAccept != null ) + contentType = contentTypeTextPlain ; + + // Better : dispatch on MediaType + if ( equal(serializationType, contentTypeResultsXML) ) + sparqlXMLOutput(action, contentType, resultSet, stylesheetURL, booleanResult) ; + else if ( equal(serializationType, contentTypeResultsJSON) ) + jsonOutput(action, contentType, resultSet, booleanResult) ; + else if ( equal(serializationType, contentTypeTextPlain) ) + textOutput(action, contentType, resultSet, qPrologue, booleanResult) ; + else if ( equal(serializationType, contentTypeTextCSV) ) + csvOutput(action, contentType, resultSet, booleanResult) ; + else if (equal(serializationType, contentTypeTextTSV) ) + tsvOutput(action, contentType, resultSet, booleanResult) ; + else if (equal(serializationType, WebContent.contentTypeResultsThrift) ) + thriftOutput(action, contentType, resultSet, booleanResult) ; + else + ServletOps.errorBadRequest("Can't determine output serialization: "+serializationType) ; + } + + + public static void setHttpResponse(HttpAction action, +// HttpServletRequest httpRequest, +// HttpServletResponse httpResponse, + String contentType, String charset) + { + // ---- Set up HTTP Response + // Stop caching (not that ?queryString URLs are cached anyway) + if ( true ) + ServletOps.setNoCache(action) ; + // See: http://www.w3.org/International/O-HTTP-charset.html + if ( contentType != null ) + { + if ( charset != null && ! isXML(contentType) ) + contentType = contentType+"; charset="+charset ; + action.log.trace("Content-Type for response: "+contentType) ; + action.response.setContentType(contentType) ; + } + } + + private static boolean isXML(String contentType) + { + return contentType.equals(contentTypeRDFXML) + || contentType.equals(contentTypeResultsXML) + || contentType.equals(contentTypeXML) ; + } + + private static void sparqlXMLOutput(HttpAction action, String contentType, final ResultSet resultSet, final String stylesheetURL, final Boolean booleanResult) + { + OutputContent proc = + new OutputContent(){ + @Override + public void output(ServletOutputStream out) + { + if ( resultSet != null ) + ResultSetFormatter.outputAsXML(out, resultSet, stylesheetURL) ; + if ( booleanResult != null ) + ResultSetFormatter.outputAsXML(out, booleanResult.booleanValue(), stylesheetURL) ; + }} ; + output(action, contentType, null, proc) ; + } + + private static void jsonOutput(HttpAction action, String contentType, final ResultSet resultSet, final Boolean booleanResult) + { + OutputContent proc = new OutputContent(){ + @Override + public void output(ServletOutputStream out) + { + if ( resultSet != null ) + ResultSetFormatter.outputAsJSON(out, resultSet) ; + if ( booleanResult != null ) + ResultSetFormatter.outputAsJSON(out, booleanResult.booleanValue()) ; + } + } ; + + try { + String callback = ResponseOps.paramCallback(action.request) ; + ServletOutputStream out = action.response.getOutputStream() ; + + if ( callback != null ) + { + callback = callback.replace("\r", "") ; + callback = callback.replace("\n", "") ; + out.print(callback) ; + out.println("(") ; + } + + output(action, contentType, charsetUTF8, proc) ; + + if ( callback != null ) + out.println(")") ; + } catch (IOException ex) { IO.exception(ex) ; } + } + + private static void textOutput(HttpAction action, String contentType, final ResultSet resultSet, final Prologue qPrologue, final Boolean booleanResult) + { + // Text is not streaming. + OutputContent proc = new OutputContent(){ + @Override + public void output(ServletOutputStream out) + { + if ( resultSet != null ) + ResultSetFormatter.out(out, resultSet, qPrologue) ; + if ( booleanResult != null ) + ResultSetFormatter.out(out, booleanResult.booleanValue()) ; + } + }; + + output(action, contentType, charsetUTF8, proc) ; + } + + private static void csvOutput(HttpAction action, String contentType, final ResultSet resultSet, final Boolean booleanResult) { + OutputContent proc = new OutputContent(){ + @Override + public void output(ServletOutputStream out) + { + if ( resultSet != null ) + ResultSetFormatter.outputAsCSV(out, resultSet) ; + if ( booleanResult != null ) + ResultSetFormatter.outputAsCSV(out, booleanResult.booleanValue()) ; + } + } ; + output(action, contentType, charsetUTF8, proc) ; + } + + private static void tsvOutput(HttpAction action, String contentType, final ResultSet resultSet, final Boolean booleanResult) { + OutputContent proc = new OutputContent(){ + @Override + public void output(ServletOutputStream out) + { + if ( resultSet != null ) + ResultSetFormatter.outputAsTSV(out, resultSet) ; + if ( booleanResult != null ) + ResultSetFormatter.outputAsTSV(out, booleanResult.booleanValue()) ; + } + } ; + output(action, contentType, charsetUTF8, proc) ; + } + + private static void thriftOutput(HttpAction action, String contentType, final ResultSet resultSet, final Boolean booleanResult) { + OutputContent proc = new OutputContent(){ + @Override + public void output(ServletOutputStream out) + { + if ( resultSet != null ) + ResultSetMgr.write(out, resultSet, ResultSetLang.SPARQLResultSetThrift) ; + if ( booleanResult != null ) + xlog.error("Can't write boolen result in thrift") ; + } + } ; + output(action, contentType, WebContent.charsetUTF8, proc) ; + } + + private static void output(HttpAction action, String contentType, String charset, OutputContent proc) + { + try { + setHttpResponse(action, contentType, charset) ; + action.response.setStatus(HttpSC.OK_200) ; + ServletOutputStream out = action.response.getOutputStream() ; + try + { + proc.output(out) ; + out.flush() ; + } catch (QueryCancelledException ex) { + // Bother. Status code 200 already sent. + action.log.info(format("[%d] Query Cancelled - results truncated (but 200 already sent)", action.id)) ; + out.println() ; + out.println("## Query cancelled due to timeout during execution ##") ; + out.println("## **** Incomplete results **** ##") ; + out.flush() ; + // No point raising an exception - 200 was sent already. + //errorOccurred(ex) ; + } + // Includes client gone. + } catch (IOException ex) + { ServletOps.errorOccurred(ex) ; } + // Do not call httpResponse.flushBuffer(); here - Jetty closes the stream if it is a gzip stream + // then the JSON callback closing details can't be added. + } +} http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_GSP.java ---------------------------------------------------------------------- diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_GSP.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_GSP.java new file mode 100644 index 0000000..3f6a3b4 --- /dev/null +++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_GSP.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.servlets; + +import java.util.Enumeration ; + +import javax.servlet.http.HttpServletRequest ; + +import org.apache.jena.riot.web.HttpNames ; +import org.apache.jena.riot.system.IRIResolver ; + +import com.hp.hpl.jena.graph.Graph ; +import com.hp.hpl.jena.graph.Node ; +import com.hp.hpl.jena.graph.NodeFactory ; +import com.hp.hpl.jena.sparql.core.DatasetGraph ; + +public abstract class SPARQL_GSP extends ActionREST +{ + protected final static Target determineTarget(HttpAction action) { + // Delayed until inside a transaction. + if ( action.getActiveDSG() == null ) + ServletOps.errorOccurred("Internal error : No action graph (not in a transaction?)") ; + + boolean dftGraph = getOneOnly(action.request, HttpNames.paramGraphDefault) != null ; + String uri = getOneOnly(action.request, HttpNames.paramGraph) ; + + if ( !dftGraph && uri == null ) { + ServletOps.errorBadRequest("Neither default graph nor named graph specified; no direct name") ; + + // Direct naming or error. + uri = action.request.getRequestURL().toString() ; + if ( action.request.getRequestURI().equals(action.getDatasetName()) ) + // No name + ServletOps.errorBadRequest("Neither default graph nor named graph specified; no direct name") ; + } + + String dsTarget = action.getDatasetName() ; + + if ( dftGraph ) + return Target.createDefault(action.getActiveDSG()) ; + + // Named graph + if ( uri.equals(HttpNames.valueDefault ) ) + // But "named" default + return Target.createDefault(action.getActiveDSG()) ; + + // Strictly, a bit naughty on the URI resolution. But more sensible. + // Base is dataset. + + // XXX Remove any service. + + String base = action.request.getRequestURL().toString() ; //wholeRequestURL(request) ; + // Make sure it ends in "/", ie. dataset as container. + if ( action.request.getQueryString() != null && ! base.endsWith("/") ) + base = base + "/" ; + + String absUri = IRIResolver.resolveString(uri, base) ; + Node gn = NodeFactory.createURI(absUri) ; + return Target.createNamed(action.getActiveDSG(), absUri, gn) ; + } + + // struct for target + protected static final class Target + { + final boolean isDefault ; + final DatasetGraph dsg ; + private Graph _graph ; + final String name ; + final Node graphName ; + + static Target createNamed(DatasetGraph dsg, String name, Node graphName) { + return new Target(false, dsg, name, graphName) ; + } + + static Target createDefault(DatasetGraph dsg) { + return new Target(true, dsg, null, null) ; + } + + /** Create a new Target which is like the original but aimed at a different DatasetGraph */ + static Target retarget(Target target, DatasetGraph dsg) { + Target target2 = new Target(target, dsg) ; + target2._graph = null ; + return target2 ; + } + + private Target(boolean isDefault, DatasetGraph dsg, String name, Node graphName) { + this.isDefault = isDefault ; + this.dsg = dsg ; + this._graph = null ; + this.name = name ; + this.graphName = graphName ; + + if ( isDefault ) + { + if ( name != null || graphName != null ) + throw new IllegalArgumentException("Inconsistent: default and a graph name/node") ; + } + else + { + if ( name == null || graphName == null ) + throw new IllegalArgumentException("Inconsistent: not default and/or no graph name/node") ; + } + } + + private Target(Target other, DatasetGraph dsg) { + this.isDefault = other.isDefault ; + this.dsg = dsg ; //other.dsg ; + this._graph = other._graph ; + this.name = other.name ; + this.graphName = other.graphName ; + } + + /** Get a graph for the action - this may create a graph in the dataset - this is not a test for graph existence */ + public Graph graph() { + if ( ! isGraphSet() ) + { + if ( isDefault ) + _graph = dsg.getDefaultGraph() ; + else + _graph = dsg.getGraph(graphName) ; + } + return _graph ; + } + + public boolean exists() + { + if ( isDefault ) return true ; + return dsg.containsGraph(graphName) ; + } + + public boolean isGraphSet() + { + return _graph != null ; + } + + @Override +// protected static ErrorHandler errorHandler = ErrorHandlerFactory.errorHandlerStd(log) ; + + public String toString() + { + if ( isDefault ) return "default" ; + return name ; + } + } + + public SPARQL_GSP() + { super() ; } + + @Override + protected void validate(HttpAction action) + { + HttpServletRequest request = action.request ; + if ( request.getQueryString() == null ) { + //errorBadRequest("No query string") ; + return ; + } + + String g = request.getParameter(HttpNames.paramGraph) ; + String d = request.getParameter(HttpNames.paramGraphDefault) ; + + if ( g != null && d !=null ) + ServletOps.errorBadRequest("Both ?default and ?graph in the query string of the request") ; + + if ( g == null && d == null ) + ServletOps.errorBadRequest("Neither ?default nor ?graph in the query string of the request") ; + + int x1 = SPARQL_Protocol.countParamOccurences(request, HttpNames.paramGraph) ; + int x2 = SPARQL_Protocol.countParamOccurences(request, HttpNames.paramGraphDefault) ; + + if ( x1 > 1 ) + ServletOps.errorBadRequest("Multiple ?default in the query string of the request") ; + if ( x2 > 1 ) + ServletOps.errorBadRequest("Multiple ?graph in the query string of the request") ; + + Enumeration<String> en = request.getParameterNames() ; + for ( ; en.hasMoreElements() ; ) + { + String h = en.nextElement() ; + if ( ! HttpNames.paramGraph.equals(h) && ! HttpNames.paramGraphDefault.equals(h) ) + ServletOps.errorBadRequest("Unknown parameter '"+h+"'") ; + // one of ?default and &graph + if ( request.getParameterValues(h).length != 1 ) + ServletOps.errorBadRequest("Multiple parameters '"+h+"'") ; + } + } + + protected static String getOneOnly(HttpServletRequest request, String name) + { + String[] values = request.getParameterValues(name) ; + if ( values == null ) + return null ; + if ( values.length == 0 ) + return null ; + if ( values.length > 1 ) + ServletOps.errorBadRequest("Multiple occurrences of '"+name+"'") ; + return values[0] ; + } +} http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_GSP_R.java ---------------------------------------------------------------------- diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_GSP_R.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_GSP_R.java new file mode 100644 index 0000000..a706e57 --- /dev/null +++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_GSP_R.java @@ -0,0 +1,123 @@ +/* + * 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.servlets; + +import static java.lang.String.format ; + +import java.io.IOException ; + +import javax.servlet.ServletOutputStream ; + +import org.apache.jena.atlas.web.MediaType ; +import org.apache.jena.atlas.web.TypedOutputStream ; +import org.apache.jena.riot.web.HttpNames ; +import org.apache.jena.riot.* ; + +import com.hp.hpl.jena.graph.Graph ; + +/** Only the READ operations */ +public class SPARQL_GSP_R extends SPARQL_GSP +{ + public SPARQL_GSP_R() + { super() ; } + + @Override + protected String mapRequestToDataset(HttpAction action) { return ActionLib.mapRequestToDatasetLongest$(action.request.getRequestURI()) ; } + + @Override + protected void doGet(HttpAction action) { + // Assume success - do the set up before grabbing the lock. + // Sets content type. + MediaType mediaType = ActionLib.contentNegotationRDF(action) ; + + ServletOutputStream output ; + try { output = action.response.getOutputStream() ; } + catch (IOException ex) { ServletOps.errorOccurred(ex) ; output = null ; } + + TypedOutputStream out = new TypedOutputStream(output, mediaType) ; + Lang lang = RDFLanguages.contentTypeToLang(mediaType.getContentType()) ; + + if ( action.verbose ) + action.log.info(format("[%d] Get: Content-Type=%s, Charset=%s => %s", + action.id, mediaType.getContentType(), mediaType.getCharset(), lang.getName())) ; + + action.beginRead() ; + setCommonHeaders(action.response) ; + try { + Target target = determineTarget(action) ; + if ( action.log.isDebugEnabled() ) + action.log.debug("GET->"+target) ; + boolean exists = target.exists() ; + if ( ! exists ) + ServletOps.errorNotFound("No such graph: <"+target.name+">") ; + // If we want to set the Content-Length, we need to buffer. + //response.setContentLength(??) ; + String ct = lang.getContentType().toHeaderString() ; + action.response.setContentType(ct) ; + Graph g = target.graph() ; + //Special case RDF/XML to be the plain (faster, less readable) form + RDFFormat fmt = + ( lang == Lang.RDFXML ) ? RDFFormat.RDFXML_PLAIN : RDFWriterRegistry.defaultSerialization(lang) ; + RDFDataMgr.write(out, g, fmt) ; + ServletOps.success(action) ; + } finally { action.endRead() ; } + } + + @Override + protected void doOptions(HttpAction action) { + setCommonHeadersForOptions(action.response) ; + action.response.setHeader(HttpNames.hAllow, "GET,HEAD,OPTIONS") ; + action.response.setHeader(HttpNames.hContentLengh, "0") ; + ServletOps.success(action) ; + } + + @Override + protected void doHead(HttpAction action) { + action.beginRead() ; + setCommonHeaders(action.response) ; + try { + Target target = determineTarget(action) ; + if ( action.log.isDebugEnabled() ) + action.log.debug("HEAD->"+target) ; + if ( ! target.exists() ) + { + ServletOps.successNotFound(action) ; + return ; + } + MediaType mediaType = ActionLib.contentNegotationRDF(action) ; + ServletOps.success(action) ; + } finally { action.endRead() ; } + } + + @Override + protected void doPost(HttpAction action) + { ServletOps.errorMethodNotAllowed("POST") ; } + + @Override + protected void doDelete(HttpAction action) + { ServletOps.errorMethodNotAllowed("DELETE") ; } + + @Override + protected void doPut(HttpAction action) + { ServletOps.errorMethodNotAllowed("PUT") ; } + + @Override + protected void doPatch(HttpAction action) + { ServletOps.errorMethodNotAllowed("PATCH") ; } +} http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_GSP_RW.java ---------------------------------------------------------------------- diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_GSP_RW.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_GSP_RW.java new file mode 100644 index 0000000..d3c3179 --- /dev/null +++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_GSP_RW.java @@ -0,0 +1,208 @@ +/* + * 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.servlets; + +import static org.apache.jena.riot.WebContent.ctMultipartMixed ; +import static org.apache.jena.riot.WebContent.matchContentType ; + +import java.util.Map ; +import java.util.Map.Entry ; + +import org.apache.jena.atlas.web.ContentType ; +import org.apache.jena.atlas.web.MediaType ; +import org.apache.jena.fuseki.DEF ; +import org.apache.jena.fuseki.FusekiLib ; +import org.apache.jena.fuseki.conneg.ConNeg ; +import org.apache.jena.fuseki.servlets.UploadDetails.PreState ; +import org.apache.jena.riot.RiotException ; +import org.apache.jena.riot.system.StreamRDF ; +import org.apache.jena.riot.system.StreamRDFLib ; +import org.apache.jena.riot.web.HttpNames ; +import org.apache.jena.web.HttpSC ; + +import com.hp.hpl.jena.graph.Graph ; +import com.hp.hpl.jena.sparql.graph.GraphFactory ; + +/** The WRITE operations added to the READ operations */ +public class SPARQL_GSP_RW extends SPARQL_GSP_R +{ + public SPARQL_GSP_RW() + { super() ; } + + @Override + protected void doOptions(HttpAction action) { + setCommonHeadersForOptions(action.response) ; + action.response.setHeader(HttpNames.hAllow, "GET,HEAD,OPTIONS,PUT,DELETE,POST"); + action.response.setHeader(HttpNames.hContentLengh, "0") ; + ServletOps.success(action) ; + } + + @Override + protected void doDelete(HttpAction action) { + action.beginWrite() ; + try { + Target target = determineTarget(action) ; + if ( action.log.isDebugEnabled() ) + action.log.debug("DELETE->"+target) ; + boolean existedBefore = target.exists() ; + if ( ! existedBefore) + { + // commit, not abort, because locking "transactions" don't support abort. + action.commit() ; + ServletOps.errorNotFound("No such graph: "+target.name) ; + } + deleteGraph(action) ; + action.commit() ; + } + finally { action.endWrite() ; } + ServletOps.successNoContent(action) ; + } + + @Override + protected void doPut(HttpAction action) { doPutPost(action, true) ; } + + @Override + protected void doPost(HttpAction action) { doPutPost(action, false) ; } + + private void doPutPost(HttpAction action, boolean overwrite) { + ContentType ct = FusekiLib.getContentType(action) ; + if ( ct == null ) + ServletOps.errorBadRequest("No Content-Type:") ; + + if ( matchContentType(ctMultipartMixed, ct) ) { + ServletOps.error(HttpSC.UNSUPPORTED_MEDIA_TYPE_415, "multipart/mixed not supported") ; + } + + UploadDetails details ; + if ( action.isTransactional() ) + details = addDataIntoTxn(action, overwrite) ; + else + details = addDataIntoNonTxn(action, overwrite) ; + + MediaType mt = ConNeg.chooseCharset(action.request, DEF.jsonOffer, DEF.acceptJSON) ; + + if ( mt == null ) { + // No return body. + if ( details.getExistedBefore().equals(PreState.ABSENT) ) + ServletOps.successCreated(action) ; + else + ServletOps.successNoContent(action) ; + return ; + } + ServletOps.uploadResponse(action, details) ; + } + + /** Directly add data in a transaction. + * Assumes recovery from parse errors by transaction abort. + * Return whether the target existed before. + * @param action + * @param cleanDest Whether to remove data first (true = PUT, false = POST) + * @return whether the target existed beforehand + */ + protected static UploadDetails addDataIntoTxn(HttpAction action, boolean overwrite) { + action.beginWrite(); + Target target = determineTarget(action) ; + boolean existedBefore = false ; + try { + if ( action.log.isDebugEnabled() ) + action.log.debug(" ->"+target) ; + existedBefore = target.exists() ; + Graph g = target.graph() ; + if ( overwrite && existedBefore ) + clearGraph(target) ; + StreamRDF sink = StreamRDFLib.graph(g) ; + UploadDetails upload = Upload.incomingData(action, sink); + upload.setExistedBefore(existedBefore) ; + action.commit() ; + return upload ; + } catch (RiotException ex) { + // Parse error + action.abort() ; + ServletOps.errorBadRequest(ex.getMessage()) ; + return null ; + } catch (Exception ex) { + // Something else went wrong. Backout. + action.abort() ; + ServletOps.errorOccurred(ex.getMessage()) ; + return null ; + } finally { + action.endWrite() ; + } + } + + /** Add data where the destination does not support full transactions. + * In particular, with no abort, and actions probably going to the real storage + * parse errors can lead to partial updates. Instead, parse to a temporary + * graph, then insert that data. + * @param action + * @param cleanDest Whether to remove data first (true = PUT, false = POST) + * @return whether the target existed beforehand. + */ + + protected static UploadDetails addDataIntoNonTxn(HttpAction action, boolean overwrite) { + Graph graphTmp = GraphFactory.createGraphMem() ; + StreamRDF dest = StreamRDFLib.graph(graphTmp) ; + + UploadDetails details ; + try { details = Upload.incomingData(action, dest); } + catch (RiotException ex) { + ServletOps.errorBadRequest(ex.getMessage()) ; + return null ; + } + // Now insert into dataset + action.beginWrite() ; + Target target = determineTarget(action) ; + boolean existedBefore = false ; + try { + if ( action.log.isDebugEnabled() ) + action.log.debug(" ->"+target) ; + existedBefore = target.exists() ; + if ( overwrite && existedBefore ) + clearGraph(target) ; + FusekiLib.addDataInto(graphTmp, target.dsg, target.graphName) ; + details.setExistedBefore(existedBefore) ; + action.commit() ; + return details ; + } catch (Exception ex) { + // We parsed into a temporary graph so an exception at this point + // is not because of a parse error. + // We're in the non-transactional branch, this probably will not work + // but it might and there is no harm safely trying. + try { action.abort() ; } catch (Exception ex2) {} + ServletOps.errorOccurred(ex.getMessage()) ; + return null ; + } finally { action.endWrite() ; } + } + + protected static void deleteGraph(HttpAction action) { + Target target = determineTarget(action) ; + if ( target.isDefault ) + target.graph().clear() ; + else + action.getActiveDSG().removeGraph(target.graphName) ; + } + + protected static void clearGraph(Target target) { + Graph g = target.graph() ; + g.clear() ; + Map<String, String> pm = g.getPrefixMapping().getNsPrefixMap() ; + for ( Entry<String, String> e : pm.entrySet() ) + g.getPrefixMapping().removeNsPrefix(e.getKey()) ; + } +} http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Protocol.java ---------------------------------------------------------------------- diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Protocol.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Protocol.java new file mode 100644 index 0000000..87d234e --- /dev/null +++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Protocol.java @@ -0,0 +1,101 @@ +/** + * 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.servlets; + +import static org.apache.jena.riot.web.HttpNames.paramDefaultGraphURI ; +import static org.apache.jena.riot.web.HttpNames.paramNamedGraphURI ; + +import java.util.Arrays ; +import java.util.Collections ; +import java.util.List ; + +import javax.servlet.http.HttpServletRequest ; + +import org.apache.jena.atlas.iterator.Filter ; +import org.apache.jena.atlas.iterator.Iter ; +import org.apache.jena.atlas.lib.Lib ; + +import com.hp.hpl.jena.query.Query ; +import com.hp.hpl.jena.query.QueryParseException ; +import com.hp.hpl.jena.sparql.core.DatasetDescription ; + +/** Support for the SPARQL protocol (SPARQL Query, SPARQL Update) + */ +public abstract class SPARQL_Protocol extends ActionSPARQL +{ + protected SPARQL_Protocol() { super() ; } + + protected static String messageForQPE(QueryParseException ex) + { + if ( ex.getMessage() != null ) + return ex.getMessage() ; + if ( ex.getCause() != null ) + return Lib.classShortName(ex.getCause().getClass()) ; + return null ; + } + + protected static DatasetDescription getDatasetDescription(HttpAction action) + { + List<String> graphURLs = toStrList(action.request.getParameterValues(paramDefaultGraphURI)) ; + List<String> namedGraphs = toStrList(action.request.getParameterValues(paramNamedGraphURI)) ; + + graphURLs = removeEmptyValues(graphURLs) ; + namedGraphs = removeEmptyValues(namedGraphs) ; + + if ( graphURLs.size() == 0 && namedGraphs.size() == 0 ) + return null ; + return DatasetDescription.create(graphURLs, namedGraphs) ; + } + + protected static DatasetDescription getDatasetDescription(Query query) + { + return DatasetDescription.create(query) ; + } + + private static List<String> toStrList(String[] array) + { + if ( array == null ) + return Collections.emptyList() ; + return Arrays.asList(array) ; + } + + private static List<String> removeEmptyValues(List<String> list) + { + return Iter.iter(list).filter(acceptNonEmpty).toList() ; + } + + private static Filter<String> acceptNonEmpty = new Filter<String>(){ + @Override + public boolean accept(String item) + { + return item != null && item.length() != 0 ; + } + } ; + + protected static int countParamOccurences(HttpServletRequest request, String param) + { + String[] x = request.getParameterValues(param) ; + if ( x == null ) + return 0 ; + return x.length ; + } + + +} + http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Query.java ---------------------------------------------------------------------- diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Query.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Query.java new file mode 100644 index 0000000..6ab3f71 --- /dev/null +++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Query.java @@ -0,0 +1,393 @@ +/* + * 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.servlets ; + +import static java.lang.String.format ; +import static org.apache.jena.fuseki.server.CounterName.QueryTimeouts ; +import static org.apache.jena.riot.WebContent.ctHTMLForm ; +import static org.apache.jena.riot.WebContent.ctSPARQLQuery ; +import static org.apache.jena.riot.WebContent.isHtmlForm ; +import static org.apache.jena.riot.WebContent.matchContentType ; +import static org.apache.jena.riot.web.HttpNames.paramAccept ; +import static org.apache.jena.riot.web.HttpNames.paramCallback ; +import static org.apache.jena.riot.web.HttpNames.paramDefaultGraphURI ; +import static org.apache.jena.riot.web.HttpNames.paramForceAccept ; +import static org.apache.jena.riot.web.HttpNames.paramNamedGraphURI ; +import static org.apache.jena.riot.web.HttpNames.paramOutput1 ; +import static org.apache.jena.riot.web.HttpNames.paramOutput2 ; +import static org.apache.jena.riot.web.HttpNames.paramQuery ; +import static org.apache.jena.riot.web.HttpNames.paramQueryRef ; +import static org.apache.jena.riot.web.HttpNames.paramStyleSheet ; +import static org.apache.jena.riot.web.HttpNames.paramTimeout ; + +import java.io.IOException ; +import java.io.InputStream ; +import java.util.* ; + +import javax.servlet.http.HttpServletRequest ; +import javax.servlet.http.HttpServletResponse ; + +import org.apache.jena.atlas.io.IO ; +import org.apache.jena.atlas.io.IndentedLineBuffer ; +import org.apache.jena.atlas.web.ContentType ; +import org.apache.jena.fuseki.Fuseki ; +import org.apache.jena.fuseki.FusekiException ; +import org.apache.jena.fuseki.FusekiLib ; +import org.apache.jena.riot.web.HttpNames ; +import org.apache.jena.riot.web.HttpOp ; +import org.apache.jena.web.HttpSC ; + +import com.hp.hpl.jena.query.* ; +import com.hp.hpl.jena.rdf.model.Model ; +import com.hp.hpl.jena.sparql.core.Prologue ; +import com.hp.hpl.jena.sparql.resultset.SPARQLResult ; + +/** Handle SPARQL Query requests overt eh SPARQL Protocol. + * Subclasses provide this algorithm with the actual dataset to query, whether + * a dataset hosted by this server ({@link SPARQL_QueryDataset}) or + * speciifed in the protocol request ({@link SPARQL_QueryGeneral}). + */ +public abstract class SPARQL_Query extends SPARQL_Protocol +{ + private static final String QueryParseBase = Fuseki.BaseParserSPARQL ; + + public SPARQL_Query() { + super() ; + } + + // Choose REST verbs to support. + + @Override + protected void doPost(HttpServletRequest request, HttpServletResponse response) { + doCommon(request, response) ; + } + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) { + doCommon(request, response) ; + } + + // HEAD + + @Override + protected void doOptions(HttpServletRequest request, HttpServletResponse response) { + setCommonHeadersForOptions(response) ; + response.setHeader(HttpNames.hAllow, "GET,OPTIONS,POST") ; + response.setHeader(HttpNames.hContentLengh, "0") ; + } + + @Override + protected final void perform(HttpAction action) { + // GET + if ( action.request.getMethod().equals(HttpNames.METHOD_GET) ) { + executeWithParameter(action) ; + return ; + } + + ContentType ct = FusekiLib.getContentType(action) ; + String incoming = ct.getContentType() ; + + // POST application/x-www-form-url + if ( isHtmlForm(ct) ) { + executeWithParameter(action) ; + return ; + } + + // POST application/sparql-query + if ( matchContentType(ct, ctSPARQLQuery) ) { + executeBody(action) ; + return ; + } + + ServletOps.error(HttpSC.UNSUPPORTED_MEDIA_TYPE_415, "Bad content type: " + incoming) ; + } + + // All the params we support + + protected static List<String> allParams = Arrays.asList(paramQuery, paramDefaultGraphURI, paramNamedGraphURI, + paramQueryRef, paramStyleSheet, paramAccept, paramOutput1, + paramOutput2, paramCallback, paramForceAccept, paramTimeout) ; + + /** + * Validate the request, checking HTTP method and HTTP Parameters. + * @param action HTTP Action + */ + @Override + protected void validate(HttpAction action) { + String method = action.request.getMethod().toUpperCase(Locale.ROOT) ; + + if ( !HttpNames.METHOD_POST.equals(method) && !HttpNames.METHOD_GET.equals(method) ) + ServletOps.errorMethodNotAllowed("Not a GET or POST request") ; + + if ( HttpNames.METHOD_GET.equals(method) && action.request.getQueryString() == null ) { + ServletOps.warning(action, "Service Description / SPARQL Query / " + action.request.getRequestURI()) ; + ServletOps.errorNotFound("Service Description: " + action.request.getRequestURI()) ; + } + + // Use of the dataset describing parameters is check later. + try { + validateParams(action, allParams) ; + validateRequest(action) ; + } catch (ActionErrorException ex) { + throw ex ; + } + // Query not yet parsed. + } + + /** + * Validate the request after checking HTTP method and HTTP Parameters. + * @param action HTTP Action + */ + protected abstract void validateRequest(HttpAction action) ; + + /** + * Helper method for validating request. + * @param request HTTP request + * @param params parameters in a collection of Strings + */ + protected void validateParams(HttpAction action, Collection<String> params) { + HttpServletRequest request = action.request ; + ContentType ct = FusekiLib.getContentType(request) ; + boolean mustHaveQueryParam = true ; + if ( ct != null ) { + String incoming = ct.getContentType() ; + + if ( matchContentType(ctSPARQLQuery, ct) ) { + mustHaveQueryParam = false ; + } else if ( matchContentType(ctHTMLForm, ct)) + {} + else + ServletOps.error(HttpSC.UNSUPPORTED_MEDIA_TYPE_415, "Unsupported: " + incoming) ; + } + + // GET/POST of a form at this point. + + if ( mustHaveQueryParam ) { + int N = countParamOccurences(request, paramQuery) ; + + if ( N == 0 ) + ServletOps.errorBadRequest("SPARQL Query: No 'query=' parameter") ; + if ( N > 1 ) + ServletOps.errorBadRequest("SPARQL Query: Multiple 'query=' parameters") ; + + // application/sparql-query does not use a query param. + String queryStr = request.getParameter(HttpNames.paramQuery) ; + + if ( queryStr == null ) + ServletOps.errorBadRequest("SPARQL Query: No query specified (no 'query=' found)") ; + if ( queryStr.isEmpty() ) + ServletOps.errorBadRequest("SPARQL Query: Empty query string") ; + } + + if ( params != null ) { + Enumeration<String> en = request.getParameterNames() ; + for (; en.hasMoreElements();) { + String name = en.nextElement() ; + if ( !params.contains(name) ) + ServletOps.warning(action, "SPARQL Query: Unrecognize request parameter (ignored): " + name) ; + } + } + } + + private void executeWithParameter(HttpAction action) { + String queryString = action.request.getParameter(paramQuery) ; + execute(queryString, action) ; + } + + private void executeBody(HttpAction action) { + String queryString = null ; + try { + InputStream input = action.request.getInputStream() ; + queryString = IO.readWholeFileAsUTF8(input) ; + } catch (IOException ex) { + ServletOps.errorOccurred(ex) ; + } + execute(queryString, action) ; + } + + private void execute(String queryString, HttpAction action) { + String queryStringLog = ServletOps.formatForLog(queryString) ; + if ( action.verbose ) + action.log.info(format("[%d] Query = \n%s", action.id, queryString)) ; + else + action.log.info(format("[%d] Query = %s", action.id, queryStringLog)) ; + + Query query = null ; + try { + // NB syntax is ARQ (a superset of SPARQL) + query = QueryFactory.create(queryString, QueryParseBase, Syntax.syntaxARQ) ; + queryStringLog = formatForLog(query) ; + validateQuery(action, query) ; + } catch (ActionErrorException ex) { + throw ex ; + } catch (QueryParseException ex) { + ServletOps.errorBadRequest("Parse error: \n" + queryString + "\n\r" + messageForQPE(ex)) ; + } + // Should not happen. + catch (QueryException ex) { + ServletOps.errorBadRequest("Error: \n" + queryString + "\n\r" + ex.getMessage()) ; + } + + // Assumes finished whole thing by end of sendResult. + try { + action.beginRead() ; + Dataset dataset = decideDataset(action, query, queryStringLog) ; + try ( QueryExecution qExec = createQueryExecution(query, dataset) ; ) { + SPARQLResult result = executeQuery(action, qExec, query, queryStringLog) ; + // Deals with exceptions itself. + sendResults(action, result, query.getPrologue()) ; + } + } catch (QueryCancelledException ex) { + // Additional counter information. + incCounter(action.getEndpoint().getCounters(), QueryTimeouts) ; + throw ex ; + } finally { action.endRead() ; } + } + + /** + * Check the query - if unacceptable, throw ActionErrorException or call + * super.error + * @param action HTTP Action + * @param query SPARQL Query + */ + protected abstract void validateQuery(HttpAction action, Query query) ; + + /** Create the {@link QueryExecution} for this operation. + * @param query + * @param dataset + * @return QueryExecution + */ + protected QueryExecution createQueryExecution(Query query, Dataset dataset) { + return QueryExecutionFactory.create(query, dataset) ; + } + + /** Perform the {@link QueryExecution} once. + * @param action + * @param queryExecution + * @param query + * @param queryStringLog Informational string created from the initial query. + * @return + */ + protected SPARQLResult executeQuery(HttpAction action, QueryExecution queryExecution, Query query, String queryStringLog) { + setAnyTimeouts(queryExecution, action) ; + + if ( query.isSelectType() ) { + ResultSet rs = queryExecution.execSelect() ; + + // Force some query execution now. + // + // If the timeout-first-row goes off, the output stream has not + // been started so the HTTP error code is sent. + + rs.hasNext() ; + + // If we wanted perfect query time cancellation, we could consume + // the result now + // to see if the timeout-end-of-query goes off. + + // rs = ResultSetFactory.copyResults(rs) ; + + action.log.info(format("[%d] exec/select", action.id)) ; + return new SPARQLResult(rs) ; + } + + if ( query.isConstructType() ) { + Model model = queryExecution.execConstruct() ; + action.log.info(format("[%d] exec/construct", action.id)) ; + return new SPARQLResult(model) ; + } + + if ( query.isDescribeType() ) { + Model model = queryExecution.execDescribe() ; + action.log.info(format("[%d] exec/describe", action.id)) ; + return new SPARQLResult(model) ; + } + + if ( query.isAskType() ) { + boolean b = queryExecution.execAsk() ; + action.log.info(format("[%d] exec/ask", action.id)) ; + return new SPARQLResult(b) ; + } + + ServletOps.errorBadRequest("Unknown query type - " + queryStringLog) ; + return null ; + } + + private void setAnyTimeouts(QueryExecution qexec, HttpAction action) { +// if ( !(action.getDataService().allowTimeoutOverride) ) +// return ; + + long desiredTimeout = Long.MAX_VALUE ; + String timeoutHeader = action.request.getHeader("Timeout") ; + String timeoutParameter = action.request.getParameter("timeout") ; + if ( timeoutHeader != null ) { + try { + desiredTimeout = (int)(Float.parseFloat(timeoutHeader) * 1000) ; + } catch (NumberFormatException e) { + throw new FusekiException("Timeout header must be a number", e) ; + } + } else if ( timeoutParameter != null ) { + try { + desiredTimeout = (int)(Float.parseFloat(timeoutParameter) * 1000) ; + } catch (NumberFormatException e) { + throw new FusekiException("timeout parameter must be a number", e) ; + } + } + +// desiredTimeout = Math.min(action.getDataService().maximumTimeoutOverride, desiredTimeout) ; + if ( desiredTimeout != Long.MAX_VALUE ) + qexec.setTimeout(desiredTimeout) ; + } + + /** Choose the dataset for this SPARQL Query request. + * @param action + * @param query + * @param queryStringLog + * @return {@link Dataset} + */ + protected abstract Dataset decideDataset(HttpAction action, Query query, String queryStringLog) ; + + /** Ship the results to the remote caller. + * @param action + * @param result + * @param qPrologue + */ + protected void sendResults(HttpAction action, SPARQLResult result, Prologue qPrologue) { + if ( result.isResultSet() ) + ResponseResultSet.doResponseResultSet(action, result.getResultSet(), qPrologue) ; + else if ( result.isGraph() ) + ResponseModel.doResponseModel(action, result.getModel()) ; + else if ( result.isBoolean() ) + ResponseResultSet.doResponseResultSet(action, result.getBooleanResult()) ; + else + ServletOps.errorOccurred("Unknown or invalid result type") ; + } + + private String formatForLog(Query query) { + IndentedLineBuffer out = new IndentedLineBuffer() ; + out.setFlatMode(true) ; + query.serialize(out) ; + return out.asString() ; + } + + private String getRemoteString(String queryURI) { + return HttpOp.execHttpGetString(queryURI) ; + } + +} http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_QueryDataset.java ---------------------------------------------------------------------- diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_QueryDataset.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_QueryDataset.java new file mode 100644 index 0000000..9e9df36 --- /dev/null +++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_QueryDataset.java @@ -0,0 +1,60 @@ +/* + * 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.servlets; + +import com.hp.hpl.jena.query.Dataset ; +import com.hp.hpl.jena.query.DatasetFactory ; +import com.hp.hpl.jena.query.Query ; +import com.hp.hpl.jena.sparql.core.DatasetDescription ; +import com.hp.hpl.jena.sparql.core.DatasetGraph ; +import com.hp.hpl.jena.sparql.core.DynamicDatasets ; + +public class SPARQL_QueryDataset extends SPARQL_Query +{ + public SPARQL_QueryDataset(boolean verbose) { super() ; } + + public SPARQL_QueryDataset() + { this(false) ; } + + @Override + protected void validateRequest(HttpAction action) + { } + + @Override + protected void validateQuery(HttpAction action, Query query) + { } + + @Override + protected Dataset decideDataset(HttpAction action, Query query, String queryStringLog) + { + DatasetGraph dsg = action.getActiveDSG() ; + + // query.getDatasetDescription() ; + + // Protocol. + DatasetDescription dsDesc = getDatasetDescription(action) ; + if (dsDesc != null ) + { + //errorBadRequest("SPARQL Query: Dataset description in the protocol request") ; + dsg = DynamicDatasets.dynamicDataset(dsDesc, dsg, false) ; + } + + return DatasetFactory.create(dsg) ; + } +}
