/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2000-2003
* Sleepycat Software. All rights reserved.
*
* $Id: CurrentTransaction.java,v 1.6 2003/10/18 19:54:19 mhayes Exp $
*/
package com.sleepycat.bdb;
import com.sleepycat.db.Db;
import com.sleepycat.db.DbEnv;
import com.sleepycat.db.DbException;
import com.sleepycat.db.DbTxn;
import com.sleepycat.bdb.util.RuntimeExceptionWrapper;
import java.io.FileNotFoundException;
import java.util.WeakHashMap;
/**
* Provides access to the current transaction for the current thread within the
* context of a Berkeley DB environment. This class provides explicit
* transaction control beyond that provided by the {@link TransactionRunner}
* class. However, both methods of transaction control manage per-thread
* transactions.
*
* @author Mark Hayes
*/
public class CurrentTransaction {
private DbEnv dbEnv;
private ThreadLocal currentTrans = new ThreadLocal();
/**
* Gets the CurrentTransaction accessor for a specified Berkeley DB
* environment. This method always returns the same reference when called
* more than once with the same environment parameter.
*
* @param env is an open Berkeley DB environment.
*
* @return the CurrentTransaction accessor for the given environment, or
* null if the environment is not transactional.
*/
public static CurrentTransaction getInstance(DbEnv env) {
DataEnvironment currentTxn = DataEnvironment.getEnvironment(env);
return currentTxn.isTxnMode() ? currentTxn : null;
}
CurrentTransaction(DbEnv dbEnv) {
this.dbEnv = dbEnv;
}
/**
* Returns the underlying Berkeley DB environment.
*/
public final DbEnv getEnv() {
return dbEnv;
}
/**
* Returns whether AUTO_COMMIT will be used for all non-cursor write
* operations when no transaction is active.
*/
public final boolean isAutoCommit() {
try {
return (dbEnv.getFlags() & Db.DB_AUTO_COMMIT) != 0;
} catch (DbException e) {
throw new RuntimeExceptionWrapper(e);
}
}
/**
* Returns whether dirty-read is used for the current transaction.
*/
public final boolean isDirtyRead() {
Trans trans = (Trans) currentTrans.get();
return (trans != null) ? trans.dirtyRead : false;
}
/**
* Returns whether no-wait is used for the current transaction.
*/
public final boolean isNoWait() {
Trans trans = (Trans) currentTrans.get();
return (trans != null) ? trans.noWait : false;
}
/**
* Returns the transaction associated with the current thread for this
* environment, or null if no transaction is active.
*/
public final DbTxn getTxn() {
Trans trans = (Trans) currentTrans.get();
return (trans != null) ? trans.txn : null;
}
/**
* Begins a new transaction for this environment and associates it with
* the current thread. If a transaction is already active for this
* environment and thread, a nested transaction will be created.
*
* @return the new transaction.
*
* @throws DbException if the transaction cannot be started, in which case
* any existing transaction is not affected.
*/
public final DbTxn beginTxn()
throws DbException {
return beginTxn(false, false);
}
/**
* Begins a new transaction for this environment and associates it with
* the current thread. If a transaction is already active for this
* environment and thread, a nested transaction will be created.
*
* @param dirtyRead true if this transaction should read data that is
* modified by another transaction but not committed.
*
* @param noWait true if this transaction should throw
* DbLockNotGrantedException instead of blocking when trying to access data
* that is locked by another transaction.
*
* @return the new transaction.
*
* @throws DbException if the transaction cannot be started, in which case
* any existing transaction is not affected.
*/
public final DbTxn beginTxn(boolean dirtyRead, boolean noWait)
throws DbException {
int flags = 0;
if (dirtyRead) flags |= Db.DB_DIRTY_READ;
if (noWait) flags |= Db.DB_TXN_NOWAIT;
Trans trans = (Trans) currentTrans.get();
if (trans != null) {
if (trans.txn != null) {
DbTxn parentTxn = trans.txn;
trans = new Trans(trans, dirtyRead, noWait);
trans.txn = dbEnv.txnBegin(parentTxn, flags);
currentTrans.set(trans);
} else {
trans.txn = dbEnv.txnBegin(null, flags);
}
} else {
trans = new Trans(null, dirtyRead, noWait);
trans.txn = dbEnv.txnBegin(null, flags);
currentTrans.set(trans);
}
return trans.txn;
}
/**
* Commits the transaction that is active for the current thread for this
* environment and makes the parent transaction (if any) the current
* transaction.
*
* @return the parent transaction or null if the committed transaction was
* not nested.
*
* @throws DbException if an error occurs commiting the transaction. The
* transaction will still be closed and the parent transaction will become
* the current transaction.
*
* @throws IllegalStateException if no transaction is active for the
* current thread for this environement.
*/
public final DbTxn commitTxn()
throws DbException, IllegalStateException {
Trans trans = (Trans) currentTrans.get();
if (trans != null && trans.txn != null) {
DbTxn parent = closeTxn(trans);
trans.txn.commit(0);
return parent;
} else {
throw new IllegalStateException("No transaction is active");
}
}
/**
* Aborts the transaction that is active for the current thread for this
* environment and makes the parent transaction (if any) the current
* transaction.
*
* @return the parent transaction or null if the aborted transaction was
* not nested.
*
* @throws DbException if an error occurs aborting the transaction. The
* transaction will still be closed and the parent transaction will become
* the current transaction.
*
* @throws IllegalStateException if no transaction is active for the
* current thread for this environement.
*/
public final DbTxn abortTxn()
throws DbException, IllegalStateException {
Trans trans = (Trans) currentTrans.get();
if (trans != null && trans.txn != null) {
DbTxn parent = closeTxn(trans);
trans.txn.abort();
return parent;
} else {
throw new IllegalStateException("No transaction is active");
}
}
private DbTxn closeTxn(Trans trans) {
currentTrans.set(trans.parent);
return (trans.parent != null) ? trans.parent.txn : null;
}
private static class Trans {
private DbTxn txn;
private Trans parent;
private boolean dirtyRead;
private boolean noWait;
private Trans(Trans parent, boolean dirtyRead, boolean noWait) {
this.parent = parent;
this.dirtyRead = dirtyRead;
this.noWait = noWait;
}
}
}