[Mulgara-svn] r1258 - in trunk/src/jar: querylang/java/org/mulgara/protocol querylang/java/org/mulgara/protocol/http server/java/org/mulgara/server

pag at mulgara.org pag at mulgara.org
Tue Sep 9 06:07:52 UTC 2008


Author: pag
Date: 2008-09-08 23:07:51 -0700 (Mon, 08 Sep 2008)
New Revision: 1258

Added:
   trunk/src/jar/querylang/java/org/mulgara/protocol/StreamedSparqlJSONObject.java
   trunk/src/jar/querylang/java/org/mulgara/protocol/StreamedSparqlXMLObject.java
   trunk/src/jar/querylang/java/org/mulgara/protocol/http/
   trunk/src/jar/querylang/java/org/mulgara/protocol/http/BadRequestException.java
   trunk/src/jar/querylang/java/org/mulgara/protocol/http/InternalErrorException.java
   trunk/src/jar/querylang/java/org/mulgara/protocol/http/ServiceUnavailableException.java
   trunk/src/jar/querylang/java/org/mulgara/protocol/http/ServletException.java
   trunk/src/jar/querylang/java/org/mulgara/protocol/http/SparqlServlet.java
Modified:
   trunk/src/jar/server/java/org/mulgara/server/HttpServices.java
Log:
Added a new servlet to execute SPARQL queries, and return the results in XML or JSON

Added: trunk/src/jar/querylang/java/org/mulgara/protocol/StreamedSparqlJSONObject.java
===================================================================
--- trunk/src/jar/querylang/java/org/mulgara/protocol/StreamedSparqlJSONObject.java	                        (rev 0)
+++ trunk/src/jar/querylang/java/org/mulgara/protocol/StreamedSparqlJSONObject.java	2008-09-09 06:07:51 UTC (rev 1258)
@@ -0,0 +1,75 @@
+/*
+ * The contents of this file are subject to the Open Software License
+ * Version 3.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.opensource.org/licenses/osl-3.0.txt
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ */
+
+package org.mulgara.protocol;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+
+
+/**
+ * Represents a data object as JSON.
+ *
+ * @created Jul 8, 2008
+ * @author Paul Gearon
+ * @copyright &copy; 2008 <a href="http://www.topazproject.org/">The Topaz Project</a>
+ * @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a>
+ */
+public class StreamedSparqlJSONObject implements StreamedAnswer {
+
+  /** The encoded data. */
+  final Object objectData;
+
+  /** The writer used for creating the XML. */
+  protected OutputStreamWriter s = null;
+
+  /** The byte output stream used for creating the XML. */
+  protected OutputStream output = null;
+
+  /**
+   * Creates an XML object encoding.
+   * @param objectData The data to encode.
+   * @param output Where to send the output.
+   */
+  public StreamedSparqlJSONObject(Object objectData, OutputStream output) {
+    this.objectData = objectData;
+    this.output = output;
+  }
+
+  /**
+   * Put the parts of the document together, and close the stream.
+   * @see org.mulgara.protocol.StreamedXMLAnswer#emit()
+   */
+  public void emit() throws IOException {
+    s = new OutputStreamWriter(output);
+    s.append("{ \"data\": ");
+    s.append(jsonEscape(objectData));
+    s.append(" }");
+    s.flush();
+  }
+
+  /** Trivial escaping. */
+  public static String jsonEscape(Object o) {
+    if (o instanceof Number) return o.toString();
+    String data = o.toString();
+    data = data.replace("\"", "\\\"");
+    data = data.replace("\\", "\\\\");
+    data = data.replace("/", "\\/");
+    data = data.replace("\b", "\\b");
+    data = data.replace("\f", "\\f");
+    data = data.replace("\n", "\\n");
+    data = data.replace("\r", "\\r");
+    data = data.replace("\t", "\\t");
+    return data;
+  }
+}

