/* * 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): * XAResource access copyright 2007 The Topaz Foundation. * * [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.server.rmi; // Java 2 standard packages import java.io.InputStream; import java.io.OutputStream; import java.net.URI; import java.rmi.RemoteException; import java.rmi.server.Unreferenced; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import javax.activation.MimeType; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.jrdf.graph.Triple; import org.mulgara.query.Answer; import org.mulgara.query.ArrayAnswer; import org.mulgara.query.AskQuery; import org.mulgara.query.ConstructQuery; import org.mulgara.query.GraphExpression; import org.mulgara.query.Query; import org.mulgara.query.QueryException; import org.mulgara.query.TuplesException; import org.mulgara.rules.InitializerException; import org.mulgara.rules.Rules; import org.mulgara.rules.RulesRef; import org.mulgara.server.Session; /** * Wrapper around a {@link Session} to make it look like a * {@link RemoteSession}. * * @author <a href="http://staff.pisoftware.com/raboczi">Simon Raboczi</a> * @created 2002-01-03 * @company <A href="mailto:info@PIsoftware.com">Plugged In Software</A> * @copyright © 2002-2003 <A href="http://www.PIsoftware.com/">Plugged In * Software Pty Ltd</A> * @licence <a href="{@docRoot}/../../LICENCE">Mozilla Public License v1.1</a> */ class SessionWrapperRemoteSession implements RemoteSession, Unreferenced { /** Logger. */ private static final Logger logger = Logger.getLogger(SessionWrapperRemoteSession.class.getName()); /** The wrapped {@link Session} */ private final Session session; // // Constructor // /** * @param session the wrapped session * @throws IllegalArgumentException if <var>session</var> is <code>null</code> */ protected SessionWrapperRemoteSession(Session session) { // Validate "session" parameter if (session == null) throw new IllegalArgumentException("Null \"session\" parameter"); // Initialize fields this.session = session; } /** * Sets the Graph attribute of the SessionWrapperRemoteSession object * * @param destinationUri The new Graph value * @param sourceUri The new Graph value * @return RETURNED VALUE TO DO * @throws QueryException EXCEPTION TO DO * @throws RemoteException EXCEPTION TO DO */ public long setModel(URI destinationUri, URI sourceUri) throws QueryException, RemoteException { try { return session.setModel(destinationUri, sourceUri); } catch (Throwable t) { throw convertToQueryException(t); } } /** * Define the contents of a model via an inputstream. * * @param inputStream a remote inputstream * @param destinationUri the {@link URI} of the model to be redefined * @param sourceUri the new content for the model * @return The number of statements inserted into the model * @throws QueryException if the model can't be modified */ public long setModel(InputStream inputStream, URI destinationUri, URI sourceUri, MimeType contentType) throws QueryException { try { return session.setModel(inputStream, destinationUri, sourceUri, contentType); } catch (Throwable t) { throw convertToQueryException(t); } } /** * Sets the AutoCommit attribute of the SessionWrapperRemoteSession object * * @param autoCommit The new AutoCommit value * @throws QueryException EXCEPTION TO DO * @throws RemoteException EXCEPTION TO DO */ public void setAutoCommit(boolean autoCommit) throws QueryException, RemoteException { try { session.setAutoCommit(autoCommit); } catch (Throwable t) { throw convertToQueryException(t); } } // // Methods implementing the RemoteSession interface // public void insert(URI modelURI, Set<? extends Triple> statements) throws QueryException, RemoteException { try { session.insert(modelURI, statements); } catch (Throwable t) { throw convertToQueryException(t); } } public void insert(URI modelURI, Query query) throws QueryException, RemoteException { try { session.insert(modelURI, query); } catch (Throwable t) { throw convertToQueryException(t); } } public void delete(URI modelURI, Set<? extends Triple> statements) throws QueryException, RemoteException { try { session.delete(modelURI, statements); } catch (Throwable t) { throw convertToQueryException(t); } } public void delete(URI modelURI, Query query) throws QueryException, RemoteException { try { session.delete(modelURI, query); } catch (Throwable t) { throw convertToQueryException(t); } } /** * Backup all the data on the specified server. The database is not changed by * this method. * * @param destinationURI The URI of the file to backup into. * @throws QueryException if the backup cannot be completed. */ public void backup(URI destinationURI) throws QueryException, RemoteException { try { session.backup(destinationURI); } catch (Throwable t) { throw convertToQueryException(t); } } /** * Backup all the data on the specified server to an output stream. * The database is not changed by this method. * * @param outputStream The stream to receive the contents * @throws QueryException if the backup cannot be completed. */ public void backup(OutputStream outputStream) throws QueryException, RemoteException { try { session.backup(outputStream); } catch (Throwable t) { throw convertToQueryException(t); } } /** * Export the data in the specified graph. The database is not changed by this method. * * @param graphURI The URI of the graph to export. * @param destinationURI The URI of the file to export into. * @throws QueryException if the export cannot be completed. */ public void export(URI graphURI, URI destinationURI) throws QueryException, RemoteException { try { session.export(graphURI, destinationURI); } catch (Throwable t) { throw convertToQueryException(t); } } /** * Export the data in the specified graph using predefined namespace prefixes. * The database is not changed by this method. * * @param graphURI The URI of the graph to export. * @param destinationURI The URI of the file to export into. * @param prefixes An optional mapping for pre-populating the RDF/XML namespace prefixes. * @throws QueryException if the export cannot be completed. */ public void export(URI graphURI, URI destinationURI, Map<String,URI> prefixes) throws QueryException, RemoteException { try { session.export(graphURI, destinationURI, prefixes); } catch (Throwable t) { throw convertToQueryException(t); } } /** * 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 server or model 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, MimeType contentType) throws QueryException, RemoteException { try { session.export(graphURI, outputStream, contentType); } catch (Throwable t) { throw convertToQueryException(t); } } /** * Export the data in the specified graph to an output stream using predefined namespace prefixes. * The database is not changed by this method. * * @param graphURI The URI of the server or model to export. * @param outputStream The stream to receive the contents * @param prefixes An optional mapping for pre-populating the RDF/XML namespace prefixes. * @throws QueryException if the export cannot be completed. */ public void export(URI graphURI, OutputStream outputStream, Map<String,URI> prefixes, MimeType contentType) throws QueryException, RemoteException { try { session.export(graphURI, outputStream, prefixes, contentType); } catch (Throwable t) { throw convertToQueryException(t); } } /** * Restore all the data on the server. If the database is not * currently empty then the current contents of the database will be replaced * with the content of the backup file when this method returns. * * @param sourceURI The URI of the backup file to restore from. * @throws QueryException if the restore cannot be completed. */ public void restore(URI sourceURI) throws QueryException, RemoteException { try { session.restore(sourceURI); } catch (Throwable t) { throw convertToQueryException(t); } } /** * Restore all the data on the server. If the database is not * currently empty then the current contents of the database will be replaced * with 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 sourceURI The URI of the backup file to restore from. * @throws QueryException if the restore cannot be completed. */ public void restore(InputStream inputStream, URI sourceURI) throws QueryException, RemoteException { try { session.restore(inputStream, sourceURI); } catch (Throwable t) { throw convertToQueryException(t); } } /** * Adds a new graph to the SystemGraph set. * * @param graphURI The URI of the graph to create. * @param graphTypeURI The URI of the type for the new graph. * @throws QueryException Unable to create the new graph. * @throws RemoteException Network error attempting to create the new graph. */ public void createModel(URI graphURI, URI graphTypeURI) throws QueryException, RemoteException { try { session.createModel(graphURI, graphTypeURI); } catch (Throwable t) { throw convertToQueryException(t); } } /** * Remove a graph and all its statements. * * @param uri The URI of the graph. * @throws QueryException Unable to remove the graph. * @throws RemoteException Network error attempting to remove the new graph. */ public void removeModel(URI uri) throws QueryException, RemoteException { try { session.removeModel(uri); } catch (Throwable t) { throw convertToQueryException(t); } } /** * Tests if a graph exists. * @param uri the URI of the graph. * @see org.mulgara.server.rmi.RemoteSession#modelExists(java.net.URI) */ public boolean modelExists(URI uri) throws QueryException, RemoteException { try { return session.modelExists(uri); } catch (Throwable t) { throw convertToQueryException(t); } } /** * Commits a transaction on this session. * NOTE: This is not for general use. Use the transaction API. * * @throws QueryException Unable to commit the transaction. * @throws RemoteException There was a network error. */ public void commit() throws QueryException, RemoteException { try { session.commit(); } catch (Throwable t) { throw convertToQueryException(t); } } /** * Rolls back a transaction on this session. * NOTE: This is not for general use. Use the transaction API. * * @throws QueryException Unable to roll back the transaction. * @throws RemoteException There was a network error. */ public void rollback() throws QueryException, RemoteException { try { session.rollback(); } catch (Throwable t) { throw convertToQueryException(t); } } /** * Queries the local server and returns a remote reference to an Answer. * * @param query The query to perform. * @return A remote reference to an Answer. * @throws QueryException The query caused an exception. * @throws RemoteException Thrown when there is a network error. */ public RemoteAnswer query(Query query) throws QueryException, RemoteException { return convertToRemoteAnswer(session.query(query)); } /** * Queries the local server and returns the boolean result. * * @param query The query to perform. * @return <code>true</code> if the query returns a non-empty result. * @throws QueryException The query caused an exception. * @throws RemoteException Thrown when there is a network error. */ public boolean query(AskQuery query) throws QueryException, RemoteException { try { return session.query(query); } catch (Throwable t) { throw convertToQueryException(t); } } /** * Queries the local server and returns a remote reference to an Answer. * * @param query The query to perform. * @return A remote reference to an Answer. * @throws QueryException The query caused an exception. * @throws RemoteException Thrown when there is a network error. */ public RemoteAnswer query(ConstructQuery query) throws QueryException, RemoteException { return convertToRemoteAnswer(session.query(query)); } /** * Queries a local server for a list of queries. Wraps the resulting answers in remote * objects before returning them. * * @param queries A List of Query objects to be executed in order. * @return A list of remote references to Answer objects. This list gets fully marshalled for returning. * @throws QueryException There was an exception on one of the queries, or a query returned a non-Answer. * @throws RemoteException Thrown when there is a network error. */ public List<Object> query(List<Query> queries) throws QueryException, RemoteException { try { List<Answer> localAnswers = session.query(queries); List<Object> servedAnswers = new ArrayList<Object>(localAnswers.size()); Iterator<Answer> i = localAnswers.iterator(); while (i.hasNext()) { Object servedAnswer = null; Answer ans = i.next(); try { if (ans.getRowExpectedCount() <= RemoteAnswer.MARSHALL_SIZE_LIMIT) { // don't need to wrap this in an // AnswerWrapperRemoteAnswerSerialised as the other end can handle // any kind of object as it comes out of the list servedAnswer = new ArrayAnswer(ans); } else { servedAnswer = new AnswerWrapperRemoteAnswer(ans); } ans.close(); } catch (TuplesException e) { throw new QueryException("Error getting information for answer", e); } servedAnswers.add(servedAnswer); } return servedAnswers; } catch (Throwable t) { throw convertToQueryException(t); } } /** * Extract {@link Rules} from the data found in a model. * * @param ruleModel The URI of the model with the rule structure. * @param baseModel The graph expression with the base data to read. * @param destModel The URI of the model to receive the entailed data. * @return The extracted rule structure. * @throws InitializerException If there was a problem accessing the rule loading module. * @throws QueryException If there was a problem loading the rule structure. */ public RulesRef buildRules(URI ruleModel, GraphExpression baseModel, URI destModel) throws QueryException, org.mulgara.rules.InitializerException, RemoteException { RulesRef r = null; try { r = session.buildRules(ruleModel, baseModel, destModel); } catch (QueryException qe) { throw qe; } catch (org.mulgara.rules.InitializerException ie) { throw ie; } catch (Throwable t) { throw convertToQueryException(t); } return r; } /** * Rules a set of {@link Rules} on its defined model. * * @param rules The rules to be run. */ public void applyRules(RulesRef rules) throws QueryException, RemoteException { try { session.applyRules(rules); } catch (QueryException re) { throw re; } catch (Throwable t) { throw new QueryException(t.toString(), t); } } /** * METHOD TO DO * * @throws RemoteException EXCEPTION TO DO */ public void close() throws QueryException, RemoteException { try { session.close(); } catch (Throwable t) { throw convertToQueryException(t); } } /** * METHOD TO DO * * @param securityDomain PARAMETER TO DO * @param username PARAMETER TO DO * @param password PARAMETER TO DO * @throws RemoteException EXCEPTION TO DO */ public void login(URI securityDomain, String username, char[] password) throws RemoteException { session.login(securityDomain, username, password); } public RemoteXAResource getXAResource() throws QueryException, RemoteException { try { return new XAResourceWrapperRemoteXAResource(session.getXAResource()); } catch (Throwable t) { throw convertToQueryException(t); } } public RemoteXAResource getReadOnlyXAResource() throws QueryException, RemoteException { try { return new XAResourceWrapperRemoteXAResource(session.getReadOnlyXAResource()); } catch (Throwable t) { throw convertToQueryException(t); } } public void setIdleTimeout(long millis) throws QueryException, RemoteException { try { session.setIdleTimeout(millis); } catch (Throwable t) { throw convertToQueryException(t); } } public void setTransactionTimeout(long millis) throws QueryException, RemoteException { try { session.setTransactionTimeout(millis); } catch (Throwable t) { throw convertToQueryException(t); } } public boolean ping() throws QueryException, RemoteException { try { return session.ping(); } catch (Throwable t) { throw convertToQueryException(t); } } public void unreferenced() { if (logger.isDebugEnabled()) logger.debug("Closing unreferenced remote session " + session); try { close(); } catch (Exception e) { if (logger.isEnabledFor(Level.WARN)) logger.warn("Error closing unreferenced session " + session, e); } } // Construct an exception chain that will pass over RMI. protected Throwable mapThrowable(Throwable t) { Throwable cause = t.getCause(); Throwable mappedCause = cause != null ? mapThrowable(cause) : null; Class<? extends Throwable> tClass = t.getClass(); String tClassName = tClass.getName(); if ( t instanceof QueryException || ( t instanceof Error || t instanceof RuntimeException ) && tClassName.startsWith("java.") ) { // This exception can pass over RMI - but maybe not the cause. // Check if the cause has been reinstantiated. if (cause == mappedCause) { // There has been no change to the cause chain so just return this // Throwable unchanged. return t; } // TODO use reflection to instantiate a Throwable of the same class. // for now we just fall through and construct a QueryException. } String message = t.getMessage(); if (!(t instanceof QueryException)) { // Prepend the class name to the message message = tClassName + ": " + message; } QueryException qe = new QueryException(message, mappedCause); qe.setStackTrace(t.getStackTrace()); return qe; } /** * Converts an Answer to a RemoteAnswer. Closure of the original Answer is handled. * @param ans The Answer to convert. * @return A new RemoteAnswer containing the same data as the original Answer. This * needs to be closed when it is finished with. * @throws QueryException Accessing the data caused an exception. * @throws RemoteException Thrown when there is a network error. */ private RemoteAnswer convertToRemoteAnswer(Answer ans) throws QueryException, RemoteException { try { try { if (ans.getRowExpectedCount() <= RemoteAnswer.MARSHALL_SIZE_LIMIT) { RemoteAnswer serialAnswer = new AnswerWrapperRemoteAnswerSerialised(new ArrayAnswer(ans)); ans.close(); return serialAnswer; } else { return new AnswerWrapperRemoteAnswer(ans); } } catch (TuplesException e) { throw new QueryException("Error getting information for answer", e); } } catch (Throwable t) { throw convertToQueryException(t); } } /** * Return t if it is already a QueryException or wrap it as one. * * @return t if it is already a QueryException or wrap it as one. */ private QueryException convertToQueryException(Throwable t) { t = mapThrowable(t); if (t instanceof QueryException)return (QueryException) t; return new QueryException(t.toString(), t); } }