CurrentTransaction.java [plain text]
package com.sleepycat.collections;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.WeakHashMap;
import com.sleepycat.compat.DbCompat;
import com.sleepycat.db.Cursor;
import com.sleepycat.db.CursorConfig;
import com.sleepycat.db.Database;
import com.sleepycat.db.DatabaseException;
import com.sleepycat.db.Environment;
import com.sleepycat.db.EnvironmentConfig;
import com.sleepycat.db.LockMode;
import com.sleepycat.db.Transaction;
import com.sleepycat.db.TransactionConfig;
import com.sleepycat.util.RuntimeExceptionWrapper;
public class CurrentTransaction {
private static WeakHashMap<Environment,CurrentTransaction> envMap =
new WeakHashMap<Environment,CurrentTransaction>();
private LockMode writeLockMode;
private boolean cdbMode;
private boolean txnMode;
private boolean lockingMode;
private ThreadLocal localTrans = new ThreadLocal();
private ThreadLocal localCdbCursors;
private WeakReference<Environment> envRef;
public static CurrentTransaction getInstance(Environment env) {
CurrentTransaction currentTxn = getInstanceInternal(env);
return currentTxn.isTxnMode() ? currentTxn : null;
}
static CurrentTransaction getInstanceInternal(Environment env) {
synchronized (envMap) {
CurrentTransaction ct = envMap.get(env);
if (ct == null) {
ct = new CurrentTransaction(env);
envMap.put(env, ct);
}
return ct;
}
}
private CurrentTransaction(Environment env) {
envRef = new WeakReference<Environment>(env);
try {
EnvironmentConfig config = env.getConfig();
txnMode = config.getTransactional();
lockingMode = DbCompat.getInitializeLocking(config);
if (txnMode || lockingMode) {
writeLockMode = LockMode.RMW;
} else {
writeLockMode = LockMode.DEFAULT;
}
cdbMode = DbCompat.getInitializeCDB(config);
if (cdbMode) {
localCdbCursors = new ThreadLocal();
}
} catch (DatabaseException e) {
throw new RuntimeExceptionWrapper(e);
}
}
final boolean isLockingMode() {
return lockingMode;
}
final boolean isTxnMode() {
return txnMode;
}
final boolean isCdbMode() {
return cdbMode;
}
final LockMode getWriteLockMode() {
return writeLockMode;
}
public final Environment getEnvironment() {
return envRef.get();
}
public final Transaction getTransaction() {
Trans trans = (Trans) localTrans.get();
return (trans != null) ? trans.txn : null;
}
boolean isAutoCommitAllowed()
throws DatabaseException {
return getTransaction() == null &&
DbCompat.getThreadTransaction(getEnvironment()) == null;
}
public final Transaction beginTransaction(TransactionConfig config)
throws DatabaseException {
Environment env = getEnvironment();
Trans trans = (Trans) localTrans.get();
if (trans != null) {
if (trans.txn != null) {
if (!DbCompat.NESTED_TRANSACTIONS) {
throw new IllegalStateException(
"Nested transactions are not supported");
}
Transaction parentTxn = trans.txn;
trans = new Trans(trans, config);
trans.txn = env.beginTransaction(parentTxn, config);
localTrans.set(trans);
} else {
trans.txn = env.beginTransaction(null, config);
trans.config = config;
}
} else {
trans = new Trans(null, config);
trans.txn = env.beginTransaction(null, config);
localTrans.set(trans);
}
return trans.txn;
}
public final Transaction commitTransaction()
throws DatabaseException, IllegalStateException {
Trans trans = (Trans) localTrans.get();
if (trans != null && trans.txn != null) {
Transaction parent = closeTxn(trans);
trans.txn.commit();
return parent;
} else {
throw new IllegalStateException("No transaction is active");
}
}
public final Transaction abortTransaction()
throws DatabaseException, IllegalStateException {
Trans trans = (Trans) localTrans.get();
if (trans != null && trans.txn != null) {
Transaction parent = closeTxn(trans);
trans.txn.abort();
return parent;
} else {
throw new IllegalStateException("No transaction is active");
}
}
final boolean isReadUncommitted() {
Trans trans = (Trans) localTrans.get();
if (trans != null && trans.config != null) {
return trans.config.getReadUncommitted();
} else {
return false;
}
}
private Transaction closeTxn(Trans trans) {
localTrans.set(trans.parent);
return (trans.parent != null) ? trans.parent.txn : null;
}
private static class Trans {
private Trans parent;
private Transaction txn;
private TransactionConfig config;
private Trans(Trans parent, TransactionConfig config) {
this.parent = parent;
this.config = config;
}
}
Cursor openCursor(Database db, CursorConfig cursorConfig,
boolean writeCursor, Transaction txn)
throws DatabaseException {
if (cdbMode) {
CdbCursors cdbCursors = null;
WeakHashMap cdbCursorsMap = (WeakHashMap) localCdbCursors.get();
if (cdbCursorsMap == null) {
cdbCursorsMap = new WeakHashMap();
localCdbCursors.set(cdbCursorsMap);
} else {
cdbCursors = (CdbCursors) cdbCursorsMap.get(db);
}
if (cdbCursors == null) {
cdbCursors = new CdbCursors();
cdbCursorsMap.put(db, cdbCursors);
}
List cursors;
CursorConfig cdbConfig;
if (writeCursor) {
if (cdbCursors.readCursors.size() > 0) {
throw new IllegalStateException(
"cannot open CDB write cursor when read cursor is open");
}
cursors = cdbCursors.writeCursors;
cdbConfig = new CursorConfig();
DbCompat.setWriteCursor(cdbConfig, true);
} else {
cursors = cdbCursors.readCursors;
cdbConfig = null;
}
Cursor cursor;
if (cursors.size() > 0) {
Cursor other = ((Cursor) cursors.get(0));
cursor = other.dup(false);
} else {
cursor = db.openCursor(null, cdbConfig);
}
cursors.add(cursor);
return cursor;
} else {
return db.openCursor(txn, cursorConfig);
}
}
Cursor dupCursor(Cursor cursor, boolean writeCursor, boolean samePosition)
throws DatabaseException {
if (cdbMode) {
WeakHashMap cdbCursorsMap = (WeakHashMap) localCdbCursors.get();
if (cdbCursorsMap != null) {
Database db = cursor.getDatabase();
CdbCursors cdbCursors = (CdbCursors) cdbCursorsMap.get(db);
if (cdbCursors != null) {
List cursors = writeCursor ? cdbCursors.writeCursors
: cdbCursors.readCursors;
if (cursors.contains(cursor)) {
Cursor newCursor = cursor.dup(samePosition);
cursors.add(newCursor);
return newCursor;
}
}
}
throw new IllegalStateException("cursor to dup not tracked");
} else {
return cursor.dup(samePosition);
}
}
void closeCursor(Cursor cursor)
throws DatabaseException {
if (cursor == null) {
return;
}
if (cdbMode) {
WeakHashMap cdbCursorsMap = (WeakHashMap) localCdbCursors.get();
if (cdbCursorsMap != null) {
Database db = cursor.getDatabase();
CdbCursors cdbCursors = (CdbCursors) cdbCursorsMap.get(db);
if (cdbCursors != null) {
if (cdbCursors.readCursors.remove(cursor) ||
cdbCursors.writeCursors.remove(cursor)) {
cursor.close();
return;
}
}
}
throw new IllegalStateException(
"closing CDB cursor that was not known to be open");
} else {
cursor.close();
}
}
boolean isCDBCursorOpen(Database db)
throws DatabaseException {
if (cdbMode) {
WeakHashMap cdbCursorsMap = (WeakHashMap) localCdbCursors.get();
if (cdbCursorsMap != null) {
CdbCursors cdbCursors = (CdbCursors) cdbCursorsMap.get(db);
if (cdbCursors != null &&
(cdbCursors.readCursors.size() > 0 ||
cdbCursors.writeCursors.size() > 0)) {
return true;
}
}
}
return false;
}
static final class CdbCursors {
List writeCursors = new ArrayList();
List readCursors = new ArrayList();
}
}