/** * Copyright 2011-2013 Terracotta, Inc. * Copyright 2011-2013 Oracle, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jsr107.tck.integration; import org.jsr107.tck.processor.AssertNotPresentEntryProcessor; import org.jsr107.tck.processor.CombineEntryProcessor; import org.jsr107.tck.processor.RemoveEntryProcessor; import org.jsr107.tck.processor.SetEntryProcessor; import org.jsr107.tck.processor.SetEntryWithComputedValueProcessor; import org.jsr107.tck.testutil.ExcludeListExcluder; import org.jsr107.tck.testutil.TestSupport; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import javax.cache.Cache; import javax.cache.CacheManager; import javax.cache.Caching; import javax.cache.configuration.FactoryBuilder; import javax.cache.configuration.MutableConfiguration; import javax.cache.integration.CacheWriterException; import javax.cache.integration.CompletionListenerFuture; import javax.cache.processor.EntryProcessor; import java.io.IOException; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; /** * Functional test for {@link javax.cache.integration.CacheWriter}s. * <p> * Cache methods are tested in order listed in Write-Through Caching table in JCache specification. * </p> * @author Joe Fialli */ public class CacheWriterTest extends TestSupport { /** * Rule used to exclude tests */ @Rule public ExcludeListExcluder rule = new ExcludeListExcluder(this.getClass()); /** * The test Cache that will be configured to use the CacheWriter. */ private Cache<Integer, String> cache; /** * The {@link javax.cache.CacheManager} for the each test. */ private CacheManager cacheManager; private RecordingCacheWriter<Integer, String> cacheWriter; /** * A {@link CacheWriterServer} that will delegate {@link Cache} request * onto the recording {@link javax.cache.integration.CacheWriter}. */ private CacheWriterServer<Integer, String> cacheWriterServer; /** * Configure write-through before each test. */ @Before public void onBeforeEachTest() throws IOException { // establish and open a CacheWriterServer to handle cache // cache loading requests from a CacheWriterClient cacheWriter = new RecordingCacheWriter<>(); cacheWriterServer = new CacheWriterServer<>(10000, cacheWriter); cacheWriterServer.open(); // establish the CacheManager for the tests cacheManager = Caching.getCachingProvider().getCacheManager(); // establish a CacheWriterClient that a Cache can use for writing/deleting entries // (via the CacheWriterServer) CacheWriterClient<Integer, String> theCacheWriter = new CacheWriterClient<>(cacheWriterServer.getInetAddress(), cacheWriterServer.getPort()); MutableConfiguration<Integer, String> configuration = new MutableConfiguration<>(); configuration.setTypes(Integer.class, String.class); configuration.setCacheWriterFactory(FactoryBuilder.factoryOf(theCacheWriter)); configuration.setWriteThrough(true); getCacheManager().createCache("cache-writer-test", configuration); cache = getCacheManager().getCache("cache-writer-test", Integer.class, String.class); } @After public void cleanup() { // destroy the cache String cacheName = cache.getName(); cacheManager.destroyCache(cacheName); // close the CacheWriterServer cacheWriterServer.close(); cacheWriterServer = null; cache = null; } @Test public void shouldNotWriteThroughCallingContainsKeyOnExistingKey() { assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); // containsKey returns true case. cache.put(1, "Gudday World"); assertEquals(1, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); cache.containsKey(1); assertEquals(1, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); } @Test public void shouldNotInvokeWriteThroughCallingContainsKeyOnMissingKey() { // containsKey returns false case. assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); cache.containsKey(1); assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); } @Test public void shouldNotInvokeWriteThroughCallingGetOnMissingEntry() { // get returns null case. assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); cache.get(1); assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); } @Test public void shouldNotInvokeWriteThroughCallingGetOnExistingEntry() { assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); // get returns non-null case. cache.put(1, "Gudday World"); assertEquals(1, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); String value = cache.get(1); assertEquals("Gudday World", value); assertEquals(1, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); } @Test public void shouldNotInvokeWriteThroughCallingGetAll() { int NUM_KEYS = 4; Set<Integer> keys = new HashSet<>(); for (int i = 1; i <= NUM_KEYS; i++) { keys.add(i); } // getAll returns null case. assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); Map<Integer, String> map = cache.getAll(keys); assertTrue(map.size() == 0); assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); // getAll returns non-null case. for (Integer key : keys) { cache.put(key, "value" + key); } assertEquals(NUM_KEYS, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); map = cache.getAll(keys); assertEquals(keys.size(), map.size()); assertEquals(NUM_KEYS, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); } @Test public void shouldWriteThroughUsingGetAndPut_SingleEntryMultipleTimes() { assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); cache.getAndPut(1, "Gudday World"); cache.getAndPut(1, "Gudday World"); cache.getAndPut(1, "Gudday World"); assertEquals(3, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); assertTrue(cacheWriter.hasWritten(1)); assertEquals("Gudday World", cacheWriter.get(1)); } @Test public void shouldWriteThroughUsingGetAndPut_DifferentEntries() { assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); cache.getAndPut(1, "Gudday World"); cache.getAndPut(2, "Bonjour World"); cache.getAndPut(3, "Hello World"); assertEquals(3, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); assertTrue(cacheWriter.hasWritten(1)); assertEquals("Gudday World", cacheWriter.get(1)); assertTrue(cacheWriter.hasWritten(2)); assertEquals("Bonjour World", cacheWriter.get(2)); assertTrue(cacheWriter.hasWritten(3)); assertEquals("Hello World", cacheWriter.get(3)); } @Test public void shouldWriteThroughUsingGetAndRemove_MissingSingleEntry() { assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); // remove a missing entry case String value = cache.getAndRemove(1); assertEquals(value, null); assertEquals(0, cacheWriter.getWriteCount()); assertEquals(1, cacheWriter.getDeleteCount()); } @Test public void shouldWriteThroughUsingGetAndRemove_ExistingSingleEntry() { int nDelete = 0; assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); // actual remove of an entry case cache.put(1, "Gudday World"); String value = cache.getAndRemove(1); assertEquals("Gudday World", value); nDelete++; assertEquals(1, cacheWriter.getWriteCount()); assertEquals(nDelete, cacheWriter.getDeleteCount()); assertFalse(cacheWriter.hasWritten(1)); } @Test public void shouldNotWriteThroughUsingGetAndReplace_MissingSingleEntry() { assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); // replace does not occur since key 1 does not exist in cache String value = cache.getAndReplace(1, "Gudday World"); assertEquals(value, null); assertEquals(cache.containsKey(1), false); assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); } @Test public void shouldWriteThroughUsingGetAndReplace_ExistingSingleEntry() { assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); // actual replace of an entry case cache.put(1, "Gudday World"); String value = cache.getAndReplace(1, "Hello World"); assertEquals(value, "Gudday World"); assertEquals(2, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); assertEquals(cache.get(1), cacheWriter.get(1)); assertTrue(cacheWriter.hasWritten(1)); } @Test public void shouldWriteThroughUsingGetAndReplace_SingleEntryMultipleTimes() { int nWrite = 0; assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); String previousValue = cache.getAndReplace(1, "Gudday World"); assertEquals(previousValue, null); assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); boolean result = cache.putIfAbsent(1, "Gudday World"); assertTrue(result); nWrite++; assertEquals(nWrite, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); previousValue = cache.getAndReplace(1, "Bonjour World"); assertEquals(previousValue, "Gudday World"); nWrite++; assertEquals(cache.get(1), cacheWriter.get(1)); assertEquals("Bonjour World", cacheWriter.get(1)); assertEquals("Bonjour World", cache.get(1)); assertEquals(nWrite, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); previousValue = cache.getAndReplace(1, "Hello World"); assertEquals("Bonjour World", previousValue); nWrite++; assertEquals(cache.get(1), cacheWriter.get(1)); assertEquals(previousValue, "Bonjour World"); assertEquals(nWrite, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); assertTrue(cacheWriter.hasWritten(1)); assertEquals("Hello World", cacheWriter.get(1)); } @Test public void shouldWriteThroughUsingInvoke_setValue_CreateEntry() { assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); cache.invoke(1, new SetEntryProcessor<Integer, String>("Gudday World")); assertEquals(1, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); assertTrue(cacheWriter.hasWritten(1)); assertEquals("Gudday World", cacheWriter.get(1)); } @Test public void shouldWriteThroughUsingInvokeAll_setValue_CreateEntry() { final String VALUE_PREFIX = "value_"; final int NUM_KEYS = 10; assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); Set<Integer> keys = new HashSet<>(); for (int key = 1; key <= NUM_KEYS; key++) { keys.add(key); } cache.invokeAll(keys, new SetEntryWithComputedValueProcessor<Integer>(VALUE_PREFIX, "")); assertEquals(NUM_KEYS, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); for (Integer key : keys) { String computedValue = VALUE_PREFIX + key; assertTrue(cacheWriter.hasWritten(key)); assertEquals(computedValue, cacheWriter.get(key)); assertEquals(computedValue, cache.get(key)); } } @Test public void shouldWriteThroughUsingInvoke_setValue_UpdateEntry() { assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); cache.put(1, "Gudday World"); cache.invoke(1, new SetEntryProcessor("Hello World")); assertEquals(2, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); assertTrue(cacheWriter.hasWritten(1)); assertEquals("Hello World", cacheWriter.get(1)); } @Test public void shouldWriteThroughUsingInvokeAll_setValue_UpdateEntry() { final String VALUE_PREFIX_ORIGINAL = "value_"; final String VALUE_PREFIX_UPDATED = "updateValue_"; final int NUMBER_OF_KEYS = 10; assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); Set<Integer> keys = new HashSet<>(); for (int key = 1; key <= NUMBER_OF_KEYS; key++) { keys.add(key); cache.put(key, VALUE_PREFIX_ORIGINAL + key); } assertEquals(NUMBER_OF_KEYS, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); cache.invokeAll(keys, new SetEntryWithComputedValueProcessor<Integer>(VALUE_PREFIX_UPDATED, "")); assertEquals(NUMBER_OF_KEYS * 2, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); for (Integer key : keys) { String computedValue = VALUE_PREFIX_UPDATED + key; assertTrue(cacheWriter.hasWritten(key)); assertEquals(computedValue, cacheWriter.get(key)); assertEquals(computedValue, cache.get(key)); } } @Test public void shouldWriteThroughUsingInvoke_remove() { assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); cache.put(1, "Gudday World"); cache.invoke(1, new RemoveEntryProcessor<Integer, String, Object>(true)); assertEquals(1, cacheWriter.getWriteCount()); assertEquals(1, cacheWriter.getDeleteCount()); assertFalse(cacheWriter.hasWritten(1)); } @Test public void shouldWriteThroughUsingInvokeAll_setValue_RemoveEntry() { final String VALUE_PREFIX = "value_"; final int NUM_KEYS = 10; assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); Set<Integer> keys = new HashSet<>(); for (int key = 1; key <= NUM_KEYS; key++) { keys.add(key); cache.put(key, VALUE_PREFIX + key); } assertEquals(NUM_KEYS, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); cache.invokeAll(keys, new RemoveEntryProcessor<Integer, String, Object>(true)); assertEquals(NUM_KEYS, cacheWriter.getWriteCount()); assertEquals(NUM_KEYS, cacheWriter.getDeleteCount()); for (Integer key : keys) { assertFalse(cacheWriter.hasWritten(key)); assertEquals(null, cacheWriter.get(key)); assertEquals(null, cache.get(key)); } } @Test public void shouldWriteThroughUsingInvoke_remove_nonExistingEntry() { assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); cache.invoke(1, new RemoveEntryProcessor<Integer, String, Object>()); assertEquals(0, cacheWriter.getWriteCount()); assertEquals(1, cacheWriter.getDeleteCount()); assertFalse(cacheWriter.hasWritten(1)); } @Test public void shouldWriteThroughUsingInvoke_remove_createEntry() { assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); cache.put(1, "Gudday World"); EntryProcessor processors[] = new EntryProcessor[] { new RemoveEntryProcessor<Integer, String, Object>(true), new AssertNotPresentEntryProcessor(null), new SetEntryProcessor<Integer, String>("After remove") }; cache.invoke(1, new CombineEntryProcessor<Integer, String>(processors)); assertEquals(2, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); assertTrue(cacheWriter.hasWritten(1)); } @Test public void shouldWriteThroughUsingInvoke_setValue_CreateEntryThenRemove() { assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); EntryProcessor processors[] = new EntryProcessor[] { new AssertNotPresentEntryProcessor(null), new SetEntryProcessor<Integer, String>("Gudday World"), new RemoveEntryProcessor<Integer, String, Object>(true) }; cache.invoke(1, new CombineEntryProcessor<Integer, String>(processors)); assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); assertTrue(!cacheWriter.hasWritten(1)); assertTrue(cache.get(1) == null); assertFalse(cache.containsKey(1)); } @Test public void shouldWriteThroughUsingInvoke_setValue_CreateEntryGetValue() { assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); EntryProcessor processors[] = new EntryProcessor[] { new AssertNotPresentEntryProcessor(null), new SetEntryProcessor<Integer, String>("Gudday World") }; Object[] result = cache.invoke(1, new CombineEntryProcessor<Integer, String>(processors)); assertEquals(result[1], "Gudday World"); assertEquals(result[1], cache.get(1)); assertEquals(cache.get(1), cacheWriter.get(1)); assertEquals(1, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); assertTrue(cacheWriter.hasWritten(1)); assertEquals("Gudday World", cacheWriter.get(1)); } @Test public void shouldNotWriteThroughUsingIterator() { final String VALUE_PREFIX = "value_"; final int NUMBER_OF_KEYS = 10; assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); Set<Integer> keys = new HashSet<>(); for (int aKey = 1; aKey <= NUMBER_OF_KEYS; aKey++) { keys.add(aKey); cache.put(aKey, VALUE_PREFIX + aKey); } assertEquals(NUMBER_OF_KEYS, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); int i = 0; for (Cache.Entry<Integer, String> entry : cache) { i++; assertEquals(entry.getValue(), cacheWriter.get(entry.getKey())); } assertEquals(NUMBER_OF_KEYS, i); assertEquals(NUMBER_OF_KEYS, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); } /** * Test constraint that cache is not mutated when CacheWriterException is thrown by * {@link javax.cache.integration.CacheWriter#write(javax.cache.Cache.Entry)} */ @Test public void shouldNotPutWhenWriteThroughFails() { cacheWriterServer.setCacheWriter(new FailingCacheWriter<Integer, String>()); try { cache.put(1, "Gudday World"); assertTrue("expected exception on write-through", false); } catch (CacheWriterException e) { // ignore expected exception. } assertFalse(cache.containsKey(1)); } @Test public void shouldWriteThoughUsingPutSingleEntry() { assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); cache.put(1, "Gudday World"); assertEquals(1, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); assertTrue(cacheWriter.hasWritten(1)); assertEquals("Gudday World", cacheWriter.get(1)); } @Test public void shouldWriteThroughUsingPutSingleEntryMultipleTimes() { assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); cache.put(1, "Gudday World"); cache.put(1, "Bonjour World"); cache.put(1, "Hello World"); assertEquals(3, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); assertTrue(cacheWriter.hasWritten(1)); assertEquals("Hello World", cacheWriter.get(1)); } @Test public void shouldWriteThroughUsingPutOfDifferentEntries() { assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); cache.put(1, "Gudday World"); cache.put(2, "Bonjour World"); cache.put(3, "Hello World"); assertEquals(3, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); assertTrue(cacheWriter.hasWritten(1)); assertEquals("Gudday World", cacheWriter.get(1)); assertTrue(cacheWriter.hasWritten(2)); assertEquals("Bonjour World", cacheWriter.get(2)); assertTrue(cacheWriter.hasWritten(3)); assertEquals("Hello World", cacheWriter.get(3)); } @Test public void shouldWriteThoughUsingPutAll() { assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); HashMap<Integer, String> map = new HashMap<>(); map.put(1, "Gudday World"); map.put(2, "Bonjour World"); map.put(3, "Hello World"); cache.putAll(map); assertEquals(3, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); for (Integer key : map.keySet()) { assertTrue(cacheWriter.hasWritten(key)); assertTrue(cache.containsKey(key)); assertEquals(cache.get(key), cacheWriter.get(key)); assertEquals(map.get(key), cache.get(key)); } map.put(4, "Hola World"); cache.putAll(map); assertEquals(7, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); for (Integer key : map.keySet()) { assertTrue(cacheWriter.hasWritten(key)); assertEquals(map.get(key), cacheWriter.get(key)); assertTrue(cache.containsKey(key)); assertEquals(map.get(key), cache.get(key)); } } @Test public void shouldWriteThoughUsingPutAll_partialSuccess() { cacheWriter = new BatchPartialSuccessRecordingClassWriter<>(3, 100); cacheWriterServer.setCacheWriter(cacheWriter); assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); HashMap<Integer, String> map = new HashMap<>(); map.put(1, "Gudday World"); map.put(2, "Bonjour World"); map.put(3, "Hello World"); map.put(4, "Ciao World"); try { cache.putAll(map); assertTrue("expected CacheWriterException to be thrown for BatchPartialSuccessRecordingClassWriter", false); } catch (CacheWriterException cwe) { // ignore expected exception } int numSuccess = 0; int numFailure = 0; for (Integer key : map.keySet()) { if (cacheWriter.hasWritten(key)) { assertTrue(cache.containsKey(key)); assertEquals(map.get(key), cacheWriter.get(key)); assertEquals(map.get(key), cache.get(key)); numSuccess++; } else { assertFalse(cache.containsKey(key)); assertFalse(cacheWriter.hasWritten(key)); assertEquals(cache.get(key), cacheWriter.get(key)); numFailure++; } assertEquals(cache.get(key), cacheWriter.get(key)); } assertEquals(numSuccess + numFailure, map.size()); assertEquals(numSuccess, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); } @Test public void shouldWriteThoughUsingPutIfAbsent_SingleEntryMultipleTimes() { int nWrite = 0; assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); boolean result = cache.putIfAbsent(1, "Gudday World"); assertTrue(result); nWrite++; result = cache.putIfAbsent(1, "Bonjour World"); assertFalse(result); result = cache.putIfAbsent(1, "Hello World"); assertFalse(result); assertEquals(nWrite, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); assertTrue(cacheWriter.hasWritten(1)); assertEquals("Gudday World", cacheWriter.get(1)); } @Test public void shouldWriteThroughRemoveNonexistentKey() { assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); boolean result = cache.remove(1); assertFalse(result); assertEquals(1, cacheWriter.getDeleteCount()); } @Test public void shouldWriteThroughRemove_SingleEntry() { assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); cache.put(1, "Gudday World"); assertEquals(1, cacheWriter.getWriteCount()); boolean result = cache.remove(1); assertTrue(result); assertEquals(1, cacheWriter.getDeleteCount()); assertFalse(cacheWriter.hasWritten(1)); } /** * Test constraint that cache is not mutated when CacheWriterException is thrown by * {@link javax.cache.integration.CacheWriter#delete(Object)} */ @Test public void shouldNotRemoveWhenWriteThroughFails() { cache.put(1, "Gudday World"); assertTrue(cache.containsKey(1)); cacheWriterServer.setCacheWriter(new FailingCacheWriter<Integer, String>()); try { cache.remove(1); assertTrue("expected exception on write-through", false); } catch (CacheWriterException e) { // ignore expected exception. } assertTrue(cache.containsKey(1)); } @Test public void shouldWriteThroughRemove_SingleEntryMultipleTimes() { int nDelete = 0; assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); cache.put(1, "Gudday World"); boolean result = cache.remove(1); assertTrue(result); nDelete++; result = cache.remove(1); assertFalse(result); nDelete++; result = cache.remove(1); assertFalse(result); nDelete++; assertEquals(1, cacheWriter.getWriteCount()); assertEquals(nDelete, cacheWriter.getDeleteCount()); } @Test public void shouldWriteThroughRemove_SpecificEntry() { int nDelete = 0; assertEquals(0, cacheWriter.getWriteCount()); assertEquals(nDelete, cacheWriter.getDeleteCount()); cache.put(1, "Gudday World"); boolean result = cache.remove(1, "Hello World"); assertFalse(result); assertEquals(nDelete, cacheWriter.getDeleteCount()); result = cache.remove(1, "Gudday World"); assertTrue(result); nDelete++; assertEquals(nDelete, cacheWriter.getDeleteCount()); result = cache.remove(1, "Gudday World"); assertFalse(result); assertEquals(nDelete, cacheWriter.getDeleteCount()); assertEquals(1, cacheWriter.getWriteCount()); } @Test public void shouldWriteThroughCacheIteratorRemove() { int nDelete = 0; assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); cache.getAndPut(1, "Gudday World"); cache.getAndPut(2, "Bonjour World"); cache.getAndPut(3, "Hello World"); Iterator<Cache.Entry<Integer, String>> iterator = cache.iterator(); iterator.next(); iterator.remove(); nDelete++; iterator.next(); iterator.next(); iterator.remove(); nDelete++; assertEquals(3, cacheWriter.getWriteCount()); assertEquals(nDelete, cacheWriter.getDeleteCount()); } @Test public void shouldWriteThroughRemoveAll() { assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); HashMap<Integer, String> map = new HashMap<>(); map.put(1, "Gudday World"); map.put(2, "Bonjour World"); map.put(3, "Hello World"); cache.putAll(map); assertEquals(3, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); for (Integer key : map.keySet()) { assertTrue(cacheWriter.hasWritten(key)); assertEquals(map.get(key), cacheWriter.get(key)); assertTrue(cache.containsKey(key)); assertEquals(map.get(key), cache.get(key)); } cache.removeAll(); assertEquals(3, cacheWriter.getWriteCount()); assertEquals(3, cacheWriter.getDeleteCount()); for (Integer key : map.keySet()) { assertFalse(cacheWriter.hasWritten(key)); assertFalse(cache.containsKey(key)); } //the following should not change the state of the cache cache.removeAll(); assertEquals(3, cacheWriter.getWriteCount()); assertEquals(3, cacheWriter.getDeleteCount()); for (Integer key : map.keySet()) { assertFalse(cacheWriter.hasWritten(key)); assertFalse(cache.containsKey(key)); } map.put(4, "Hola World"); cache.putAll(map); assertEquals(7, cacheWriter.getWriteCount()); assertEquals(3, cacheWriter.getDeleteCount()); for (Integer key : map.keySet()) { assertTrue(cacheWriter.hasWritten(key)); assertEquals(map.get(key), cacheWriter.get(key)); assertTrue(cache.containsKey(key)); assertEquals(map.get(key), cache.get(key)); } } @Test public void shouldWriteThroughRemoveAll_partialSuccess() { cacheWriter = new BatchPartialSuccessRecordingClassWriter<>(100, 3); cacheWriterServer.setCacheWriter(cacheWriter); assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); HashMap<Integer, String> map = new HashMap<>(); map.put(1, "Gudday World"); map.put(2, "Bonjour World"); map.put(3, "Hello World"); cache.putAll(map); assertEquals(3, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); for (Integer key : map.keySet()) { assertTrue(cacheWriter.hasWritten(key)); assertEquals(map.get(key), cacheWriter.get(key)); assertTrue(cache.containsKey(key)); assertEquals(map.get(key), cache.get(key)); } try { cache.removeAll(); assertTrue("expected CacheWriterException to be thrown for BatchPartialSuccessRecordingClassWriter", false); } catch (CacheWriterException cwe) { // ignore expected exception } int numSuccess = 0; int numFailure = 0; for (Integer key : map.keySet()) { if (cacheWriter.hasWritten(key)) { assertTrue(cache.containsKey(key)); assertEquals(map.get(key), cacheWriter.get(key)); assertEquals(map.get(key), cache.get(key)); numFailure++; } else { assertFalse(cache.containsKey(key)); numSuccess++; } assertEquals(cache.get(key), cacheWriter.get(key)); } assertEquals(numSuccess + numFailure, map.size()); assertEquals(3, cacheWriter.getWriteCount()); assertEquals(numSuccess, cacheWriter.getDeleteCount()); } @Test public void shouldUseWriteThroughRemoveAllSpecific() { assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); HashMap<Integer, String> map = new HashMap<>(); map.put(1, "Gudday World"); map.put(2, "Bonjour World"); map.put(3, "Hello World"); map.put(4, "Hola World"); cache.putAll(map); assertEquals(4, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); for (Integer key : map.keySet()) { assertTrue(cacheWriter.hasWritten(key)); assertEquals(map.get(key), cacheWriter.get(key)); assertTrue(cache.containsKey(key)); assertEquals(map.get(key), cache.get(key)); } HashSet<Integer> set = new HashSet<>(); set.add(1); set.add(4); cache.removeAll(set); assertEquals(4, cacheWriter.getWriteCount()); assertEquals(2, cacheWriter.getDeleteCount()); for (Integer key : set) { assertFalse(cacheWriter.hasWritten(key)); assertFalse(cache.containsKey(key)); } cache.put(4, "Howdy World"); assertEquals(5, cacheWriter.getWriteCount()); assertEquals(2, cacheWriter.getDeleteCount()); set.clear(); set.add(2); cache.removeAll(set); assertEquals(3, cacheWriter.getDeleteCount()); assertTrue(cacheWriter.hasWritten(3)); assertTrue(cache.containsKey(3)); assertTrue(cacheWriter.hasWritten(4)); assertTrue(cache.containsKey(4)); } @Test public void shouldWriteThroughRemoveAllSpecific_partialSuccess() { cacheWriter = new BatchPartialSuccessRecordingClassWriter<>(100, 3); cacheWriterServer.setCacheWriter(cacheWriter); assertEquals(0, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); HashMap<Integer, String> entriesAdded = new HashMap<>(); entriesAdded.put(1, "Gudday World"); entriesAdded.put(2, "Bonjour World"); entriesAdded.put(3, "Hello World"); entriesAdded.put(4, "Hola World"); entriesAdded.put(5, "Ciao World"); cache.putAll(entriesAdded); assertEquals(5, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); for (Integer key : entriesAdded.keySet()) { assertTrue(cacheWriter.hasWritten(key)); assertEquals(entriesAdded.get(key), cacheWriter.get(key)); assertTrue(cache.containsKey(key)); assertEquals(entriesAdded.get(key), cache.get(key)); } HashSet<Integer> keysToRemove = new HashSet<>(); keysToRemove.add(1); keysToRemove.add(2); keysToRemove.add(3); keysToRemove.add(4); try { cache.removeAll(keysToRemove); assertTrue("expected CacheWriterException to be thrown for BatchPartialSuccessRecordingClassWriter", false); } catch (CacheWriterException ce) { // ignore expected exception } int numSuccess = 0; int numFailure = 0; for (Integer key : entriesAdded.keySet()) { if (cacheWriter.hasWritten(key)) { assertTrue(cache.containsKey(key)); assertEquals(entriesAdded.get(key), cacheWriter.get(key)); assertEquals(entriesAdded.get(key), cache.get(key)); numFailure++; } else { assertFalse(cache.containsKey(key)); numSuccess++; } assertEquals(cache.get(key), cacheWriter.get(key)); } assertEquals(numSuccess + numFailure, entriesAdded.size()); assertEquals(5, cacheWriter.getWriteCount()); assertEquals(numSuccess, cacheWriter.getDeleteCount()); } /** * Write-through Test for method * boolean replace(K key, V value) */ @Test public void shouldNotWriteThroughReplaceNonExistentKey() { int nWrites = 0; assertEquals(nWrites, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); boolean result = cache.replace(1, "Gudday World"); assertFalse(result); assertEquals(nWrites, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); } /** * Write-through Test for method * boolean replace(K key, V value) */ @Test public void shouldWriteThroughReplaceExisting_SingleEntryMultipleTimes() { int nWrites = 0; assertEquals(nWrites, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); boolean result = cache.replace(1, "Gudday World"); assertFalse(result); result = cache.putIfAbsent(1, "Gudday World"); assertTrue(result); nWrites++; result = cache.replace(1, "Bonjour World"); assertTrue(result); nWrites++; result = cache.replace(1, "Hello World"); assertTrue(result); nWrites++; assertEquals(nWrites, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); assertTrue(cacheWriter.hasWritten(1)); assertEquals("Hello World", cacheWriter.get(1)); assertEquals(cache.get(1), cacheWriter.get(1)); } /** * Write-through Test for method * boolean replace(K key, V oldValue, V newValue) */ @Test public void shouldNotUseWriteThroughReplaceDoesNotMatch() { int nWriter = 0; assertEquals(nWriter, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); boolean result = cache.putIfAbsent(1, "Gudday World"); assertTrue(result); nWriter++; assertEquals(1, cacheWriter.getWriteCount()); result = cache.replace(1, "Bonjour World", "Hello World"); assertFalse(result); assertFalse("Hello World".equals(cache.get(1))); assertEquals(nWriter, cacheWriter.getWriteCount()); assertEquals(0, cacheWriter.getDeleteCount()); assertTrue(cacheWriter.hasWritten(1)); assertEquals(cache.get(1), cacheWriter.get(1)); } @Test public void shouldWrapWriterExceptions() throws IOException { //we need to create a custom writer cleanup(); // establish and open a CacheLoaderServer to handle cache // cache loading requests from a CacheLoaderClient cacheWriter = new FailingCacheWriter<>(); cacheWriterServer = new CacheWriterServer<>(10000, cacheWriter); cacheWriterServer.open(); // establish the CacheManager for the tests cacheManager = Caching.getCachingProvider().getCacheManager(); // establish a CacheWriterClient that a Cache can use for writing/deleting entries // (via the CacheWriterServer) CacheWriterClient<Integer, String> theCacheWriter = new CacheWriterClient<>(cacheWriterServer.getInetAddress(), cacheWriterServer.getPort()); MutableConfiguration<Integer, String> configuration = new MutableConfiguration<>(); configuration.setTypes(Integer.class, String.class); configuration.setCacheWriterFactory(FactoryBuilder.factoryOf(theCacheWriter)); configuration.setWriteThrough(true); getCacheManager().createCache("failing-cache-writer-test", configuration); cache = getCacheManager().getCache("failing-cache-writer-test", Integer.class, String.class); try { cache.put(12, "Tonto"); fail(); } catch (CacheWriterException e) { //expected } try { HashMap<Integer, String> map = new HashMap<>(); map.put(12, "Tonto"); cache.putAll(map); fail(); } catch (CacheWriterException e) { //expected } try { cache.remove(12); fail(); } catch (CacheWriterException e) { //expected } try { HashSet<Integer> set = new HashSet<>(); set.add(12); cache.removeAll(set); fail(); } catch (CacheWriterException e) { //expected } // the following should not throw an exception as there are no entries // in the cache cache.removeAll(); } static public class Entry<K, V> implements Cache.Entry<K, V> { private K key; private V value; public Entry(K k, V v) { this.key = k; this.value = v; } @Override public K getKey() { return key; } @Override public V getValue() { return value; } @Override public <T> T unwrap(Class<T> clazz) { throw new UnsupportedOperationException("not implemented"); } } }