KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sleepycat > collections > CurrentTransaction


1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 2000,2006 Oracle. All rights reserved.
5  *
6  * $Id: CurrentTransaction.java,v 1.46 2006/10/30 21:14:10 bostic Exp $
7  */

8
9 package com.sleepycat.collections;
10
11 import java.util.ArrayList JavaDoc;
12 import java.util.List JavaDoc;
13 import java.util.WeakHashMap JavaDoc;
14
15 import com.sleepycat.compat.DbCompat;
16 import com.sleepycat.je.Cursor;
17 import com.sleepycat.je.CursorConfig;
18 import com.sleepycat.je.Database;
19 import com.sleepycat.je.DatabaseException;
20 import com.sleepycat.je.Environment;
21 import com.sleepycat.je.EnvironmentConfig;
22 import com.sleepycat.je.LockMode;
23 import com.sleepycat.je.Transaction;
24 import com.sleepycat.je.TransactionConfig;
25 import com.sleepycat.util.RuntimeExceptionWrapper;
26
27 /**
28  * Provides access to the current transaction for the current thread within the
29  * context of a Berkeley DB environment. This class provides explicit
30  * transaction control beyond that provided by the {@link TransactionRunner}
31  * class. However, both methods of transaction control manage per-thread
32  * transactions.
33  *
34  * @author Mark Hayes
35  */

