[Mulgara-svn] r1933 - in trunk/src/jar/querylang/java/org/mulgara/protocol: . http
pag at mulgara.org
pag at mulgara.org
Wed Apr 7 18:34:04 UTC 2010
Author: pag
Date: 2010-04-07 11:34:03 -0700 (Wed, 07 Apr 2010)
New Revision: 1933
Modified:
trunk/src/jar/querylang/java/org/mulgara/protocol/AbstractStreamedAnswer.java
trunk/src/jar/querylang/java/org/mulgara/protocol/AbstractStreamedXMLAnswer.java
trunk/src/jar/querylang/java/org/mulgara/protocol/StreamedAnswer.java
trunk/src/jar/querylang/java/org/mulgara/protocol/StreamedN3Answer.java
trunk/src/jar/querylang/java/org/mulgara/protocol/StreamedRdfXmlAnswer.java
trunk/src/jar/querylang/java/org/mulgara/protocol/StreamedSparqlJSONAnswer.java
trunk/src/jar/querylang/java/org/mulgara/protocol/StreamedSparqlJSONObject.java
trunk/src/jar/querylang/java/org/mulgara/protocol/StreamedSparqlXMLAnswer.java
trunk/src/jar/querylang/java/org/mulgara/protocol/StreamedSparqlXMLObject.java
trunk/src/jar/querylang/java/org/mulgara/protocol/StreamedTqlXMLAnswer.java
trunk/src/jar/querylang/java/org/mulgara/protocol/http/ProtocolServlet.java
trunk/src/jar/querylang/java/org/mulgara/protocol/http/TqlServlet.java
Log:
Added the option to emit multiple answers in a single TQL response. This meant opening up XML streaming to allow the parts to be emitted manually, with one header, multiple answers and a single closing.
Modified: trunk/src/jar/querylang/java/org/mulgara/protocol/AbstractStreamedAnswer.java
===================================================================
--- trunk/src/jar/querylang/java/org/mulgara/protocol/AbstractStreamedAnswer.java 2010-03-24 19:07:10 UTC (rev 1932)
+++ trunk/src/jar/querylang/java/org/mulgara/protocol/AbstractStreamedAnswer.java 2010-04-07 18:34:03 UTC (rev 1933)
@@ -161,4 +161,21 @@
addDocFooter();
}
+ /**
+ * Frees resources.
+ * @throws IOException
+ */
+ public void close() throws IOException {
+ s.flush();
+ output.close();
+ }
+
+ /**
+ * Not to be called for when doing an "emit". Only used when more manual control is needed
+ * due to streaming multiple answers.
+ */
+ public void initOutput() {
+ if (s == null) s = new OutputStreamWriter(output, charset);
+ }
+
}
Modified: trunk/src/jar/querylang/java/org/mulgara/protocol/AbstractStreamedXMLAnswer.java
===================================================================
--- trunk/src/jar/querylang/java/org/mulgara/protocol/AbstractStreamedXMLAnswer.java 2010-03-24 19:07:10 UTC (rev 1932)
+++ trunk/src/jar/querylang/java/org/mulgara/protocol/AbstractStreamedXMLAnswer.java 2010-04-07 18:34:03 UTC (rev 1933)
@@ -154,13 +154,13 @@
* Adds the document header to the buffer.
*/
@Override
- protected abstract void addDocHeader() throws IOException;
+ public abstract void addDocHeader() throws IOException;
/**
* Adds the document footer to the buffer.
*/
@Override
- protected abstract void addDocFooter() throws IOException;
+ public abstract void addDocFooter() throws IOException;
/**
* Adds the header to the document data.
Modified: trunk/src/jar/querylang/java/org/mulgara/protocol/StreamedAnswer.java
===================================================================
--- trunk/src/jar/querylang/java/org/mulgara/protocol/StreamedAnswer.java 2010-03-24 19:07:10 UTC (rev 1932)
+++ trunk/src/jar/querylang/java/org/mulgara/protocol/StreamedAnswer.java 2010-04-07 18:34:03 UTC (rev 1933)
@@ -18,6 +18,7 @@
import java.io.IOException;
+import org.mulgara.query.Answer;
import org.mulgara.query.TuplesException;
/**
@@ -35,4 +36,17 @@
*/
public void emit() throws TuplesException, IOException;
+ public void initOutput();
+
+ /**
+ * Closes all resources.
+ * @throws IOException If there was an error closing any resources.
+ */
+ public void close() throws IOException;
+
+ public void addDocHeader() throws IOException;
+
+ public void addAnswer(Answer data) throws TuplesException, IOException;
+
+ public void addDocFooter() throws IOException;
}
Modified: trunk/src/jar/querylang/java/org/mulgara/protocol/StreamedN3Answer.java
===================================================================
--- trunk/src/jar/querylang/java/org/mulgara/protocol/StreamedN3Answer.java 2010-03-24 19:07:10 UTC (rev 1932)
+++ trunk/src/jar/querylang/java/org/mulgara/protocol/StreamedN3Answer.java 2010-04-07 18:34:03 UTC (rev 1933)
@@ -141,4 +141,25 @@
}
}
+
+
+ public void addAnswer(Answer data) throws TuplesException, IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void addDocFooter() throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void addDocHeader() throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void close() throws IOException {
+ p.close();
+ }
+
+ public void initOutput() {
+ // do nothing
+ }
}
Modified: trunk/src/jar/querylang/java/org/mulgara/protocol/StreamedRdfXmlAnswer.java
===================================================================
--- trunk/src/jar/querylang/java/org/mulgara/protocol/StreamedRdfXmlAnswer.java 2010-03-24 19:07:10 UTC (rev 1932)
+++ trunk/src/jar/querylang/java/org/mulgara/protocol/StreamedRdfXmlAnswer.java 2010-04-07 18:34:03 UTC (rev 1933)
@@ -63,4 +63,24 @@
}
}
+ public void addAnswer(Answer data) throws TuplesException, IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void addDocFooter() throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void addDocHeader() throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void close() throws IOException {
+ out.close();
+ }
+
+ public void initOutput() {
+ // do nothing
+ }
+
}
Modified: trunk/src/jar/querylang/java/org/mulgara/protocol/StreamedSparqlJSONAnswer.java
===================================================================
--- trunk/src/jar/querylang/java/org/mulgara/protocol/StreamedSparqlJSONAnswer.java 2010-03-24 19:07:10 UTC (rev 1932)
+++ trunk/src/jar/querylang/java/org/mulgara/protocol/StreamedSparqlJSONAnswer.java 2010-04-07 18:34:03 UTC (rev 1933)
@@ -139,12 +139,12 @@
}
/** {@inheritDoc} */
- protected void addDocHeader() throws IOException {
+ public void addDocHeader() throws IOException {
s.append("{ ");
}
/** {@inheritDoc} */
- protected void addDocFooter() throws IOException {
+ public void addDocFooter() throws IOException {
s.append(" }");
}
@@ -268,4 +268,9 @@
prependComma = true;
return s;
}
+
+ public void addAnswer(Answer data) throws TuplesException, IOException {
+ addResults(data);
+ }
+
}
Modified: trunk/src/jar/querylang/java/org/mulgara/protocol/StreamedSparqlJSONObject.java
===================================================================
--- trunk/src/jar/querylang/java/org/mulgara/protocol/StreamedSparqlJSONObject.java 2010-03-24 19:07:10 UTC (rev 1932)
+++ trunk/src/jar/querylang/java/org/mulgara/protocol/StreamedSparqlJSONObject.java 2010-04-07 18:34:03 UTC (rev 1933)
@@ -21,7 +21,10 @@
import java.io.OutputStreamWriter;
import java.util.Map;
+import org.mulgara.query.Answer;
+import org.mulgara.query.TuplesException;
+
/**
* Represents a data object as JSON.
*
@@ -97,4 +100,29 @@
data = data.replace("\t", "\\t");
return "\"" + data + "\"";
}
+
+ public void addAnswer(Answer data) throws TuplesException, IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void addDocFooter() throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void addDocHeader() throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void close() throws IOException {
+ s.flush();
+ output.close();
+ }
+
+ /**
+ * Not to be called for when doing an "emit". Only used when more manual control is needed
+ * due to streaming multiple answers.
+ */
+ public void initOutput() {
+ if (s == null) s = new OutputStreamWriter(output);
+ }
}
Modified: trunk/src/jar/querylang/java/org/mulgara/protocol/StreamedSparqlXMLAnswer.java
===================================================================
--- trunk/src/jar/querylang/java/org/mulgara/protocol/StreamedSparqlXMLAnswer.java 2010-03-24 19:07:10 UTC (rev 1932)
+++ trunk/src/jar/querylang/java/org/mulgara/protocol/StreamedSparqlXMLAnswer.java 2010-04-07 18:34:03 UTC (rev 1933)
@@ -101,7 +101,7 @@
}
/** {@inheritDoc} */
- protected void addDocHeader() throws IOException {
+ public void addDocHeader() throws IOException {
s.append("<?xml version=\"1.0\" encoding=\"");
s.append(charset.name());
s.append("\"?>\n");
@@ -115,7 +115,7 @@
}
/** {@inheritDoc} */
- protected void addDocFooter() throws IOException {
+ public void addDocFooter() throws IOException {
s.append(i(0)).append("</sparql>");
}
@@ -190,4 +190,11 @@
s.append(">").append(StringUtil.quoteAV(literal.getLexicalForm())).append("</literal>");
}
+ /**
+ * @see org.mulgara.protocol.StreamedAnswer#addAnswer(org.mulgara.query.Answer)
+ */
+ public void addAnswer(Answer data) throws TuplesException, IOException {
+ addResults(data);
+ }
+
}
Modified: trunk/src/jar/querylang/java/org/mulgara/protocol/StreamedSparqlXMLObject.java
===================================================================
--- trunk/src/jar/querylang/java/org/mulgara/protocol/StreamedSparqlXMLObject.java 2010-03-24 19:07:10 UTC (rev 1932)
+++ trunk/src/jar/querylang/java/org/mulgara/protocol/StreamedSparqlXMLObject.java 2010-04-07 18:34:03 UTC (rev 1933)
@@ -23,6 +23,8 @@
import java.nio.charset.Charset;
import java.util.Map;
+import org.mulgara.query.Answer;
+import org.mulgara.query.TuplesException;
import org.mulgara.util.StringUtil;
@@ -74,14 +76,14 @@
}
/** {@inheritDoc} */
- protected void addDocHeader() throws IOException {
+ public void addDocHeader() throws IOException {
s.append("<?xml version=\"1.0\"?>\n");
s.append("<sparql xmlns=\"http://www.w3.org/2005/sparql-results#\">");
if (prettyPrint) s.append("\n");
}
/** {@inheritDoc} */
- protected void addDocFooter() throws IOException {
+ public void addDocFooter() throws IOException {
s.append("</sparql>");
}
@@ -103,7 +105,7 @@
* @see org.mulgara.protocol.StreamedXMLAnswer#emit()
*/
public void emit() throws IOException {
- s = new OutputStreamWriter(output);
+ s = new OutputStreamWriter(output, charset);
addDocHeader();
addResults();
addDocFooter();
@@ -143,4 +145,21 @@
if (prettyPrint) s.append("\n").append(indent);
return s.toString();
}
+
+ public void addAnswer(Answer data) throws TuplesException, IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ public void close() throws IOException {
+ output.close();
+ }
+
+ /**
+ * Not to be called for when doing an "emit". Only used when more manual control is needed
+ * due to streaming multiple answers.
+ */
+ public void initOutput() {
+ if (s == null) s = new OutputStreamWriter(output, charset);
+ }
+
}
Modified: trunk/src/jar/querylang/java/org/mulgara/protocol/StreamedTqlXMLAnswer.java
===================================================================
--- trunk/src/jar/querylang/java/org/mulgara/protocol/StreamedTqlXMLAnswer.java 2010-03-24 19:07:10 UTC (rev 1932)
+++ trunk/src/jar/querylang/java/org/mulgara/protocol/StreamedTqlXMLAnswer.java 2010-04-07 18:34:03 UTC (rev 1933)
@@ -55,13 +55,13 @@
}
/** {@inheritDoc} */
- protected void addDocHeader() throws IOException {
+ public void addDocHeader() throws IOException {
s.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
s.append("<answer xmlns=\"http://mulgara.org/tql#\">");
}
/** {@inheritDoc} */
- protected void addDocFooter() throws IOException {
+ public void addDocFooter() throws IOException {
s.append(i(0)).append("</answer>");
}
@@ -175,4 +175,12 @@
s.append(">").append(literal.getLexicalForm());
}
+ /**
+ * @see org.mulgara.protocol.StreamedAnswer#addAnswer(org.mulgara.query.Answer)
+ */
+ public void addAnswer(Answer data) throws TuplesException, IOException {
+ addHeader(data);
+ addResults(data);
+ }
+
}
Modified: trunk/src/jar/querylang/java/org/mulgara/protocol/http/ProtocolServlet.java
===================================================================
--- trunk/src/jar/querylang/java/org/mulgara/protocol/http/ProtocolServlet.java 2010-03-24 19:07:10 UTC (rev 1932)
+++ trunk/src/jar/querylang/java/org/mulgara/protocol/http/ProtocolServlet.java 2010-04-07 18:34:03 UTC (rev 1933)
@@ -150,7 +150,7 @@
private static final String[] OBJECT_PARAM_NAMES = { "object", "obj", "o" };
/** A query to get the entire contents of a graph */
- private static final String CONSTRUCT_ALL_QUERY = "CONSTRUCT { ?s ?p ?o } WHERE { ?s ?p ?o }";
+ protected static final String CONSTRUCT_ALL_QUERY = "CONSTRUCT { ?s ?p ?o } WHERE { ?s ?p ?o }";
/** This object maps request types to the constructors for that output. */
protected final Map<Output,AnswerStreamConstructor> streamBuilders = new EnumMap<Output,AnswerStreamConstructor>(Output.class);
@@ -187,34 +187,11 @@
* @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
- Answer result = null;
try {
RestParams params = new RestParams(req);
- RestParams.ResourceType type = params.getType();
-
- // build a query based on either the resource being requested, or an explicit query
- Query query = null;
- if (type == RestParams.ResourceType.QUERY) {
- query = getQuery(params.getQuery(), req);
- } else if (type == RestParams.ResourceType.GRAPH) {
- query = getQuery(CONSTRUCT_ALL_QUERY, req);
- } else if (type == RestParams.ResourceType.STATEMENT) {
- query = getQuery(createAskQuery(params.getTriple()), req);
- }
-
- result = executeQuery(query, req);
-
- Output outputType = getOutputType(req, query);
- sendAnswer(result, outputType, resp);
-
+ doResponse(req, resp, params);
} catch (ServletException e) {
e.sendResponseTo(resp);
- } finally {
- try {
- if (result != null) result.close();
- } catch (TuplesException e) {
- logger.warn("Error closing: " + e.getMessage(), e);
- }
}
}
@@ -276,7 +253,45 @@
*/
public abstract String getServletInfo();
+ /**
+ * Respond to a parameterized request. The parameters have already been extracted.
+ * @param req The initial HTTP request.
+ * @param resp The response object to send data on.
+ * @param params The parameters already extracted from the request.
+ * @throws ServletException If there was an error in the servlet.
+ * @throws IOException If there is an error responding to the requesting client.
+ */
+ protected void doResponse(HttpServletRequest req, HttpServletResponse resp, RestParams params) throws ServletException, IOException {
+ Answer result = null;
+ try {
+ RestParams.ResourceType type = params.getType();
+ // build a query based on either the resource being requested, or an explicit query
+ Query query = null;
+ if (type == RestParams.ResourceType.QUERY) {
+ query = getQuery(params.getQuery(), req);
+ } else if (type == RestParams.ResourceType.GRAPH) {
+ query = getQuery(CONSTRUCT_ALL_QUERY, req);
+ } else if (type == RestParams.ResourceType.STATEMENT) {
+ query = getQuery(createAskQuery(params.getTriple()), req);
+ } else {
+ throw new InternalErrorException("Unknown request type");
+ }
+
+ result = executeQuery(query, req);
+
+ Output outputType = getOutputType(req, query);
+ sendAnswer(result, outputType, resp);
+
+ } finally {
+ try {
+ if (result != null) result.close();
+ } catch (TuplesException e) {
+ logger.warn("Error closing: " + e.getMessage(), e);
+ }
+ }
+ }
+
/**
* Converts a SPARQL query string into a Query object. This uses extra parameters from the
* client where appropriate, such as the default graph.
@@ -712,7 +727,7 @@
* @param triple The triple to ask the existence of.
* @return A string containing an ASK query.
*/
- private String createAskQuery(LocalTriple triple) {
+ String createAskQuery(LocalTriple triple) {
StringBuffer query = new StringBuffer("ASK ?s ?p ?o { ?s ?p ?o ");
query.append(". ?s <http://mulgara.org/is> <").append(triple.getSubject()).append(">");
query.append(". ?p <http://mulgara.org/is> <").append(triple.getPredicate()).append(">");
@@ -794,7 +809,7 @@
* @param req The request object for the servlet connection.
* @return xml, json, rdfXml or rdfN3.
*/
- private Output getOutputType(HttpServletRequest req, Command cmd) {
+ protected Output getOutputType(HttpServletRequest req, Command cmd) {
Output type = DEFAULT_OUTPUT_TYPE;
// get the accepted types
Modified: trunk/src/jar/querylang/java/org/mulgara/protocol/http/TqlServlet.java
===================================================================
--- trunk/src/jar/querylang/java/org/mulgara/protocol/http/TqlServlet.java 2010-03-24 19:07:10 UTC (rev 1932)
+++ trunk/src/jar/querylang/java/org/mulgara/protocol/http/TqlServlet.java 2010-04-07 18:34:03 UTC (rev 1933)
@@ -18,17 +18,25 @@
import java.io.IOException;
import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.mulgara.itql.TqlInterpreter;
+import org.mulgara.parser.Interpreter;
import org.mulgara.protocol.StreamedAnswer;
import org.mulgara.protocol.StreamedSparqlJSONAnswer;
import org.mulgara.protocol.StreamedSparqlJSONObject;
import org.mulgara.protocol.StreamedSparqlXMLObject;
import org.mulgara.protocol.StreamedTqlXMLAnswer;
import org.mulgara.query.Answer;
+import org.mulgara.query.Query;
+import org.mulgara.query.TuplesException;
+import org.mulgara.query.operation.Command;
import org.mulgara.server.SessionFactoryProvider;
/**
@@ -112,4 +120,110 @@
return interpreter;
}
+ /**
+ * Respond to a request for the servlet.
+ * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+ */
+ protected void doResponse(HttpServletRequest req, HttpServletResponse resp, RestParams params) throws ServletException, IOException {
+ RestParams.ResourceType type = params.getType();
+
+ if (type != RestParams.ResourceType.QUERY) {
+
+ // This is a GET on a graph or a statement
+ super.doResponse(req, resp, params);
+
+ } else {
+
+ // build a list of queries
+ List<Query> queries = getQueries(params.getQuery(), req);
+ boolean firstItem = true;
+ Output outputType = null;
+ StreamedAnswer output = null;
+ // respond to everything in the list
+ for (Query q: queries) {
+ Answer result = null;
+ try {
+ result = executeQuery(q, req);
+
+ if (firstItem) {
+ firstItem = false;
+ outputType = getOutputType(req, q);
+ // start the response
+ output = sendDocumentHeader(streamBuilders, outputType, resp);
+ } else {
+ assert outputType != null;
+ if (outputType != getOutputType(req, q)) {
+ throw new BadRequestException("Incompatible mMixed response types requested");
+ }
+ }
+ assert output != null;
+ output.addAnswer(result);
+ } catch (TuplesException e) {
+ throw new InternalErrorException("Error reading answers: " + e.getMessage());
+ } finally {
+ try {
+ if (result != null) result.close();
+ } catch (TuplesException e) {
+ logger.warn("Error closing: " + e.getMessage(), e);
+ }
+ }
+ }
+ output.addDocFooter();
+ output.close();
+ }
+ }
+
+
+ /**
+ * Converts a SPARQL query string into a Query object. This uses extra parameters from the
+ * client where appropriate, such as the default graph.
+ * @param query The query string issued by the client.
+ * @param req The request from the client.
+ * @return A new Query object, built from the query string.
+ * @throws BadRequestException Due to an invalid command string.
+ */
+ List<Query> getQueries(String query, HttpServletRequest req) throws BadRequestException {
+ if (query == null) throw new BadRequestException("Query must be supplied");
+ try {
+ Interpreter interpreter = getInterpreter(req);
+ List<Command> cmdList = interpreter.parseCommands(query);
+ List<Query> qList = new ArrayList<Query>();
+ for (Command c: cmdList) {
+ if (!(c instanceof Query)) throw new BadRequestException("Modifying command used instead of a query: " + c.getText());
+ qList.add((Query)c);
+ }
+ return qList;
+ } catch (Exception e) {
+ throw new BadRequestException(e.getMessage());
+ }
+ }
+
+
+ StreamedAnswer sendDocumentHeader(Map<Output,? extends StreamConstructor<Answer>> builders, Output type, HttpServletResponse resp) throws IOException, BadRequestException, InternalErrorException {
+ // establish the output type
+ if (type == null) type = DEFAULT_OUTPUT_TYPE;
+
+ resp.setContentType(type.mimeText);
+ resp.setHeader("pragma", "no-cache");
+
+ // get the constructor for the stream outputter
+ StreamConstructor<Answer> constructor = builders.get(type);
+ if (constructor == null) throw new BadRequestException("Unknown result type: " + type);
+
+ try {
+ OutputStream out = resp.getOutputStream();
+ StreamedAnswer strAns = constructor.fn(null, out);
+ strAns.initOutput();
+ strAns.addDocHeader();
+ return strAns;
+ } catch (IOException ioe) {
+ // There's no point in telling the client if we can't talk to the client
+ throw ioe;
+ } catch (Exception e) {
+ throw new InternalErrorException(e.getMessage());
+ }
+ }
+
+
+
}
More information about the Mulgara-svn
mailing list