Added: trunk/src/jar/querylang/java/org/mulgara/protocol/StreamedSparqlXMLObject.java
===================================================================
--- trunk/src/jar/querylang/java/org/mulgara/protocol/StreamedSparqlXMLObject.java	                        (rev 0)
+++ trunk/src/jar/querylang/java/org/mulgara/protocol/StreamedSparqlXMLObject.java	2008-09-09 06:07:51 UTC (rev 1258)
@@ -0,0 +1,123 @@
+/*
+ * The contents of this file are subject to the Open Software License
+ * Version 3.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.opensource.org/licenses/osl-3.0.txt
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ */
+
+package org.mulgara.protocol;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.net.URI;
+import java.nio.charset.Charset;
+
+import org.mulgara.util.StringUtil;
+
+
+/**
+ * Represents a data object as XML.
+ *
+ * @created Jul 8, 2008
+ * @author Paul Gearon
+ * @copyright &copy; 2008 <a href="http://www.topazproject.org/">The Topaz Project</a>
+ * @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a>
+ */
+public class StreamedSparqlXMLObject implements StreamedXMLAnswer {
+
+  /** A single indent for use when pretty printing. */
+  static final private String INDENT_STR = "  ";
+
+  /** Indicates that pretty printing should be used. */
+  boolean prettyPrint = true;
+
+  /** The encoded data. */
+  final Object objectData;
+
+  /** The writer used for creating the XML. */
+  protected OutputStreamWriter s = null;
+
+  /** The byte output stream used for creating the XML. */
+  protected OutputStream output = null;
+
+  /** The charset encoding to use when writing to the output stream. */
+  Charset charset = Charset.defaultCharset();
+
+  /**
+   * Creates an XML object encoding.
+   * @param objectData The data to encode.
+   * @param output Where to send the output.
+   */
+  public StreamedSparqlXMLObject(Object objectData, OutputStream output) {
+    this.objectData = objectData;
+    this.output = output;
+  }
+
+  /** @see org.mulgara.protocol.StreamedXMLAnswer#setCharacterEncoding(java.lang.String) */
+  public void setCharacterEncoding(String encoding) {
+    charset = Charset.forName(encoding);
+  }
+
+  /** @see org.mulgara.protocol.StreamedXMLAnswer#setCharacterEncoding(java.nio.Charset) */
+  public void setCharacterEncoding(Charset charset) {
+    this.charset = charset;
+  }
+
+  /** {@inheritDoc} */
+  protected void addDocHeader() throws IOException {
+    s.append("<?xml version=\"1.0\"?>\n");
+    s.append("<sparql xmlns=\"http://www.w3.org/2005/sparql-results#\"");
+    s.append(">");
+  }
+
+  /** {@inheritDoc} */
+  protected void addDocFooter() throws IOException {
+    s.append("</sparql>");
+  }
+
+  /** {@inheritDoc} */
+  protected void addResults() throws IOException {
+    if (prettyPrint) s.append(INDENT_STR).append("<data>");
+    if (objectData != null) s.append(StringUtil.quoteAV(objectData.toString()));
+    s.append("</data>");
+  }
+
+
+  /**
+   * Put the parts of the document together, and close the stream.
+   * @see org.mulgara.protocol.StreamedXMLAnswer#emit()
+   */
+  public void emit() throws IOException {
+    s = new OutputStreamWriter(output);
+    addDocHeader();
+    addResults();
+    addDocFooter();
+    s.flush();
+  }
+
+
+  /**
+   * @see org.mulgara.protocol.XMLAnswer#addNamespace(java.lang.String, java.net.URI)
+   * Ignored.
+   */
+  public void addNamespace(String name, URI nsValue) {
+  }
+
+  /**
+   * @see org.mulgara.protocol.XMLAnswer#addNamespace(java.lang.String, java.net.URI)
+   * Ignored.
+   */
+  public void clearNamespaces() {
+  }
+
+  public void setPrettyPrint(boolean prettyPrint) {
+    this.prettyPrint = prettyPrint;
+  }
+
+}

