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 }