001: package org.apache.ojb.broker.locking;
002:
003: /* Copyright 2005 The Apache Software Foundation
004: *
005: * Licensed under the Apache License, Version 2.0 (the "License");
006: * you may not use this file except in compliance with the License.
007: * You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: import org.apache.commons.transaction.locking.GenericLock;
019: import org.apache.commons.transaction.locking.GenericLockManager;
020: import org.apache.commons.transaction.locking.LockException;
021: import org.apache.commons.transaction.locking.MultiLevelLock;
022: import org.apache.commons.transaction.util.LoggerFacade;
023:
024: /**
025: * Extension of {@link org.apache.commons.transaction.locking.GenericLockManager} to
026: * support all locking isolation level defined in OJB locking api and a provider of
027: * specific {@link org.apache.commons.transaction.locking.GenericLock} implementation classes
028: * representing the isolation levels specified in {@link org.apache.ojb.broker.locking.LockManager}, like
029: * {@link org.apache.ojb.broker.locking.LockManager#IL_READ_COMMITTED}, ... .
030: * <p/>
031: * The specific lock classes will be returned on call of
032: * {@link #createIsolationLevel(Object, Object, org.apache.commons.transaction.util.LoggerFacade)}
033: * dependend on the specified isolation level.
034: *
035: * @author <a href="mailto:arminw@apache.org">Armin Waibel</a>
036: * @version $Id: CommonsOJBLockManager.java,v 1.1.2.4 2005/12/21 22:25:32 tomdz Exp $
037: */
038: class CommonsOJBLockManager extends GenericLockManager {
039: static final int COMMON_READ_LOCK = 101;
040: static final int COMMON_WRITE_LOCK = 107;
041: static final int COMMON_UPGRADE_LOCK = 113;
042:
043: public CommonsOJBLockManager(LoggerFacade logger,
044: long timeoutMSecs, long checkThreshholdMSecs)
045: throws IllegalArgumentException {
046: super (1, logger, timeoutMSecs, checkThreshholdMSecs);
047: }
048:
049: /**
050: * @see org.apache.commons.transaction.locking.GenericLockManager#tryLock(Object, Object, int, boolean)
051: */
052: public boolean tryLock(Object ownerId, Object resourceId,
053: int targetLockLevel, boolean reentrant) {
054: return tryLock(ownerId, resourceId, targetLockLevel, reentrant,
055: null);
056: }
057:
058: /**
059: * Tries to acquire a lock on a resource. <br>
060: * <br>
061: * This method does not block, but immediatly returns. If a lock is not
062: * available <code>false</code> will be returned.
063: *
064: * @param ownerId a unique id identifying the entity that wants to acquire this
065: * lock
066: * @param resourceId the resource to get the level for
067: * @param targetLockLevel the lock level to acquire
068: * @param reentrant <code>true</code> if this request shall not be influenced by
069: * other locks held by the same owner
070: * @param isolationId the isolation level identity key. See {@link CommonsOJBLockManager}.
071: * @return <code>true</code> if the lock has been acquired, <code>false</code> otherwise
072: */
073: public boolean tryLock(Object ownerId, Object resourceId,
074: int targetLockLevel, boolean reentrant, Object isolationId) {
075: timeoutCheck(ownerId);
076:
077: OJBLock lock = atomicGetOrCreateLock(resourceId, isolationId);
078: boolean acquired = lock.tryLock(ownerId, targetLockLevel,
079: reentrant ? GenericLock.COMPATIBILITY_REENTRANT
080: : GenericLock.COMPATIBILITY_NONE, false);
081:
082: if (acquired) {
083: addOwner(ownerId, lock);
084: }
085: return acquired;
086: }
087:
088: /**
089: * @see org.apache.commons.transaction.locking.GenericLockManager#lock(Object, Object, int, int, boolean, long)
090: */
091: public void lock(Object ownerId, Object resourceId,
092: int targetLockLevel, int compatibility, boolean preferred,
093: long timeoutMSecs) throws LockException {
094: lock(ownerId, resourceId, targetLockLevel, compatibility,
095: preferred, timeoutMSecs, null);
096: }
097:
098: /**
099: * Most flexible way to acquire a lock on a resource. <br>
100: * <br>
101: * This method blocks and waits for the lock in case it is not avaiable. If
102: * there is a timeout or a deadlock or the thread is interrupted a
103: * LockException is thrown.
104: *
105: * @param ownerId a unique id identifying the entity that wants to acquire this
106: * lock
107: * @param resourceId the resource to get the level for
108: * @param targetLockLevel the lock level to acquire
109: * @param compatibility {@link GenericLock#COMPATIBILITY_NONE}if no additional compatibility is
110: * desired (same as reentrant set to false) ,
111: * {@link GenericLock#COMPATIBILITY_REENTRANT}if lock level by the same
112: * owner shall not affect compatibility (same as reentrant set to
113: * true), or {@link GenericLock#COMPATIBILITY_SUPPORT}if lock levels that
114: * are the same as the desired shall not affect compatibility, or
115: * finally {@link GenericLock#COMPATIBILITY_REENTRANT_AND_SUPPORT}which is
116: * a combination of reentrant and support
117: * @param preferred in case this lock request is incompatible with existing ones
118: * and we wait, it shall be granted before other waiting requests
119: * that are not preferred
120: * @param timeoutMSecs specifies the maximum wait time in milliseconds
121: * @param isolationId the isolation level identity key. See {@link CommonsOJBLockManager}.
122: * @throws LockException will be thrown when the lock can not be acquired
123: */
124: public void lock(Object ownerId, Object resourceId,
125: int targetLockLevel, int compatibility, boolean preferred,
126: long timeoutMSecs, Object isolationId) throws LockException {
127: timeoutCheck(ownerId);
128: GenericLock lock = atomicGetOrCreateLock(resourceId,
129: isolationId);
130: super .doLock(lock, ownerId, resourceId, targetLockLevel,
131: compatibility, preferred, timeoutMSecs);
132: }
133:
134: /**
135: * @see org.apache.commons.transaction.locking.GenericLockManager#atomicGetOrCreateLock(Object)
136: */
137: public MultiLevelLock atomicGetOrCreateLock(Object resourceId) {
138: return atomicGetOrCreateLock(resourceId, null);
139: }
140:
141: /**
142: * Either gets an existing lock on the specified resource or creates one if none exists.
143: * This methods guarantees to do this atomically.
144: *
145: * @param resourceId the resource to get or create the lock on
146: * @param isolationId the isolation level identity key. See {@link CommonsOJBLockManager}.
147: * @return the lock for the specified resource
148: */
149: public OJBLock atomicGetOrCreateLock(Object resourceId,
150: Object isolationId) {
151: synchronized (globalLocks) {
152: MultiLevelLock lock = getLock(resourceId);
153: if (lock == null) {
154: lock = createLock(resourceId, isolationId);
155: }
156: return (OJBLock) lock;
157: }
158: }
159:
160: /**
161: * @see org.apache.commons.transaction.locking.GenericLockManager#createLock(Object)
162: */
163: protected GenericLock createLock(Object resourceId) {
164: return createLock(resourceId, null);
165: }
166:
167: protected GenericLock createLock(Object resourceId,
168: Object isolationId) {
169: synchronized (globalLocks) {
170: if (isolationId != null) {
171: GenericLock lock = createIsolationLevel(resourceId,
172: isolationId, logger);
173: globalLocks.put(resourceId, lock);
174: return lock;
175: } else {
176: GenericLock lock = new GenericLock(resourceId,
177: maxLockLevel, logger);
178: globalLocks.put(resourceId, lock);
179: return lock;
180: }
181: }
182: }
183:
184: /**
185: * Creates {@link org.apache.commons.transaction.locking.GenericLock} based
186: * {@link org.apache.commons.transaction.locking.MultiLevelLock2} instances
187: * dependend on the specified isolation identity object.
188: */
189: public OJBLock createIsolationLevel(Object resourceId,
190: Object isolationId, LoggerFacade logger) {
191: OJBLock result = null;
192: switch (((Integer) isolationId).intValue()) {
193: case LockManager.IL_READ_UNCOMMITTED:
194: result = new ReadUncommittedLock(resourceId, logger);
195: break;
196: case LockManager.IL_READ_COMMITTED:
197: result = new ReadCommitedLock(resourceId, logger);
198: break;
199: case LockManager.IL_REPEATABLE_READ:
200: result = new RepeadableReadsLock(resourceId, logger);
201: break;
202: case LockManager.IL_SERIALIZABLE:
203: result = new SerializeableLock(resourceId, logger);
204: break;
205: case LockManager.IL_OPTIMISTIC:
206: throw new LockRuntimeException(
207: "Optimistic locking must be handled on top of this class");
208: default:
209: throw new LockRuntimeException(
210: "Unknown lock isolation level specified");
211: }
212: return result;
213: }
214:
215: /**
216: * Helper method to map the specified common lock level (e.g like
217: * {@link #COMMON_READ_LOCK}, {@link #COMMON_UPGRADE_LOCK, ...}) based
218: * on the isolation level to the internal used lock level value by the
219: * {@link org.apache.commons.transaction.locking.MultiLevelLock2} implementation.
220: *
221: * @param isolationId
222: * @param lockLevel
223: * @return
224: */
225: int mapLockLevelDependendOnIsolationLevel(Integer isolationId,
226: int lockLevel) {
227: int result = 0;
228: switch (isolationId.intValue()) {
229: case LockManager.IL_READ_UNCOMMITTED:
230: result = ReadUncommittedLock.mapLockLevel(lockLevel);
231: break;
232: case LockManager.IL_READ_COMMITTED:
233: result = ReadCommitedLock.mapLockLevel(lockLevel);
234: break;
235: case LockManager.IL_REPEATABLE_READ:
236: result = RepeadableReadsLock.mapLockLevel(lockLevel);
237: break;
238: case LockManager.IL_SERIALIZABLE:
239: result = SerializeableLock.mapLockLevel(lockLevel);
240: break;
241: case LockManager.IL_OPTIMISTIC:
242: throw new LockRuntimeException(
243: "Optimistic locking must be handled on top of this class");
244: default:
245: throw new LockRuntimeException(
246: "Unknown lock isolation level specified");
247: }
248: return result;
249: }
250:
251: //===================================================
252: // inner class, commons-tx lock
253: //===================================================
254: /**
255: * Abstract base class to implement the different {@link org.apache.commons.transaction.locking.GenericLock}
256: * extension classes representing the OJB isolation levels (e.g. READ_COMMITTED, REPEADABLE_READ,...)
257: */
258: abstract static class OJBLock extends GenericLock {
259: public OJBLock(Object resourceId, int maxLockLevel,
260: LoggerFacade logger) {
261: super (resourceId, maxLockLevel, logger);
262: }
263:
264: /**
265: * Need to override this method to make it accessible in
266: * {@link CommonsOJBLockManager}.
267: *
268: * @see GenericLock#tryLock(Object, int, int, boolean)
269: */
270: protected boolean tryLock(Object ownerId, int targetLockLevel,
271: int compatibility, boolean preferred) {
272: return super .tryLock(ownerId, targetLockLevel,
273: compatibility, preferred);
274: }
275:
276: /**
277: * Convenience method.
278: */
279: abstract boolean hasRead(Object ownerId);
280:
281: /**
282: * Convenience method.
283: */
284: abstract boolean hasWrite(Object ownerId);
285:
286: /**
287: * Convenience method.
288: */
289: abstract boolean hasUpgrade(Object ownerId);
290:
291: /**
292: * Convenience method.
293: */
294: abstract boolean readLock(Object ownerId, long timeout)
295: throws InterruptedException;
296:
297: /**
298: * Convenience method.
299: */
300: abstract boolean writeLock(Object ownerId, long timeout)
301: throws InterruptedException;
302:
303: /**
304: * Convenience method.
305: */
306: abstract boolean upgradeLock(Object ownerId, long timeout)
307: throws InterruptedException;
308: }
309:
310: //===================================================
311: // inner class, commons-tx lock
312: //===================================================
313: /**
314: * Implementation of isolation level {@link LockManager#IL_READ_UNCOMMITTED}.
315: */
316: static class ReadUncommittedLock extends RepeadableReadsLock {
317: public ReadUncommittedLock(Object resourceId,
318: LoggerFacade logger) {
319: super (resourceId, logger);
320: }
321:
322: protected boolean isCompatible(int targetLockLevel,
323: int currentLockLevel) {
324: if (currentLockLevel == READ_LOCK
325: || targetLockLevel == READ_LOCK) {
326: return true;
327: } else {
328: return super .isCompatible(targetLockLevel,
329: currentLockLevel);
330: }
331: }
332: }
333:
334: //===================================================
335: // inner class, commons-tx lock
336: //===================================================
337: /**
338: * Implementation of isolation level {@link LockManager#IL_READ_COMMITTED}.
339: */
340: static final class ReadCommitedLock extends RepeadableReadsLock {
341: public ReadCommitedLock(Object resourceId, LoggerFacade logger) {
342: super (resourceId, logger);
343: }
344:
345: protected boolean isCompatible(int targetLockLevel,
346: int currentLockLevel) {
347: if (currentLockLevel == READ_LOCK) {
348: return true;
349: } else {
350: return super .isCompatible(targetLockLevel,
351: currentLockLevel);
352: }
353: }
354: }
355:
356: //===================================================
357: // inner class, commons-tx lock
358: //===================================================
359: /**
360: * Implementation of isolation level {@link LockManager#IL_REPEATABLE_READ}.
361: */
362: static class RepeadableReadsLock extends OJBLock {
363: static final int NO_LOCK = 0;
364: static final int READ_LOCK = 1;
365: static final int UPGRADE_LOCK = 2;
366: static final int WRITE_LOCK = 3;
367:
368: public RepeadableReadsLock(Object resourceId,
369: LoggerFacade logger) {
370: super (resourceId, WRITE_LOCK, logger);
371: }
372:
373: static int mapLockLevel(int commonLockLevel) {
374: int result = 0;
375: switch (commonLockLevel) {
376: case COMMON_READ_LOCK:
377: result = READ_LOCK;
378: break;
379: case COMMON_UPGRADE_LOCK:
380: result = UPGRADE_LOCK;
381: break;
382: case COMMON_WRITE_LOCK:
383: result = WRITE_LOCK;
384: break;
385: default:
386: throw new LockRuntimeException(
387: "Unknown common lock type: " + commonLockLevel);
388: }
389: return result;
390: }
391:
392: public boolean readLock(Object ownerId, long timeout)
393: throws InterruptedException {
394: return acquire(ownerId, READ_LOCK, false,
395: GenericLock.COMPATIBILITY_REENTRANT, false, timeout);
396: }
397:
398: public boolean writeLock(Object ownerId, long timeout)
399: throws InterruptedException {
400: return acquire(ownerId, WRITE_LOCK, true,
401: GenericLock.COMPATIBILITY_REENTRANT, false, timeout);
402: }
403:
404: public boolean upgradeLock(Object ownerId, long timeout)
405: throws InterruptedException {
406: return acquire(ownerId, UPGRADE_LOCK, true,
407: GenericLock.COMPATIBILITY_REENTRANT, true, timeout);
408: }
409:
410: public boolean hasRead(Object ownerId) {
411: return has(ownerId, READ_LOCK);
412: }
413:
414: public boolean hasWrite(Object ownerId) {
415: return has(ownerId, WRITE_LOCK);
416: }
417:
418: public boolean hasUpgrade(Object ownerId) {
419: return has(ownerId, UPGRADE_LOCK);
420: }
421: }
422:
423: //===================================================
424: // inner class, commons-tx lock
425: //===================================================
426: /**
427: * Implementation of isolation level {@link LockManager#IL_SERIALIZABLE}.
428: */
429: static final class SerializeableLock extends ReadUncommittedLock {
430: public SerializeableLock(Object resourceId, LoggerFacade logger) {
431: super (resourceId, logger);
432: }
433:
434: protected boolean isCompatible(int targetLockLevel,
435: int currentLockLevel) {
436: return currentLockLevel > NO_LOCK ? false : true;
437: }
438: }
439: }
|