[Mulgara-svn] r1516 - trunk/src/jar/querylang/java/org/mulgara/protocol/http

pag at mulgara.org pag at mulgara.org
Tue Feb 17 16:09:11 UTC 2009


Author: pag
Date: 2009-02-17 08:09:09 -0800 (Tue, 17 Feb 2009)
New Revision: 1516

Added:
   trunk/src/jar/querylang/java/org/mulgara/protocol/http/LocalTriple.java
   trunk/src/jar/querylang/java/org/mulgara/protocol/http/LocalTripleUnitTest.java
   trunk/src/jar/querylang/java/org/mulgara/protocol/http/MulgaraServlet.java
Log:
Added REST support for creating and deleting graphs and individual statements. Also refactored the servlet to allow for deployment in a WAR

Added: trunk/src/jar/querylang/java/org/mulgara/protocol/http/LocalTriple.java
===================================================================
--- trunk/src/jar/querylang/java/org/mulgara/protocol/http/LocalTriple.java	                        (rev 0)
+++ trunk/src/jar/querylang/java/org/mulgara/protocol/http/LocalTriple.java	2009-02-17 16:09:09 UTC (rev 1516)
@@ -0,0 +1,263 @@
+/*
+ * 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.net.URI;
+import java.net.URISyntaxException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.jrdf.graph.Literal;
+import org.jrdf.graph.ObjectNode;
+import org.jrdf.graph.PredicateNode;
+import org.jrdf.graph.SubjectNode;
+import org.jrdf.graph.Triple;
+import org.mulgara.query.rdf.BlankNodeImpl;
+import org.mulgara.query.rdf.LiteralImpl;
+import org.mulgara.query.rdf.URIReferenceImpl;
+
+/**
+ * Represents a triple to be created or removed. Null values indicate blank nodes.
+ *
+ * @created Feb 15, 2009
+ * @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>
+ */
+class LocalTriple implements Triple {
+
+  /** Generated serialization ID */
+  private static final long serialVersionUID = -2922482409580801899L;
+
+  final URI subject;
+  final URI predicate;
+  final Object object;
+
+  final boolean hasLiteral;
+
+  public LocalTriple(String s, String p, String o) throws ServletException {
+    this(s, p, o, false);
+  }
+
+
+  /**
+   * Creates a new triple.
+   * @param s The subject.
+   * @param p The predicate.
+   * @param o The object.
+   */
+  public LocalTriple(String s, String p, String o, boolean validate) throws ServletException {
+    if (validate) {
+      if (s == null || s.length() == 0) throw new BadRequestException("Blank subject node not permitted in this operation");
+      if (o == null || o.length() == 0) throw new BadRequestException("Blank object node not permitted in this operation");
+    }
+    try {
+      subject = s == null ? null : new URI(s);
+    } catch (URISyntaxException e) {
+      throw new BadRequestException("Invalid subject URI: " + s);
+    }
+
+    if (p == null) throw new BadRequestException("Illegal triple presented. Predicate cannot be blank.");
+    try {
+      predicate = new URI(p);
+    } catch (URISyntaxException e) {
+      throw new BadRequestException("Invalid predicate URI: " + p);
+    }
+
+    // temporary variables because the compiler doesn't understand code paths when assigning finals
+    Object tmpo;
+    boolean tmpl;
+    try {
+      tmpl = false;
+      if (o == null || o.length() == 0) {  // no object, so it's blank
+        tmpo = null;
+      } else {
+        char zc = o.charAt(0);
+        if (zc == '\'' || zc == '"') {     // starts with quote, so treat it as a literal
+          tmpo = new LocalLiteral(o);
+          tmpl = true;
+        } else {                           // attempt to load as a URI
+          tmpo = new URI(o);
+        }
+      }
+    } catch (URISyntaxException e) {
+      tmpo = new LocalLiteral(o);
+      tmpl = true;
+    }
+    object = tmpo;
+    hasLiteral = tmpl;
+  }
+
+
+  /** @return the subject. <code>null</code> indicates a blank node. */
+  public SubjectNode getSubject() {
+    return subject == null ? new BlankNodeImpl() : new URIReferenceImpl(subject);
+  }
+
+
+  /** @return the predicate. */
+  public PredicateNode getPredicate() {
+    return new URIReferenceImpl(predicate);
+  }
+
+
+  /**
+   * Get the object. <code>null</code> indicates a blank node.
+   * The resulting type is either a URI or a Literal.
+   * @return the object.
+   */
+  public ObjectNode getObject() {
+    return object == null ? new BlankNodeImpl() :
+      (hasLiteral ? ((LocalLiteral)object).toJRDFLiteral() : new URIReferenceImpl((URI)object, false));
+  }
+
+
+  /**
+   * Present this object as a singleton set.
+   * @return A set containing only this triple.
+   */
+  public Set<Triple> toSet() {
+    return Collections.singleton((Triple)this);
+  }
+
+
+  /**
+   * Represents a literal node found in a request.
+   */
+  public class LocalLiteral {
+    String text;
+    URI type;
+    String lang;
+
+    LocalLiteral(String s) {
+      type = null;
+      lang = null;
+
+      String quot = null;
+      char c = s.charAt(0);
+      if (c == '\'') {
+        quot = s.charAt(1) == '\'' ? "'''" : "'";
+      } else if (c == '"') {
+        quot = s.charAt(1) == '"' ? "\"\"\"" : "\"";
+      }
+      text = parseQuoted(quot, s);
+    }
+
+
+    /**
+     * Construct a JRDF literal for use in the query system.
+     * @return A new Literal object with the established parameters.
+     */
+    public Literal toJRDFLiteral() {
+      if (type != null) return new LiteralImpl(text, type);
+      if (lang != null) return new LiteralImpl(text, lang);
+      return new LiteralImpl(text);
+    }
+
+
+    /**
+     * Pull a literal apart into its constituents. If any part is not valid, then the entire
+     * string is treated as a single literal. This method sets the lang or uri sections of
+     * the literal.
+     * @param q The quote character used to wrap the string
+     * @param n The string for the literal node.
+     * @return The text portion of the literal.
+     */
+    private String parseQuoted(String q, String n) {
+      // unquoted, so return entire string
+      if (q == null) return n;
+      Pattern p = Pattern.compile(q + "(.*)" + q + "((@([a-zA-Z\\-]+))|(\\^\\^((.+))))?");
+      Matcher m = p.matcher(n);
+      // no match, so entire thing is literal
+      if (!m.find()) return n;
+      // partial match, so return entire string
+      if (m.end() != n.length()) return n;
+      // test for unquoted characters
+      String g1 = m.group(1).replaceAll("\\\\\\\\", "");
+      if (containsUnquoted(g1, q.substring(0, 1))) return n;
+      // get the language
+      lang = m.group(4);
+      if (lang == null) {
+        // no language, so get the type URI
+        String t = m.group(6);
+        if (t != null) {
+          try {
+            type = new URI(t);
+          } catch (URISyntaxException e) {
+            // invalid URI, so return the entire string as a literal
+            return n;
+          }
+        }
+      }
+      return unescape(m.group(1));
+    }
+
+
+    /**
+     * Tests the contents of a string for unescaped quote characters.
+     * @param str The string to test.
+     * @param q The type of quote character to look for.
+     * @return <code>true</code> if the string contains an unescaped quote.
+     */
+    private boolean containsUnquoted(String str, String q) {
+      Matcher m = Pattern.compile(q).matcher(str);
+      while (m.find()) {
+        int s = m.start();
+        if (s == 0 || str.charAt(s - 1) != '\\') return true;
+      }
+      return false;
+    }
+  }
+
+
+  /** Map of escape characters to their character codes */
+  private static Map<Character,String> map = new HashMap<Character,String>();
+  static {
+    map.put('t', "\t");
+    map.put('t', "\t");
+    map.put('b', "\b");
+    map.put('n', "\n");
+    map.put('r', "\r");
+    map.put('f', "\f");
+    map.put('\\', "\\");
+    map.put('"', "\"");
+    map.put('\'', "'");
+  }
+
+
+  /**
+   * Search for escape characters in a string, and replace them with the request values.
+   * @param s The string to search.
+   * @return A new string with all escape characters replaced with the originals.
+   */
+  static final String unescape(String s) {
+    StringBuilder sb = new StringBuilder();
+    int last = 0;
+    int pos = 0;
+    while ((pos = s.indexOf('\\', pos)) >= 0) {
+      sb.append(s.substring(last, pos));
+      if (++pos == s.length()) break;
+      char c = s.charAt(pos);
+      String m = map.get(c);
+      if (m != null) sb.append(m);
+      else sb.append(c);
+      last = ++pos;
+    }
+    sb.append(s.substring(last));
+    return sb.toString();
+  }
+}

