[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