1 22 package org.jboss.ejb.plugins.lock; 23 24 import java.util.LinkedList ; 25 import java.util.HashMap ; 26 import java.util.HashSet ; 27 import java.util.Stack ; 28 import java.util.Collections ; 29 import java.lang.reflect.Method ; 30 31 import javax.ejb.EJBObject ; 32 import javax.ejb.EJBException ; 33 import javax.transaction.Status ; 34 import javax.transaction.Transaction ; 35 import javax.transaction.Synchronization ; 36 37 import org.jboss.invocation.Invocation; 38 39 61 public class SimpleReadWriteEJBLock extends BeanLockSupport 62 { 63 int writersWaiting = 0; 64 Transaction promotingReader = null; 65 Transaction writer = null; 66 HashSet readers = new HashSet (); 67 Object methodLock = new Object (); 68 boolean trace = log.isTraceEnabled(); 69 70 private void trace(Transaction tx, String message) 71 { 72 trace(tx, message, null); 73 } 74 75 private void trace(Transaction tx, String message, Method 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 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 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 e) 137 { 138 throw new EJBException (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 tx) 159 { 160 boolean done = false; 161 boolean isReader; 162 163 if(tx == null) 164 throw new EJBException ("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 ("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 206 private void waitAWhile(Transaction tx) 207 { 208 releaseSync(); 209 try 210 { 211 synchronized(readers) 212 { 213 try 214 { 215 readers.wait(txTimeout); 216 } 217 catch(InterruptedException e) 218 {} 219 checkTransaction(tx); 220 } 221 } 222 finally 223 { 224 sync(); 225 } 226 } 227 228 232 private void notifyWaiters() 233 { 234 synchronized(readers) 235 { 236 readers.notifyAll(); 237 } 238 } 239 240 private void releaseReadLock(Transaction transaction) 241 { 242 if(trace) 243 trace(transaction, "READ (UL)"); 244 245 if(!readers.remove(transaction)) 246 throw new IllegalStateException ("ReadWriteEJBLock: Read lock released when it wasn't taken"); 247 248 notifyWaiters(); 249 } 250 251 private void releaseWriteLock(Transaction transaction) 252 { 253 if(trace) 254 trace(transaction, "WRITE (UL)"); 255 256 if (synched == null) 257 throw new IllegalStateException ("ReadWriteEJBLock: Do not call nextTransaction while not synched!"); 258 259 if(writer != null && !writer.equals(transaction)) 260 throw new IllegalStateException ("ReadWriteEJBLock: can't unlock a write lock with a different transaction"); 261 262 writer = null; 263 notifyWaiters(); 264 } 265 266 public void endTransaction(Transaction transaction) 267 { 268 releaseWriteLock(transaction); 269 } 270 271 public void wontSynchronize(Transaction transaction) 272 { 273 releaseWriteLock(transaction); 274 } 275 276 public void endInvocation(Invocation mi) 277 { 278 } 279 280 private static Stack kRecycledRelievers = new Stack (); 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 294 { 295 SimpleReadWriteEJBLock lock; 296 Transaction 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 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 tx) 336 { 337 try 338 { 339 if(tx != null && tx.getStatus() == Status.STATUS_MARKED_ROLLBACK) 340 throw new EJBException ("Transaction marked for rollback - probably a timeout."); 341 } 342 catch (Exception e) 343 { 344 throw new EJBException (e); 345 } 346 } 347 } 348 | Popular Tags |