KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > ejb > plugins > lock > SimpleReadWriteEJBLock


1 /*
2 * JBoss, Home of Professional Open Source
3 * Copyright 2005, JBoss Inc., and individual contributors as indicated
4 * by the @authors tag. See the copyright.txt in the distribution for a
5 * full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */

22 package org.jboss.ejb.plugins.lock;
23
24 import java.util.LinkedList JavaDoc;
25 import java.util.HashMap JavaDoc;
26 import java.util.HashSet JavaDoc;
27 import java.util.Stack JavaDoc;
28 import java.util.Collections JavaDoc;
29 import java.lang.reflect.Method JavaDoc;
30
31 import javax.ejb.EJBObject JavaDoc;
32 import javax.ejb.EJBException JavaDoc;
33 import javax.transaction.Status JavaDoc;
34 import javax.transaction.Transaction JavaDoc;
35 import javax.transaction.Synchronization JavaDoc;
36
37 import org.jboss.invocation.Invocation;
38
39 /**
40  *
41  * This lock allows multiple read locks concurrently. Once a writer
42  * has requested the lock, future read-lock requests whose transactions
43  * do not already have the read lock will block until all writers are
44  * done -- then all the waiting readers will concurrently go (depending
45  * on the reentrant setting / methodLock). A reader who promotes gets
46  * first crack at the write lock -- ahead of other waiting writers. If
47  * there is already a reader that is promoting, we throw an inconsistent
48  * read exception. Of course, writers have to wait for all read-locks
49  * to release before taking the write lock.
50  *
51  * @author <a HREF="pete@subx.com">Peter Murray</a>
52  *
53  * @version $Revision: 37459 $
54  *
55  * <p><b>Revisions:</b><br>
56  * <p><b>2002/6/4: yarrumretep</b>
57  * <ol>
58  * <li>Initial revision
59  * </ol>
60  */