Added: trunk/src/jar/querylang/java/org/mulgara/protocol/http/BadRequestException.java
===================================================================
--- trunk/src/jar/querylang/java/org/mulgara/protocol/http/BadRequestException.java	                        (rev 0)
+++ trunk/src/jar/querylang/java/org/mulgara/protocol/http/BadRequestException.java	2008-09-09 06:07:51 UTC (rev 1258)
@@ -0,0 +1,42 @@
+/*
+ * The contents of this file are subject to the Open Software License
+ * Version 3.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.opensource.org/licenses/osl-3.0.txt
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ */
+
+package org.mulgara.protocol.http;
+
+import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
+
+/**
+ * Encodes the condition of a bad HTTP servlet request.
+ *
+ * @created Sep 8, 2008
+ * @author Paul Gearon
+ * @copyright &copy; 2008 <a href="http://www.topazproject.org/">The Topaz Project</a>
+ * @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a>
+ */
+public class BadRequestException extends ServletException {
+
+  /** The serialization ID */
+  private static final long serialVersionUID = 6283252576312243830L;
+
+  /** An default constructor to indicate a problem. */
+  public BadRequestException() {
+    super(SC_BAD_REQUEST);
+  }
+
+  /**
+   * @param message The message to send with a bad request code.
+   */
+  public BadRequestException(String message) {
+    super(SC_BAD_REQUEST, message);
+  }
+
+}

Added: trunk/src/jar/querylang/java/org/mulgara/protocol/http/InternalErrorException.java
===================================================================
--- trunk/src/jar/querylang/java/org/mulgara/protocol/http/InternalErrorException.java	                        (rev 0)
+++ trunk/src/jar/querylang/java/org/mulgara/protocol/http/InternalErrorException.java	2008-09-09 06:07:51 UTC (rev 1258)
@@ -0,0 +1,42 @@
+/*
+ * The contents of this file are subject to the Open Software License
+ * Version 3.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.opensource.org/licenses/osl-3.0.txt
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ */
+
+package org.mulgara.protocol.http;
+
+import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
+
+/**
+ * Encodes the condition of a bad HTTP servlet request.
+ *
+ * @created Sep 8, 2008
+ * @author Paul Gearon
+ * @copyright &copy; 2008 <a href="http://www.topazproject.org/">The Topaz Project</a>
+ * @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a>
+ */
+public class InternalErrorException extends ServletException {
+
+  /** The serialization ID */
+  private static final long serialVersionUID = -5804436992868574135L;
+
+  /** An default constructor to indicate a problem. */
+  public InternalErrorException() {
+    super(SC_INTERNAL_SERVER_ERROR);
+  }
+
+  /**
+   * @param message The message to send with a server error code.
+   */
+  public InternalErrorException(String message) {
+    super(SC_INTERNAL_SERVER_ERROR, message);
+  }
+
+}

Added: trunk/src/jar/querylang/java/org/mulgara/protocol/http/ServiceUnavailableException.java
===================================================================
--- trunk/src/jar/querylang/java/org/mulgara/protocol/http/ServiceUnavailableException.java	                        (rev 0)
+++ trunk/src/jar/querylang/java/org/mulgara/protocol/http/ServiceUnavailableException.java	2008-09-09 06:07:51 UTC (rev 1258)
@@ -0,0 +1,42 @@
+/*
+ * The contents of this file are subject to the Open Software License
+ * Version 3.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.opensource.org/licenses/osl-3.0.txt
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ */
+
+package org.mulgara.protocol.http;
+
+import static javax.servlet.http.HttpServletResponse.SC_SERVICE_UNAVAILABLE;
+
+/**
+ * Encodes the condition of an unavailable service.
+ *
+ * @created Sep 8, 2008
+ * @author Paul Gearon
+ * @copyright &copy; 2008 <a href="http://www.topazproject.org/">The Topaz Project</a>
+ * @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a>
+ */
+public class ServiceUnavailableException extends ServletException {
+
+  /** The serialization ID */
+  private static final long serialVersionUID = 3424641674531209118L;
+
+  /** An default constructor to indicate a problem. */
+  public ServiceUnavailableException() {
+    super(SC_SERVICE_UNAVAILABLE);
+  }
+
+  /**
+   * @param message The message to send with a server error code.
+   */
+  public ServiceUnavailableException(String message) {
+    super(SC_SERVICE_UNAVAILABLE, message);
+  }
+
+}

