001    package org.apache.ojb.otm.lock;
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.ArrayList;
019    import java.util.Collection;
020    import java.util.Collections;
021    import java.util.HashMap;
022    import java.util.Iterator;
023    
024    import org.apache.ojb.broker.Identity;
025    import org.apache.ojb.otm.OTMKit;
026    import org.apache.ojb.otm.core.Transaction;
027    import org.apache.ojb.otm.lock.wait.LockWaitStrategy;
028    
029    /**
030     *
031     * Represents the locks held for an object. The basic assertion is that at any given point
032     * in time, there can be multiple readers, but just one writer.
033     *
034     * @author <a href="mailto:rraghuram@hotmail.com">Raghu Rajah</a>
035     *
036     */
037    public class ObjectLock
038    {
039        //////////////////////////////////
040        // IVars
041        //////////////////////////////////
042    
043        private Identity _oid;
044        private LockEntry _writer;
045        private HashMap _readers = new HashMap();
046    
047    
048        ////////////////////////////////////////
049        // Constructor
050        ////////////////////////////////////////
051    
052        public ObjectLock(Identity oid)
053        {
054            _oid = oid;
055        }
056    
057        ////////////////////////////////////////
058        // Operations
059        ////////////////////////////////////////
060    
061        public Identity getTargetIdentity()
062        {
063            return _oid;
064        }
065    
066        public Transaction getWriter()
067        {
068            return (_writer == null)? null: _writer.getTx();
069        }
070    
071        public boolean isReader(Transaction tx)
072        {
073            return _readers.containsKey(tx);
074        }
075    
076        public boolean doesReaderExists()
077        {
078            return (_readers.size() > 0);
079        }
080    
081        public Collection getReaders()
082        {
083            return Collections.unmodifiableCollection(_readers.keySet());
084        }
085    
086        public void readLock(Transaction tx)
087        {
088            if (!isReader(tx))
089            {
090                new LockEntry(tx);
091            }
092        }
093    
094        public void writeLock(Transaction tx)
095            throws LockingException
096        {
097            if (getWriter() != tx)
098            {
099                LockEntry lock = (LockEntry) _readers.get(tx);
100    
101                if (lock == null)
102                {
103                    lock = new LockEntry(tx);
104                }
105                lock.writeLock();
106            }
107        }
108    
109        public void releaseLock(Transaction tx)
110        {
111            LockEntry lock = (LockEntry)_readers.get(tx);
112    
113            if (lock != null)
114            {
115                lock.release();
116            }
117        }
118    
119        public void waitForTx(Transaction tx)
120            throws LockingException
121        {
122            OTMKit kit = tx.getKit();
123            LockWaitStrategy waitStrategy = kit.getLockWaitStrategy();
124            waitStrategy.waitForLock(this, tx);
125        }
126    
127        public boolean isFree()
128        {
129            return ((_writer == null) && _readers.isEmpty());
130        }
131    
132        /////////////////////////////////////////
133        // Inner classes
134        /////////////////////////////////////////
135    
136        private class LockEntry
137        {
138            public Transaction _tx;
139            public ArrayList _listeners;
140    
141            public LockEntry(Transaction tx)
142            {
143                _tx = tx;
144                _listeners = null;
145                _readers.put(_tx, LockEntry.this);
146            }
147    
148            /**
149             *
150             * Returns the LockListeners for this entry.
151             *
152             * @return      ArrayList of LockListeners
153             *
154             */
155            public ArrayList getListeners()
156            {
157                return _listeners;
158            }
159    
160            /**
161             *
162             * Returns the transaction held by this LockEntry.
163             *
164             * @return      Transaction
165             *
166             */
167            public Transaction getTx()
168            {
169                return _tx;
170            }
171    
172            /**
173             *
174             *  Add a listener to the list of LockListeners. LockListener is notified, when this
175             *  LockEntry is released.
176             *
177             *  @param listener         the LockListener
178             *
179             */
180            public void addListener(LockListener listener)
181            {
182                if (listener != null)
183                {
184                    if (_listeners == null)
185                    {
186                        _listeners = new ArrayList();
187                    }
188    
189                    _listeners.add(listener);
190                }
191            }
192    
193            /**
194             *
195             *  Make this lock a writer. If a writer is already present, the call will block until
196             *  the lock is released by the writer.
197             *
198             */
199            public void writeLock() throws LockingException
200            {
201                while (true)
202                {
203                    if (_writer != null && _writer._tx != _tx)
204                    {
205                        waitForTx(_tx);
206                    }
207    
208                    synchronized (ObjectLock.this)
209                    {
210                        if (_writer == null || _writer._tx == _tx)
211                        {
212                            _writer = this;
213                            return;
214                        }
215                    }
216                }
217            }
218    
219            public void release()
220            {
221                synchronized (ObjectLock.this)
222                {
223                    if (_writer == this)
224                    {
225                        _writer = null;
226                    }
227                }
228    
229                _readers.remove(_tx);
230    
231                if (_listeners != null)
232                {
233                    for (Iterator iterator = _listeners.iterator(); iterator.hasNext();)
234                    {
235                      LockListener listener = (LockListener) iterator.next();
236                      listener.lockReleased(_tx, _oid);
237                    }
238                }
239            }
240        }
241    }