org.apache.jena.tdb.transaction.DatasetGraphTransaction Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jena-tdb Show documentation
Show all versions of jena-tdb Show documentation
TDB is a storage subsystem for Jena and ARQ, it is a native triple store providing persistent storage of triples/quads.
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jena.tdb.transaction ;
import static java.lang.ThreadLocal.withInitial ;
import org.apache.jena.atlas.lib.Sync ;
import org.apache.jena.graph.Graph ;
import org.apache.jena.graph.Node ;
import org.apache.jena.query.ReadWrite ;
import org.apache.jena.sparql.JenaTransactionException ;
import org.apache.jena.sparql.core.DatasetGraph ;
import org.apache.jena.sparql.core.DatasetGraphTrackActive ;
import org.apache.jena.sparql.util.Context ;
import org.apache.jena.tdb.StoreConnection ;
import org.apache.jena.tdb.TDB ;
import org.apache.jena.tdb.base.file.Location ;
import org.apache.jena.tdb.store.DatasetGraphTDB ;
import org.apache.jena.tdb.store.GraphNonTxnTDB ;
import org.apache.jena.tdb.store.GraphTxnTDB ;
/**
* A transactional {@code DatasetGraph} that allows one active transaction per thread.
*
* {@link DatasetGraphTxn} holds the {@link Transaction} object.
*
* This is analogous to a "connection" in JDBC.
* It is a holder of a {@link StoreConnection} combined with the machinary from
* {@link DatasetGraphTrackActive}.
*
* Not considered to be in the public API.
*/
public class DatasetGraphTransaction extends DatasetGraphTrackActive implements Sync {
/*
* Initially, the app can use this DatasetGraph non-transactionally. But as
* soon as it starts a transaction, the dataset can only be used inside
* transactions.
*
* There are two per-thread state variables: txn: ThreadLocalTxn -- the
* transactional , one time use dataset isInTransactionB: ThreadLocalBoolean
* -- flags true between begin and commit/abort, and end for read
* transactions.
*/
// Transaction per thread per DatasetGraphTransaction object.
private ThreadLocal txn = withInitial(() -> null);
private ThreadLocal inTransaction = withInitial(() -> false);
private final StoreConnection sConn;
private boolean isClosed = false;
public DatasetGraphTransaction(Location location) {
sConn = StoreConnection.make(location) ;
}
public Location getLocation() {
return sConn.getLocation() ;
}
// getCurrentTxnDSG
public DatasetGraphTDB getDatasetGraphToQuery() {
checkNotClosed() ;
return get() ;
}
/** Access the base storage - use with care */
public DatasetGraphTDB getBaseDatasetGraph() {
checkNotClosed() ;
return sConn.getBaseDataset() ;
}
/*private*/public/*for development*/ static boolean promotion = false ;
/*private*/public/*for development*/ static boolean readCommittedPromotion = true ;
@Override public DatasetGraph getW() {
if ( isInTransaction() ) {
if ( promotion ) {
DatasetGraphTxn dsgTxn = txn.get() ;
if ( dsgTxn.getTransaction().isRead() ) {
TransactionManager txnMgr = dsgTxn.getTransaction().getTxnMgr() ;
DatasetGraphTxn dsgTxn2 = txnMgr.promote(dsgTxn, readCommittedPromotion) ;
txn.set(dsgTxn2);
}
}
}
return super.getW() ;
}
/** Get the current DatasetGraphTDB */
@Override
public DatasetGraphTDB get() {
if ( isInTransaction() ) {
DatasetGraphTxn dsgTxn = txn.get() ;
if ( dsgTxn == null )
throw new TDBTransactionException("In a transaction but no transactional DatasetGraph") ;
return dsgTxn.getView() ;
}
if ( sConn.haveUsedInTransaction() )
throw new TDBTransactionException("Not in a transaction") ;
// Never used in a transaction - return underlying database for old
// style (non-transactional) usage.
return sConn.getBaseDataset() ;
}
@Override
protected void checkActive() {
checkNotClosed() ;
if ( !isInTransaction() )
throw new JenaTransactionException("Not in a transaction (" + getLocation() + ")") ;
}
@Override
protected void checkNotActive() {
checkNotClosed() ;
if ( sConn.haveUsedInTransaction() && isInTransaction() )
throw new JenaTransactionException("Currently in a transaction (" + getLocation() + ")") ;
}
protected void checkNotClosed() {
if ( isClosed )
throw new JenaTransactionException("Already closed") ;
}
@Override
public boolean isInTransaction() {
checkNotClosed() ;
return inTransaction.get() ;
}
public boolean isClosed() {
return isClosed ;
}
public void syncIfNotTransactional() {
if ( !sConn.haveUsedInTransaction() )
sConn.getBaseDataset().sync() ;
}
@Override
public Graph getDefaultGraph() {
if ( sConn.haveUsedInTransaction() )
return new GraphTxnTDB(this, null) ;
else
return new GraphNonTxnTDB(getBaseDatasetGraph(), null) ;
}
@Override
public Graph getGraph(Node graphNode) {
if ( sConn.haveUsedInTransaction() )
return new GraphTxnTDB(this, graphNode) ;
else
return new GraphNonTxnTDB(getBaseDatasetGraph(), graphNode) ;
}
@Override
protected void _begin(ReadWrite readWrite) {
checkNotClosed() ;
DatasetGraphTxn dsgTxn = sConn.begin(readWrite) ;
txn.set(dsgTxn) ;
inTransaction.set(true) ;
}
@Override
protected void _commit() {
checkNotClosed() ;
txn.get().commit() ;
inTransaction.set(false) ;
}
@Override
protected void _abort() {
checkNotClosed() ;
txn.get().abort() ;
inTransaction.set(false) ;
}
@Override
protected void _end() {
checkNotClosed() ;
DatasetGraphTxn dsg = txn.get() ;
// It's null if end() already called.
if ( dsg == null ) {
TDB.logInfo.warn("Transaction already ended") ;
return ;
}
try {
// begin(W)..end() throws an exception.
txn.get().end() ;
} finally {
// May already be false due to .commit/.abort.
inTransaction.set(false) ;
txn.set(null) ;
}
}
@Override
public boolean supportsTransactions() { return true ; }
@Override
public boolean supportsTransactionAbort() { return true ; }
@Override
public String toString() {
try {
if ( isInTransaction() )
// Risky ...
return get().toString() ;
// Hence ...
return getBaseDatasetGraph().toString() ;
}
catch (Throwable th) {
return "DatasetGraphTransaction" ;
}
}
@Override
protected void _close() {
if ( isClosed )
return ;
if ( !sConn.haveUsedInTransaction() ) {
synchronized(this) {
if ( isClosed ) return ;
isClosed = true ;
if ( ! sConn.isValid() ) {
// There may be another DatasetGraphTransaction using this location
// and that DatasetGraphTransaction has been closed, invalidating
// the StoreConnection.
return ;
}
DatasetGraphTDB dsg = sConn.getBaseDataset() ;
dsg.sync() ;
dsg.close() ;
StoreConnection.release(getLocation()) ;
return ;
}
}
if ( isInTransaction() ) {
TDB.logInfo.warn("Attempt to close a DatasetGraphTransaction while a transaction is active - ignored close (" + getLocation() + ")") ;
return ;
}
txn.remove() ;
inTransaction.remove() ;
isClosed = true ;
}
@Override
public Context getContext() {
// Not the transactional dataset.
return getBaseDatasetGraph().getContext() ;
}
public StoreConnection getStoreConnection() {
return sConn ;
}
@Override
public void sync() {
if ( !sConn.haveUsedInTransaction() && get() != null )
get().sync() ;
}
}