[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 © 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 © 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