Added: trunk/src/jar/querylang/java/org/mulgara/protocol/http/ServletException.java
===================================================================
--- trunk/src/jar/querylang/java/org/mulgara/protocol/http/ServletException.java	                        (rev 0)
+++ trunk/src/jar/querylang/java/org/mulgara/protocol/http/ServletException.java	2008-09-09 06:07:51 UTC (rev 1258)
@@ -0,0 +1,57 @@
+/*
+ * The contents of this file are subject to the Open Software License
+ * Version 3.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.opensource.org/licenses/osl-3.0.txt
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ */
+
+package org.mulgara.protocol.http;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Encodes the condition of a bad HTTP servlet request.
+ *
+ * @created Sep 8, 2008
+ * @author Paul Gearon
+ * @copyright &copy; 2008 <a href="http://www.topazproject.org/">The Topaz Project</a>
+ * @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a>
+ */
+public class ServletException extends Exception {
+
+  /** The serialization ID */
+  private static final long serialVersionUID = -3284974975526320276L;
+
+  private final int errorCode;
+
+  /** An default constructor to indicate a problem. */
+  public ServletException(int code) {
+    errorCode = code;
+  }
+
+  /**
+   * @param message The message to send with a bad request code.
+   */
+  public ServletException(int code, String message) {
+    super(message);
+    errorCode = code;
+  }
+
+  /**
+   * Sends this exception to a client through a response object.
+   * @param resp The object to respond through.
+   * @throws IOException If there was an error sending to the client.
+   */
+  public void sendResponseTo(HttpServletResponse resp) throws IOException {
+    String msg = getMessage();
+    if (msg == null) resp.sendError(errorCode);
+    else resp.sendError(errorCode, msg);
+  }
+}