Added: trunk/src/jar/querylang/java/org/mulgara/protocol/http/LocalTripleUnitTest.java
===================================================================
--- trunk/src/jar/querylang/java/org/mulgara/protocol/http/LocalTripleUnitTest.java	                        (rev 0)
+++ trunk/src/jar/querylang/java/org/mulgara/protocol/http/LocalTripleUnitTest.java	2009-02-17 16:09:09 UTC (rev 1516)
@@ -0,0 +1,181 @@
+package org.mulgara.protocol.http;
+
+import java.net.URI;
+
+import org.jrdf.graph.BlankNode;
+import org.jrdf.graph.Literal;
+import org.jrdf.graph.URIReference;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Unit test for LocalTriple and the inner class Literal.
+ *
+ * @created Feb 15, 2009
+ * @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 LocalTripleUnitTest  extends TestCase {
+
+  public LocalTripleUnitTest(String name) {
+    super(name);
+  }
+
+  /**
+   * Default text runner.
+   * @param args The command line arguments
+   */
+  public static void main(String[] args) {
+    junit.textui.TestRunner.run(suite());
+  }
+
+  /**
+   * Hook for test runner to obtain a test suite from.
+   * @return The test suite
+   */
+  public static Test suite() {
+    TestSuite suite = new TestSuite();
+    suite.addTest(new LocalTripleUnitTest("testUriConstructor"));
+    suite.addTest(new LocalTripleUnitTest("testBlanks"));
+    suite.addTest(new LocalTripleUnitTest("testLiterals"));
+    suite.addTest(new LocalTripleUnitTest("testLiteralTypes"));
+    suite.addTest(new LocalTripleUnitTest("testLiteralLang"));
+    suite.addTest(new LocalTripleUnitTest("testLiteralQuote"));
+    return suite;
+  }
+
+  public void testUriConstructor() throws Exception {
+    LocalTriple t = new LocalTriple("foo:sub", "foo:pred", "foo:obj");
+    assertTrue(t.getSubject() instanceof URIReference);
+    assertTrue(t.getPredicate() instanceof URIReference);
+    assertTrue(t.getObject() instanceof URIReference);
+    assertEquals("foo:sub", ((URIReference)t.getSubject()).toString());
+    assertEquals("foo:pred", ((URIReference)t.getPredicate()).toString());
+    assertEquals("foo:obj", ((URIReference)t.getObject()).toString());
+  }
+
+  public void testBlanks() throws Exception {
+    LocalTriple t = new LocalTriple(null, "foo:pred", "foo:obj");
+    assertTrue(t.getSubject() instanceof BlankNode);
+    assertTrue(t.getPredicate() instanceof URIReference);
+    assertTrue(t.getObject() instanceof URIReference);
+    assertEquals("foo:pred", ((URIReference)t.getPredicate()).toString());
+    assertEquals("foo:obj", ((URIReference)t.getObject()).toString());
+
+    t = new LocalTriple("foo:sub", "foo:pred", null);
+    assertTrue(t.getSubject() instanceof URIReference);
+    assertTrue(t.getPredicate() instanceof URIReference);
+    assertTrue(t.getObject() instanceof BlankNode);
+    assertEquals("foo:sub", ((URIReference)t.getSubject()).toString());
+    assertEquals("foo:pred", ((URIReference)t.getPredicate()).toString());
+
+    try {
+      t = new LocalTriple("foo:sub", null, "foo:obj");
+      fail("Illegal blank node in predicate");
+    } catch (BadRequestException e) { }
+  }
+
+
+  public void testLiterals() throws Exception {
+    LocalTriple t = new LocalTriple("foo:bar", "foo:pred", "foo bar");
+    assertTrue(t.getSubject() instanceof URIReference);
+    assertTrue(t.getPredicate() instanceof URIReference);
+    assertTrue(t.getObject() instanceof Literal);
+    assertEquals("foo:bar", ((URIReference)t.getSubject()).toString());
+    assertEquals("foo:pred", ((URIReference)t.getPredicate()).toString());
+    assertEquals("foo bar", ((Literal)t.getObject()).getLexicalForm());
+
+    try {
+      t = new LocalTriple("foo:sub", "foo pred", "foo:bar");
+      fail("Illegal literal in predicate: " + t.getPredicate());
+    } catch (BadRequestException e) { }
+
+    try {
+      t = new LocalTriple("foo sub", "foo:pred", "foo:obj");
+      fail("Illegal literal in subject: " + t.getSubject());
+    } catch (BadRequestException e) { }
+  }
+
+
+  public void testLiteralTypes() throws Exception {
+    LocalTriple t = new LocalTriple("foo:bar", "foo:pred", "foo bar");
+    assertTrue(t.getObject() instanceof Literal);
+    Literal l = (Literal)t.getObject();
+    assertEquals("foo bar", l.getLexicalForm());
+    assertNull(l.getLanguage());
+    assertNull(l.getDatatype());
+
+    t = new LocalTriple("foo:bar", "foo:pred", "'foo bar'^^foo:bar");
+    assertTrue(t.getObject() instanceof Literal);
+    l = (Literal)t.getObject();
+    assertEquals("foo bar", l.getLexicalForm());
+    assertNull(l.getLanguage());
+    assertEquals(URI.create("foo:bar").toString(), l.getDatatype().toString());
+
+    t = new LocalTriple("foo:bar", "foo:pred", "'foo bar'^^foo bar");
+    assertTrue(t.getObject() instanceof Literal);
+    l = (Literal)t.getObject();
+    assertEquals("'foo bar'^^foo bar", l.getLexicalForm());
+    assertNull(l.getLanguage());
+    assertNull(l.getDatatype());
+
+    t = new LocalTriple("foo:bar", "foo:pred", "foobar'^^foo:bar");
+    assertTrue(t.getObject() instanceof Literal);
+    l = (Literal)t.getObject();
+    assertEquals("foobar'^^foo:bar", l.getLexicalForm());
+    assertNull(l.getLanguage());
+    assertNull(l.getDatatype());
+
+    t = new LocalTriple("foo:bar", "foo:pred", "'foobar^^foo:bar");
+    assertTrue(t.getObject() instanceof Literal);
+    l = (Literal)t.getObject();
+    assertEquals("'foobar^^foo:bar", l.getLexicalForm());
+    assertNull(l.getLanguage());
+    assertNull(l.getDatatype());
+
+  }
+
+
+  public void testLiteralLang() throws Exception {
+    LocalTriple t = new LocalTriple("foo:bar", "foo:pred", "'foobar'@en");
+    assertTrue(t.getObject() instanceof Literal);
+    Literal l = (Literal)t.getObject();
+    assertEquals("foobar", l.getLexicalForm());
+    assertEquals("en", l.getLanguage());
+    assertNull(l.getDatatype());
+
+    t = new LocalTriple("foo:bar", "foo:pred", "'foobar'@en ");
+    assertTrue(t.getObject() instanceof Literal);
+    l = (Literal)t.getObject();
+    assertEquals("'foobar'@en ", l.getLexicalForm());
+    assertNull(l.getLanguage());
+    assertNull(l.getDatatype());
+  }
+
+
+  public void testLiteralQuote() throws Exception {
+    LocalTriple t = new LocalTriple("foo:bar", "foo:pred", "'''foobar'''");
+    assertTrue(t.getObject() instanceof Literal);
+    Literal l = (Literal)t.getObject();
+    assertEquals("foobar", l.getLexicalForm());
+    assertNull(l.getLanguage());
+    assertNull(l.getDatatype());
+
+    t = new LocalTriple("foo:bar", "foo:pred", "'''foobar'''@en");
+    assertTrue(t.getObject() instanceof Literal);
+    l = (Literal)t.getObject();
+    assertEquals("foobar", l.getLexicalForm());
+    assertEquals("en", l.getLanguage());
+    assertNull(l.getDatatype());
+
+    t = new LocalTriple("foo:bar", "foo:pred", "''foobar''");
+    assertTrue(t.getObject() instanceof Literal);
+    l = (Literal)t.getObject();
+    assertEquals("''foobar''", l.getLexicalForm());
+    assertNull(l.getLanguage());
+    assertNull(l.getDatatype());
+  }
+}

