00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 package com.sleepycat.collections;
00011
00012 import java.util.ArrayList;
00013 import java.util.List;
00014 import java.util.WeakHashMap;
00015
00016 import com.sleepycat.compat.DbCompat;
00017 import com.sleepycat.db.Cursor;
00018 import com.sleepycat.db.CursorConfig;
00019 import com.sleepycat.db.Database;
00020 import com.sleepycat.db.DatabaseException;
00021 import com.sleepycat.db.Environment;
00022 import com.sleepycat.db.EnvironmentConfig;
00023 import com.sleepycat.db.LockMode;
00024 import com.sleepycat.db.Transaction;
00025 import com.sleepycat.db.TransactionConfig;
00026 import com.sleepycat.util.RuntimeExceptionWrapper;
00027
00037 public class CurrentTransaction {
00038
00039
00040
00041 private static WeakHashMap envMap = new WeakHashMap();
00042
00043 private LockMode writeLockMode;
00044 private boolean cdbMode;
00045 private boolean txnMode;
00046 private Environment env;
00047 private ThreadLocal localTrans = new ThreadLocal();
00048 private ThreadLocal localCdbCursors;
00049
00060 public static CurrentTransaction getInstance(Environment env) {
00061
00062 CurrentTransaction currentTxn = getInstanceInternal(env);
00063 return currentTxn.isTxnMode() ? currentTxn : null;
00064 }
00065
00072 static CurrentTransaction getInstanceInternal(Environment env) {
00073 synchronized (envMap) {
00074 CurrentTransaction myEnv = (CurrentTransaction) envMap.get(env);
00075 if (myEnv == null) {
00076 myEnv = new CurrentTransaction(env);
00077 envMap.put(env, myEnv);
00078 }
00079 return myEnv;
00080 }
00081 }
00082
00083 private CurrentTransaction(Environment env) {
00084 this.env = env;
00085 try {
00086 EnvironmentConfig config = env.getConfig();
00087 txnMode = config.getTransactional();
00088 if (txnMode || DbCompat.getInitializeLocking(config)) {
00089 writeLockMode = LockMode.RMW;
00090 } else {
00091 writeLockMode = LockMode.DEFAULT;
00092 }
00093 cdbMode = DbCompat.getInitializeCDB(config);
00094 if (cdbMode) {
00095 localCdbCursors = new ThreadLocal();
00096 }
00097 } catch (DatabaseException e) {
00098 throw new RuntimeExceptionWrapper(e);
00099 }
00100 }
00101
00105 final boolean isTxnMode() {
00106
00107 return txnMode;
00108 }
00109
00113 final boolean isCdbMode() {
00114
00115 return cdbMode;
00116 }
00117
00124 final LockMode getWriteLockMode() {
00125
00126 return writeLockMode;
00127 }
00128
00132 public final Environment getEnvironment() {
00133
00134 return env;
00135 }
00136
00141 public final Transaction getTransaction() {
00142
00143 Trans trans = (Trans) localTrans.get();
00144 return (trans != null) ? trans.txn : null;
00145 }
00146
00152 boolean isAutoCommitAllowed()
00153 throws DatabaseException {
00154
00155 return getTransaction() == null &&
00156 DbCompat.getThreadTransaction(env) == null;
00157 }
00158
00176 public final Transaction beginTransaction(TransactionConfig config)
00177 throws DatabaseException {
00178
00179 Trans trans = (Trans) localTrans.get();
00180 if (trans != null) {
00181 if (trans.txn != null) {
00182 if (!DbCompat.NESTED_TRANSACTIONS) {
00183 throw new IllegalStateException(
00184 "Nested transactions are not supported");
00185 }
00186 Transaction parentTxn = trans.txn;
00187 trans = new Trans(trans, config);
00188 trans.txn = env.beginTransaction(parentTxn, config);
00189 localTrans.set(trans);
00190 } else {
00191 trans.txn = env.beginTransaction(null, config);
00192 trans.config = config;
00193 }
00194 } else {
00195 trans = new Trans(null, config);
00196 trans.txn = env.beginTransaction(null, config);
00197 localTrans.set(trans);
00198 }
00199 return trans.txn;
00200 }
00201
00217 public final Transaction commitTransaction()
00218 throws DatabaseException, IllegalStateException {
00219
00220 Trans trans = (Trans) localTrans.get();
00221 if (trans != null && trans.txn != null) {
00222 Transaction parent = closeTxn(trans);
00223 trans.txn.commit();
00224 return parent;
00225 } else {
00226 throw new IllegalStateException("No transaction is active");
00227 }
00228 }
00229
00245 public final Transaction abortTransaction()
00246 throws DatabaseException, IllegalStateException {
00247
00248 Trans trans = (Trans) localTrans.get();
00249 if (trans != null && trans.txn != null) {
00250 Transaction parent = closeTxn(trans);
00251 trans.txn.abort();
00252 return parent;
00253 } else {
00254 throw new IllegalStateException("No transaction is active");
00255 }
00256 }
00257
00262 final boolean isReadUncommitted() {
00263
00264 Trans trans = (Trans) localTrans.get();
00265 if (trans != null && trans.config != null) {
00266 return trans.config.getReadUncommitted();
00267 } else {
00268 return false;
00269 }
00270 }
00271
00272 private Transaction closeTxn(Trans trans) {
00273
00274 localTrans.set(trans.parent);
00275 return (trans.parent != null) ? trans.parent.txn : null;
00276 }
00277
00278 private static class Trans {
00279
00280 private Trans parent;
00281 private Transaction txn;
00282 private TransactionConfig config;
00283
00284 private Trans(Trans parent, TransactionConfig config) {
00285
00286 this.parent = parent;
00287 this.config = config;
00288 }
00289 }
00290
00295 Cursor openCursor(Database db, CursorConfig cursorConfig,
00296 boolean writeCursor, Transaction txn)
00297 throws DatabaseException {
00298
00299 if (cdbMode) {
00300 CdbCursors cdbCursors = null;
00301 WeakHashMap cdbCursorsMap = (WeakHashMap) localCdbCursors.get();
00302 if (cdbCursorsMap == null) {
00303 cdbCursorsMap = new WeakHashMap();
00304 localCdbCursors.set(cdbCursorsMap);
00305 } else {
00306 cdbCursors = (CdbCursors) cdbCursorsMap.get(db);
00307 }
00308 if (cdbCursors == null) {
00309 cdbCursors = new CdbCursors();
00310 cdbCursorsMap.put(db, cdbCursors);
00311 }
00312
00313
00314
00315
00316
00317
00318
00319 List cursors;
00320 CursorConfig cdbConfig;
00321 if (writeCursor) {
00322 if (cdbCursors.readCursors.size() > 0) {
00323
00324
00325
00326
00327
00328
00329
00330 throw new IllegalStateException(
00331 "cannot open CDB write cursor when read cursor is open");
00332 }
00333 cursors = cdbCursors.writeCursors;
00334 cdbConfig = new CursorConfig();
00335 DbCompat.setWriteCursor(cdbConfig, true);
00336 } else {
00337 cursors = cdbCursors.readCursors;
00338 cdbConfig = null;
00339 }
00340 Cursor cursor;
00341 if (cursors.size() > 0) {
00342 Cursor other = ((Cursor) cursors.get(0));
00343 cursor = other.dup(false);
00344 } else {
00345 cursor = db.openCursor(null, cdbConfig);
00346 }
00347 cursors.add(cursor);
00348 return cursor;
00349 } else {
00350 return db.openCursor(txn, cursorConfig);
00351 }
00352 }
00353
00366 Cursor dupCursor(Cursor cursor, boolean writeCursor, boolean samePosition)
00367 throws DatabaseException {
00368
00369 if (cdbMode) {
00370 WeakHashMap cdbCursorsMap = (WeakHashMap) localCdbCursors.get();
00371 if (cdbCursorsMap != null) {
00372 Database db = cursor.getDatabase();
00373 CdbCursors cdbCursors = (CdbCursors) cdbCursorsMap.get(db);
00374 if (cdbCursors != null) {
00375 List cursors = writeCursor ? cdbCursors.writeCursors
00376 : cdbCursors.readCursors;
00377 if (cursors.contains(cursor)) {
00378 Cursor newCursor = cursor.dup(samePosition);
00379 cursors.add(newCursor);
00380 return newCursor;
00381 }
00382 }
00383 }
00384 throw new IllegalStateException("cursor to dup not tracked");
00385 } else {
00386 return cursor.dup(samePosition);
00387 }
00388 }
00389
00397 void closeCursor(Cursor cursor)
00398 throws DatabaseException {
00399
00400 if (cursor == null) {
00401 return;
00402 }
00403 if (cdbMode) {
00404 WeakHashMap cdbCursorsMap = (WeakHashMap) localCdbCursors.get();
00405 if (cdbCursorsMap != null) {
00406 Database db = cursor.getDatabase();
00407 CdbCursors cdbCursors = (CdbCursors) cdbCursorsMap.get(db);
00408 if (cdbCursors != null) {
00409 if (cdbCursors.readCursors.remove(cursor) ||
00410 cdbCursors.writeCursors.remove(cursor)) {
00411 cursor.close();
00412 return;
00413 }
00414 }
00415 }
00416 throw new IllegalStateException(
00417 "closing CDB cursor that was not known to be open");
00418 } else {
00419 cursor.close();
00420 }
00421 }
00422
00427 boolean isCDBCursorOpen(Database db)
00428 throws DatabaseException {
00429
00430 if (cdbMode) {
00431 WeakHashMap cdbCursorsMap = (WeakHashMap) localCdbCursors.get();
00432 if (cdbCursorsMap != null) {
00433 CdbCursors cdbCursors = (CdbCursors) cdbCursorsMap.get(db);
00434
00435 if (cdbCursors != null &&
00436 (cdbCursors.readCursors.size() > 0 ||
00437 cdbCursors.writeCursors.size() > 0)) {
00438 return true;
00439 }
00440 }
00441 }
00442 return false;
00443 }
00444
00445 static final class CdbCursors {
00446
00447 List writeCursors = new ArrayList();
00448 List readCursors = new ArrayList();
00449 }
00450 }