1 package org.hibernate.cache; 3 4 import java.io.Serializable ; 5 import java.util.Comparator ; 6 7 import org.apache.commons.logging.Log; 8 import org.apache.commons.logging.LogFactory; 9 10 26 public class ReadWriteCache implements CacheConcurrencyStrategy { 27 28 private static final Log log = LogFactory.getLog(ReadWriteCache.class); 29 30 private Cache cache; 31 private int nextLockId; 32 33 public ReadWriteCache() {} 34 35 public void setCache(Cache cache) { 36 this.cache=cache; 37 } 38 39 public Cache getCache() { 40 return cache; 41 } 42 43 public String getRegionName() { 44 return cache.getRegionName(); 45 } 46 47 52 private int nextLockId() { 53 if (nextLockId==Integer.MAX_VALUE) nextLockId = Integer.MIN_VALUE; 54 return nextLockId++; 55 } 56 57 73 public synchronized Object get(Object key, long txTimestamp) throws CacheException { 74 75 if ( log.isTraceEnabled() ) log.trace("Cache lookup: " + key); 76 77 try { 78 cache.lock(key); 79 80 Lockable lockable = (Lockable) cache.get(key); 81 82 boolean gettable = lockable!=null && lockable.isGettable(txTimestamp); 83 84 if (gettable) { 85 if ( log.isTraceEnabled() ) log.trace("Cache hit: " + key); 86 return ( (Item) lockable ).getValue(); 87 } 88 else { 89 if ( log.isTraceEnabled() ) { 90 if (lockable==null) { 91 log.trace("Cache miss: " + key); 92 } 93 else { 94 log.trace("Cached item was locked: " + key); 95 } 96 } 97 return null; 98 } 99 } 100 finally { 101 cache.unlock(key); 102 } 103 } 104 105 112 public synchronized SoftLock lock(Object key, Object version) throws CacheException { 113 if ( log.isTraceEnabled() ) log.trace("Invalidating: " + key); 114 115 try { 116 cache.lock(key); 117 118 Lockable lockable = (Lockable) cache.get(key); 119 long timeout = cache.nextTimestamp() + cache.getTimeout(); 120 final Lock lock = (lockable==null) ? 121 new Lock( timeout, nextLockId(), version ) : 122 lockable.lock( timeout, nextLockId() ); 123 cache.update(key, lock); 124 return lock; 125 } 126 finally { 127 cache.unlock(key); 128 } 129 130 } 131 132 140 public synchronized boolean put( 141 Object key, 142 Object value, 143 long txTimestamp, 144 Object version, 145 Comparator versionComparator, 146 boolean minimalPut) 147 throws CacheException { 148 if ( log.isTraceEnabled() ) log.trace("Caching: " + key); 149 150 try { 151 cache.lock(key); 152 153 Lockable lockable = (Lockable) cache.get(key); 154 155 boolean puttable = lockable==null || 156 lockable.isPuttable(txTimestamp, version, versionComparator); 157 158 if (puttable) { 159 cache.put( key, new Item( value, version, cache.nextTimestamp() ) ); 160 if ( log.isTraceEnabled() ) log.trace("Cached: " + key); 161 return true; 162 } 163 else { 164 if ( log.isTraceEnabled() ) { 165 if ( lockable.isLock() ) { 166 log.trace("Item was locked: " + key); 167 } 168 else { 169 log.trace("Item was already cached: " + key); 170 } 171 } 172 return false; 173 } 174 } 175 finally { 176 cache.unlock(key); 177 } 178 } 179 180 183 private void decrementLock(Object key, Lock lock) throws CacheException { 184 lock.unlock( cache.nextTimestamp() ); 186 cache.update(key, lock); 187 } 188 189 194 public synchronized void release(Object key, SoftLock clientLock) throws CacheException { 195 if ( log.isTraceEnabled() ) log.trace("Releasing: " + key); 196 197 try { 198 cache.lock(key); 199 200 Lockable lockable = (Lockable) cache.get(key); 201 if ( isUnlockable(clientLock, lockable) ) { 202 decrementLock(key, (Lock) lockable); 203 } 204 else { 205 handleLockExpiry(key); 206 } 207 } 208 finally { 209 cache.unlock(key); 210 } 211 } 212 213 void handleLockExpiry(Object key) throws CacheException { 214 log.warn("An item was expired by the cache while it was locked (increase your cache timeout): " + key); 215 long ts = cache.nextTimestamp() + cache.getTimeout(); 216 Lock lock = new Lock( ts, nextLockId(), null ); 218 lock.unlock(ts); 219 cache.update(key, lock); 220 } 221 222 public void clear() throws CacheException { 223 cache.clear(); 224 } 225 226 public void remove(Object key) throws CacheException { 227 cache.remove(key); 228 } 229 230 public void destroy() { 231 try { 232 cache.destroy(); 233 } 234 catch (Exception e) { 235 log.warn("could not destroy cache", e); 236 } 237 } 238 239 243 public synchronized boolean afterUpdate(Object key, Object value, Object version, SoftLock clientLock) 244 throws CacheException { 245 246 if ( log.isTraceEnabled() ) log.trace("Updating: " + key); 247 248 try { 249 cache.lock(key); 250 251 Lockable lockable = (Lockable) cache.get(key); 252 if ( isUnlockable(clientLock, lockable) ) { 253 Lock lock = (Lock) lockable; 254 if ( lock.wasLockedConcurrently() ) { 255 decrementLock(key, lock); 258 return false; 259 } 260 else { 261 cache.update( key, new Item( value, version, cache.nextTimestamp() ) ); 263 if ( log.isTraceEnabled() ) log.trace("Updated: " + key); 264 return true; 265 } 266 } 267 else { 268 handleLockExpiry(key); 269 return false; 270 } 271 272 } 273 finally { 274 cache.unlock(key); 275 } 276 } 277 278 282 public synchronized boolean afterInsert(Object key, Object value, Object version) 283 throws CacheException { 284 285 if ( log.isTraceEnabled() ) log.trace("Inserting: " + key); 286 try { 287 cache.lock(key); 288 289 Lockable lockable = (Lockable) cache.get(key); 290 if (lockable==null) { 291 cache.update( key, new Item( value, version, cache.nextTimestamp() ) ); 292 if ( log.isTraceEnabled() ) log.trace("Inserted: " + key); 293 return true; 294 } 295 else { 296 return false; 297 } 298 } 299 finally { 300 cache.unlock(key); 301 } 302 } 303 304 307 public void evict(Object key) throws CacheException { 308 } 310 311 314 public boolean insert(Object key, Object value) throws CacheException { 315 return false; 316 } 317 318 321 public boolean update(Object key, Object value) throws CacheException { 322 return false; 323 } 324 325 330 private boolean isUnlockable(SoftLock clientLock, Lockable myLock) 331 throws CacheException { 332 return myLock!=null && 334 myLock.isLock() && 335 clientLock!=null && 336 ( (Lock) clientLock ).getId()==( (Lock) myLock ).getId(); 337 } 338 339 public static interface Lockable { 340 public Lock lock(long timeout, int id); 341 public boolean isLock(); 342 public boolean isGettable(long txTimestamp); 343 public boolean isPuttable(long txTimestamp, Object newVersion, Comparator comparator); 344 } 345 346 350 public static final class Item implements Serializable , Lockable { 351 352 private final long freshTimestamp; 353 private final Object value; 354 private final Object version; 355 356 public Item(Object value, Object version, long currentTimestamp) { 357 this.value = value; 358 this.version = version; 359 freshTimestamp = currentTimestamp; 360 } 361 364 public long getFreshTimestamp() { 365 return freshTimestamp; 366 } 367 370 public Object getValue() { 371 return value; 372 } 373 374 377 public Lock lock(long timeout, int id) { 378 return new Lock(timeout, id, version); 379 } 380 383 public boolean isLock() { 384 return false; 385 } 386 390 public boolean isGettable(long txTimestamp) { 391 return freshTimestamp < txTimestamp; 392 } 393 394 397 public boolean isPuttable(long txTimestamp, Object newVersion, Comparator comparator) { 398 return version!=null && comparator.compare(version, newVersion) < 0; 402 } 403 404 public String toString() { 405 return "Item{version=" + version + 406 ",freshTimestamp=" + freshTimestamp; 407 } 408 } 409 410 415 public static final class Lock implements Serializable , Lockable, SoftLock { 416 private long unlockTimestamp = -1; 417 private int multiplicity = 1; 418 private boolean concurrentLock = false; 419 private long timeout; 420 private final int id; 421 private final Object version; 422 423 public Lock(long timeout, int id, Object version) { 424 this.timeout = timeout; 425 this.id = id; 426 this.version = version; 427 } 428 429 public long getUnlockTimestamp() { 430 return unlockTimestamp; 431 } 432 436 public Lock lock(long timeout, int id) { 437 concurrentLock = true; 438 multiplicity++; 439 this.timeout = timeout; 440 return this; 441 } 442 447 public void unlock(long currentTimestamp) { 448 if ( --multiplicity == 0 ) { 449 unlockTimestamp = currentTimestamp; 450 } 451 } 452 453 457 public boolean isPuttable(long txTimestamp, Object newVersion, Comparator comparator) { 458 if (timeout < txTimestamp) return true; 459 if (multiplicity>0) return false; 460 return version==null ? 461 unlockTimestamp < txTimestamp : 462 comparator.compare(version, newVersion) < 0; } 464 465 469 public boolean wasLockedConcurrently() { 470 return concurrentLock; 471 } 472 475 public boolean isLock() { 476 return true; 477 } 478 481 public boolean isGettable(long txTimestamp) { 482 return false; 483 } 484 485 public int getId() { return id; } 486 487 public String toString() { 488 return "Lock{id=" + id + 489 ",version=" + version + 490 ",multiplicity=" + multiplicity + 491 ",unlockTimestamp=" + unlockTimestamp; 492 } 493 494 } 495 496 public String toString() { 497 return cache + "(read-write)"; 498 } 499 500 } 501 502 503 504 505 506 507 | Popular Tags |