Added: trunk/src/jar/querylang/java/org/mulgara/protocol/http/MulgaraServlet.java
===================================================================
--- trunk/src/jar/querylang/java/org/mulgara/protocol/http/MulgaraServlet.java	                        (rev 0)
+++ trunk/src/jar/querylang/java/org/mulgara/protocol/http/MulgaraServlet.java	2009-02-17 16:09:09 UTC (rev 1516)
@@ -0,0 +1,148 @@
+package org.mulgara.protocol.http;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+import org.apache.log4j.Logger;
+import org.mulgara.connection.Connection;
+import org.mulgara.connection.ConnectionException;
+import org.mulgara.connection.ConnectionFactory;
+import org.mulgara.connection.SessionConnection;
+import org.mulgara.query.QueryException;
+import org.mulgara.server.SessionFactory;
+import org.mulgara.server.SessionFactoryProvider;
+
+public abstract class MulgaraServlet extends HttpServlet {
+
+  /** Serialization ID. */
+  private static final long serialVersionUID = -8019499041312491482L;
+
+  /** The logger. */
+  final static Logger logger = Logger.getLogger(MulgaraServlet.class.getName());
+
+  /** Session value for database connection. */
+  static final String CONNECTION = "session.connection";
+
+  /** The name of the parameter for the host name. */
+  protected static final String HOST_NAME_PARAM = "mulgara.config.hostname";
+
+  /** The name of the parameter for the server name. */
+  protected static final String SERVER_NAME_PARAM = "mulgara.config.servername";
+
+  /** The default name to use for the host. */
+  protected static final String DEFAULT_HOSTNAME = "localhost";
+
+  /** The default name to use for the server. */
+  protected static final String DEFAULT_SERVERNAME = "server1";
+
+  /** The server for finding a session factory. */
+  protected SessionFactoryProvider server;
+
+  /** A URI for the server, to be used if no server is provided. */
+  private URI serverUri;
+
+  /** Session factory for accessing the database. */
+  private SessionFactory cachedSessionFactory;
+
+  /** Factory for building and caching connections, based on URI. */
+  private ConnectionFactory connectionFactory;
+
+
+  public MulgaraServlet() {
+    server = null;
+    cachedSessionFactory = null;
+    connectionFactory = new ConnectionFactory();
+    serverUri = null;
+  }
+
+
+  public MulgaraServlet(SessionFactoryProvider server) {
+    this.server = server;
+    cachedSessionFactory = null;
+    connectionFactory = null;
+    serverUri = null;
+  }
+
+
+  /**
+   * This is irrelevant except when the server is not provided, for instance, when deployed
+   * in a Web ARchive (WAR) file.
+   * @see javax.servlet.GenericServlet#init(javax.servlet.ServletConfig)
+   */
+  public void init(ServletConfig config) {
+    ServletContext context = config.getServletContext();
+    String host = context.getInitParameter(HOST_NAME_PARAM);
+    if (host == null) host = DEFAULT_HOSTNAME;
+
+    String servername = context.getInitParameter(SERVER_NAME_PARAM);
+    if (servername == null) servername = DEFAULT_SERVERNAME;
+
+    String uri = "rmi://" + host + "/" + servername;
+    try {
+      serverUri = new URI(uri);
+    } catch (URISyntaxException e) {
+      logger.error("Badly formed server URI: " + uri);
+    }
+  }
+
+
+  /**
+   * 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.
+   */
+  protected Connection getConnection(HttpServletRequest req) throws IOException, IllegalStateException {
+    HttpSession httpSession = req.getSession();
+    Connection connection = (Connection)httpSession.getAttribute(CONNECTION);
+    if (connection == null) {
+      try {
+        connection = getConnection();
+      } catch (QueryException qe) {
+        throw new IOException("Unable to create a connection to the database. " + qe.getMessage());
+      }
+      httpSession.setAttribute(CONNECTION, connection);
+    }
+    return connection;
+  }
+
+
+  /**
+   * Get an existing connection to the configured server, or else create a new one.
+   * @return A Connection to the required server.
+   * @throws QueryException If there was an error asking an internal server for a connection.
+   */
+  private Connection getConnection() throws QueryException, IOException {
+    assert connectionFactory != null ^ server != null;
+    if (connectionFactory == null) {
+      return new SessionConnection(getSessionFactory().newSession(), null, null);
+    } else {
+      try {
+        return connectionFactory.newConnection(serverUri);
+      } catch (ConnectionException e) {
+        throw new IOException("Unable to create a connection to the database identified by: " + serverUri + " (" + e.getMessage() + ")");
+      }
+    }
+  }
+
+  /**
+   * 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;
+  }
+
+}
\ No newline at end of file




More information about the Mulgara-svn mailing list