Added: trunk/src/jar/querylang/java/org/mulgara/protocol/http/SparqlServlet.java
===================================================================
--- trunk/src/jar/querylang/java/org/mulgara/protocol/http/SparqlServlet.java	                        (rev 0)
+++ trunk/src/jar/querylang/java/org/mulgara/protocol/http/SparqlServlet.java	2008-09-09 06:07:51 UTC (rev 1258)
@@ -0,0 +1,428 @@
+/*
+ * The contents of this file are subject to the Open Software License
+ * Version 3.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.opensource.org/licenses/osl-3.0.txt
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ */
+
+package org.mulgara.protocol.http;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+import org.mulgara.connection.Connection;
+import org.mulgara.connection.SessionConnection;
+import org.mulgara.protocol.StreamedAnswer;
+import org.mulgara.protocol.StreamedSparqlJSONAnswer;
+import org.mulgara.protocol.StreamedSparqlJSONObject;
+import org.mulgara.protocol.StreamedSparqlXMLAnswer;
+import org.mulgara.protocol.StreamedSparqlXMLObject;
+import org.mulgara.query.Answer;
+import org.mulgara.query.Query;
+import org.mulgara.query.QueryException;
+import org.mulgara.query.TuplesException;
+import org.mulgara.query.operation.Command;
+import org.mulgara.server.SessionFactory;
+import org.mulgara.server.SessionFactoryProvider;
+import org.mulgara.sparql.SparqlInterpreter;
+import org.mulgara.util.functional.C;
+import org.mulgara.util.functional.Fn1E;
+import org.mulgara.util.functional.Fn2;
+
+/**
+ * A query gateway for SPARQL.
+ *
+ * @created Sep 7, 2008
+ * @author Paul Gearon
+ * @copyright &copy; 2008 <a href="http://www.topazproject.org/">The Topaz Project</a>
+ * @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a>
+ */
+public class SparqlServlet extends HttpServlet {
+
+  /** Serialization ID */
+  private static final long serialVersionUID = -830221563286477537L;
+
+  /**
+   * Internal type definition of a function that takes "something" and an output stream,
+   * and returns a {@link StreamedAnswer}
+   */
+  private interface StreamConstructor<T> extends Fn2<T,OutputStream,StreamedAnswer> { }
+
+  /**
+   * Internal type definition of a function that takes an Answer and an output stream,
+   * and returns a {@link StreamedAnswer}
+   */
+  private interface AnswerStreamConstructor extends StreamConstructor<Answer> { }
+
+  /**
+   * Internal type definition of a function that takes an Object and an output stream,
+   * and returns a {@link StreamedAnswer}
+   */
+  private interface ObjectStreamConstructor extends StreamConstructor<Object> { }
+
+  /** The parameter identifying the query. */
+  private static final String QUERY_ARG = "query";
+
+  /** The parameter identifying the graph. */
+  private static final String DEFAULT_GRAPH_ARG = "default-graph-uri";
+
+  /** The parameter identifying the graph. We don't set these in SPARQL yet. */
+  @SuppressWarnings("unused")
+  private static final String NAMED_GRAPH_ARG = "named-graph-uri";
+
+  /** The parameter identifying the output type. */
+  private static final String OUTPUT_ARG = "out";
+
+  /** The output parameter value for indicating JSON output. */
+  private static final String OUTPUT_JSON = "json";
+
+  /** The output parameter value for indicating XML output. */
+  private static final String OUTPUT_XML = "xml";
+
+  /** The default output type to use. */
+  private static final String DEFAULT_OUTPUT_TYPE = OUTPUT_XML;
+
+  /** The content type of the results. */
+  private static final String CONTENT_TYPE = "application/sparql-results+xml";
+
+  /** Session value for database connection. */
+  private static final String CONNECTION = "session.connection";
+
+  /** Session value for interpreter. */
+  private static final String INTERPRETER = "session.interpreter";
+
+  /** The server for finding a session factory. */
+  private SessionFactoryProvider server;
+
+  /** Session factory for accessing the database. */
+  private SessionFactory cachedSessionFactory;
+
+  /** This object maps request types to the constructors for that output. */
+  private static final Map<String,AnswerStreamConstructor> streamBuilders = new HashMap<String,AnswerStreamConstructor>();
+
+  /** This object maps request types to the constructors for sending objects to that output. */
+  private static final Map<String,ObjectStreamConstructor> objectStreamBuilders = new HashMap<String,ObjectStreamConstructor>();
+
+  static {
+    AnswerStreamConstructor jsonBuilder = new AnswerStreamConstructor() {
+      public StreamedAnswer fn(Answer ans, OutputStream s) { return new StreamedSparqlJSONAnswer(ans, s); }
+    };
+    AnswerStreamConstructor xmlBuilder = new AnswerStreamConstructor() {
+      public StreamedAnswer fn(Answer ans, OutputStream s) { return new StreamedSparqlXMLAnswer(ans, s); }
+    };
+    streamBuilders.put(OUTPUT_JSON, jsonBuilder);
+    streamBuilders.put(OUTPUT_XML, xmlBuilder);
+    streamBuilders.put(null, xmlBuilder);
+
+    ObjectStreamConstructor jsonObjBuilder = new ObjectStreamConstructor() {
+      public StreamedAnswer fn(Object o, OutputStream s) { return new StreamedSparqlJSONObject(o, s); }
+    };
+    ObjectStreamConstructor xmlObjBuilder = new ObjectStreamConstructor() {
+      public StreamedAnswer fn(Object o, OutputStream s) { return new StreamedSparqlXMLObject(o, s); }
+    };
+    objectStreamBuilders.put(OUTPUT_JSON, jsonObjBuilder);
+    objectStreamBuilders.put(OUTPUT_XML, xmlObjBuilder);
+    objectStreamBuilders.put(null, xmlObjBuilder);
+  }
+
+
+  /**
+   * Creates the servlet for communicating with the given server.
+   * @param server The server that provides access to the database.
+   */
+  public SparqlServlet(SessionFactoryProvider server) throws IOException {
+    this.cachedSessionFactory = null;
+    this.server = server;
+  }
+
+
+  /**
+   * Respond to a request for the servlet.
+   * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+   */
+  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+    String queryStr = req.getParameter(QUERY_ARG);
+    try {
+      Query query = getQuery(queryStr, req);
+  
+      Answer result = executeQuery(query, req);
+
+      String outputType = req.getParameter(OUTPUT_ARG);
+      sendAnswer(result, outputType, resp);
+
+      try {
+        result.close();
+      } catch (TuplesException e) {
+        throw new InternalErrorException("Error closing: " + e.getMessage());
+      }
+
+    } catch (ServletException e) {
+      e.sendResponseTo(resp);
+    }
+  }
+
+
+  /**
+   * Respond to a request for the servlet. This may handle update queries, though SPARQL
+   * is not yet standardized to handle these yet.
+   * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+   */
+  protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+    String queryStr = req.getParameter(QUERY_ARG);
+    try {
+      Command cmd = getCommand(queryStr, req);
+  
+      Object result = executeCommand(cmd, req);
+
+      String outputType = req.getParameter(OUTPUT_ARG);
+      if (result instanceof Answer) {
+        sendAnswer((Answer)result, outputType, resp);
+      } else {
+        sendStatus(result, outputType, resp);
+      }
+
+    } catch (ServletException e) {
+      e.sendResponseTo(resp);
+    }
+  }
+
+
+  /**
+   * Provide a description for the servlet.
+   * @see javax.servlet.GenericServlet#getServletInfo()
+   */
+  public String getServletInfo() {
+    return "Mulgara SPARQL Query Endpoint";
+  }
+
+
+  /**
+   * 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.
+   */
+  Query getQuery(String query, HttpServletRequest req) throws BadRequestException {
+    if (query == null) throw new BadRequestException("Query must be supplied");
+    try {
+      SparqlInterpreter interpreter = getInterpreter(req);
+      interpreter.setDefaultGraphUris(getRequestedDefaultGraphs(req));
+      return interpreter.parseQuery(query);
+    } catch (Exception e) {
+      throw new BadRequestException(e.getMessage());
+    }
+  }
+
+
+  /**
+   * Converts a SPARQL query to a Command. For normal SPARQL this will be a Query,
+   * but SPARQL Update may create other command types.
+   * @param cmd The command string.
+   * @param req The client request object.
+   * @return The Command object specified by the cmd string.
+   * @throws BadRequestException Due to an invalid command string.
+   */
+  Command getCommand(String cmd, HttpServletRequest req) throws BadRequestException {
+    if (cmd == null) throw new BadRequestException("Command must be supplied");
+    try {
+      SparqlInterpreter interpreter = getInterpreter(req);
+      interpreter.setDefaultGraphUris(getRequestedDefaultGraphs(req));
+      return interpreter.parseCommand(cmd);
+    } catch (Exception e) {
+      throw new BadRequestException(e.getMessage());
+    }
+  }
+
+
+  /**
+   * Execute a query on the database, and return the {@link Answer}.
+   * @param query The query to run.
+   * @param req The client request object.
+   * @return An Answer containing the results of the query.
+   * @throws ServletException Due to an error executing the query.
+   * @throws IOException If there was an error establishing a connection.
+   */
+  Answer executeQuery(Query query, HttpServletRequest req) throws ServletException, IOException {
+    try {
+      return query.execute(getConnection(req));
+    } catch (IllegalStateException e) {
+      throw new ServiceUnavailableException(e.getMessage());
+    } catch (QueryException e) {
+      throw new InternalErrorException(e.getMessage());
+    } catch (TuplesException e) {
+      throw new InternalErrorException(e.getMessage());
+    }
+  }
+
+
+  /**
+   * Execute a command on the database, and return whatever the result is.
+   * @param cmd The command to run.
+   * @param req The client request object.
+   * @return An Object containing the results of the query.
+   * @throws ServletException Due to an error executing the query.
+   * @throws IOException If there was an error establishing a connection.
+   */
+  Object executeCommand(Command cmd, HttpServletRequest req) throws ServletException, IOException {
+    try {
+      return cmd.execute(getConnection(req));
+    } catch (IllegalStateException e) {
+      throw new ServiceUnavailableException(e.getMessage());
+    } catch (Exception e) {
+      throw new InternalErrorException(e.getMessage());
+    }
+  }
+
+
+  /**
+   * Sends an Answer back to a client, using the request protocol.
+   * @param answer The answer to send to the client.
+   * @param outputType The protocol requested by the client.
+   * @param resp The response object for communicating with the client.
+   * @throws IOException Due to a communications error with the client.
+   * @throws BadRequestException Due to a bad protocol type.
+   * @throws InternalErrorException Due to an error accessing the answer.
+   */
+  void sendAnswer(Answer answer, String outputType, HttpServletResponse resp) throws IOException, BadRequestException, InternalErrorException {
+    send(streamBuilders, answer, outputType, resp);
+  }
+
+
+  /**
+   * Writes information to the client stream. This is a general catch-all for non-answer
+   * information.
+   * @param result The data to return to the client.
+   * @param outputType The requested format for the response.
+   * @param resp The object for responding to a client.
+   * @throws IOException Due to an error communicating with the client.
+   * @throws BadRequestException Due to a bad protocol type.
+   * @throws InternalErrorException Due to an error accessing the result.
+   */
+  void sendStatus(Object result, String outputType, HttpServletResponse resp) throws IOException, BadRequestException, InternalErrorException {
+    send(objectStreamBuilders, result, outputType, resp);
+  }
+
+
+  /**
+   * Sends an result back to a client, using the requested protocol.
+   * @param <T> The type of the data that is to be streamed to the client.
+   * @param builders The map of protocol types to the objects that implement streaming for
+   *        that protocol.
+   * @param data The result to send to the client.
+   * @param type The protocol type to use when talking to the client.
+   * @param resp The respons object for talking to the client.
+   * @throws IOException Due to a communications error with the client.
+   * @throws BadRequestException Due to a bad protocol type.
+   * @throws InternalErrorException Due to an error accessing the answer.
+   */
+  <T> void send(Map<String,? extends StreamConstructor<T>> builders, T data, String type, HttpServletResponse resp) throws IOException, BadRequestException, InternalErrorException {
+    resp.setContentType(CONTENT_TYPE);
+    resp.setHeader("pragma", "no-cache");
+
+    // establish the output type
+    if (type == null) type = DEFAULT_OUTPUT_TYPE;
+    else type = type.toLowerCase();
+
+    // get the constructor for the stream outputter
+    StreamConstructor<T> constructor = builders.get(type);
+    if (constructor == null) throw new BadRequestException("Unknown result type: " + type);
+
+    try {
+      OutputStream out = resp.getOutputStream();
+      constructor.fn(data, out).emit();
+      out.close();
+    } 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());
+    }
+  }
+
+
+  /**
+   * Gets the SPARQL interpreter for the current session,
+   * creating it if it doesn't exist yet.
+   * @param req The current request environment.
+   * @return A connection that is tied to this HTTP session.
+   */
+  private SparqlInterpreter getInterpreter(HttpServletRequest req) {
+    HttpSession httpSession = req.getSession();
+    SparqlInterpreter interpreter = (SparqlInterpreter)httpSession.getAttribute(INTERPRETER);
+    if (interpreter == null) {
+      interpreter = new SparqlInterpreter();
+      httpSession.setAttribute(INTERPRETER, interpreter);
+    }
+    return interpreter;
+  }
+
+
+  /**
+   * Gets the default graphs the user requested.
+   * @param req The request object from the user.
+   * @return A list of URIs for graphs. This may be null if no URIs were requested.
+   * @throws BadRequestException If a graph name was an invalid URI.
+   */
+  private List<URI> getRequestedDefaultGraphs(HttpServletRequest req) throws BadRequestException {
+    String[] defaults = req.getParameterValues(DEFAULT_GRAPH_ARG);
+    if (defaults == null) return null;
+    try {
+      return C.map(defaults, new Fn1E<String,URI,URISyntaxException>(){public URI fn(String s)throws URISyntaxException{return new URI(s);}});
+    } catch (URISyntaxException e) {
+      throw new BadRequestException("Invalid URI. " + e.getMessage());
+    }
+  }
+
+
+  /**
+   * Gets the connection for the current session, creating it if it doesn't exist yet.
+   * @param req The current request environment.
+   * @return A connection that is tied to this HTTP session.
+   * @throws IOException When an error occurs creating a new session.
+   */
+  private Connection getConnection(HttpServletRequest req) throws IOException, IllegalStateException {
+    HttpSession httpSession = req.getSession();
+    Connection connection = (Connection)httpSession.getAttribute(CONNECTION);
+    if (connection == null) {
+      try {
+        connection = new SessionConnection(getSessionFactory().newSession(), null, null);
+      } catch (QueryException qe) {
+        throw new IOException("Unable to create a connection to the database. " + qe.getMessage());
+      }
+      httpSession.setAttribute(CONNECTION, connection);
+    }
+    return connection;
+  }
+
+
+  /**
+   * This method allows us to put off getting a session factory until the server is
+   * ready to provide one.
+   * @return A new session factory.
+   */
+  private SessionFactory getSessionFactory() throws IllegalStateException {
+    if (cachedSessionFactory == null) {
+      cachedSessionFactory = server.getSessionFactory();
+      if (cachedSessionFactory == null) throw new IllegalStateException("Server not yet ready. Try again soon.");
+    }
+    return cachedSessionFactory;
+  }
+
+}