36 public class CurrentTransaction {
37
38     /* For internal use, this class doubles as an Environment wrapper. */
39
40     private static WeakHashMap JavaDoc envMap = new WeakHashMap JavaDoc();
41
42     private LockMode writeLockMode;
43     private boolean cdbMode;
44     private boolean txnMode;
45     private boolean lockingMode;
46     private Environment env;
47     private ThreadLocal JavaDoc localTrans = new ThreadLocal JavaDoc();
48     private ThreadLocal JavaDoc localCdbCursors;
49
50     /**
51      * Gets the CurrentTransaction accessor for a specified Berkeley DB
52      * environment. This method always returns the same reference when called
53      * more than once with the same environment parameter.
54      *
55      * @param env is an open Berkeley DB environment.
56      *
57      * @return the CurrentTransaction accessor for the given environment, or
58      * null if the environment is not transactional.
59      */

60     public static CurrentTransaction getInstance(Environment env) {
61
62         CurrentTransaction currentTxn = getInstanceInternal(env);
63         return currentTxn.isTxnMode() ? currentTxn : null;
64     }
65
66     /**
67      * Gets the CurrentTransaction accessor for a specified Berkeley DB
68      * environment. Unlike getInstance(), this method never returns null.
69      *
70      * @param env is an open Berkeley DB environment.
71      */

72     static CurrentTransaction getInstanceInternal(Environment env) {
73         synchronized (envMap) {
74             CurrentTransaction myEnv = (CurrentTransaction) envMap.get(env);
75             if (myEnv == null) {
76                 myEnv = new CurrentTransaction(env);
77                 envMap.put(env, myEnv);
78             }
79             return myEnv;
80         }
81     }
82
83     private CurrentTransaction(Environment env) {
84         this.env = env;
85         try {
86             EnvironmentConfig config = env.getConfig();
87             txnMode = config.getTransactional();
88             lockingMode = DbCompat.getInitializeLocking(config);
89             if (txnMode || lockingMode) {
90                 writeLockMode = LockMode.RMW;
91             } else {
92                 writeLockMode = LockMode.DEFAULT;
93             }
94             cdbMode = DbCompat.getInitializeCDB(config);
95             if (cdbMode) {
96                 localCdbCursors = new ThreadLocal JavaDoc();
97             }
98         } catch (DatabaseException e) {
99             throw new RuntimeExceptionWrapper(e);
100         }
101     }
102
103     /**
104      * Returns whether environment is configured for locking.
105      */

106     final boolean isLockingMode() {
107
108         return lockingMode;
109     }
110
111     /**
112      * Returns whether this is a transactional environment.
113      */

114     final boolean isTxnMode() {
115
116         return txnMode;
117     }
118
119     /**
120      * Returns whether this is a Concurrent Data Store environment.
121      */

122     final boolean isCdbMode() {
123
124         return cdbMode;
125     }
126
127     /**
128      * Return the LockMode.RMW or null, depending on whether locking is
129      * enabled. LockMode.RMW will cause an error if passed when locking
130      * is not enabled. Locking is enabled if locking or transactions were
131      * specified for this environment.
132      */

133     final LockMode getWriteLockMode() {
134
135         return writeLockMode;
136     }
137
138     /**
139      * Returns the underlying Berkeley DB environment.
140      */

141     public final Environment getEnvironment() {
142
143         return env;
144     }
145
146     /**
147      * Returns the transaction associated with the current thread for this
148      * environment, or null if no transaction is active.
149      */

150     public final Transaction getTransaction() {
151
152         Trans trans = (Trans) localTrans.get();
153         return (trans != null) ? trans.txn : null;
154     }
155
156     /**
157      * Returns whether auto-commit may be performed by the collections API.
158      * True is returned if no collections API transaction is currently active,
159      * and no XA transaction is currently active.
160      */

161     boolean isAutoCommitAllowed()
162     throws DatabaseException {
163
164         return getTransaction() == null &&
165                DbCompat.getThreadTransaction(env) == null;
166     }
167
168     /**
169      * Begins a new transaction for this environment and associates it with
170      * the current thread. If a transaction is already active for this
171      * environment and thread, a nested transaction will be created.
172      *
173      * @param config the transaction configuration used for calling
174      * {@link Environment#beginTransaction}, or null to use the default
175      * configuration.
176      *
177      * @return the new transaction.
178      *
179      * @throws DatabaseException if the transaction cannot be started, in which
180      * case any existing transaction is not affected.
181      *
182      * @throws IllegalStateException if a transaction is already active and
183      * nested transactions are not supported by the environment.
184      */

185     public final Transaction beginTransaction(TransactionConfig config)
186         throws DatabaseException {
187
188         Trans trans = (Trans) localTrans.get();
189         if (trans != null) {
190             if (trans.txn != null) {
191                 if (!DbCompat.NESTED_TRANSACTIONS) {
192                     throw new IllegalStateException JavaDoc(
193                             "Nested transactions are not supported");
194                 }
195                 Transaction parentTxn = trans.txn;
196                 trans = new Trans(trans, config);
197                 trans.txn = env.beginTransaction(parentTxn, config);
198                 localTrans.set(trans);
199             } else {
200                 trans.txn = env.beginTransaction(null, config);
201                 trans.config = config;
202             }
203         } else {
204             trans = new Trans(null, config);
205             trans.txn = env.beginTransaction(null, config);
206             localTrans.set(trans);
207         }
208         return trans.txn;
209     }
210
211     /**
212      * Commits the transaction that is active for the current thread for this
213      * environment and makes the parent transaction (if any) the current
214      * transaction.
215      *
216      * @return the parent transaction or null if the committed transaction was
217      * not nested.
218      *
219      * @throws DatabaseException if an error occurs committing the transaction.
220      * The transaction will still be closed and the parent transaction will
221      * become the current transaction.
222      *
223      * @throws IllegalStateException if no transaction is active for the
224      * current thread for this environment.
225      */

226     public final Transaction commitTransaction()
227         throws DatabaseException, IllegalStateException JavaDoc {
228
229         Trans trans = (Trans) localTrans.get();
230         if (trans != null && trans.txn != null) {
231             Transaction parent = closeTxn(trans);
232             trans.txn.commit();
233             return parent;
234         } else {
235             throw new IllegalStateException JavaDoc("No transaction is active");
236         }
237     }
238
239     /**
240      * Aborts the transaction that is active for the current thread for this
241      * environment and makes the parent transaction (if any) the current
242      * transaction.
243      *
244      * @return the parent transaction or null if the aborted transaction was
245      * not nested.
246      *
247      * @throws DatabaseException if an error occurs aborting the transaction.
248      * The transaction will still be closed and the parent transaction will
249      * become the current transaction.
250      *
251      * @throws IllegalStateException if no transaction is active for the
252      * current thread for this environment.
253      */

254     public final Transaction abortTransaction()
255         throws DatabaseException, IllegalStateException JavaDoc {
256
257         Trans trans = (Trans) localTrans.get();
258         if (trans != null && trans.txn != null) {
259             Transaction parent = closeTxn(trans);
260             trans.txn.abort();
261             return parent;
262         } else {
263             throw new IllegalStateException JavaDoc("No transaction is active");
264         }
265     }
266
267     /**
268      * Returns whether the current transaction is a readUncommitted
269      * transaction.
270      */

271     final boolean isReadUncommitted() {
272
273         Trans trans = (Trans) localTrans.get();
274         if (trans != null && trans.config != null) {
275             return trans.config.getReadUncommitted();
276         } else {
277             return false;
278         }
279     }
280
281     private Transaction closeTxn(Trans trans) {
282
283         localTrans.set(trans.parent);
284         return (trans.parent != null) ? trans.parent.txn : null;
285     }
286
287     private static class Trans {
288
289         private Trans parent;
290         private Transaction txn;
291         private TransactionConfig config;
292
293         private Trans(Trans parent, TransactionConfig config) {
294
295             this.parent = parent;
296             this.config = config;
297         }
298     }
299
300     /**
301      * Opens a cursor for a given database, dup'ing an existing CDB cursor if
302      * one is open for the current thread.
303      */

304     Cursor openCursor(Database db, CursorConfig cursorConfig,
305                       boolean writeCursor, Transaction txn)
306         throws DatabaseException {
307
308         if (cdbMode) {
309             CdbCursors cdbCursors = null;
310             WeakHashMap JavaDoc cdbCursorsMap = (WeakHashMap JavaDoc) localCdbCursors.get();
311             if (cdbCursorsMap == null) {
312                 cdbCursorsMap = new WeakHashMap JavaDoc();
313                 localCdbCursors.set(cdbCursorsMap);
314             } else {
315                 cdbCursors = (CdbCursors) cdbCursorsMap.get(db);
316             }
317             if (cdbCursors == null) {
318                 cdbCursors = new CdbCursors();
319                 cdbCursorsMap.put(db, cdbCursors);
320             }
321
322             /*
323              * In CDB mode the cursorConfig specified by the user is ignored
324              * and only the writeCursor parameter is honored. This is the only
325              * meaningful cursor attribute for CDB, and here we count on
326              * writeCursor flag being set correctly by the caller.
327              */

328             List JavaDoc cursors;
329             CursorConfig cdbConfig;
330             if (writeCursor) {
331                 if (cdbCursors.readCursors.size() > 0) {
332                     
333                     /*
334                      * Although CDB allows opening a write cursor when a read
335                      * cursor is open, a self-deadlock will occur if a write is
336                      * attempted for a record that is read-locked; we should
337                      * avoid self-deadlocks at all costs
338                      */

339                     throw new IllegalStateException JavaDoc(
340                       "cannot open CDB write cursor when read cursor is open");
341                 }
342                 cursors = cdbCursors.writeCursors;
343                 cdbConfig = new CursorConfig();
344                 DbCompat.setWriteCursor(cdbConfig, true);
345             } else {
346                 cursors = cdbCursors.readCursors;
347                 cdbConfig = null;
348             }
349             Cursor cursor;
350             if (cursors.size() > 0) {
351                 Cursor other = ((Cursor) cursors.get(0));
352                 cursor = other.dup(false);
353             } else {
354                 cursor = db.openCursor(null, cdbConfig);
355             }
356             cursors.add(cursor);
357             return cursor;
358         } else {
359             return db.openCursor(txn, cursorConfig);
360         }
361     }
362
363     /**
364      * Duplicates a cursor for a given database.
365      *
366      * @param writeCursor true to open a write cursor in a CDB environment, and
367      * ignored for other environments.
368      *
369      * @param samePosition is passed through to Cursor.dup().
370      *
371      * @return the open cursor.
372      *
373      * @throws DatabaseException if a database problem occurs.
374      */

375     Cursor dupCursor(Cursor cursor, boolean writeCursor, boolean samePosition)
376         throws DatabaseException {
377
378         if (cdbMode) {
379             WeakHashMap JavaDoc cdbCursorsMap = (WeakHashMap JavaDoc) localCdbCursors.get();
380             if (cdbCursorsMap != null) {
381                 Database db = cursor.getDatabase();
382                 CdbCursors cdbCursors = (CdbCursors) cdbCursorsMap.get(db);
383                 if (cdbCursors != null) {
384                     List JavaDoc cursors = writeCursor ? cdbCursors.writeCursors
385                                                : cdbCursors.readCursors;
386                     if (cursors.contains(cursor)) {
387                         Cursor newCursor = cursor.dup(samePosition);
388                         cursors.add(newCursor);
389                         return newCursor;
390                     }
391                 }
392             }
393             throw new IllegalStateException JavaDoc("cursor to dup not tracked");
394         } else {
395             return cursor.dup(samePosition);
396         }
397     }
398
399     /**
400      * Closes a cursor.
401      *
402      * @param cursor the cursor to close.
403      *
404      * @throws DatabaseException if a database problem occurs.
405      */

406     void closeCursor(Cursor cursor)
407         throws DatabaseException {
408
409         if (cursor == null) {
410             return;
411         }
412         if (cdbMode) {
413             WeakHashMap JavaDoc cdbCursorsMap = (WeakHashMap JavaDoc) localCdbCursors.get();
414             if (cdbCursorsMap != null) {
415                 Database db = cursor.getDatabase();
416                 CdbCursors cdbCursors = (CdbCursors) cdbCursorsMap.get(db);
417                 if (cdbCursors != null) {
418                     if (cdbCursors.readCursors.remove(cursor) ||
419                         cdbCursors.writeCursors.remove(cursor)) {
420                         cursor.close();
421                         return;
422                     }
423                 }
424             }
425             throw new IllegalStateException JavaDoc(
426               "closing CDB cursor that was not known to be open");
427         } else {
428             cursor.close();
429         }
430     }
431
432     /**
433      * Returns true if a CDB cursor is open and therefore a Database write
434      * operation should not be attempted since a self-deadlock may result.
435      */

436     boolean isCDBCursorOpen(Database db)
437         throws DatabaseException {
438
439         if (cdbMode) {
440             WeakHashMap JavaDoc cdbCursorsMap = (WeakHashMap JavaDoc) localCdbCursors.get();
441             if (cdbCursorsMap != null) {
442                 CdbCursors cdbCursors = (CdbCursors) cdbCursorsMap.get(db);
443
444                 if (cdbCursors != null &&
445                     (cdbCursors.readCursors.size() > 0 ||
446                      cdbCursors.writeCursors.size() > 0)) {
447                     return true;
448                 }
449             }
450         }
451         return false;
452     }
453
454     static final class CdbCursors {
455
456         List JavaDoc writeCursors = new ArrayList JavaDoc();
457         List JavaDoc readCursors = new ArrayList JavaDoc();
458     }
459 }
460
Popular Tags