61 public class SimpleReadWriteEJBLock extends BeanLockSupport
62 {
63     int writersWaiting = 0;
64     Transaction JavaDoc promotingReader = null;
65     Transaction JavaDoc writer = null;
66     HashSet JavaDoc readers = new HashSet JavaDoc();
67     Object JavaDoc methodLock = new Object JavaDoc();
68     boolean trace = log.isTraceEnabled();
69
70     private void trace(Transaction JavaDoc tx, String JavaDoc message)
71     {
72     trace(tx, message, null);
73     }
74
75     private void trace(Transaction JavaDoc tx, String JavaDoc message, Method JavaDoc method)
76     {
77     if(method != null)
78         log.trace("LOCK(" + id + "):" + message + " : " + tx + " - " + method.getDeclaringClass().getName() + "." + method.getName());
79     else
80         log.trace("LOCK(" + id + "):" + message + " : " + tx);
81     }
82
83     public void schedule(Invocation mi)
84     {
85         boolean reading = mi.getMethod() == null ? false : container.getBeanMetaData().isMethodReadOnly(mi.getMethod().getName());
86     Transaction JavaDoc miTx = mi.getTransaction();
87
88     sync();
89     try
90     {
91         if(reading)
92         {
93         if(trace)
94             trace(miTx, "READ (RQ)", mi.getMethod());
95         getReadLock(miTx);
96         if(trace)
97             trace(miTx, "READ (GT)", mi.getMethod());
98         }
99         else
100         {
101         if(trace)
102             trace(miTx, "WRITE (RQ)", mi.getMethod());
103         getWriteLock(miTx);
104         if(trace)
105             trace(miTx, "WRITE (GT)", mi.getMethod());
106         }
107     }
108     finally
109     {
110         releaseSync();
111     }
112     }
113
114     private void getReadLock(Transaction JavaDoc tx)
115     {
116     boolean done = false;
117
118     while(!done)
119     {
120         if(tx == null)
121         {
122         done = writer == null;
123         }
124         else if(readers.contains(tx))
125         {
126         done = true;
127         }
128         else if(writer == null && promotingReader == null && writersWaiting == 0)
129         {
130         try
131         {
132             ReadLockReliever reliever = getReliever();
133             reliever.setup(this, tx);
134             tx.registerSynchronization(reliever);
135         }
136         catch (Exception JavaDoc e)
137         {
138             throw new EJBException JavaDoc(e);
139         }
140         readers.add(tx);
141         done = true;
142         }
143         else if (writer != null && writer.equals(tx))
144         {
145         done = true;
146         }
147
148         if(!done)
149         {
150         if(trace)
151             trace(tx, "READ (WT) writer:" + writer + " writers waiting: " + writersWaiting + " reader count: " + readers.size());
152         
153         waitAWhile(tx);
154         }
155     }
156     }
157
158     private void getWriteLock(Transaction JavaDoc tx)
159     {
160     boolean done = false;
161     boolean isReader;
162
163     if(tx == null)
164         throw new EJBException JavaDoc("Write lock requested without transaction.");
165
166     isReader = readers.contains(tx);
167     writersWaiting++;
168     while(!done)
169     {
170         if(writer == null && (readers.isEmpty() || (readers.size() == 1 && isReader)))
171         {
172         writersWaiting--;
173         promotingReader = null;
174         writer = tx;
175         done = true;
176         }
177         else if (writer != null && writer.equals(tx))
178         {
179         writersWaiting--;
180         done = true;
181         }
182         else
183         {
184         if(isReader)
185         {
186             if(promotingReader != null && !promotingReader.equals(tx))
187             {
188             writersWaiting--;
189             throw new EJBException JavaDoc("Contention on read lock promotion for bean. Exception in second transaction");
190             }
191             promotingReader = tx;
192         }
193
194         if(trace)
195             trace(tx, "WRITE (WT) writer:" + writer + " writers waiting: " + writersWaiting + " reader count: " + readers.size());
196
197         waitAWhile(tx);
198         }
199     }
200     }
201
202     /**
203      * Use readers as a semaphore object to avoid
204      * creating another object
205      */

206     private void waitAWhile(Transaction JavaDoc tx)
207     {
208     releaseSync();
209     try
210     {
211         synchronized(readers)
212         {
213         try
214         {
215             readers.wait(txTimeout);
216         }
217         catch(InterruptedException JavaDoc e)
218         {}
219         checkTransaction(tx);
220         }
221     }
222     finally
223     {
224         sync();
225     }
226     }
227     
228     /**
229      * Use readers as a semaphore object to avoid
230      * creating another object
231      */

232     private void notifyWaiters()
233     {
234     synchronized(readers)
235     {
236         readers.notifyAll();
237     }
238     }
239
240     private void releaseReadLock(Transaction JavaDoc transaction)
241     {
242     if(trace)
243         trace(transaction, "READ (UL)");
244
245     if(!readers.remove(transaction))
246         throw new IllegalStateException JavaDoc("ReadWriteEJBLock: Read lock released when it wasn't taken");
247
248     notifyWaiters();
249     }
250
251     private void releaseWriteLock(Transaction JavaDoc transaction)
252     {
253     if(trace)
254         trace(transaction, "WRITE (UL)");
255
256     if (synched == null)
257         throw new IllegalStateException JavaDoc("ReadWriteEJBLock: Do not call nextTransaction while not synched!");
258
259     if(writer != null && !writer.equals(transaction))
260         throw new IllegalStateException JavaDoc("ReadWriteEJBLock: can't unlock a write lock with a different transaction");
261
262     writer = null;
263     notifyWaiters();
264     }
265
266     public void endTransaction(Transaction JavaDoc transaction)
267     {
268     releaseWriteLock(transaction);
269     }
270     
271     public void wontSynchronize(Transaction JavaDoc transaction)
272     {
273     releaseWriteLock(transaction);
274     }
275
276     public void endInvocation(Invocation mi)
277     {
278     }
279
280     private static Stack JavaDoc kRecycledRelievers = new Stack JavaDoc();
281
282     static synchronized ReadLockReliever getReliever()
283     {
284     ReadLockReliever reliever;
285     if(!kRecycledRelievers.empty())
286         reliever = (ReadLockReliever)kRecycledRelievers.pop();
287     else
288         reliever = new ReadLockReliever();
289
290     return reliever;
291     }
292
293     private static class ReadLockReliever implements Synchronization JavaDoc
294     {
295     SimpleReadWriteEJBLock lock;
296     Transaction JavaDoc transaction;
297     
298     protected void finalize()
299     {
300         recycle();
301     }
302
303     protected void recycle()
304     {
305         lock = null;
306         transaction = null;
307         kRecycledRelievers.push(this);
308     }
309
310     void setup(SimpleReadWriteEJBLock lock, Transaction JavaDoc transaction)
311     {
312         this.lock = lock;
313         this.transaction = transaction;
314     }
315
316     public void beforeCompletion()
317     {
318     }
319     
320     public void afterCompletion(int status)
321     {
322         lock.sync();
323         try
324         {
325         lock.releaseReadLock(transaction);
326         }
327         finally
328         {
329         lock.releaseSync();
330         }
331         recycle();
332     }
333     }
334     
335     private void checkTransaction(Transaction JavaDoc tx)
336     {
337     try
338     {
339         if(tx != null && tx.getStatus() == Status.STATUS_MARKED_ROLLBACK)
340         throw new EJBException JavaDoc ("Transaction marked for rollback - probably a timeout.");
341     }
342     catch (Exception JavaDoc e)
343     {
344         throw new EJBException JavaDoc(e);
345     }
346     }
347 }
348
Popular Tags