/** * Fetches a value from the shared cache. If values were wrapped, then they will be unwrapped * before being returned. If code requires direct access to the wrapper object as well, then this * call should not be used. * * <p>If a TransactionStats instance is passed in, then cache access stats are tracked, otherwise * - if null is passed in then stats are not tracked. * * @param key the key * @return Returns the value or <tt>null</tt> */ @SuppressWarnings("unchecked") public static <KEY extends Serializable, VAL> VAL getSharedCacheValue( SimpleCache<KEY, ValueHolder<VAL>> sharedCache, KEY key, TransactionStats stats) { final long startNanos = stats != null ? System.nanoTime() : 0; Object possibleWrapper = sharedCache.get(key); final long endNanos = stats != null ? System.nanoTime() : 0; if (possibleWrapper == null) { if (stats != null) { stats.record(startNanos, endNanos, OpType.GET_MISS); } return null; } else if (possibleWrapper instanceof ValueHolder) { if (stats != null) { stats.record(startNanos, endNanos, OpType.GET_HIT); } ValueHolder<VAL> wrapper = (ValueHolder<VAL>) possibleWrapper; return wrapper.getValue(); } else { if (stats != null) { stats.record(startNanos, endNanos, OpType.GET_MISS); } throw new IllegalStateException( "All entries for TransactionalCache must be put using TransactionalCache.putSharedCacheValue."); } }
/** Merge the transactional caches into the shared cache */ public void beforeCommit(boolean readOnly) { if (isDebugEnabled) { logger.debug("Processing before-commit"); } TransactionData txnData = getTransactionData(); try { if (txnData.isClearOn) { // clear shared cache final long startNanos = cacheStatsEnabled ? System.nanoTime() : 0; sharedCache.clear(); final long endNanos = cacheStatsEnabled ? System.nanoTime() : 0; if (cacheStatsEnabled) { TransactionStats stats = txnData.stats; stats.record(startNanos, endNanos, OpType.CLEAR); } if (isDebugEnabled) { logger.debug("Clear notification recieved in commit - clearing shared cache"); } } else { // transfer any removed items for (Serializable key : txnData.removedItemsCache) { final long startNanos = System.nanoTime(); sharedCache.remove(key); final long endNanos = System.nanoTime(); TransactionStats stats = txnData.stats; stats.record(startNanos, endNanos, OpType.REMOVE); } if (isDebugEnabled) { logger.debug( "Removed " + txnData.removedItemsCache.size() + " values from shared cache in commit"); } } // transfer updates Set<Serializable> keys = (Set<Serializable>) txnData.updatedItemsCache.keySet(); for (Map.Entry<Serializable, CacheBucket<V>> entry : (Set<Map.Entry<Serializable, CacheBucket<V>>>) txnData.updatedItemsCache.entrySet()) { Serializable key = entry.getKey(); CacheBucket<V> bucket = entry.getValue(); bucket.doPreCommit( sharedCache, key, this.isMutable, this.allowEqualsChecks, txnData.isReadOnly); } if (isDebugEnabled) { logger.debug("Pre-commit called for " + keys.size() + " values."); } } catch (Throwable e) { throw new AlfrescoRuntimeException("Failed to transfer updates to shared cache", e); } finally { // Block any further updates txnData.isClosed = true; } }
/** * Values written to the backing cache need proper wrapping and unwrapping * * @param sharedCache the cache to operate on * @param key the key * @param value the value to wrap * @since 4.2.3 */ public static <KEY extends Serializable, VAL> void putSharedCacheValue( SimpleCache<KEY, ValueHolder<VAL>> sharedCache, KEY key, VAL value, TransactionStats stats) { ValueHolder<VAL> wrapper = new ValueHolder<VAL>(value); final long startNanos = System.nanoTime(); // TODO: enabled? sharedCache.put(key, wrapper); final long endNanos = System.nanoTime(); if (stats != null) { stats.record(startNanos, endNanos, OpType.PUT); } }
/** * Transfers cache removals or clears. This allows explicit cache cleanup to be propagated to the * shared cache even in the event of rollback - useful if the cause of a problem is the shared * cache value. */ public void afterRollback() { TransactionData txnData = getTransactionData(); try { if (txnData.isClearOn) { // clear shared cache final long startNanos = cacheStatsEnabled ? System.nanoTime() : 0; sharedCache.clear(); final long endNanos = cacheStatsEnabled ? System.nanoTime() : 0; if (cacheStatsEnabled) { TransactionStats stats = txnData.stats; stats.record(startNanos, endNanos, OpType.CLEAR); } if (isDebugEnabled) { logger.debug("Clear notification recieved in rollback - clearing shared cache"); } } else { // transfer any removed items for (Serializable key : txnData.removedItemsCache) { final long startNanos = System.nanoTime(); sharedCache.remove(key); final long endNanos = System.nanoTime(); TransactionStats stats = txnData.stats; stats.record(startNanos, endNanos, OpType.REMOVE); } if (isDebugEnabled) { logger.debug( "Removed " + txnData.removedItemsCache.size() + " values from shared cache in rollback"); } } } catch (Throwable e) { throw new AlfrescoRuntimeException("Failed to transfer updates to shared cache", e); } finally { removeCaches(txnData); // Aggregate this transaction's stats with centralised cache stats. if (cacheStatsEnabled) { cacheStats.add(name, txnData.stats); } } }
/** Merge the transactional caches into the shared cache */ public void afterCommit() { if (isDebugEnabled) { logger.debug("Processing after-commit"); } TransactionData txnData = getTransactionData(); try { if (txnData.isClearOn) { // clear shared cache final long startNanos = cacheStatsEnabled ? System.nanoTime() : 0; sharedCache.clear(); final long endNanos = cacheStatsEnabled ? System.nanoTime() : 0; if (cacheStatsEnabled) { TransactionStats stats = txnData.stats; stats.record(startNanos, endNanos, OpType.CLEAR); } if (isDebugEnabled) { logger.debug("Clear notification recieved in commit - clearing shared cache"); } } else { // transfer any removed items for (Serializable key : txnData.removedItemsCache) { final long startNanos = System.nanoTime(); sharedCache.remove(key); final long endNanos = System.nanoTime(); TransactionStats stats = txnData.stats; stats.record(startNanos, endNanos, OpType.REMOVE); } if (isDebugEnabled) { logger.debug( "Removed " + txnData.removedItemsCache.size() + " values from shared cache in commit"); } } // transfer updates Set<Serializable> keys = (Set<Serializable>) txnData.updatedItemsCache.keySet(); for (Map.Entry<Serializable, CacheBucket<V>> entry : (Set<Map.Entry<Serializable, CacheBucket<V>>>) txnData.updatedItemsCache.entrySet()) { Serializable key = entry.getKey(); CacheBucket<V> bucket = entry.getValue(); try { bucket.doPostCommit( sharedCache, key, this.isMutable, this.allowEqualsChecks, txnData.isReadOnly, txnData.stats); } catch (Exception e) { // MNT-10486: NPE in NodeEntity during post-commit write through to shared cache // This try-catch is diagnostic in nature. We need to know the names of the // caches // and details of the values involved. // The causal exception will be rethrown. throw new AlfrescoRuntimeException( "CacheBucket postCommit transfer to shared cache failed: \n" + " Cache: " + sharedCache + "\n" + " Key: " + key + "\n" + " New Value: " + bucket.getValue() + "\n" + " Cache Value:" + sharedCache.get(key), e); } } if (isDebugEnabled) { logger.debug("Post-commit called for " + keys.size() + " values."); } } catch (Throwable e) { throw new AlfrescoRuntimeException("Failed to transfer updates to shared cache", e); } finally { removeCaches(txnData); // Aggregate this transaction's stats with centralised cache stats. if (cacheStatsEnabled) { cacheStats.add(name, txnData.stats); } } }