001: /*-
002: * See the file LICENSE for redistribution information.
003: *
004: * Copyright (c) 2002,2008 Oracle. All rights reserved.
005: *
006: * $Id: LockerFactory.java,v 1.8.2.3 2008/01/07 15:14:17 cwl Exp $
007: */
008:
009: package com.sleepycat.je.txn;
010:
011: import com.sleepycat.je.Database;
012: import com.sleepycat.je.DatabaseException;
013: import com.sleepycat.je.DbInternal;
014: import com.sleepycat.je.Environment;
015: import com.sleepycat.je.Transaction;
016: import com.sleepycat.je.TransactionConfig;
017: import com.sleepycat.je.dbi.DatabaseImpl;
018: import com.sleepycat.je.dbi.EnvironmentImpl;
019:
020: /**
021: * Factory of static methods for creating Locker objects.
022: */
023: public class LockerFactory {
024:
025: /**
026: * Get a locker for a writable operation, checking whether the db and
027: * environment is transactional or not. Must return a non null locker.
028: */
029: public static Locker getWritableLocker(Environment env,
030: Transaction userTxn, boolean dbIsTransactional)
031: throws DatabaseException {
032:
033: return getWritableLocker(env, userTxn, dbIsTransactional,
034: false, null);
035: }
036:
037: /**
038: * Get a locker for a writable operation, also specifying whether to retain
039: * non-transactional locks when a new locker must be created.
040: *
041: * @param retainNonTxnLocks is true for DbTree operations, so that the
042: * handle lock may be transferred out of the locker when the operation is
043: * complete.
044: */
045: public static Locker getWritableLocker(Environment env,
046: Transaction userTxn, boolean dbIsTransactional,
047: boolean retainNonTxnLocks,
048: TransactionConfig autoCommitConfig)
049: throws DatabaseException {
050:
051: EnvironmentImpl envImpl = DbInternal.envGetEnvironmentImpl(env);
052: boolean envIsTransactional = envImpl.isTransactional();
053:
054: if (userTxn == null) {
055: Transaction xaLocker = env.getThreadTransaction();
056: if (xaLocker != null) {
057: return DbInternal.getLocker(xaLocker);
058: }
059: }
060:
061: if (dbIsTransactional && userTxn == null) {
062:
063: if (autoCommitConfig == null) {
064: autoCommitConfig = DbInternal.getDefaultTxnConfig(env);
065: }
066: return new AutoTxn(envImpl, autoCommitConfig);
067:
068: } else if (userTxn == null) {
069:
070: if (retainNonTxnLocks) {
071: return new BasicLocker(envImpl);
072: } else {
073: return new ThreadLocker(envImpl);
074: }
075:
076: } else {
077:
078: /*
079: * The user provided a transaction, the environment and the
080: * database had better be opened transactionally.
081: */
082: if (!envIsTransactional) {
083: throw new DatabaseException(
084: "A Transaction cannot be used because the"
085: + " environment was opened"
086: + " non-transactionally");
087: }
088: if (!dbIsTransactional) {
089: throw new DatabaseException(
090: "A Transaction cannot be used because the"
091: + " database was opened"
092: + " non-transactionally");
093: }
094:
095: /*
096: * Use the locker for the given transaction. For read-comitted,
097: * wrap the given transactional locker in a special locker for that
098: * isolation level. But if retainNonTxnLocks we cannot use
099: * read-committed, since retainNonTxnLocks is used for handle locks
100: * that must be retained across operations.
101: */
102: Locker locker = DbInternal.getLocker(userTxn);
103: if (locker.isReadCommittedIsolation() && !retainNonTxnLocks) {
104: return new ReadCommittedLocker(envImpl, locker);
105: } else {
106: return locker;
107: }
108: }
109: }
110:
111: /**
112: * Get a locker for a read or cursor operation.
113: * See getWritableLocker for an explanation of retainNonTxnLocks.
114: */
115: public static Locker getReadableLocker(Environment env,
116: Transaction userTxn, boolean dbIsTransactional,
117: boolean retainNonTxnLocks, boolean readCommittedIsolation)
118: throws DatabaseException {
119:
120: if (userTxn != null && !dbIsTransactional) {
121: throw new DatabaseException(
122: "A Transaction cannot be used because the"
123: + " database was opened"
124: + " non-transactionally");
125: }
126:
127: Locker locker = null;
128: if (userTxn != null) {
129:
130: /*
131: * Use the locker for the given transaction. Request read-comitted
132: * if that isolation level is configured for the transaction, or if
133: * true is passed for the parameter (this is the case when
134: * read-committed is configured for the cursor).
135: */
136: locker = DbInternal.getLocker(userTxn);
137: if (locker.isReadCommittedIsolation()) {
138: readCommittedIsolation = true;
139: }
140: }
141:
142: return getReadableLocker(env, locker, retainNonTxnLocks,
143: readCommittedIsolation);
144: }
145:
146: /**
147: * Get a locker for this database handle for a read or cursor operation.
148: * See getWritableLocker for an explanation of retainNonTxnLocks.
149: */
150: public static Locker getReadableLocker(Environment env,
151: Database dbHandle, Locker locker,
152: boolean retainNonTxnLocks, boolean readCommittedIsolation)
153: throws DatabaseException {
154:
155: DatabaseImpl dbImpl = DbInternal.dbGetDatabaseImpl(dbHandle);
156: if (!dbImpl.isTransactional() && locker != null
157: && locker.isTransactional()) {
158: throw new DatabaseException(
159: "A Transaction cannot be used because the"
160: + " database was opened"
161: + " non-transactionally");
162: }
163:
164: /*
165: * Don't reuse a non-transactional locker unless retaining
166: * non-transactional locks was requested.
167: */
168: if (locker != null && !locker.isTransactional()
169: && !retainNonTxnLocks) {
170: locker = null;
171: }
172:
173: /*
174: * Request read-comitted if that isolation level is configured for the
175: * locker being reused, or if true is passed for the parameter (this is
176: * the case when read-committed is configured for the cursor).
177: */
178: if (locker != null && locker.isReadCommittedIsolation()) {
179: readCommittedIsolation = true;
180: }
181:
182: return getReadableLocker(env, locker, retainNonTxnLocks,
183: readCommittedIsolation);
184: }
185:
186: /**
187: * Get a locker for a read or cursor operation.
188: * See getWritableLocker for an explanation of retainNonTxnLocks.
189: */
190: private static Locker getReadableLocker(Environment env,
191: Locker locker, boolean retainNonTxnLocks,
192: boolean readCommittedIsolation) throws DatabaseException {
193:
194: EnvironmentImpl envImpl = DbInternal.envGetEnvironmentImpl(env);
195:
196: if (locker == null) {
197: Transaction xaTxn = env.getThreadTransaction();
198: if (xaTxn != null) {
199: return DbInternal.getLocker(xaTxn);
200: }
201: }
202:
203: if (locker == null) {
204:
205: /*
206: * A non-transactional locker is requested. If we're retaining
207: * non-transactional locks across operations, use a BasicLocker
208: * since the locker may be used across threads; this is used when
209: * acquiring handle locks internally (open, close, remove, etc).
210: * Otherwise, use a ThreadLocker to avoid self-deadlocks within the
211: * same thread; this used for ordinary user operations.
212: */
213: if (retainNonTxnLocks) {
214: locker = new BasicLocker(envImpl);
215: } else {
216: locker = new ThreadLocker(envImpl);
217: }
218: } else {
219:
220: /*
221: * Use the given locker. For read-committed, wrap the given
222: * transactional locker in a special locker for that isolation
223: * level. But if retainNonTxnLocks we cannot use read-committed,
224: * since retainNonTxnLocks is used for handle locks that must be
225: * retained across operations.
226: */
227: if (readCommittedIsolation && !retainNonTxnLocks) {
228: locker = new ReadCommittedLocker(envImpl, locker);
229: }
230: }
231: return locker;
232: }
233: }
|