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    }