diff --git a/source/java/org/alfresco/repo/cache/TransactionalCache.java b/source/java/org/alfresco/repo/cache/TransactionalCache.java index 4a81ecb664..65c02cea75 100644 --- a/source/java/org/alfresco/repo/cache/TransactionalCache.java +++ b/source/java/org/alfresco/repo/cache/TransactionalCache.java @@ -83,7 +83,7 @@ public class TransactionalCache /** enable/disable write through to the shared cache */ private boolean disableSharedCache; /** the shared cache that will get updated after commits */ - private SimpleCache sharedCache; + private SimpleCache> sharedCache; /** can the cached values be modified */ private boolean isMutable; /** can values be compared using full equality checking */ @@ -145,7 +145,7 @@ public class TransactionalCache * * @param sharedCache underlying cache shared by transactions */ - public void setSharedCache(SimpleCache sharedCache) + public void setSharedCache(SimpleCache> sharedCache) { this.sharedCache = sharedCache; } @@ -406,15 +406,46 @@ public class TransactionalCache } /** - * Fetches a value from the shared cache. + * 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. * * @param key the key * @return Returns the value or null */ @SuppressWarnings("unchecked") - private V getSharedCacheValue(Serializable key) + public static VAL getSharedCacheValue(SimpleCache> sharedCache, KEY key) { - return (V) sharedCache.get(key); + Object possibleWrapper = sharedCache.get(key); + if (possibleWrapper == null) + { + return null; + } + else if (possibleWrapper instanceof ValueHolder) + { + ValueHolder wrapper = (ValueHolder) possibleWrapper; + return wrapper.getValue(); + } + else + { + throw new IllegalStateException("All entries for TransactionalCache must be put using TransactionalCache.putSharedCacheValue."); + } + } + + /** + * 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 void putSharedCacheValue(SimpleCache> sharedCache, KEY key, VAL value) + { + ValueHolder wrapper = new ValueHolder(value); + sharedCache.put(key, wrapper); } /** @@ -551,7 +582,7 @@ public class TransactionalCache { // There is no in-txn entry for the key // Use the value direct from the shared cache - V value = getSharedCacheValue(key); + V value = TransactionalCache.getSharedCacheValue(sharedCache, key); bucket = new ReadCacheBucket(value); txnData.updatedItemsCache.put(key, bucket); return value; @@ -561,7 +592,7 @@ public class TransactionalCache // no value found - must we ignore the shared cache? if (!ignoreSharedCache) { - V value = getSharedCacheValue(key); + V value = TransactionalCache.getSharedCacheValue(sharedCache, key); // go to the shared cache if (isDebugEnabled) { @@ -590,7 +621,6 @@ public class TransactionalCache * Where a transaction is present, a cache of updated items is lazily added to the * thread and the Object put onto that. */ - @SuppressWarnings("unchecked") public void put(K keyIn, V value) { final Serializable key = getTenantAwareCacheKey(keyIn); @@ -599,7 +629,7 @@ public class TransactionalCache if (AlfrescoTransactionSupport.getTransactionId() == null) // not in transaction { // no transaction - sharedCache.put(key, value); + TransactionalCache.putSharedCacheValue(sharedCache, key, value); // done if (isDebugEnabled) { @@ -652,9 +682,9 @@ public class TransactionalCache txnData.haveIssuedFullWarning = true; } } - Object existingValueObj = txnData.noSharedCacheRead ? null : sharedCache.get(key); + ValueHolder existingValueHolder = txnData.noSharedCacheRead ? null : sharedCache.get(key); CacheBucket bucket = null; - if (existingValueObj == null) + if (existingValueHolder == null) { // ALF-5134: Performance of Alfresco cluster less than performance of single node // The 'null' marker that used to be inserted also triggered an update in the afterCommit @@ -667,7 +697,7 @@ public class TransactionalCache else { // Record the existing value as is - bucket = new UpdateCacheBucket((V)existingValueObj, value); + bucket = new UpdateCacheBucket(existingValueHolder, value); } txnData.updatedItemsCache.put(key, bucket); // remove the item from the removed cache, if present @@ -1045,7 +1075,7 @@ public class TransactionalCache * @param key the key that the bucket was stored against */ public void doPreCommit( - SimpleCache sharedCache, + SimpleCache> sharedCache, Serializable key, boolean mutable, boolean allowEqualsCheck, boolean readOnly); /** @@ -1055,7 +1085,7 @@ public class TransactionalCache * @param key the key that the bucket was stored against */ public void doPostCommit( - SimpleCache sharedCache, + SimpleCache> sharedCache, Serializable key, boolean mutable, boolean allowEqualsCheck, boolean readOnly); } @@ -1079,21 +1109,21 @@ public class TransactionalCache return value; } public void doPreCommit( - SimpleCache sharedCache, + SimpleCache> sharedCache, Serializable key, boolean mutable, boolean allowEqualsCheck, boolean readOnly) { } public void doPostCommit( - SimpleCache sharedCache, + SimpleCache> sharedCache, Serializable key, boolean mutable, boolean allowEqualsCheck, boolean readOnly) { - Object sharedObj = sharedCache.get(key); - if (sharedObj == null) + ValueHolder sharedObjValueHolder = sharedCache.get(key); + if (sharedObjValueHolder == null) { // Nothing has changed, write it through - sharedCache.put(key, value); + TransactionalCache.putSharedCacheValue(sharedCache, key, value); } else if (!mutable) { @@ -1101,15 +1131,9 @@ public class TransactionalCache // The assumption is that the value will be correct because the values are immutable // Don't write it unnecessarily. } - else if (sharedObj == value) + else if (allowEqualsCheck && EqualsHelper.nullSafeEquals(value, sharedObjValueHolder.getValue())) { - // Someone else put exactly the same value into the cache - // Don't write it unnecessarily. - } - else if (allowEqualsCheck && EqualsHelper.nullSafeEquals(value, sharedObj)) - { - // Someone else added a value but we have validated that it is the same - // as the new one that we where going to add. + // The value we want to write is the same as the one in the shared cache. // Don't write it unnecessarily. } else @@ -1131,10 +1155,10 @@ public class TransactionalCache private static final long serialVersionUID = 7885689778259779578L; private final BV value; - private final BV originalValue; - public UpdateCacheBucket(BV originalValue, BV value) + private final ValueHolder originalValueHolder; + public UpdateCacheBucket(ValueHolder originalValueHolder, BV value) { - this.originalValue = originalValue; + this.originalValueHolder = originalValueHolder; this.value = value; } public BV getValue() @@ -1142,47 +1166,45 @@ public class TransactionalCache return value; } public void doPreCommit( - SimpleCache sharedCache, + SimpleCache> sharedCache, Serializable key, boolean mutable, boolean allowEqualsCheck, boolean readOnly) { } public void doPostCommit( - SimpleCache sharedCache, + SimpleCache> sharedCache, Serializable key, boolean mutable, boolean allowEqualsCheck, boolean readOnly) { - Object sharedObj = sharedCache.get(key); - if (sharedObj == null) + ValueHolder sharedObjValueHolder = sharedCache.get(key); + if (sharedObjValueHolder == null) { // Someone removed the value if (!mutable) { // We can assume that our value is correct because it's immutable - sharedCache.put(key, value); + TransactionalCache.putSharedCacheValue(sharedCache, key, value); } else { - // The value is mutable, so we must behave pessimistically + // The value is mutable, so we must behave pessimistically i.e. leave the shared cache empty } } else if (!mutable) { - // Someone else has already updated the value. - // This is not normally seen for immutable values. The assumption is that the values - // are equal. + // We assume the configuration is correct and therefore, that we do not need to compare + // the cached value with the updated value. This applies to null as well. + } + else if (allowEqualsCheck && EqualsHelper.nullSafeEquals(value, sharedObjValueHolder.getValue())) + { + // The value we want to write is the same as the one in the shared cache. // Don't write it unnecessarily. } - else if (sharedObj == originalValue) + else if (EqualsHelper.nullSafeEquals(originalValueHolder, sharedObjValueHolder)) { - // Nothing has changed, write it through - sharedCache.put(key, value); - } - else if (allowEqualsCheck && EqualsHelper.nullSafeEquals(value, sharedObj)) - { - // Someone else updated the value but we have validated that it is the same - // as the one that we where going to update. - // Don't write it unnecessarily. + // The value in the cache did not change from what we observed before. + // Update the value. + TransactionalCache.putSharedCacheValue(sharedCache, key, value); } else { @@ -1211,13 +1233,13 @@ public class TransactionalCache return value; } public void doPreCommit( - SimpleCache sharedCache, + SimpleCache> sharedCache, Serializable key, boolean mutable, boolean allowEqualsCheck, boolean readOnly) { } public void doPostCommit( - SimpleCache sharedCache, + SimpleCache> sharedCache, Serializable key, boolean mutable, boolean allowEqualsCheck, boolean readOnly) { @@ -1338,4 +1360,57 @@ public class TransactionalCache return hashCode; } } + + /** + * A wrapper object to carry object values, but forcing a straight equality check + * based on a random integer only. This is used in cases where cache values do NOT + * have an adequate equals method and we expect serialization of objects. + * + * @author Derek Hulley + * @since 4.2.4 + */ + public static final class ValueHolder implements Serializable + { + private static final long serialVersionUID = -3462098329153772713L; + + /** + * A random, positive integer since we only have to + * prevent short-term duplication between values with the same keys. + */ + private final int rand; + private final V2 value; + private ValueHolder(V2 value) + { + + this.rand = (int) (Math.random() * Integer.MAX_VALUE); + this.value = value; + } + public final V2 getValue() + { + return value; + } + @Override + public final int hashCode() + { + return rand; + } + @SuppressWarnings("rawtypes") + @Override + public final boolean equals(Object obj) + { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ValueHolder other = (ValueHolder) obj; + return this.rand == other.rand; + } + @Override + public final String toString() + { + return "ValueHolder [value=" + value + "]"; + } + } } diff --git a/source/test-java/org/alfresco/repo/cache/CacheTest.java b/source/test-java/org/alfresco/repo/cache/CacheTest.java index fdaeabdacf..b776a4c54d 100644 --- a/source/test-java/org/alfresco/repo/cache/CacheTest.java +++ b/source/test-java/org/alfresco/repo/cache/CacheTest.java @@ -26,6 +26,7 @@ import javax.transaction.UserTransaction; import junit.framework.TestCase; +import org.alfresco.repo.cache.TransactionalCache.ValueHolder; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState; import org.alfresco.repo.transaction.RetryingTransactionHelper; @@ -53,10 +54,9 @@ public class CacheTest extends TestCase ApplicationContextHelper.CONFIG_LOCATIONS[0]}); private ServiceRegistry serviceRegistry; - private SimpleCache standaloneCache; - private SimpleCache backingCache; - private TransactionalCache transactionalCache; private SimpleCache objectCache; + private SimpleCache> backingCache; + private TransactionalCache transactionalCache; @SuppressWarnings("unchecked") @Override @@ -68,10 +68,9 @@ public class CacheTest extends TestCase } serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY); - standaloneCache = (SimpleCache) ctx.getBean("simpleCache1"); - backingCache = (SimpleCache) ctx.getBean("backingCache"); - transactionalCache = (TransactionalCache) ctx.getBean("transactionalCache"); objectCache = (SimpleCache) ctx.getBean("objectCache"); + backingCache = (SimpleCache>) ctx.getBean("backingCache"); + transactionalCache = (TransactionalCache) ctx.getBean("transactionalCache"); // Make sure that the backing cache is empty backingCache.clear(); @@ -85,7 +84,7 @@ public class CacheTest extends TestCase public void tearDown() { serviceRegistry = null; - standaloneCache = null; + objectCache = null; backingCache = null; transactionalCache = null; } @@ -94,30 +93,26 @@ public class CacheTest extends TestCase { assertNotNull(serviceRegistry); assertNotNull(backingCache); - assertNotNull(standaloneCache); - assertNotNull(transactionalCache); assertNotNull(objectCache); + assertNotNull(transactionalCache); } public void testObjectCache() throws Exception { + objectCache.clear(); + objectCache.put("A", this); Object obj = objectCache.get("A"); assertTrue("Object not cached properly", this == obj); - } - - public void testEhcacheAdaptors() throws Exception - { - backingCache.put("A", "AAA"); - assertNull("Second cache should not have first's present", standaloneCache.get("A")); + + objectCache.put("A", "AAA"); + assertEquals("AAA", objectCache.get("A")); - assertEquals("AAA", backingCache.get("A")); + Collection keys = objectCache.getKeys(); + assertEquals("Cache didn't return correct number of keys", 1, keys.size()); - Collection keys = backingCache.getKeys(); - assertEquals("Backing cache didn't return correct number of keys", 1, keys.size()); - - backingCache.remove("A"); - assertNull(backingCache.get("A")); + objectCache.remove("A"); + assertNull(objectCache.get("A")); } public void testTransactionalCacheNoTxn() throws Exception @@ -127,18 +122,18 @@ public class CacheTest extends TestCase // no transaction - do a put transactionalCache.put(key, value); // check that the value appears in the backing cache, backingCache - assertEquals("Backing cache not used for put when no transaction present", value, backingCache.get(key)); + assertEquals("Backing cache not used for put when no transaction present", value, TransactionalCache.getSharedCacheValue(backingCache, key)); // remove the value from the backing cache and check that it is removed from the transaction cache backingCache.remove(key); assertNull("Backing cache not used for removed when no transaction present", transactionalCache.get(key)); // add value into backing cache - backingCache.put(key, value); + TransactionalCache.putSharedCacheValue(backingCache, key, value); // remove it from the transactional cache transactionalCache.remove(key); // check that it is gone from the backing cache - assertNull("Non-transactional remove didn't go to backing cache", backingCache.get(key)); + assertNull("Non-transactional remove didn't go to backing cache", TransactionalCache.getSharedCacheValue(backingCache, key)); } private static final String NEW_GLOBAL_ONE = "new_global_one"; @@ -153,7 +148,7 @@ public class CacheTest extends TestCase RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper(); // Add items to the global cache - backingCache.put(NEW_GLOBAL_ONE, NEW_GLOBAL_ONE); + TransactionalCache.putSharedCacheValue(backingCache, NEW_GLOBAL_ONE, NEW_GLOBAL_ONE); RetryingTransactionCallback callback = new RetryingTransactionCallback() { @@ -195,9 +190,9 @@ public class CacheTest extends TestCase public void testTransactionalCacheWithSingleTxn() throws Throwable { // add item to global cache - backingCache.put(NEW_GLOBAL_ONE, NEW_GLOBAL_ONE); - backingCache.put(NEW_GLOBAL_TWO, NEW_GLOBAL_TWO); - backingCache.put(NEW_GLOBAL_THREE, NEW_GLOBAL_THREE); + TransactionalCache.putSharedCacheValue(backingCache, NEW_GLOBAL_ONE, NEW_GLOBAL_ONE); + TransactionalCache.putSharedCacheValue(backingCache, NEW_GLOBAL_TWO, NEW_GLOBAL_TWO); + TransactionalCache.putSharedCacheValue(backingCache, NEW_GLOBAL_THREE, NEW_GLOBAL_THREE); TransactionService transactionService = serviceRegistry.getTransactionService(); UserTransaction txn = transactionService.getUserTransaction(); @@ -215,7 +210,7 @@ public class CacheTest extends TestCase // read 2 from the cache assertEquals("Item not read from backing cache", NEW_GLOBAL_TWO, transactionalCache.get(NEW_GLOBAL_TWO)); // Change the backing cache - backingCache.put(NEW_GLOBAL_TWO, NEW_GLOBAL_TWO + "-updated"); + TransactionalCache.putSharedCacheValue(backingCache, NEW_GLOBAL_TWO, NEW_GLOBAL_TWO + "-updated"); // Ensure read-committed assertEquals("Read-committed not preserved", NEW_GLOBAL_TWO, transactionalCache.get(NEW_GLOBAL_TWO)); @@ -254,8 +249,8 @@ public class CacheTest extends TestCase // check that backing cache was updated with the in-transaction changes assertFalse("Item was not removed from backing cache", backingCache.contains(NEW_GLOBAL_ONE)); - assertNull("Item could still be fetched from backing cache", backingCache.get(NEW_GLOBAL_ONE)); - assertEquals("Item not updated in backing cache", "XXX", backingCache.get(UPDATE_TXN_THREE)); + assertNull("Item could still be fetched from backing cache", TransactionalCache.getSharedCacheValue(backingCache, NEW_GLOBAL_ONE)); + assertEquals("Item not updated in backing cache", "XXX", TransactionalCache.getSharedCacheValue(backingCache, UPDATE_TXN_THREE)); // Check that the transactional cache serves get requests assertEquals("Transactional cache must serve post-commit get requests", "XXX", @@ -342,9 +337,9 @@ public class CacheTest extends TestCase public void testTransactionalCacheDisableSharedCaches() throws Throwable { // add item to global cache - backingCache.put(NEW_GLOBAL_ONE, NEW_GLOBAL_ONE); - backingCache.put(NEW_GLOBAL_TWO, NEW_GLOBAL_TWO); - backingCache.put(NEW_GLOBAL_THREE, NEW_GLOBAL_THREE); + TransactionalCache.putSharedCacheValue(backingCache, NEW_GLOBAL_ONE, NEW_GLOBAL_ONE); + TransactionalCache.putSharedCacheValue(backingCache, NEW_GLOBAL_TWO, NEW_GLOBAL_TWO); + TransactionalCache.putSharedCacheValue(backingCache, NEW_GLOBAL_THREE, NEW_GLOBAL_THREE); TransactionService transactionService = serviceRegistry.getTransactionService(); UserTransaction txn = transactionService.getUserTransaction(); @@ -432,7 +427,7 @@ public class CacheTest extends TestCase int count = (int) Math.pow(10D, (double)i); // test standalone - long timePlain = runPerformanceTestOnCache(standaloneCache, count); + long timePlain = runPerformanceTestOnCache(objectCache, count); // do transactional cache in a transaction TransactionService transactionService = serviceRegistry.getTransactionService(); @@ -524,7 +519,7 @@ public class CacheTest extends TestCase txn.begin(); - backingCache.put("A", null); + TransactionalCache.putSharedCacheValue(backingCache, "A", null); transactionalCache.put("A", "AAA"); try @@ -581,8 +576,14 @@ public class CacheTest extends TestCase RetryingTransactionCallback callback, boolean readOnly, String key, - Object expectedValue) throws Throwable + Object expectedValue, + boolean mustContainKey) throws Throwable { + if (expectedValue != null && !mustContainKey) + { + throw new IllegalArgumentException("Why have a value when the key should not be there?"); + } + TransactionService transactionService = serviceRegistry.getTransactionService(); UserTransaction txn = transactionService.getUserTransaction(readOnly); try @@ -595,8 +596,9 @@ public class CacheTest extends TestCase { try { txn.rollback(); } catch (Throwable ee) {} } - Object actualValue = backingCache.get(key); + Object actualValue = TransactionalCache.getSharedCacheValue(backingCache, key); assertEquals("Backing cache value was not correct", expectedValue, actualValue); + assertEquals("Backing cache contains(key): ", mustContainKey, backingCache.contains(key)); // Clear the backing cache to ensure that subsequent tests don't run into existing data backingCache.clear(); @@ -610,8 +612,8 @@ public class CacheTest extends TestCase public void testValueLockingInTxn() throws Exception { // add item to global cache - backingCache.put(DEFINITIVE_TWO, "initial_two"); - backingCache.put(DEFINITIVE_THREE, "initial_three"); + TransactionalCache.putSharedCacheValue(backingCache, DEFINITIVE_TWO, "initial_two"); + TransactionalCache.putSharedCacheValue(backingCache, DEFINITIVE_THREE, "initial_three"); TransactionService transactionService = serviceRegistry.getTransactionService(); UserTransaction txn = transactionService.getUserTransaction(); @@ -668,9 +670,9 @@ public class CacheTest extends TestCase txn.commit(); // Check post-commit values - assertEquals("Definitive change not written through.", DEFINITIVE_ONE, backingCache.get(DEFINITIVE_ONE)); - assertEquals("Definitive change not written through.", DEFINITIVE_TWO, backingCache.get(DEFINITIVE_TWO)); - assertEquals("Definitive change not written through.", null, backingCache.get(DEFINITIVE_THREE)); + assertEquals("Definitive change not written through.", DEFINITIVE_ONE, TransactionalCache.getSharedCacheValue(backingCache, DEFINITIVE_ONE)); + assertEquals("Definitive change not written through.", DEFINITIVE_TWO, TransactionalCache.getSharedCacheValue(backingCache, DEFINITIVE_TWO)); + assertEquals("Definitive change not written through.", null, TransactionalCache.getSharedCacheValue(backingCache, DEFINITIVE_THREE)); } finally { @@ -696,25 +698,25 @@ public class CacheTest extends TestCase public Object execute() throws Throwable { transactionalCache.put(COMMON_KEY, VALUE_ONE_A); - backingCache.put(COMMON_KEY, VALUE_ONE_B); + TransactionalCache.putSharedCacheValue(backingCache, COMMON_KEY, VALUE_ONE_B); return null; } }; transactionalCache.setAllowEqualsChecks(false); transactionalCache.setMutable(true); - executeAndCheck(callback, false, COMMON_KEY, null); // Mutable: Pessimistic removal - executeAndCheck(callback, true, COMMON_KEY, null); // Mutable: Pessimistic removal + executeAndCheck(callback, false, COMMON_KEY, null, false); // Mutable: Pessimistic removal + executeAndCheck(callback, true, COMMON_KEY, null, false); // Mutable: Pessimistic removal transactionalCache.setMutable(false); - executeAndCheck(callback, false, COMMON_KEY, VALUE_ONE_B); // Immutable: Assume backing cache is correct - executeAndCheck(callback, true, COMMON_KEY, VALUE_ONE_B); // Immutable: Assume backing cache is correct + executeAndCheck(callback, false, COMMON_KEY, VALUE_ONE_B, true); // Immutable: Assume backing cache is correct + executeAndCheck(callback, true, COMMON_KEY, VALUE_ONE_B, true); // Immutable: Assume backing cache is correct transactionalCache.setAllowEqualsChecks(true); transactionalCache.setMutable(true); - executeAndCheck(callback, false, COMMON_KEY, VALUE_ONE_B); // Mutable: Shared cache value checked - executeAndCheck(callback, true, COMMON_KEY, VALUE_ONE_B); // Mutable: Shared cache value checked + executeAndCheck(callback, false, COMMON_KEY, VALUE_ONE_B, true); // Mutable: Shared cache value checked + executeAndCheck(callback, true, COMMON_KEY, VALUE_ONE_B, true); // Mutable: Shared cache value checked transactionalCache.setMutable(false); - executeAndCheck(callback, false, COMMON_KEY, VALUE_ONE_B); // Immutable: Assume backing cache is correct - executeAndCheck(callback, true, COMMON_KEY, VALUE_ONE_B); // Immutable: Assume backing cache is correct + executeAndCheck(callback, false, COMMON_KEY, VALUE_ONE_B, true); // Immutable: Assume backing cache is correct + executeAndCheck(callback, true, COMMON_KEY, VALUE_ONE_B, true); // Immutable: Assume backing cache is correct } /** *
    @@ -730,25 +732,59 @@ public class CacheTest extends TestCase public Object execute() throws Throwable { transactionalCache.put(COMMON_KEY, VALUE_ONE_A); - backingCache.put(COMMON_KEY, VALUE_ONE_A); + TransactionalCache.putSharedCacheValue(backingCache, COMMON_KEY, VALUE_ONE_A); return null; } }; transactionalCache.setAllowEqualsChecks(false); transactionalCache.setMutable(true); - executeAndCheck(callback, false, COMMON_KEY, VALUE_ONE_A); // Mutable: Object handle is match - executeAndCheck(callback, true, COMMON_KEY, VALUE_ONE_A); // Mutable: Object handle is match + executeAndCheck(callback, false, COMMON_KEY, null, false); // Mutable: No equality check + executeAndCheck(callback, true, COMMON_KEY, null, false); // Mutable: No equality check transactionalCache.setMutable(false); - executeAndCheck(callback, false, COMMON_KEY, VALUE_ONE_A); // Immutable: Object handle is match - executeAndCheck(callback, true, COMMON_KEY, VALUE_ONE_A); // Immutable: Object handle is match + executeAndCheck(callback, false, COMMON_KEY, VALUE_ONE_A, true); // Immutable: Assumed to be same + executeAndCheck(callback, true, COMMON_KEY, VALUE_ONE_A, true); // Immutable: Assumed to be same transactionalCache.setAllowEqualsChecks(true); transactionalCache.setMutable(true); - executeAndCheck(callback, false, COMMON_KEY, VALUE_ONE_A); // Mutable: Object handle is match - executeAndCheck(callback, true, COMMON_KEY, VALUE_ONE_A); // Mutable: Object handle is match + executeAndCheck(callback, false, COMMON_KEY, VALUE_ONE_A, true); // Mutable: Equality check done + executeAndCheck(callback, true, COMMON_KEY, VALUE_ONE_A, true); // Mutable: Equality check done transactionalCache.setMutable(false); - executeAndCheck(callback, false, COMMON_KEY, VALUE_ONE_A); // Immutable: Object handle is match - executeAndCheck(callback, true, COMMON_KEY, VALUE_ONE_A); // Immutable: Object handle is match + executeAndCheck(callback, false, COMMON_KEY, VALUE_ONE_A, true); // Immutable: Assumed to be same + executeAndCheck(callback, true, COMMON_KEY, VALUE_ONE_A, true); // Immutable: Assumed to be same + } + /** + *
      + *
    • Add to the transaction cache
    • + *
    • Add null to the backing cache
    • + *
    • Commit
    • + *
    + */ + public void testConcurrentAddAgainstAddNull()throws Throwable + { + RetryingTransactionCallback callback = new RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + transactionalCache.put(COMMON_KEY, VALUE_ONE_A); + TransactionalCache.putSharedCacheValue(backingCache, COMMON_KEY, null); + return null; + } + }; + transactionalCache.setAllowEqualsChecks(false); + transactionalCache.setMutable(true); + executeAndCheck(callback, false, COMMON_KEY, null, false); // Mutable: No equality check + executeAndCheck(callback, true, COMMON_KEY, null, false); // Mutable: No equality check + transactionalCache.setMutable(false); + executeAndCheck(callback, false, COMMON_KEY, null, true); // Immutable: Assume backing cache is correct + executeAndCheck(callback, true, COMMON_KEY, null, true); // Immutable: Assume backing cache is correct + + transactionalCache.setAllowEqualsChecks(true); + transactionalCache.setMutable(true); + executeAndCheck(callback, false, COMMON_KEY, null, false); // Mutable: Equality check done + executeAndCheck(callback, true, COMMON_KEY, null, false); // Mutable: Equality check done + transactionalCache.setMutable(false); + executeAndCheck(callback, false, COMMON_KEY, null, true); // Immutable: Assume backing cache is correct + executeAndCheck(callback, true, COMMON_KEY, null, true); // Immutable: Assume backing cache is correct } /** *
      @@ -770,19 +806,19 @@ public class CacheTest extends TestCase }; transactionalCache.setAllowEqualsChecks(false); transactionalCache.setMutable(true); - executeAndCheck(callback, false, COMMON_KEY, VALUE_ONE_A); // Mutable: Add back to backing cache - executeAndCheck(callback, true, COMMON_KEY, VALUE_ONE_A); // Mutable: Add back to backing cache + executeAndCheck(callback, false, COMMON_KEY, VALUE_ONE_A, true); // Mutable: Add back to backing cache + executeAndCheck(callback, true, COMMON_KEY, VALUE_ONE_A, true); // Mutable: Add back to backing cache transactionalCache.setMutable(false); - executeAndCheck(callback, false, COMMON_KEY, VALUE_ONE_A); // Immutable: Add back to backing cache - executeAndCheck(callback, true, COMMON_KEY, VALUE_ONE_A); // Immutable: Add back to backing cache + executeAndCheck(callback, false, COMMON_KEY, VALUE_ONE_A, true); // Immutable: Add back to backing cache + executeAndCheck(callback, true, COMMON_KEY, VALUE_ONE_A, true); // Immutable: Add back to backing cache transactionalCache.setAllowEqualsChecks(true); transactionalCache.setMutable(true); - executeAndCheck(callback, false, COMMON_KEY, VALUE_ONE_A); // Mutable: Add back to backing cache - executeAndCheck(callback, true, COMMON_KEY, VALUE_ONE_A); // Mutable: Add back to backing cache + executeAndCheck(callback, false, COMMON_KEY, VALUE_ONE_A, true); // Mutable: Add back to backing cache + executeAndCheck(callback, true, COMMON_KEY, VALUE_ONE_A, true); // Mutable: Add back to backing cache transactionalCache.setMutable(false); - executeAndCheck(callback, false, COMMON_KEY, VALUE_ONE_A); // Immutable: Add back to backing cache - executeAndCheck(callback, true, COMMON_KEY, VALUE_ONE_A); // Immutable: Add back to backing cache + executeAndCheck(callback, false, COMMON_KEY, VALUE_ONE_A, true); // Immutable: Add back to backing cache + executeAndCheck(callback, true, COMMON_KEY, VALUE_ONE_A, true); // Immutable: Add back to backing cache } /** *
        @@ -798,27 +834,27 @@ public class CacheTest extends TestCase { public Object execute() throws Throwable { - backingCache.put(COMMON_KEY, VALUE_ONE_A); + TransactionalCache.putSharedCacheValue(backingCache, COMMON_KEY, VALUE_ONE_A); transactionalCache.put(COMMON_KEY, VALUE_ONE_B); - backingCache.put(COMMON_KEY, VALUE_TWO_A); + TransactionalCache.putSharedCacheValue(backingCache, COMMON_KEY, VALUE_TWO_A); return null; } }; transactionalCache.setAllowEqualsChecks(false); transactionalCache.setMutable(true); - executeAndCheck(callback, false, COMMON_KEY, null); // Mutable: Pessimistic removal - executeAndCheck(callback, true, COMMON_KEY, null); // Mutable: Pessimistic removal + executeAndCheck(callback, false, COMMON_KEY, null, false); // Mutable: Pessimistic removal + executeAndCheck(callback, true, COMMON_KEY, null, false); // Mutable: Pessimistic removal transactionalCache.setMutable(false); - executeAndCheck(callback, false, COMMON_KEY, VALUE_TWO_A); // Immutable: Assume backing cache is correct - executeAndCheck(callback, true, COMMON_KEY, VALUE_TWO_A); // Immutable: Assume backing cache is correct + executeAndCheck(callback, false, COMMON_KEY, VALUE_TWO_A, true); // Immutable: Assume backing cache is correct + executeAndCheck(callback, true, COMMON_KEY, VALUE_TWO_A, true); // Immutable: Assume backing cache is correct transactionalCache.setAllowEqualsChecks(true); transactionalCache.setMutable(true); - executeAndCheck(callback, false, COMMON_KEY, null); // Mutable: Shared cache value checked failed - executeAndCheck(callback, true, COMMON_KEY, null); // Mutable: Shared cache value checked failed + executeAndCheck(callback, false, COMMON_KEY, null, false); // Mutable: Shared cache value checked failed + executeAndCheck(callback, true, COMMON_KEY, null, false); // Mutable: Shared cache value checked failed transactionalCache.setMutable(false); - executeAndCheck(callback, false, COMMON_KEY, VALUE_TWO_A); // Immutable: Assume backing cache is correct - executeAndCheck(callback, true, COMMON_KEY, VALUE_TWO_A); // Immutable: Assume backing cache is correct + executeAndCheck(callback, false, COMMON_KEY, VALUE_TWO_A, true); // Immutable: Assume backing cache is correct + executeAndCheck(callback, true, COMMON_KEY, VALUE_TWO_A, true); // Immutable: Assume backing cache is correct } /** *
          @@ -834,27 +870,27 @@ public class CacheTest extends TestCase { public Object execute() throws Throwable { - backingCache.put(COMMON_KEY, VALUE_ONE_A); + TransactionalCache.putSharedCacheValue(backingCache, COMMON_KEY, VALUE_ONE_A); transactionalCache.put(COMMON_KEY, VALUE_ONE_B); - backingCache.put(COMMON_KEY, null); + TransactionalCache.putSharedCacheValue(backingCache, COMMON_KEY, null); return null; } }; transactionalCache.setAllowEqualsChecks(false); transactionalCache.setMutable(true); - executeAndCheck(callback, false, COMMON_KEY, null); // Mutable: Pessimistic removal - executeAndCheck(callback, true, COMMON_KEY, null); // Mutable: Pessimistic removal + executeAndCheck(callback, false, COMMON_KEY, null, false); // Mutable: Pessimistic removal + executeAndCheck(callback, true, COMMON_KEY, null, false); // Mutable: Pessimistic removal transactionalCache.setMutable(false); - executeAndCheck(callback, false, COMMON_KEY, VALUE_ONE_B); // Immutable: Add back to backing cache - executeAndCheck(callback, true, COMMON_KEY, VALUE_ONE_B); // Immutable: Add back to backing cache + executeAndCheck(callback, false, COMMON_KEY, null, true); // Immutable: Assume backing cache is correct + executeAndCheck(callback, true, COMMON_KEY, null, true); // Immutable: Assume backing cache is correct transactionalCache.setAllowEqualsChecks(true); transactionalCache.setMutable(true); - executeAndCheck(callback, false, COMMON_KEY, null); // Mutable: Pessimistic removal - executeAndCheck(callback, true, COMMON_KEY, null); // Mutable: Pessimistic removal + executeAndCheck(callback, false, COMMON_KEY, null, false); // Mutable: Pessimistic removal + executeAndCheck(callback, true, COMMON_KEY, null, false); // Mutable: Pessimistic removal transactionalCache.setMutable(false); - executeAndCheck(callback, false, COMMON_KEY, VALUE_ONE_B); // Immutable: Add back to backing cache - executeAndCheck(callback, true, COMMON_KEY, VALUE_ONE_B); // Immutable: Add back to backing cache + executeAndCheck(callback, false, COMMON_KEY, null, true); // Immutable: Assume backing cache is correct + executeAndCheck(callback, true, COMMON_KEY, null, true); // Immutable: Assume backing cache is correct } /** *
            @@ -870,27 +906,63 @@ public class CacheTest extends TestCase { public Object execute() throws Throwable { - backingCache.put(COMMON_KEY, VALUE_ONE_A); + TransactionalCache.putSharedCacheValue(backingCache, COMMON_KEY, VALUE_ONE_A); transactionalCache.put(COMMON_KEY, null); - backingCache.put(COMMON_KEY, VALUE_ONE_B); + TransactionalCache.putSharedCacheValue(backingCache, COMMON_KEY, VALUE_ONE_B); return null; } }; transactionalCache.setAllowEqualsChecks(false); transactionalCache.setMutable(true); - executeAndCheck(callback, false, COMMON_KEY, null); // Mutable: Pessimistic removal - executeAndCheck(callback, true, COMMON_KEY, null); // Mutable: Pessimistic removal + executeAndCheck(callback, false, COMMON_KEY, null, false); // Mutable: Pessimistic removal + executeAndCheck(callback, true, COMMON_KEY, null, false); // Mutable: Pessimistic removal transactionalCache.setMutable(false); - executeAndCheck(callback, false, COMMON_KEY, VALUE_ONE_B); // Immutable: Assume backing cache is correct - executeAndCheck(callback, true, COMMON_KEY, VALUE_ONE_B); // Immutable: Assume backing cache is correct + executeAndCheck(callback, false, COMMON_KEY, VALUE_ONE_B, true); // Immutable: Assume backing cache is correct + executeAndCheck(callback, true, COMMON_KEY, VALUE_ONE_B, true); // Immutable: Assume backing cache is correct transactionalCache.setAllowEqualsChecks(true); transactionalCache.setMutable(true); - executeAndCheck(callback, false, COMMON_KEY, null); // Mutable: Pessimistic removal - executeAndCheck(callback, true, COMMON_KEY, null); // Mutable: Pessimistic removal + executeAndCheck(callback, false, COMMON_KEY, null, false); // Mutable: Pessimistic removal + executeAndCheck(callback, true, COMMON_KEY, null, false); // Mutable: Pessimistic removal transactionalCache.setMutable(false); - executeAndCheck(callback, false, COMMON_KEY, VALUE_ONE_B); // Immutable: Assume backing cache is correct - executeAndCheck(callback, true, COMMON_KEY, VALUE_ONE_B); // Immutable: Assume backing cache is correct + executeAndCheck(callback, false, COMMON_KEY, VALUE_ONE_B, true); // Immutable: Assume backing cache is correct + executeAndCheck(callback, true, COMMON_KEY, VALUE_ONE_B, true); // Immutable: Assume backing cache is correct + } + /** + *
              + *
            • Add to the backing cache
            • + *
            • Update the transactional cache with a null value
            • + *
            • Update the backing cache with a null value
            • + *
            • Commit
            • + *
            + */ + public void testConcurrentUpdateNullAgainstUpdateNull()throws Throwable + { + RetryingTransactionCallback callback = new RetryingTransactionCallback() + { + public Object execute() throws Throwable + { + TransactionalCache.putSharedCacheValue(backingCache, COMMON_KEY, VALUE_ONE_A); + transactionalCache.put(COMMON_KEY, null); + TransactionalCache.putSharedCacheValue(backingCache, COMMON_KEY, null); + return null; + } + }; + transactionalCache.setAllowEqualsChecks(false); + transactionalCache.setMutable(true); + executeAndCheck(callback, false, COMMON_KEY, null, false); // Mutable: Pessimistic removal + executeAndCheck(callback, true, COMMON_KEY, null, false); // Mutable: Pessimistic removal + transactionalCache.setMutable(false); + executeAndCheck(callback, false, COMMON_KEY, null, true); // Immutable: Assume backing cache is correct + executeAndCheck(callback, true, COMMON_KEY, null, true); // Immutable: Assume backing cache is correct + + transactionalCache.setAllowEqualsChecks(true); + transactionalCache.setMutable(true); + executeAndCheck(callback, false, COMMON_KEY, null, true); // Mutable: Equality check + executeAndCheck(callback, true, COMMON_KEY, null, true); // Mutable: Equality check + transactionalCache.setMutable(false); + executeAndCheck(callback, false, COMMON_KEY, null, true); // Immutable: Equality check + executeAndCheck(callback, true, COMMON_KEY, null, true); // Immutable: Equality check } /** *
              @@ -906,7 +978,7 @@ public class CacheTest extends TestCase { public Object execute() throws Throwable { - backingCache.put(COMMON_KEY, VALUE_ONE_A); + TransactionalCache.putSharedCacheValue(backingCache, COMMON_KEY, VALUE_ONE_A); transactionalCache.put(COMMON_KEY, VALUE_ONE_B); backingCache.remove(COMMON_KEY); return null; @@ -914,19 +986,19 @@ public class CacheTest extends TestCase }; transactionalCache.setAllowEqualsChecks(false); transactionalCache.setMutable(true); - executeAndCheck(callback, false, COMMON_KEY, null); // Mutable: Pessimistic removal - executeAndCheck(callback, true, COMMON_KEY, null); // Mutable: Pessimistic removal + executeAndCheck(callback, false, COMMON_KEY, null, false); // Mutable: Pessimistic removal + executeAndCheck(callback, true, COMMON_KEY, null, false); // Mutable: Pessimistic removal transactionalCache.setMutable(false); - executeAndCheck(callback, false, COMMON_KEY, VALUE_ONE_B); // Immutable: Add back to backing cache - executeAndCheck(callback, true, COMMON_KEY, VALUE_ONE_B); // Immutable: Add back to backing cache + executeAndCheck(callback, false, COMMON_KEY, VALUE_ONE_B, true); // Immutable: Add back to backing cache + executeAndCheck(callback, true, COMMON_KEY, VALUE_ONE_B, true); // Immutable: Add back to backing cache transactionalCache.setAllowEqualsChecks(true); transactionalCache.setMutable(true); - executeAndCheck(callback, false, COMMON_KEY, null); // Mutable: Pessimistic removal - executeAndCheck(callback, true, COMMON_KEY, null); // Mutable: Pessimistic removal + executeAndCheck(callback, false, COMMON_KEY, null, false); // Mutable: Pessimistic removal + executeAndCheck(callback, true, COMMON_KEY, null, false); // Mutable: Pessimistic removal transactionalCache.setMutable(false); - executeAndCheck(callback, false, COMMON_KEY, VALUE_ONE_B); // Immutable: Add back to backing cache - executeAndCheck(callback, true, COMMON_KEY, VALUE_ONE_B); // Immutable: Add back to backing cache + executeAndCheck(callback, false, COMMON_KEY, VALUE_ONE_B, true); // Immutable: Add back to backing cache + executeAndCheck(callback, true, COMMON_KEY, VALUE_ONE_B, true); // Immutable: Add back to backing cache } /** *
                @@ -942,7 +1014,7 @@ public class CacheTest extends TestCase { public Object execute() throws Throwable { - backingCache.put(COMMON_KEY, VALUE_ONE_A); + TransactionalCache.putSharedCacheValue(backingCache, COMMON_KEY, VALUE_ONE_A); transactionalCache.put(COMMON_KEY, VALUE_ONE_B); backingCache.clear(); return null; @@ -950,19 +1022,19 @@ public class CacheTest extends TestCase }; transactionalCache.setAllowEqualsChecks(false); transactionalCache.setMutable(true); - executeAndCheck(callback, false, COMMON_KEY, null); // Mutable: Pessimistic removal - executeAndCheck(callback, true, COMMON_KEY, null); // Mutable: Pessimistic removal + executeAndCheck(callback, false, COMMON_KEY, null, false); // Mutable: Pessimistic removal + executeAndCheck(callback, true, COMMON_KEY, null, false); // Mutable: Pessimistic removal transactionalCache.setMutable(false); - executeAndCheck(callback, false, COMMON_KEY, VALUE_ONE_B); // Immutable: Add back to backing cache - executeAndCheck(callback, true, COMMON_KEY, VALUE_ONE_B); // Immutable: Add back to backing cache + executeAndCheck(callback, false, COMMON_KEY, VALUE_ONE_B, true); // Immutable: Add back to backing cache + executeAndCheck(callback, true, COMMON_KEY, VALUE_ONE_B, true); // Immutable: Add back to backing cache transactionalCache.setAllowEqualsChecks(true); transactionalCache.setMutable(true); - executeAndCheck(callback, false, COMMON_KEY, null); // Mutable: Pessimistic removal - executeAndCheck(callback, true, COMMON_KEY, null); // Mutable: Pessimistic removal + executeAndCheck(callback, false, COMMON_KEY, null, false); // Mutable: Pessimistic removal + executeAndCheck(callback, true, COMMON_KEY, null, false); // Mutable: Pessimistic removal transactionalCache.setMutable(false); - executeAndCheck(callback, false, COMMON_KEY, VALUE_ONE_B); // Immutable: Add back to backing cache - executeAndCheck(callback, true, COMMON_KEY, VALUE_ONE_B); // Immutable: Add back to backing cache + executeAndCheck(callback, false, COMMON_KEY, VALUE_ONE_B, true); // Immutable: Add back to backing cache + executeAndCheck(callback, true, COMMON_KEY, VALUE_ONE_B, true); // Immutable: Add back to backing cache } /** *
                  @@ -980,25 +1052,25 @@ public class CacheTest extends TestCase { backingCache.remove(COMMON_KEY); transactionalCache.remove(COMMON_KEY); - backingCache.put(COMMON_KEY, VALUE_ONE_B); + TransactionalCache.putSharedCacheValue(backingCache, COMMON_KEY, VALUE_ONE_B); return null; } }; transactionalCache.setAllowEqualsChecks(false); transactionalCache.setMutable(true); - executeAndCheck(callback, false, COMMON_KEY, null); // Mutable: Pessimistic removal - executeAndCheck(callback, true, COMMON_KEY, null); // Mutable: Pessimistic removal + executeAndCheck(callback, false, COMMON_KEY, null, false); // Mutable: Pessimistic removal + executeAndCheck(callback, true, COMMON_KEY, null, false); // Mutable: Pessimistic removal transactionalCache.setMutable(false); - executeAndCheck(callback, false, COMMON_KEY, null); // Immutable: Remove from backing cache - executeAndCheck(callback, true, COMMON_KEY, null); // Immutable: Remove from backing cache + executeAndCheck(callback, false, COMMON_KEY, null, false); // Immutable: Remove from backing cache + executeAndCheck(callback, true, COMMON_KEY, null, false); // Immutable: Remove from backing cache transactionalCache.setAllowEqualsChecks(true); transactionalCache.setMutable(true); - executeAndCheck(callback, false, COMMON_KEY, null); // Mutable: Pessimistic removal - executeAndCheck(callback, true, COMMON_KEY, null); // Mutable: Pessimistic removal + executeAndCheck(callback, false, COMMON_KEY, null, false); // Mutable: Pessimistic removal + executeAndCheck(callback, true, COMMON_KEY, null, false); // Mutable: Pessimistic removal transactionalCache.setMutable(false); - executeAndCheck(callback, false, COMMON_KEY, null); // Immutable: Remove from backing cache - executeAndCheck(callback, true, COMMON_KEY, null); // Immutable: Remove from backing cache + executeAndCheck(callback, false, COMMON_KEY, null, false); // Immutable: Remove from backing cache + executeAndCheck(callback, true, COMMON_KEY, null, false); // Immutable: Remove from backing cache } /** *
                    @@ -1016,25 +1088,25 @@ public class CacheTest extends TestCase { backingCache.remove(COMMON_KEY); transactionalCache.put(COMMON_KEY, VALUE_ONE_A); - backingCache.put(COMMON_KEY, VALUE_ONE_B); + TransactionalCache.putSharedCacheValue(backingCache, COMMON_KEY, VALUE_ONE_B); return null; } }; transactionalCache.setAllowEqualsChecks(false); transactionalCache.setMutable(true); - executeAndCheck(callback, false, COMMON_KEY, null); // Mutable: Pessimistic removal - executeAndCheck(callback, true, COMMON_KEY, null); // Mutable: Pessimistic removal + executeAndCheck(callback, false, COMMON_KEY, null, false); // Mutable: Pessimistic removal + executeAndCheck(callback, true, COMMON_KEY, null, false); // Mutable: Pessimistic removal transactionalCache.setMutable(false); - executeAndCheck(callback, false, COMMON_KEY, VALUE_ONE_B); // Immutable: Assume backing cache is correct - executeAndCheck(callback, true, COMMON_KEY, VALUE_ONE_B); // Immutable: Assume backing cache is correct + executeAndCheck(callback, false, COMMON_KEY, VALUE_ONE_B, true); // Immutable: Assume backing cache is correct + executeAndCheck(callback, true, COMMON_KEY, VALUE_ONE_B, true); // Immutable: Assume backing cache is correct transactionalCache.setAllowEqualsChecks(true); transactionalCache.setMutable(true); - executeAndCheck(callback, false, COMMON_KEY, VALUE_ONE_B); // Mutable: Shared cache value checked - executeAndCheck(callback, true, COMMON_KEY, VALUE_ONE_B); // Mutable: Shared cache value checked + executeAndCheck(callback, false, COMMON_KEY, VALUE_ONE_B, true); // Mutable: Shared cache value checked + executeAndCheck(callback, true, COMMON_KEY, VALUE_ONE_B, true); // Mutable: Shared cache value checked transactionalCache.setMutable(false); - executeAndCheck(callback, false, COMMON_KEY, VALUE_ONE_B); // Immutable: Assume backing cache is correct - executeAndCheck(callback, true, COMMON_KEY, VALUE_ONE_B); // Immutable: Assume backing cache is correct + executeAndCheck(callback, false, COMMON_KEY, VALUE_ONE_B, true); // Immutable: Assume backing cache is correct + executeAndCheck(callback, true, COMMON_KEY, VALUE_ONE_B, true); // Immutable: Assume backing cache is correct } /** *
                      @@ -1050,27 +1122,27 @@ public class CacheTest extends TestCase { public Object execute() throws Throwable { - backingCache.put(COMMON_KEY, VALUE_ONE_A); + TransactionalCache.putSharedCacheValue(backingCache, COMMON_KEY, VALUE_ONE_A); transactionalCache.remove(COMMON_KEY); - backingCache.put(COMMON_KEY, VALUE_ONE_B); + TransactionalCache.putSharedCacheValue(backingCache, COMMON_KEY, VALUE_ONE_B); return null; } }; transactionalCache.setAllowEqualsChecks(false); transactionalCache.setMutable(true); - executeAndCheck(callback, false, COMMON_KEY, null); // Mutable: Pessimistic removal - executeAndCheck(callback, true, COMMON_KEY, null); // Mutable: Pessimistic removal + executeAndCheck(callback, false, COMMON_KEY, null, false); // Mutable: Pessimistic removal + executeAndCheck(callback, true, COMMON_KEY, null, false); // Mutable: Pessimistic removal transactionalCache.setMutable(false); - executeAndCheck(callback, false, COMMON_KEY, null); // Immutable: Remove from backing cache - executeAndCheck(callback, true, COMMON_KEY, null); // Immutable: Remove from backing cache + executeAndCheck(callback, false, COMMON_KEY, null, false); // Immutable: Remove from backing cache + executeAndCheck(callback, true, COMMON_KEY, null, false); // Immutable: Remove from backing cache transactionalCache.setAllowEqualsChecks(true); transactionalCache.setMutable(true); - executeAndCheck(callback, false, COMMON_KEY, null); // Mutable: Pessimistic removal - executeAndCheck(callback, true, COMMON_KEY, null); // Mutable: Pessimistic removal + executeAndCheck(callback, false, COMMON_KEY, null, false); // Mutable: Pessimistic removal + executeAndCheck(callback, true, COMMON_KEY, null, false); // Mutable: Pessimistic removal transactionalCache.setMutable(false); - executeAndCheck(callback, false, COMMON_KEY, null); // Immutable: Remove from backing cache - executeAndCheck(callback, true, COMMON_KEY, null); // Immutable: Remove from backing cache + executeAndCheck(callback, false, COMMON_KEY, null, false); // Immutable: Remove from backing cache + executeAndCheck(callback, true, COMMON_KEY, null, false); // Immutable: Remove from backing cache } /** *
                        @@ -1086,7 +1158,7 @@ public class CacheTest extends TestCase { public Object execute() throws Throwable { - backingCache.put(COMMON_KEY, VALUE_ONE_A); + TransactionalCache.putSharedCacheValue(backingCache, COMMON_KEY, VALUE_ONE_A); transactionalCache.remove(COMMON_KEY); backingCache.remove(COMMON_KEY); return null; @@ -1094,19 +1166,19 @@ public class CacheTest extends TestCase }; transactionalCache.setAllowEqualsChecks(false); transactionalCache.setMutable(true); - executeAndCheck(callback, false, COMMON_KEY, null); // Mutable: Remove from backing cache - executeAndCheck(callback, true, COMMON_KEY, null); // Mutable: Remove from backing cache + executeAndCheck(callback, false, COMMON_KEY, null, false); // Mutable: Remove from backing cache + executeAndCheck(callback, true, COMMON_KEY, null, false); // Mutable: Remove from backing cache transactionalCache.setMutable(false); - executeAndCheck(callback, false, COMMON_KEY, null); // Immutable: Remove from backing cache - executeAndCheck(callback, true, COMMON_KEY, null); // Immutable: Remove from backing cache + executeAndCheck(callback, false, COMMON_KEY, null, false); // Immutable: Remove from backing cache + executeAndCheck(callback, true, COMMON_KEY, null, false); // Immutable: Remove from backing cache transactionalCache.setAllowEqualsChecks(true); transactionalCache.setMutable(true); - executeAndCheck(callback, false, COMMON_KEY, null); // Mutable: Remove from backing cache - executeAndCheck(callback, true, COMMON_KEY, null); // Mutable: Remove from backing cache + executeAndCheck(callback, false, COMMON_KEY, null, false); // Mutable: Remove from backing cache + executeAndCheck(callback, true, COMMON_KEY, null, false); // Mutable: Remove from backing cache transactionalCache.setMutable(false); - executeAndCheck(callback, false, COMMON_KEY, null); // Immutable: Remove from backing cache - executeAndCheck(callback, true, COMMON_KEY, null); // Immutable: Remove from backing cache + executeAndCheck(callback, false, COMMON_KEY, null, false); // Immutable: Remove from backing cache + executeAndCheck(callback, true, COMMON_KEY, null, false); // Immutable: Remove from backing cache } /** *
                          @@ -1122,7 +1194,7 @@ public class CacheTest extends TestCase { public Object execute() throws Throwable { - backingCache.put(COMMON_KEY, VALUE_ONE_A); + TransactionalCache.putSharedCacheValue(backingCache, COMMON_KEY, VALUE_ONE_A); transactionalCache.remove(COMMON_KEY); backingCache.clear(); return null; @@ -1130,18 +1202,18 @@ public class CacheTest extends TestCase }; transactionalCache.setAllowEqualsChecks(false); transactionalCache.setMutable(true); - executeAndCheck(callback, false, COMMON_KEY, null); // Mutable: Nothing to do - executeAndCheck(callback, true, COMMON_KEY, null); // Mutable: Nothing to do + executeAndCheck(callback, false, COMMON_KEY, null, false); // Mutable: Nothing to do + executeAndCheck(callback, true, COMMON_KEY, null, false); // Mutable: Nothing to do transactionalCache.setMutable(false); - executeAndCheck(callback, false, COMMON_KEY, null); // Immutable: Nothing to do - executeAndCheck(callback, true, COMMON_KEY, null); // Immutable: Nothing to do + executeAndCheck(callback, false, COMMON_KEY, null, false); // Immutable: Nothing to do + executeAndCheck(callback, true, COMMON_KEY, null, false); // Immutable: Nothing to do transactionalCache.setAllowEqualsChecks(true); transactionalCache.setMutable(true); - executeAndCheck(callback, false, COMMON_KEY, null); // Mutable: Nothing to do - executeAndCheck(callback, true, COMMON_KEY, null); // Mutable: Nothing to do + executeAndCheck(callback, false, COMMON_KEY, null, false); // Mutable: Nothing to do + executeAndCheck(callback, true, COMMON_KEY, null, false); // Mutable: Nothing to do transactionalCache.setMutable(false); - executeAndCheck(callback, false, COMMON_KEY, null); // Immutable: Nothing to do - executeAndCheck(callback, true, COMMON_KEY, null); // Immutable: Nothing to do + executeAndCheck(callback, false, COMMON_KEY, null, false); // Immutable: Nothing to do + executeAndCheck(callback, true, COMMON_KEY, null, false); // Immutable: Nothing to do } } diff --git a/source/test-java/org/alfresco/repo/cache/SerializingSimpleCache.java b/source/test-java/org/alfresco/repo/cache/SerializingSimpleCache.java new file mode 100644 index 0000000000..dc6e70498b --- /dev/null +++ b/source/test-java/org/alfresco/repo/cache/SerializingSimpleCache.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2005-2014 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.cache; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.Collection; + +import org.springframework.beans.factory.BeanNameAware; + +/** + * {@link SimpleCache} implementation backed by a {@link DefaultSimpleCache} but forcing + * object serialization in and out. This is only useful for tests. + * + * @author Derek Hulley + * @since 4.2.3 + */ +public final class SerializingSimpleCache + implements SimpleCache, BeanNameAware +{ + private SimpleCache cache; + + public SerializingSimpleCache(int maxItems, String cacheName) + { + cache = new DefaultSimpleCache<>(maxItems, cacheName); + } + + public SerializingSimpleCache() + { + cache = new DefaultSimpleCache<>(); + } + + @SuppressWarnings("unchecked") + private V serialize(V value) + { + if (value == null) + { + return null; + } + try + { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(bos); + oos.writeObject(value); + byte[] bytes = bos.toByteArray(); + ByteArrayInputStream bis = new ByteArrayInputStream(bytes); + ObjectInputStream ois = new ObjectInputStream(bis); + V ret = (V) ois.readObject(); + // This is just test code! + bos.close(); + oos.close(); + bis.close(); + ois.close(); + // Done + return ret; + } + catch (Exception e) + { + throw new RuntimeException("Failure to serialize/deserialize object: " + value, e); + } + } + + @Override + public boolean contains(K key) + { + return cache.contains(key); + } + + @Override + public Collection getKeys() + { + return cache.getKeys(); + } + + @Override + public V get(K key) + { + V ret = cache.get(key); + return serialize(ret); + } + + @Override + public void put(K key, V value) + { + value = serialize(value); + cache.put(key, value); + } + + @Override + public void remove(K key) + { + cache.remove(key); + } + + @Override + public void clear() + { + cache.clear(); + } + + @Override + public void setBeanName(String cacheName) + { + } +} diff --git a/source/test-java/org/alfresco/repo/node/NodeServiceTest.java b/source/test-java/org/alfresco/repo/node/NodeServiceTest.java index 454c4135bc..0a91746a3c 100644 --- a/source/test-java/org/alfresco/repo/node/NodeServiceTest.java +++ b/source/test-java/org/alfresco/repo/node/NodeServiceTest.java @@ -42,6 +42,8 @@ import java.util.Set; import org.alfresco.model.ContentModel; import org.alfresco.repo.cache.SimpleCache; +import org.alfresco.repo.cache.TransactionalCache; +import org.alfresco.repo.cache.TransactionalCache.ValueHolder; import org.alfresco.repo.domain.node.Node; import org.alfresco.repo.domain.node.NodeDAO; import org.alfresco.repo.domain.node.NodeEntity; @@ -128,9 +130,9 @@ public class NodeServiceTest private static TransactionService txnService; private static PolicyComponent policyComponent; private static CannedQueryDAO cannedQueryDAOForTesting; - private static SimpleCache nodesCache; - private static SimpleCache propsCache; - private static SimpleCache aspectsCache; + private static SimpleCache> nodesCache; + private static SimpleCache> propsCache; + private static SimpleCache> aspectsCache; private static Long deletedTypeQNameId; @@ -151,9 +153,9 @@ public class NodeServiceTest cannedQueryDAOForTesting = (CannedQueryDAO) APP_CONTEXT_INIT.getApplicationContext().getBean("cannedQueryDAOForTesting"); // Get the caches for later testing - nodesCache = (SimpleCache) APP_CONTEXT_INIT.getApplicationContext().getBean("node.nodesSharedCache"); - propsCache = (SimpleCache) APP_CONTEXT_INIT.getApplicationContext().getBean("node.propertiesSharedCache"); - aspectsCache = (SimpleCache) APP_CONTEXT_INIT.getApplicationContext().getBean("node.aspectsSharedCache"); + nodesCache = (SimpleCache>) APP_CONTEXT_INIT.getApplicationContext().getBean("node.nodesSharedCache"); + propsCache = (SimpleCache>) APP_CONTEXT_INIT.getApplicationContext().getBean("node.propertiesSharedCache"); + aspectsCache = (SimpleCache>) APP_CONTEXT_INIT.getApplicationContext().getBean("node.aspectsSharedCache"); // Clear the caches to remove fluff nodesCache.clear(); @@ -812,7 +814,7 @@ public class NodeServiceTest /** * Looks for a key that contains the toString() of the value */ - private Object findCacheValue(SimpleCache cache, Serializable key) + private Object findCacheValue(SimpleCache> cache, Serializable key) { Collection keys = cache.getKeys(); for (Serializable keyInCache : keys) @@ -821,7 +823,7 @@ public class NodeServiceTest String keyStr = key.toString(); if (keyInCacheStr.endsWith(keyStr)) { - Object value = cache.get(keyInCache); + Object value = TransactionalCache.getSharedCacheValue(cache, keyInCache); return value; } } diff --git a/source/test-resources/cache-test/cache-test-context.xml b/source/test-resources/cache-test/cache-test-context.xml index cb6b137440..a369106fa7 100644 --- a/source/test-resources/cache-test/cache-test-context.xml +++ b/source/test-resources/cache-test/cache-test-context.xml @@ -4,9 +4,9 @@ - + - + @@ -14,6 +14,4 @@ 200000 - - \ No newline at end of file