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