1 17 package org.alfresco.repo.cache; 18 19 import java.io.IOException ; 20 import java.io.Serializable ; 21 import java.util.List ; 22 23 import net.sf.ehcache.Cache; 24 import net.sf.ehcache.CacheException; 25 import net.sf.ehcache.CacheManager; 26 import net.sf.ehcache.Element; 27 28 import org.alfresco.error.AlfrescoRuntimeException; 29 import org.alfresco.repo.transaction.AlfrescoTransactionSupport; 30 import org.alfresco.repo.transaction.TransactionListener; 31 import org.alfresco.util.EqualsHelper; 32 import org.apache.commons.logging.Log; 33 import org.apache.commons.logging.LogFactory; 34 import org.springframework.beans.factory.InitializingBean; 35 import org.springframework.util.Assert; 36 37 67 public class TransactionalCache<K extends Serializable , V extends Serializable > 68 implements SimpleCache<K, V>, TransactionListener, InitializingBean 69 { 70 private static final String RESOURCE_KEY_TXN_DATA = "TransactionalCache.TxnData"; 71 private static final String VALUE_DELETE = "TransactionalCache.DeleteMarker"; 72 73 private static Log logger = LogFactory.getLog(TransactionalCache.class); 74 75 76 private String name; 77 78 79 private SimpleCache<Serializable , Serializable > sharedCache; 80 81 82 private CacheManager cacheManager; 83 84 85 private int maxCacheSize = 500; 86 87 88 private String resourceKeyTxnData; 89 90 93 public String toString() 94 { 95 return name; 96 } 97 98 public boolean equals(Object obj) 99 { 100 if (obj == this) 101 { 102 return true; 103 } 104 if (obj == null) 105 { 106 return false; 107 } 108 if (!(obj instanceof TransactionalCache)) 109 { 110 return false; 111 } 112 TransactionalCache that = (TransactionalCache) obj; 113 return EqualsHelper.nullSafeEquals(this.name, that.name); 114 } 115 116 public int hashCode() 117 { 118 return name.hashCode(); 119 } 120 121 127 public void setSharedCache(SimpleCache<Serializable , Serializable > sharedCache) 128 { 129 this.sharedCache = sharedCache; 130 } 131 132 137 public void setCacheManager(CacheManager cacheManager) 138 { 139 this.cacheManager = cacheManager; 140 } 141 142 152 public void setMaxCacheSize(int maxCacheSize) 153 { 154 this.maxCacheSize = maxCacheSize; 155 } 156 157 162 public void setName(String name) 163 { 164 this.name = name; 165 } 166 167 170 public void afterPropertiesSet() throws Exception 171 { 172 Assert.notNull(name, "name property not set"); 173 Assert.notNull(cacheManager, "cacheManager property not set"); 174 resourceKeyTxnData = RESOURCE_KEY_TXN_DATA + "." + name; 176 } 177 178 181 private TransactionData getTransactionData() 182 { 183 TransactionData data = (TransactionData) AlfrescoTransactionSupport.getResource(resourceKeyTxnData); 184 if (data == null) 185 { 186 String txnId = AlfrescoTransactionSupport.getTransactionId(); 187 data = new TransactionData(); 188 data.updatedItemsCache = new Cache( 190 name + "_"+ txnId + "_updates", 191 maxCacheSize, false, true, 0, 0); 192 data.removedItemsCache = new Cache( 193 name + "_" + txnId + "_removes", 194 maxCacheSize, false, true, 0, 0); 195 try 196 { 197 cacheManager.addCache(data.updatedItemsCache); 198 cacheManager.addCache(data.removedItemsCache); 199 } 200 catch (CacheException e) 201 { 202 throw new AlfrescoRuntimeException("Failed to add txn caches to manager", e); 203 } 204 finally 205 { 206 AlfrescoTransactionSupport.bindListener(this); 209 } 210 AlfrescoTransactionSupport.bindResource(resourceKeyTxnData, data); 211 } 212 return data; 213 } 214 215 218 public boolean contains(K key) 219 { 220 Object value = get(key); 221 if (value == null) 222 { 223 return false; 224 } 225 else 226 { 227 return true; 228 } 229 } 230 231 235 @SuppressWarnings ("unchecked") 236 public V get(K key) 237 { 238 boolean ignoreSharedCache = false; 239 if (AlfrescoTransactionSupport.getTransactionId() != null) 241 { 242 TransactionData txnData = getTransactionData(); 243 try 244 { 245 if (!txnData.isClearOn) { 247 if (txnData.removedItemsCache.get(key) != null) 249 { 250 if (logger.isDebugEnabled()) 252 { 253 logger.debug("get returning null - item has been removed from transactional cache: \n" + 254 " cache: " + this + "\n" + 255 " key: " + key); 256 } 257 return null; 258 } 259 } 260 261 Element element = txnData.updatedItemsCache.get(key); 263 if (element != null) 264 { 265 if (logger.isDebugEnabled()) 267 { 268 logger.debug("Found item in transactional cache: \n" + 269 " cache: " + this + "\n" + 270 " key: " + key + "\n" + 271 " value: " + element.getValue()); 272 } 273 return (V) element.getValue(); 274 } 275 } 276 catch (CacheException e) 277 { 278 throw new AlfrescoRuntimeException("Cache failure", e); 279 } 280 ignoreSharedCache = txnData.isClearOn; 282 } 283 if (!ignoreSharedCache) 285 { 286 if (logger.isDebugEnabled()) 288 { 289 logger.debug("No value found in transaction - fetching instance from shared cache: \n" + 290 " cache: " + this + "\n" + 291 " key: " + key + "\n" + 292 " value: " + sharedCache.get(key)); 293 } 294 return (V) sharedCache.get(key); 295 } 296 else { 298 if (logger.isDebugEnabled()) 299 { 300 logger.debug("No value found in transaction and ignoring shared cache: \n" + 301 " cache: " + this + "\n" + 302 " key: " + key); 303 } 304 return null; 305 } 306 } 307 308 314 public void put(K key, V value) 315 { 316 if (AlfrescoTransactionSupport.getTransactionId() == null) { 319 sharedCache.put(key, value); 321 if (logger.isDebugEnabled()) 323 { 324 logger.debug("No transaction - adding item direct to shared cache: \n" + 325 " cache: " + this + "\n" + 326 " key: " + key + "\n" + 327 " value: " + value); 328 } 329 } 330 else { 332 TransactionData txnData = getTransactionData(); 333 if (txnData.updatedItemsCache.getMemoryStoreSize() >= maxCacheSize) 336 { 337 txnData.isClearOn = true; 341 } 342 Element element = new Element(key, value); 343 txnData.updatedItemsCache.put(element); 344 txnData.removedItemsCache.remove(key); 346 if (logger.isDebugEnabled()) 348 { 349 logger.debug("In transaction - adding item direct to transactional update cache: \n" + 350 " cache: " + this + "\n" + 351 " key: " + key + "\n" + 352 " value: " + value); 353 } 354 } 355 } 356 357 363 public void remove(K key) 364 { 365 if (AlfrescoTransactionSupport.getTransactionId() == null) { 368 sharedCache.remove(key); 370 if (logger.isDebugEnabled()) 372 { 373 logger.debug("No transaction - removing item from shared cache: \n" + 374 " cache: " + this + "\n" + 375 " key: " + key); 376 } 377 } 378 else { 380 TransactionData txnData = getTransactionData(); 381 if (txnData.isClearOn) 383 { 384 } 386 else 387 { 388 if (txnData.removedItemsCache.getMemoryStoreSize() >= maxCacheSize) 390 { 391 txnData.isClearOn = true; 395 if (logger.isDebugEnabled()) 396 { 397 logger.debug("In transaction - removal cache reach capacity reached: \n" + 398 " cache: " + this + "\n" + 399 " txn: " + AlfrescoTransactionSupport.getTransactionId()); 400 } 401 } 402 else 403 { 404 Element element = new Element(key, VALUE_DELETE); 406 txnData.removedItemsCache.put(element); 407 } 408 } 409 txnData.updatedItemsCache.remove(key); 411 if (logger.isDebugEnabled()) 413 { 414 logger.debug("In transaction - adding item direct to transactional removed cache: \n" + 415 " cache: " + this + "\n" + 416 " key: " + key); 417 } 418 } 419 } 420 421 424 public void clear() 425 { 426 if (AlfrescoTransactionSupport.getTransactionId() != null) 428 { 429 if (logger.isDebugEnabled()) 430 { 431 logger.debug("In transaction clearing cache: \n" + 432 " cache: " + this + "\n" + 433 " txn: " + AlfrescoTransactionSupport.getTransactionId()); 434 } 435 436 TransactionData txnData = getTransactionData(); 437 txnData.isClearOn = true; 441 try 442 { 443 txnData.updatedItemsCache.removeAll(); 444 txnData.removedItemsCache.removeAll(); 445 } 446 catch (IOException e) 447 { 448 throw new AlfrescoRuntimeException("Failed to clear caches", e); 449 } 450 } 451 else { 453 if (logger.isDebugEnabled()) 454 { 455 logger.debug("No transaction - clearing shared cache"); 456 } 457 sharedCache.clear(); 459 } 460 } 461 462 465 public void flush() 466 { 467 } 468 469 public void beforeCommit(boolean readOnly) 470 { 471 } 472 473 public void beforeCompletion() 474 { 475 } 476 477 480 @SuppressWarnings ("unchecked") 481 public void afterCommit() 482 { 483 if (logger.isDebugEnabled()) 484 { 485 logger.debug("Processing end of transaction commit"); 486 } 487 488 TransactionData txnData = getTransactionData(); 489 try 490 { 491 if (txnData.isClearOn) 492 { 493 sharedCache.clear(); 495 if (logger.isDebugEnabled()) 496 { 497 logger.debug("Clear notification recieved at end of transaction - clearing shared cache"); 498 } 499 } 500 else 501 { 502 List <Serializable > keys = txnData.removedItemsCache.getKeys(); 506 for (Serializable key : keys) 507 { 508 sharedCache.remove(key); 509 } 510 if (logger.isDebugEnabled()) 511 { 512 logger.debug("Removed " + keys.size() + " values from shared cache"); 513 } 514 } 515 516 List <Serializable > keys = txnData.updatedItemsCache.getKeys(); 518 for (Serializable key : keys) 519 { 520 Element element = txnData.updatedItemsCache.get(key); 521 sharedCache.put(key, element.getValue()); 522 } 523 if (logger.isDebugEnabled()) 524 { 525 logger.debug("Added " + keys.size() + " values to shared cache"); 526 } 527 } 528 catch (CacheException e) 529 { 530 throw new AlfrescoRuntimeException("Failed to transfer updates to shared cache", e); 531 } 532 finally 533 { 534 removeCaches(txnData); 535 } 536 } 537 538 541 public void afterRollback() 542 { 543 TransactionData txnData = getTransactionData(); 544 removeCaches(txnData); 546 } 547 548 553 private void removeCaches(TransactionData txnData) 554 { 555 cacheManager.removeCache(txnData.updatedItemsCache.getName()); 556 cacheManager.removeCache(txnData.removedItemsCache.getName()); 557 } 558 559 560 private class TransactionData 561 { 562 public Cache updatedItemsCache; 563 public Cache removedItemsCache; 564 public boolean isClearOn; 565 } 566 } 567 | Popular Tags |