/* * Copyright Aduna (http://www.aduna-software.com/) (c) 2008. * * Licensed under the Aduna BSD-style license. */ package org.openrdf.sail.rdbms; import info.aduna.concurrent.locks.Lock; import info.aduna.iteration.CloseableIteration; import info.aduna.iteration.Iterations; import java.sql.SQLException; import java.util.Collection; import java.util.Collections; import java.util.List; import org.openrdf.model.Namespace; import org.openrdf.model.Resource; import org.openrdf.model.Statement; import org.openrdf.model.URI; import org.openrdf.model.Value; import org.openrdf.query.BindingSet; import org.openrdf.query.Dataset; import org.openrdf.query.QueryEvaluationException; import org.openrdf.query.algebra.TupleExpr; import org.openrdf.query.algebra.evaluation.EvaluationStrategy; import org.openrdf.sail.SailConnection; import org.openrdf.sail.SailException; import org.openrdf.sail.helpers.DefaultSailChangedEvent; import org.openrdf.sail.helpers.SailConnectionBase; import org.openrdf.sail.inferencer.InferencerConnection; import org.openrdf.sail.rdbms.evaluation.RdbmsEvaluationFactory; import org.openrdf.sail.rdbms.exceptions.RdbmsException; import org.openrdf.sail.rdbms.iteration.NamespaceIteration; import org.openrdf.sail.rdbms.iteration.RdbmsResourceIteration; import org.openrdf.sail.rdbms.iteration.RdbmsStatementIteration; import org.openrdf.sail.rdbms.managers.NamespaceManager; import org.openrdf.sail.rdbms.model.RdbmsResource; import org.openrdf.sail.rdbms.model.RdbmsStatement; import org.openrdf.sail.rdbms.model.RdbmsURI; import org.openrdf.sail.rdbms.model.RdbmsValue; import org.openrdf.sail.rdbms.optimizers.RdbmsQueryOptimizer; /** * Coordinates the triple store, namespace manager, optimizer, and evaluation * strategy into the {@link SailConnection} interface. * * @author James Leigh * */ public class RdbmsConnection extends SailConnectionBase implements InferencerConnection { private RdbmsStore sail; private RdbmsValueFactory vf; private RdbmsTripleRepository triples; private NamespaceManager namespaces; private RdbmsQueryOptimizer optimizer; private RdbmsEvaluationFactory factory; private java.util.concurrent.locks.Lock lock; private boolean locked; public RdbmsConnection(RdbmsStore sail, RdbmsTripleRepository triples) { super(sail); this.sail = sail; this.vf = sail.getValueFactory(); this.triples = triples; } public void setNamespaces(NamespaceManager namespaces) { this.namespaces = namespaces; } public void setRdbmsQueryOptimizer(RdbmsQueryOptimizer optimizer) { this.optimizer = optimizer; } public void setRdbmsEvaluationFactory(RdbmsEvaluationFactory factory) { this.factory = factory; } public void setLock(java.util.concurrent.locks.Lock lock) { this.lock = lock; } @Override protected void addStatementInternal(Resource subj, URI pred, Value obj, Resource... contexts) throws SailException { addStatement(subj, pred, obj, true, contexts); } protected void addStatement(Resource subj, URI pred, Value obj, boolean explicit, Resource... contexts) throws SailException { try { if (contexts.length == 0) { contexts = new Resource[] { null }; } for (Resource context : contexts) { triples.add(vf.createStatement(subj, pred, obj, explicit, context)); } } catch (SQLException e) { throw new RdbmsException(e); } catch (InterruptedException e) { throw new RdbmsException(e); } } @Override protected void clearInternal(Resource... contexts) throws SailException { removeStatements(null, null, null, contexts); } @Override protected void closeInternal() throws SailException { try { triples.close(); } catch (SQLException e) { throw new RdbmsException(e); } finally { unlock(); } } @Override protected void commitInternal() throws SailException { List <RdbmsStatement> committedStatements = null; try { committedStatements = triples.commit(); unlock(); } catch (SQLException e) { throw new RdbmsException(e); } catch (InterruptedException e) { throw new RdbmsException(e); } if (hasConnectionListeners()) { for (Statement st : committedStatements) notifyStatementAdded(st); } sail.notifySailChanged(triples.getSailChangedEvent()); // create a fresh event object. triples.setSailChangedEvent(new DefaultSailChangedEvent(sail)); } @Override protected RdbmsResourceIteration getContextIDsInternal() throws SailException { try { return triples.findContexts(); } catch (SQLException e) { throw new RdbmsException(e); } } @Override protected CloseableIteration<? extends Statement, SailException> getStatementsInternal( Resource subj, URI pred, Value obj, boolean includeInferred, Resource... contexts) throws SailException { RdbmsResource s = vf.asRdbmsResource(subj); RdbmsURI p = vf.asRdbmsURI(pred); RdbmsValue o = vf.asRdbmsValue(obj, pred); RdbmsResource[] c = vf.asRdbmsResource(contexts); return triples.find(s, p, o, includeInferred, c); } @Override protected void removeStatementsInternal(Resource subj, URI pred, Value obj, Resource... contexts) throws SailException { removeStatements(subj, pred, obj, true, contexts); } protected int removeStatements(Resource subj, URI pred, Value obj, boolean explicit, Resource... contexts) throws SailException { RdbmsResource s = vf.asRdbmsResource(subj); RdbmsURI p = vf.asRdbmsURI(pred); RdbmsValue o = vf.asRdbmsValue(obj, pred); RdbmsResource[] c = vf.asRdbmsResource(contexts); int removeCount = 0; List<RdbmsStatement> removedStatements = Collections.emptyList(); if (hasConnectionListeners()) { // We need to iterate over all matching triples so that they can // be reported RdbmsStatementIteration it = triples.find(s, p, o, explicit, c); removedStatements = Iterations.asList(it); } removeCount += triples.remove(s, p, o, explicit, c); for (Statement st : removedStatements) { notifyStatementRemoved(st); } return removeCount; } @Override protected void rollbackInternal() throws SailException { try { triples.rollback(); } catch (SQLException e) { throw new RdbmsException(e); } finally { unlock(); } } @Override protected CloseableIteration<BindingSet, QueryEvaluationException> evaluateInternal( TupleExpr expr, Dataset dataset, BindingSet bindings, boolean includeInferred) throws SailException { triples.flush(); try { TupleExpr tupleExpr; EvaluationStrategy strategy; strategy = factory.createRdbmsEvaluation(dataset); tupleExpr = optimizer.optimize(expr, dataset, bindings, strategy); return strategy.evaluate(tupleExpr, bindings); } catch (QueryEvaluationException e) { throw new SailException(e); } } @Override protected void clearNamespacesInternal() throws SailException { namespaces.clearPrefixes(); } @Override protected String getNamespaceInternal(String prefix) throws SailException { Namespace ns = namespaces.findByPrefix(prefix); if (ns == null) return null; return ns.getName(); } @Override protected CloseableIteration<? extends Namespace, SailException> getNamespacesInternal() throws SailException { Collection<? extends Namespace> ns = namespaces.getNamespacesWithPrefix(); return new NamespaceIteration(ns.iterator()); } @Override protected void removeNamespaceInternal(String prefix) throws SailException { namespaces.removePrefix(prefix); } @Override protected void setNamespaceInternal(String prefix, String name) throws SailException { namespaces.setPrefix(prefix, name); } @Override protected long sizeInternal(Resource... contexts) throws SailException { try { return triples.size(vf.asRdbmsResource(contexts)); } catch (SQLException e) { throw new RdbmsException(e); } } @Override protected void startTransactionInternal() throws SailException { try { lock(); triples.begin(); } catch (SQLException e) { throw new RdbmsException(e); } } public boolean addInferredStatement(Resource subj, URI pred, Value obj, Resource... contexts) throws SailException { Lock conLock = getSharedConnectionLock(); try { verifyIsOpen(); Lock txnLock = getTransactionLock(); try { autoStartTransaction(); addStatement(subj, pred, obj, false, contexts); } finally { txnLock.release(); } } finally { conLock.release(); } return true; } public void clearInferred(Resource... contexts) throws SailException { Lock conLock = getSharedConnectionLock(); try { verifyIsOpen(); Lock txnLock = getTransactionLock(); try { autoStartTransaction(); removeStatements(null, null, null, false, contexts); } finally { txnLock.release(); } } finally { conLock.release(); } } public void flushUpdates() throws SailException { // no-op; changes are reported as soon as they come in } public boolean removeInferredStatement(Resource subj, URI pred, Value obj, Resource... contexts) throws SailException { Lock conLock = getSharedConnectionLock(); try { verifyIsOpen(); Lock txnLock = getTransactionLock(); try { autoStartTransaction(); int removeCount = removeStatements(subj, pred, obj, false, contexts); return removeCount > 0; } finally { txnLock.release(); } } finally { conLock.release(); } } @Override protected void finalize() throws Throwable { unlock(); super.finalize(); } private void lock() { if (lock != null) { lock.lock(); locked = true; } } private void unlock() { if (locked && lock != null) { locked = false; lock.unlock(); } } }