/**
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
licenses@blazegraph.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Created on Mar 19, 2012
*/
package com.bigdata.gom.om;
import java.util.List;
import org.apache.log4j.Logger;
import org.openrdf.model.Graph;
import org.openrdf.model.Statement;
import org.openrdf.query.BindingSet;
import org.openrdf.query.GraphQuery;
import org.openrdf.query.GraphQueryResult;
import org.openrdf.query.QueryEvaluationException;
import org.openrdf.query.QueryLanguage;
import org.openrdf.query.TupleQuery;
import org.openrdf.query.TupleQueryResult;
import org.openrdf.repository.RepositoryException;
import com.bigdata.bop.engine.QueryEngine;
import com.bigdata.bop.fed.QueryEngineFactory;
import com.bigdata.gom.gpo.GPO;
import com.bigdata.gom.gpo.IGPO;
import com.bigdata.journal.Journal;
import com.bigdata.rdf.internal.IV;
import com.bigdata.rdf.model.BigdataResource;
import com.bigdata.rdf.model.BigdataValue;
import com.bigdata.rdf.model.BigdataValueFactory;
import com.bigdata.rdf.sail.BigdataSailRepository;
import com.bigdata.rdf.sail.BigdataSailRepositoryConnection;
import com.bigdata.rdf.sail.Sesame2BigdataIterator;
import com.bigdata.rdf.sparql.ast.cache.CacheConnectionFactory;
import com.bigdata.rdf.sparql.ast.cache.ICacheConnection;
import com.bigdata.rdf.sparql.ast.cache.IDescribeCache;
import com.bigdata.striterator.CloseableIteratorWrapper;
import cutthecrap.utils.striterators.ICloseableIterator;
/**
* An {@link IObjectManager} for use with an embedded database, including JSP
* pages running in the same webapp as the NanoSparqlServer and applications
* that do not expose a public web interface.
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
*/
public class ObjectManager extends ObjectMgrModel {
private static final Logger log = Logger.getLogger(ObjectManager.class);
final private BigdataSailRepository m_repo;
final private boolean readOnly;
final private IDescribeCache m_describeCache;
/**
*
* @param endpoint
* A SPARQL endpoint that may be used to communicate with the
* database.
* @param cxn
* A connection to the database.
*
* @throws RepositoryException
*/
public ObjectManager(final String endpoint, final BigdataSailRepository cxn) throws RepositoryException {
super(endpoint, (BigdataValueFactory) cxn.getValueFactory());
m_repo = cxn;
// final AbstractTripleStore tripleStore = cxn.getDatabase();
this.readOnly = !cxn.isWritable();
/*
* FIXME The DESCRIBE cache feature is not yet finished. This code will
* not obtain a connection to the DESCRIBE cache unless an unisolated
* query or update operation has already run against the query engine.
* This is a known bug and will be resolved as we work through the MVCC
* cache coherence for the DESCRIBE cache.
*/
{
final QueryEngine queryEngine = QueryEngineFactory.getInstance()
.getStandaloneQueryController((Journal) m_repo
.getSail().getIndexManager());
final ICacheConnection cacheConn = CacheConnectionFactory
.getExistingCacheConnection(queryEngine);
if (cacheConn != null) {
// FIXME The sail is no longer associated with a timestamp. See BLZG-2041.
m_describeCache = null;
// m_describeCache = cacheConn.getDescribeCache(
// cxn.getSail().getNamespace(), cxn.getSail().getTimestamp());
} else {
m_describeCache = null;
}
}
/*
* Note: This MUST NOT be done by default. It breaks the ACID contract
* since any incremental write will be combined with any other writes
* because this class does not (and MUST NOT) hold the UNISOLATED
* connection across its life cycle.
*/
// /**
// * Local ObjectManager can flush incrementally from the dirty list
// *
// * A maximum size of 4000 dirty objects is a sensible default.
// */
// m_maxDirtyListSize = 4000;
}
/**
* This may be used to break ACID and perform incremental eviction of dirty
* objects to the backing store. However, the use of this method is NOT
* recommended as the updates will become durable incrementally rather than
* atomically.
*
* @param newValue
* The new maximum dirty list size (default is
* {@link Integer#MAX_VALUE}).
*/
public void setMaxDataListSize(final int newValue) {
if (newValue <= 0)
throw new IllegalArgumentException();
this.m_maxDirtyListSize = newValue;
}
/**
* @return direct repository connection
*/
public BigdataSailRepository getRepository() {
return m_repo;
}
@Override
public void close() {
super.close();
try {
if (m_repo.getSail().isOpen())
m_repo.shutDown();
} catch (RepositoryException e) {
// Per the API.
throw new IllegalStateException(e);
}
}
@Override
public ICloseableIterator<BindingSet> evaluate(final String query) {
final BigdataSailRepositoryConnection cxn;
try {
cxn = getQueryConnection();
} catch (RepositoryException e1) {
throw new RuntimeException(e1);
}
try {
// Setup the query.
final TupleQuery q = cxn.prepareTupleQuery(QueryLanguage.SPARQL,
query);
// Note: evaluate() runs asynchronously and must be closed().
final TupleQueryResult res = q.evaluate();
// Will close the TupleQueryResult.
return new Sesame2BigdataIterator<BindingSet, QueryEvaluationException>(
res) {
public void close() {
// Close the TupleQueryResult.
super.close();
try {
// Close the connection.
cxn.close();
} catch (RepositoryException e) {
throw new RuntimeException(e);
}
}
};
} catch (Throwable t) {
// Error preparing the query.
try {
// Close the connection
cxn.close();
} catch (RepositoryException e) {
log.error(e, e);
}
throw new RuntimeException("query=" + query, t);
}
}
public ICloseableIterator<Statement> evaluateGraph(final String query) {
final BigdataSailRepositoryConnection cxn;
try {
cxn = getQueryConnection();
} catch (RepositoryException e1) {
throw new RuntimeException(e1);
}
try {
// Setup the query.
final GraphQuery q = cxn.prepareGraphQuery(QueryLanguage.SPARQL,
query);
// Note: evaluate() runs asynchronously and must be closed().
final GraphQueryResult res = q.evaluate();
// Will close the TupleQueryResult.
return new Sesame2BigdataIterator<Statement, QueryEvaluationException>(
res) {
public void close() {
// Close the TupleQueryResult.
super.close();
try {
// Close the connection.
cxn.close();
} catch (RepositoryException e) {
throw new RuntimeException(e);
}
}
};
} catch (Throwable t) {
// Error preparing the query.
try {
// Close the connection
cxn.close();
} catch (RepositoryException e) {
log.error(e, e);
}
throw new RuntimeException("query=" + query, t);
}
}
@Override
public void execute(String updateStr) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
}
@Override
public boolean isPersistent() {
return true; //
}
@Override
protected void materializeWithDescribe(final IGPO gpo) {
if (gpo == null)
throw new IllegalArgumentException();
/*
* At present the DESCRIBE query will simply return a set of statements
* equivalent to a TupleQuery <id, ?, ?>.
*/
if (m_describeCache != null) {
final IV<?, ?> iv = addResolveIV(gpo);
final Graph g = m_describeCache.lookup(iv);
if (g != null) {
initGPO((GPO) gpo,
new CloseableIteratorWrapper<Statement>(g.iterator()));
return;
}
}
super.materializeWithDescribe(gpo);
}
/**
* Attempt to add/resolve the {@link IV} for the {@link IGPO}.
*
* @param gpo
* The {@link IGPO}.
*
* @return The {@link IV} -or- <code>null</code> iff this is a read-only
* connection and the {@link BigdataResource} associated with that
* {@link IGPO} is not in the lexicon.
*
* FIXME This code path is horribly inefficient. It is create a new
* connection for each such resolution (since BLZG-2041) and
* handling resolution on an object by object basis. It is also not
* making a clear distinction between a read-only and up-to-the
* moment read/write connection.
*/
private IV<?, ?> addResolveIV(final IGPO gpo) {
final BigdataResource id = gpo.getId();
IV<?, ?> iv = id.getIV();
if (iv == null) {
/*
* Attempt to resolve the IV. If the connection allows updates then
* this will cause an IV to be assigned if the Resource was not
* already in the lexicon.
*/
final BigdataValue[] values = new BigdataValue[] { id };
BigdataSailRepositoryConnection conn = null;
try {
conn = getQueryConnection();
conn.getTripleStore().getLexiconRelation()
.addTerms(values, values.length, readOnly);
// Note: MAY still be null!
iv = id.getIV();
} catch(RepositoryException ex) {
throw new RuntimeException(ex);
} finally {
if (conn != null) {
try {
conn.close();
} catch (RepositoryException ex2) {
log.warn(ex2, ex2);
}
}
}
}
// May be null.
return iv;
}
@Override
protected void flushStatements(final List<Statement> m_inserts,
final List<Statement> m_removes) {
BigdataSailRepositoryConnection cxn = null;
try {
// Connection supporting updates.
cxn = getConnection();
// handle batch removes
for (Statement stmt : m_removes) {
cxn.remove(stmt);
}
// handle batch inserts
for (Statement stmt : m_inserts) {
cxn.add(stmt);
}
// Atomic commit.
cxn.commit();
} catch (Throwable t) {
if (cxn != null) {
try {
cxn.rollback();
} catch (RepositoryException e) {
log.error(e, e);
}
}
} finally {
if (cxn != null) {
try {
cxn.close();
} catch (RepositoryException e) {
log.error(e, e);
}
}
}
}
/**
* Return an updatable connection.
*
* @throws RepositoryException
*/
private BigdataSailRepositoryConnection getConnection()
throws RepositoryException {
final BigdataSailRepositoryConnection c = m_repo.getConnection();
c.setAutoCommit(false);
return c;
}
/**
* Return a read-only connection.
*
* @throws RepositoryException
*/
private BigdataSailRepositoryConnection getQueryConnection()
throws RepositoryException {
final BigdataSailRepositoryConnection c = m_repo
.getReadOnlyConnection();
return c;
}
}