/* * The contents of this file are subject to the Mozilla Public License * Version 1.1 (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.mozilla.org/MPL/ * * 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. * * The Original Code is the Kowari Metadata Store. * * The Initial Developer of the Original Code is Plugged In Software Pty * Ltd (http://www.pisoftware.com, mailto:info@pisoftware.com). Portions * created by Plugged In Software Pty Ltd are Copyright (C) 2001,2002 * Plugged In Software Pty Ltd. All Rights Reserved. * * Contributor(s): N/A. * * [NOTE: The text of this Exhibit A may differ slightly from the text * of the notices in the Source Code files of the Original Code. You * should use the text of this Exhibit A rather than the text found in the * Original Code Source Code for Your Modifications.] * */ package org.mulgara.itql; // Java APIs import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.net.URI; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.activation.MimeType; import javax.xml.soap.SOAPException; import org.apache.axis.utils.DOM2Writer; import org.apache.axis.utils.XMLUtils; import org.apache.log4j.Logger; import org.jrdf.graph.BlankNode; import org.jrdf.graph.URIReference; import org.mulgara.connection.Connection; import org.mulgara.connection.ConnectionException; import org.mulgara.connection.ConnectionFactory; import org.mulgara.itql.lexer.LexerException; import org.mulgara.itql.parser.ParserException; import org.mulgara.parser.Interpreter; import org.mulgara.parser.MulgaraLexerException; import org.mulgara.parser.MulgaraParserException; 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.Backup; import org.mulgara.query.operation.Command; import org.mulgara.query.operation.Export; import org.mulgara.query.operation.Load; import org.mulgara.query.operation.Restore; import org.mulgara.query.operation.SetAutoCommit; import org.mulgara.query.rdf.LiteralImpl; import org.mulgara.server.Session; import org.w3c.dom.Document; import org.w3c.dom.Element; /** * iTQL Interpreter Bean. * <p> * This class provides a simple interface for the execution of iTQL queries. * </p> * <p> * Note. This class will be deprecated and is going away in favour of {@link org.mulgara.connection.Connection} * based interfaces. * </p> * * @created 2001-Aug-30 * @author Tate Jones * @author Ben Warren * @author Tom Adams * @copyright ©2001-2004 <a href="http://www.tucanatech.com/">Tucana Technology, Inc</a> * @copyright ©2005 <a href="mailto:tomjadams@gmail.com">Tom Adams</a> * @licence <a href="{@docRoot}/../../LICENCE">Mozilla Public License v1.1</a> */ public class ItqlInterpreterBean { /** The logger */ private final static Logger log = Logger.getLogger(ItqlInterpreterBean.class.getName()); /** line separator */ private static final String EOL = System.getProperty("line.separator"); /** * The TQL namespace, used when creating an XML response. * TODO: Bring this into line with the Mulgara.NAMESPACE, which may break existing client. */ private final static String TQL_NS = "http://mulgara.org/tql#"; /** A dummy URI to be used when none is provided on a data source. */ private final static URI DUMMY_RDF_URI = URI.create("http://dummy/data.rdf"); /** The ITQL interpreter Bean. */ private final TqlAutoInterpreter interpreter = new TqlAutoInterpreter(); /** A legacy session handle. Only used to remember the session a client asked for. */ private Session legacySession = null; /** An internal parser. Only used when a legacy client wants to build their own query. */ private Interpreter parser = new TqlInterpreter(); /** Indicates that the last command wanted to quit. */ private boolean quit = false; /** * Create the TQL interpreter. */ public ItqlInterpreterBean() { if (log.isInfoEnabled()) log.info("Created an ItqlInterpreterBean"); } /** * @deprecated * Create the TQL interpreter using the given <code>session</code>. * The Session will be ignored. Security is currently unimplemented, but the domain is recorded. * @param session the session to use to communicate with the Mulgara server */ public ItqlInterpreterBean(Session session, URI securityDomain) { if (log.isInfoEnabled()) { log.info("Created an ItqlInterpreterBean with a supplied session and security domain"); } legacySession = session; interpreter.preSeedSession(session); interpreter.setSecurityDomain(securityDomain); } // executeQueryToMap() /** * Splits a query containing multiple queries into an array of single queries. * * @param multiQuery PARAMETER TO DO * @return An array of query strings which include the ending ';' charater. */ public static String[] splitQuery(String multiQuery) { List<String> singleQueryList = new ArrayList<String>(); // Inside a URI? boolean INSIDE_URI = false; // Inside a text literal? boolean INSIDE_TEXT = false; // Start index for next single query int startIndex = 0; if (log.isDebugEnabled()) log.debug("About to break up query: " + multiQuery); multiQuery = multiQuery.trim(); // Iterate along the multi query and strip out the single queries. for (int lineIndex = 0; lineIndex < multiQuery.length(); lineIndex++) { char currentChar = multiQuery.charAt(lineIndex); switch (currentChar) { // Quote - end or start of a literal if not in a URI // (OK so maybe it won't appear in a legal URI but let things further // down handle this) case '\'': if (!INSIDE_URI) { if (INSIDE_TEXT) { // Check for an \' inside a literal if ( (lineIndex > 1) && (multiQuery.charAt(lineIndex - 1) != '\\')) { INSIDE_TEXT = false; } } else { INSIDE_TEXT = true; } } break; // URI start - if not in a literal case '<': if (!INSIDE_TEXT) { INSIDE_URI = true; } break; // URI end - if not in a literal case '>': if (!INSIDE_TEXT) { INSIDE_URI = false; } break; case ';': if (!INSIDE_TEXT && !INSIDE_URI) { String singleQuery = multiQuery.substring(startIndex, lineIndex + 1).trim(); startIndex = lineIndex + 1; singleQueryList.add(singleQuery); if (log.isDebugEnabled()) log.debug("Found single query: " + singleQuery); } break; default: } } // Lasy query is not terminated with a ';' if (startIndex < multiQuery.length()) { singleQueryList.add(multiQuery.substring(startIndex, multiQuery.length())); } return singleQueryList.toArray(new String[singleQueryList.size()]); } /** * Returns the session to use to communicate with the Mulgara server. * If you need a session, use a {@link ConnectionFactory} instead. * @deprecated The user should not be accessing a session through a * command interpreter. * @return the session to use to communicate with the Mulgara server. This is probably <code>null</code>. */ public Session getSession() { return legacySession; } /** * Returns the session to use to communicate with the specified Mulgara server. * @deprecated The user should not be accessing a session through a * command interpreter. * @param serverURI URI Server to get a Session for. * @return the session to use to communicate with the specified Mulgara server, * or <code>null</code> if it was not possible to create a session for the URI. */ public Session getSession(URI serverURI) { try { return interpreter.establishConnection(serverURI).getSession(); } catch (Exception e) { log.error("Unable to get session for: " + serverURI, e); return null; } } /** * Returns the aliases associated with this bean. * * @return the alias namespace map associated with this bean. */ @SuppressWarnings("deprecation") public Map<String,URI> getAliasMap() { return this.interpreter.getAliasesInUse(); } /** * Closes the session underlying this bean. */ public void close() { interpreter.close(); } // // Public API // /** * Begin a new transaction by setting the autocommit off * * @param name the name of the transaction (debug purposes only) * @throws QueryException Error setting up the transaction. */ public void beginTransaction(String name) throws QueryException { if (log.isDebugEnabled()) log.debug("Begin transaction for :" + name); SetAutoCommit autocommitCmd = new SetAutoCommit(false); try { // do what the autointerpreter does, but don't worry about the result Connection localConnection = interpreter.getLocalConnection(); autocommitCmd.execute(localConnection); interpreter.updateConnectionsForTx(localConnection, autocommitCmd); } catch (QueryException qe) { throw qe; } catch (Exception e) { throw new QueryException("Unable to start a transaction", e); } } /** * Commit a new transaction by setting the autocommit on * * @param name the name of the transaction ( debug purposes only ) * * @throws QueryException Unable to commit one of the connections. */ public void commit(String name) throws QueryException { if (log.isDebugEnabled()) log.debug("Commit transaction for :" + name); interpreter.commitAll(); } /** * Rollback an existing transaction * * @param name the name of the transaction ( debug purposes only ) * * @throws QueryException Unable to rollback one of the connections */ public void rollback(String name) throws QueryException { log.warn("Rollback transaction for :" + name); interpreter.rollbackAll(); } /** * Answer TQL queries. * * @param queryString PARAMETER TO DO * @return the answer DOM to the TQL query * @throws Exception EXCEPTION TO DO */ public Element execute(String queryString) throws Exception { try { //DocumentBuilder xdb = XMLParserUtils.getXMLDocBuilder(); //Document doc = xdb.newDocument(); Document doc = XMLUtils.newDocument(); Element answerDom = doc.createElementNS(TQL_NS, "answer"); answerDom.setAttribute("xmlns", TQL_NS); if (log.isDebugEnabled()) { log.debug("Received a TQL query : " + queryString); } String[] queries = splitQuery(queryString); for (int queryIndex = 0; queryIndex < queries.length; queryIndex++) { String singleQuery = queries[queryIndex]; // Attach the answer Element query = (Element) answerDom.appendChild(doc.createElementNS(TQL_NS, "query")); // Resolve the query if (log.isDebugEnabled()) { log.debug("Executing query : " + singleQuery); } executeCommand(singleQuery); Answer answer = null; try { answer = this.interpreter.getLastAnswer(); if ((answer == null) || answer.isUnconstrained()) { if (this.interpreter.getLastException() == null) { //Not an error, but a message does exist Element message = (Element)query.appendChild(doc.createElementNS(TQL_NS, "message")); message.appendChild(doc.createTextNode(interpreter.getLastMessage())); if (log.isDebugEnabled()) { log.debug("Attached last message: " + interpreter.getLastMessage()); } } else { //Error has occurred at the interpreter //Generate a SOAP fault System.err.println("Generating a SOAP fault due to interpreter exception:"); interpreter.getLastException().printStackTrace(); log.error("Execute query failed. Returning error", interpreter.getLastException()); throw new SOAPException("ItqlInterpreter error - " + ItqlUtil.getCause(interpreter.getLastException(), 0)); // TODO: consider adding + EOL + "Query: [" + singleQuery + "]"); // though must first consider impact on clients } // Ensure answer is null. answer = null; } else { log.debug("Building XML result set"); appendSolution(answer, query); log.debug("Attached answer text"); } } finally { if (answer != null) { answer.close(); } } } // Send the answer back to the caller return answerDom; } catch (Exception e) { log.error("Failed to execute query", e); throw e; } } /** * Answer TQL queries. * * @param queryString A query to execute * @return the answer String to the TQL query * @throws Exception General exception type to cover all possible conditions, * including bad query syntax, network errors, unknown graphs, etc. */ public String executeQueryToString(String queryString) throws Exception { String result = DOM2Writer.nodeToString(this.execute(queryString), false); if (log.isDebugEnabled()) log.debug("Sending result to caller : " + result); return result; } /** * Executes a semicolon delimited string of queries. <p> * * This method allows you to execute mulitple queries at once by specifying a * string of the following form: </p> <pre> * String queryString = "select $s $p $o from <rmi://machine/server1#model> " + * "where $s $p $o;"; * queryString += "select $s $p $o from <rmi://machine2/server1#model> " + * "where $s $p $o;"; * </pre> <p> * * The ordering of the result list will correspond to the ordering of the * queries in the input string. </p> <p> * * Note. Two different return types will be contained in the returned list. An * {@link org.mulgara.query.Answer} or a {@link java.lang.String} (error) * message. </p> * * @param queryString semi-colon delimited string containing the queries to be * executed * @return a list of answers, messages and errors, answers are of type {@link * org.mulgara.query.Answer}, the messages are {@link * java.lang.String}s */ public List<Object> executeQueryToList(String queryString) { return executeQueryToList(queryString, false); } /** * Executes a semicolon delimited string of queries. <p> * * This method allows you to execute mulitple queries at once by specifying a * string of the following form: </p> <pre> * String queryString = "select $s $p $o from <rmi://machine/server1#model> " + * "where $s $p $o;"; * queryString += "select $s $p $o from <rmi://machine2/server1#model> " + * "where $s $p $o;"; * </pre> <p> * * The ordering of the result list will correspond to the ordering of the * queries in the input string. </p> <p> * * Note. Two different return types will be contained in the returned list. An * {@link org.mulgara.query.Answer} or a {@link java.lang.String} (error) * message. </p> * * @param queryString semi-colon delimited string containing the queries to be executed * @param keepExceptions return exceptions, don't convert them to a string. * @return a list of answers, messages and errors, answers are of type * {@link org.mulgara.query.Answer}, the messages are {@link java.lang.String}s */ public List<Object> executeQueryToList(String queryString, boolean keepExceptions) { List<Object> answers = new ArrayList<Object>(); if (log.isDebugEnabled()) log.debug("Received a TQL query : " + queryString); String[] queries = splitQuery(queryString); for (int queryIndex = 0; queryIndex < queries.length; queryIndex++) { String singleQuery = queries[queryIndex]; // Resolve the query if (log.isDebugEnabled()) log.debug("Executing query : " + singleQuery); // execute it answers.add(this.executeQueryToNiceResult(singleQuery, keepExceptions)); } // Send the answers back to the caller return answers; } /** * Executes a {@link java.util.Map} of queries, returning the results of those * queries in a map keyed with the same keys as the input map. <p> * * The <var>queries</var> is a map of keys ({@link java.lang.Object}s) to * queries ({@link java.lang.String}s). Each query will be excuted (in the * order in which <var>queries</var> 's map implementation {@link * java.util.Map#keySet()}'s {@link java.util.Set#iterator()} returns keys) * and the results added to the returned map, keyed on the same key as the * original query. </p> <p> * * For example: </p> <pre> * // create the queries * URI title = new URI("http://www.foo.com/title"); * String titleQuery = "select $s $p $o from <rmi://machine/server1#model> " + * "where $s $p $o;"; * URI date = new URI("http://www.foo.com/date"); * String dateQuery = "select $s $p $o from <rmi://machine2/server1#model> " + * "where $s $p $o;"; * * // add them to the map * HashMap queries = new HashMap(); * queries.put(title, titleQuery); * queries.put(date, dateQuery); * * // execute them * ItqlInterpreterBean itb = new ItqlInterpreterBean(); * HashMap answers = itb.executeQueryToMap(queries); * * // get the answers * Answer titleAnswer = answers.get(title); * Answer dateAnswer = answers.get(date); * </pre> <p> * * Note. Each answer will be either a {@link org.mulgara.query.Answer} or a * {@link java.lang.String} (error) message. </p> * * @param queries a map of keys to queries to be executed * @return a map of answers and error messages */ public Map<Object,Object> executeQueryToMap(Map<Object,String> queries) { // create the answer map HashMap<Object,Object> answers = new HashMap<Object,Object>(); // iterate over the queries for (Iterator<Object> keyIter = queries.keySet().iterator(); keyIter.hasNext(); ) { // get the key and the query Object key = keyIter.next(); String query = queries.get(key); // log the query we're executing if (log.isDebugEnabled()) log.debug("Executing " + key + " -> " + query); // execute the query answers.put(key, this.executeQueryToNiceResult(query, false)); } // return the answers return answers; } /** * Builds a {@link org.mulgara.query.Query} from the given <var>query</var>. * * @param query Command containing a query (<em>select $x $y .....</em>). * @return a {@link org.mulgara.query.Query} constructed from the given * <var>query</var> * @throws IOException if the <var>query</var> can't be buffered * @throws LexerException if <var>query</var> can't be tokenized * @throws ParserException if the <var>query</var> is not syntactically * correct */ public Query buildQuery(String query) throws IOException, MulgaraLexerException, MulgaraParserException { Command cmd = parser.parseCommand(query); if (!(cmd instanceof Query)) throw new MulgaraParserException("Command is valid, but is not a query: " + query + "(" + cmd.getClass().getName() + ")"); return (Query)cmd; } /** * Execute an iTQL "update" statement that returns no results. * <p> * This method should be used only for commands that return no results * such as <code>INSERT</code> and <code>ALIAS</code>. * </p> * * @param itql The statement to execute. * * @throws ItqlInterpreterException if the statement fails or the command * executed returned results. */ public void executeUpdate(String itql) throws ItqlInterpreterException { try { executeCommand(itql); } catch (Exception e) { throw new ItqlInterpreterException(e); } Exception e = interpreter.getLastException(); ItqlInterpreterException exception = (e == null) ? null : new ItqlInterpreterException(e); Answer answer = interpreter.getLastAnswer(); if (answer != null) { try { answer.close(); } catch (TuplesException te) { /* use the following exception */ } throw new IllegalStateException("The execute update method should not return an Answer object!"); } if (exception != null) throw exception; } /** * Returns true if a quit command has been entered. * * @return true if a quit command has been entered */ public boolean isQuitRequested() { return quit; } /** * Returns the {@linkplain TqlAutoInterpreter#getLastMessage last message} of * the interpreter. * * @return the results of the last command execution, null if the command did * not set any message * * @see TqlAutoInterpreter#getLastMessage() */ public String getLastMessage() { return interpreter.getLastMessage(); } /** * Returns the {@linkplain TqlAutoInterpreter#getLastException last error} of * the interpreter. * * @return the results of the last command execution, null if the command did * not set any message * * @see TqlAutoInterpreter#getLastException() */ public ItqlInterpreterException getLastError() { return new ItqlInterpreterException(interpreter.getLastException()); } /** * Execute an ITQL query and return an answer. * * @param itql The query to execute. * @return The answer to the query. * @throws ItqlInterpreterException if the query fails. */ public Answer executeQuery(String itql) throws ItqlInterpreterException { try { executeCommand(itql); } catch (Exception e) { throw new ItqlInterpreterException(e); } Exception exception = interpreter.getLastException(); if (exception != null) throw new ItqlInterpreterException(exception); return interpreter.getLastAnswer(); } /** * Load the contents of a local file into a database/model. * <p> * The method assumes the source to be RDF/XML if an .rdf extension can * not be found. A .n3 extension assume n3 triples. * </p> * <p> * Note. <var>destinationURI</var> must be a valid URI, and does not include * the angle brackets (< and >) used to delimit URLs in iTQL. * </p> * * @param sourceFile a local file containing the source data * @param destinationURI destination model for the source data * @return number of rows inserted into the destination model * @throws QueryException if the data fails to load or the file does not exist * on the local file system. */ public long load(File sourceFile, URI destinationURI) throws QueryException { long numberOfStatements = 0; // check for the local file if (!sourceFile.exists()) { throw new QueryException(sourceFile+" does not exist on the local file system"); } try { Load loadCmd = new Load(sourceFile.toURI(), destinationURI, true); numberOfStatements = (Long)loadCmd.execute(interpreter.establishConnection(loadCmd.getServerURI())); } catch (QueryException ex) { throw ex; } catch (Exception ex) { throw new QueryException("Unable to load data: " + ex.getMessage(), ex); } return numberOfStatements; } /** * Backup all the data on the specified server to a client local file. * The database is not changed by this method. * * @param sourceURI The URI of the server to backup. * @param destinationFile an non-existent file on the local file system to * receive the backup contents. * @throws QueryException if the backup cannot be completed. */ @SuppressWarnings("deprecation") public void backup(URI sourceURI, File destinationFile) throws QueryException { Backup backup = new Backup(sourceURI, destinationFile.toURI(), true); try { backup.execute(interpreter.establishConnection(backup.getServerURI())); } catch (ConnectionException e) { throw new QueryException("Unable to establish connection to: " + backup.getServerURI(), e); } } /** * Backup all the data on the specified server to an output stream. * The database is not changed by this method. * * @param sourceURI The URI of the server to backup. * @param outputStream The stream to receive the contents * @throws QueryException if the backup cannot be completed. */ @SuppressWarnings("deprecation") public void backup(URI sourceURI, OutputStream outputStream) throws QueryException { Backup backup = new Backup(sourceURI, null, true); backup.setOverrideOutputStream(outputStream); try { backup.execute(interpreter.establishConnection(backup.getServerURI())); } catch (ConnectionException e) { throw new QueryException("Unable to establish connection to: " + backup.getServerURI(), e); } } /** * Export the data in the specified graph to a client local file. * The database is not changed by this method. * * @param graphURI The URI of the graph to export. * @param destinationFile an non-existent file on the local file system to * receive the export contents. * @throws QueryException if the export cannot be completed. */ public void export(URI graphURI, File destinationFile) throws QueryException { Export export = new Export(graphURI, destinationFile.toURI(), true); try { export.execute(interpreter.establishConnection(export.getServerURI())); } catch (ConnectionException e) { throw new QueryException("Unable to establish connection to: " + export.getServerURI(), e); } } /** * Export the data in the specified graph to an output stream. * The database is not changed by this method. * * @param graphURI The URI of the graph to export. * @param outputStream The stream to receive the contents * @throws QueryException if the export cannot be completed. */ public void export(URI graphURI, OutputStream outputStream) throws QueryException { Export export = new Export(graphURI, outputStream); try { export.execute(interpreter.establishConnection(export.getServerURI())); } catch (ConnectionException e) { throw new QueryException("Unable to establish connection to: " + export.getServerURI(), e); } } /** * Load the contents of an InputStream into a database/model. The method assumes * the source to be RDF/XML. * <p> * Note. <var>destinationURI</var> must be a valid URI, and does not include * the angle brackets (< and >) used to delimit URLs in iTQL. * </p> * * @param inputStream a locally supplied inputstream. * @param destinationURI destination model for the source data * @return number of rows inserted into the destination model */ public long load(InputStream inputStream, URI destinationURI) throws QueryException { return load(inputStream, null, destinationURI); } /** * Load the contents of an InputStream into a database/model. * <p>Note. <var>destinationURI</var> must be a valid URI, and does not include * the angle brackets (< and >) used to delimit URLs in iTQL.</p> * * @param inputStream a locally supplied inputstream. * @param destinationURI destination model for the source data. * @param contentType The string representation of the content type of the data in the stream. * @return number of rows inserted into the destination model */ public long load(InputStream inputStream, URI destinationURI, String contentType) throws QueryException { long numberOfStatements = 0; try { Load loadCmd = new Load(destinationURI, inputStream, new MimeType(contentType)); numberOfStatements = (Long)loadCmd.execute(interpreter.establishConnection(loadCmd.getServerURI())); } catch (QueryException ex) { throw ex; } catch (Exception ex) { throw new QueryException("Unable to load data: " + ex.getMessage(), ex); } return numberOfStatements; } /** * Load the contents of an InputStream or a URI into a database/model. * <p> * Note. <var>destinationURI</var> must be a valid URI, and does not include * the angle brackets (< and >) used to delimit URLs in iTQL. * </p> * * @param inputStream a locally supplied inputstream. Null assumes the * server will obtain the stream from the sourceURI. * @param sourceURI an idenifier for the source or inputstream. The extension * will determine the type of parser to be used. ie. .rdf or .n3 When an inputStream * is supplied the server will not attempt to read the contents of the sourceURI * @param destinationURI destination model for the source data * @return number of rows inserted into the destination model * @throws QueryException if the data fails to load */ public long load(InputStream inputStream, URI sourceURI, URI destinationURI) throws QueryException { long numberOfStatements = 0; try { if (sourceURI == null) sourceURI = DUMMY_RDF_URI; Load loadCmd = new Load(sourceURI, destinationURI, true); loadCmd.setOverrideInputStream(inputStream); numberOfStatements = (Long)loadCmd.execute(interpreter.establishConnection(loadCmd.getServerURI())); } catch (QueryException ex) { throw ex; } catch (Exception ex) { throw new QueryException("Unable to load data: " + ex.getMessage(), ex); } return numberOfStatements; } /** * Restore all the data on the specified server. If the database is not * currently empty then the database will contain the union of its current * content and the content of the backup file when this method returns. * * @param inputStream An input stream to obtain the restore from. * @param serverURI The URI of the server to restore. * @throws QueryException if the restore cannot be completed. */ public void restore(InputStream inputStream, URI serverURI) throws QueryException { restore(inputStream, serverURI, null); } /** * Restore all the data on the specified server. If the database is not * currently empty then the database will contain the union of its current * content and the content of the backup file when this method returns. * * @param inputStream a client supplied inputStream to obtain the restore * content from. If null assume the sourceURI has been supplied. * @param serverURI The URI of the server to restore. * @param sourceURI The URI of the backup file to restore from. * @throws QueryException if the restore cannot be completed. */ @SuppressWarnings("deprecation") public void restore(InputStream inputStream, URI serverURI, URI sourceURI) throws QueryException { try { Restore restoreCmd = new Restore(sourceURI, serverURI, true); restoreCmd.setOverrideInputStream(inputStream); restoreCmd.execute(interpreter.establishConnection(restoreCmd.getServerURI())); } catch (QueryException ex) { throw ex; } catch (Exception ex) { throw new QueryException("Unable to load data: " + ex.getMessage(), ex); } } /** * @param answer the answer to convert into XML * @param parent the XML element to add the query solutions to * @throws QueryException if the solutions can't be added */ private void appendSolution(Answer answer, Element parent) throws QueryException { try { Document doc = parent.getOwnerDocument(); Element VARIABLES = doc.createElementNS(TQL_NS, "variables"); // Add the variable list Element variables = (Element) parent.appendChild(VARIABLES); for (int column = 0; column < answer.getVariables().length; column++) { variables.appendChild(doc.createElement(answer.getVariables()[column].getName())); } // Add any solutions answer.beforeFirst(); while (answer.next()) { Element solution = doc.createElementNS(TQL_NS, "solution"); for (int column = 0; column < answer.getVariables().length; column++) { Object object = answer.getObject(column); // If the node is null, don't add a tag at all if (object == null) continue; // Otherwise, add a tag for the node Element variable = (Element) solution.appendChild(doc.createElementNS(TQL_NS, answer.getVariables()[column].getName())); if (object instanceof Answer) { Answer tmpAnswer = (Answer) object; try { appendSolution(tmpAnswer, variable); } finally { tmpAnswer.close(); } } else if (object instanceof LiteralImpl) { LiteralImpl literal = (LiteralImpl)object; if (literal.getDatatypeURI() != null) { variable.setAttribute("datatype", literal.getDatatypeURI().toString()); } String language = literal.getLanguage(); if (language != null && language.length() != 0) { variable.setAttribute("language", language); } variable.appendChild(doc.createTextNode(literal.getLexicalForm())); } else if (object instanceof URIReference) { variable.setAttribute("resource", ((URIReference)object).getURI().toString()); } else if (object instanceof BlankNode) { variable.setAttribute("blank-node", object.toString()); } else { throw new AssertionError("Unknown RDFNode type: " + object.getClass()); } } parent.appendChild(solution); } } catch (TuplesException e) { throw new QueryException("Couldn't build query", e); } } // // Internal methods // /** * Executes the <var>query</var> , returning a "nice" result. <p> * * The result is either a {@link java.lang.String} or a {@link * org.mulgara.query.Answer}. Any exceptions are logged, gobbled and return * as a {@link java.lang.String}. </p> * * @param query the query to execute * @param keepExceptions keep exceptions, don't convert to a message. * @return the result of the query in a "nice" format */ private Object executeQueryToNiceResult(String query, boolean keepExceptions) { // create the result Object result = null; try { // get the answer to the query executeCommand(query); Answer answer = this.interpreter.getLastAnswer(); // log the query response if (log.isDebugEnabled()) { log.debug("Query response message = " + interpreter.getLastMessage()); } // end if // turn the answer into a form we can handle if (answer != null) { // set this as the answer result = answer; } else { // get the error in an appropriate form if (this.interpreter.getLastException() != null) { // error has occurred at the interpreter if (log.isDebugEnabled()) { log.debug("Adding query error to map - " + this.interpreter.getLastException()); } // end if // set this as the answer if (keepExceptions) { result = this.interpreter.getLastException(); } else { result = this.interpreter.getLastException().getMessage(); } } else { // log that we're adding the response message if (log.isDebugEnabled()) { log.debug("Adding response message to map - " + interpreter.getLastMessage()); } // end if // set this as the answer result = interpreter.getLastMessage(); } // end if } // end if } catch (Exception e) { if (keepExceptions) { result = e; } else { // get root cause exception Throwable cause = e.getCause(); Throwable lastCause = e; while (cause != null) { lastCause = cause; cause = cause.getCause(); } // end while // format the exception message String exceptionMessage = lastCause.getMessage(); if (exceptionMessage == null) { exceptionMessage = lastCause.toString(); } // end if // turn it into a pretty string exceptionMessage = "Query Error: " + exceptionMessage; // log the message if (log.isDebugEnabled()) { log.debug(exceptionMessage); } // end if // add the exception message to the answers result = exceptionMessage; // log full stack trace StringWriter stringWriter = new StringWriter(); PrintWriter printWriter = new PrintWriter(stringWriter); e.printStackTrace(printWriter); printWriter.flush(); stringWriter.flush(); log.error("Error while processing query: '" + query + "' " + EOL + stringWriter.getBuffer().toString()); } } return result; } /** * Sets the serverURI of the interpreter. This method now does nothing. * @param serverURI The new URI of the server for the interpreter * @deprecated Establishing communication with a server now requires a connection. * Connections can be evaluated automatically with the TqlAutoInterpreter. */ public void setServerURI(String serverURI) { // Do nothing } /** * Sets the aliases associated with this bean. * * @param aliasMap the alias map associated with this bean */ @SuppressWarnings("deprecation") public void setAliasMap(HashMap<String,URI> aliasMap) { ((TqlInterpreter)parser).setAliasMap(aliasMap); interpreter.setAliasesInUse(aliasMap); } /** * Clears the last error of the interpreter. */ @SuppressWarnings("deprecation") public void clearLastError() { // Set the last error to be null interpreter.clearLastException(); } /** * Ensures all resources are closed at GC. * Especially important if this object is in a servlet * container or the user has not called close(). */ protected void finalize() throws Throwable { try { // close the interpreter session this.close(); } finally { super.finalize(); } } /** * Executes the requested command, and records if the command wants to quit. * @param command The command to execute. */ private void executeCommand(String command) { quit = !interpreter.executeCommand(command); } }