001: package org.apache.ojb.otm.lock.wait;
002:
003: /* Copyright 2003-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 java.util.HashMap;
019: import org.apache.ojb.otm.core.Transaction;
020: import org.apache.ojb.otm.lock.LockingException;
021: import org.apache.ojb.otm.lock.ObjectLock;
022:
023: public class TimeoutStrategy implements LockWaitStrategy {
024:
025: /**
026: * Maps tx to the lock that tx waits for.
027: * Is used for deadlock detection
028: */
029: private static HashMap _waitsFor = new HashMap();
030:
031: private long _timeout;
032:
033: /**
034: * @param timeout the number of milliseconds to wait before throwing exception
035: */
036: public TimeoutStrategy(long timeout) {
037: if (timeout <= 0) {
038: throw new IllegalArgumentException(
039: "Illegal timeout value: " + timeout);
040: }
041: _timeout = timeout;
042: }
043:
044: /**
045: * The default timeout is 30 seconds
046: */
047: public TimeoutStrategy() {
048: this (30000);
049: }
050:
051: /**
052: * @see org.apache.ojb.otm.lock.wait.LockWaitStrategy#waitForLock(ObjectLock, Transaction)
053: */
054: public void waitForLock(ObjectLock lock, Transaction tx)
055: throws LockingException {
056: Transaction writerTx;
057: ObjectLock writerWaitsForLock;
058:
059: // test for deadlock
060: writerTx = lock.getWriter();
061: while (writerTx != null) {
062: writerWaitsForLock = (ObjectLock) _waitsFor.get(writerTx);
063: if (writerWaitsForLock == null) {
064: break;
065: }
066: writerTx = writerWaitsForLock.getWriter();
067: if (writerTx == null) {
068: break;
069: }
070: if (writerTx == tx) {
071: StringBuffer sb = new StringBuffer();
072:
073: // deadlock detected.
074: // Now we traverse the cycle once more to provide more info
075: writerTx = lock.getWriter();
076: sb.append(lock.getTargetIdentity());
077: while (writerTx != tx) {
078: writerWaitsForLock = (ObjectLock) _waitsFor
079: .get(writerTx);
080: sb.append(" -> ");
081: sb.append(writerWaitsForLock.getTargetIdentity());
082: writerTx = writerWaitsForLock.getWriter();
083: }
084: throw new DeadlockException(sb.toString());
085: }
086: }
087:
088: // No deadlock detected, then wait the given timeout
089: _waitsFor.put(tx, lock);
090: try {
091: long now = System.currentTimeMillis();
092: long deadline = System.currentTimeMillis() + _timeout;
093:
094: do {
095: if (lock.getWriter() == null) {
096: return;
097: }
098:
099: try {
100: long toSleep = Math.min(deadline - now, 1000);
101: Thread.sleep(toSleep);
102: now += toSleep;
103: } catch (InterruptedException ex) {
104: now = System.currentTimeMillis();
105: }
106: } while (now < deadline);
107:
108: writerTx = lock.getWriter();
109:
110: if (writerTx != null) {
111: throw new ConcurrentModificationException(
112: "Object [id: " + lock.getTargetIdentity()
113: + "] locked by Transaction " + writerTx);
114: }
115: } finally {
116: _waitsFor.remove(tx);
117: }
118: }
119: }
|