Modified: trunk/src/jar/server/java/org/mulgara/server/HttpServices.java
===================================================================
--- trunk/src/jar/server/java/org/mulgara/server/HttpServices.java	2008-09-09 06:06:11 UTC (rev 1257)
+++ trunk/src/jar/server/java/org/mulgara/server/HttpServices.java	2008-09-09 06:07:51 UTC (rev 1258)
@@ -79,6 +79,9 @@
   /** The Web Query path. */
   private final static String WEBQUERY_PATH = "webui";
 
+  /** The sparql path. */
+  private final static String SPARQL_REST_PATH = "sparql";
+
   /** The default service path. */
   private final static String DEFAULT_SERVICE = WEBQUERY_PATH;
 
@@ -216,6 +219,9 @@
     starters.add(new ContextStarter() { public Service fn(Server s) throws IOException {
       return addWebQueryContext(s);
     } });
+    starters.add(new ContextStarter() { public Service fn(Server s) throws IOException {
+      return addSparqlRestQueryContext(s);
+    } });
     return starters;
   }
 
@@ -312,6 +318,25 @@
 
 
   /**
+   * Creates the Mulgara REST SPARQL servlet.
+   * @throws IOException if the servlet cannot talk to the network.
+   */
+  private Service addSparqlRestQueryContext(Server server) throws IOException {
+    if (logger.isDebugEnabled()) logger.debug("Adding REST SPARQL servlet context");
+
+    // create the web query context
+    try {
+      Servlet servlet = (Servlet)Reflect.newInstance(Class.forName("org.mulgara.protocol.http.SparqlServlet"), hostServer);
+      String webPath = "/" + SPARQL_REST_PATH;
+      new org.mortbay.jetty.servlet.Context(server, webPath, SESSIONS).addServlet(new ServletHolder(servlet), "/*");
+      return new Service("SPARQL REST Service", webPath);
+    } catch (ClassNotFoundException e) {
+      throw new IllegalStateException("Not configured to use the requested REST SPARQL servlet");
+    }
+  }
+
+
+  /**
    * Creates the servlet used to list the other servlets.
    * @param server The server to register this servlet with.
    * @param services The list of service names and paths.




More information about the Mulgara-svn mailing list