package org.infinispan.notifications.cachelistener; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.eq; import static org.mockito.Matchers.isA; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.isNotNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.hamcrest.MockitoHamcrest.argThat; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.hamcrest.CustomTypeSafeMatcher; import org.hamcrest.Matcher; import org.infinispan.Cache; import org.infinispan.commands.FlagAffectedCommand; import org.infinispan.commands.write.PutMapCommand; import org.infinispan.configuration.cache.CacheMode; import org.infinispan.configuration.cache.ConfigurationBuilder; import org.infinispan.context.InvocationContext; import org.infinispan.context.impl.FlagBitSets; import org.infinispan.manager.EmbeddedCacheManager; import org.infinispan.metadata.Metadata; import org.infinispan.test.AbstractInfinispanTest; import org.infinispan.test.TestingUtil; import org.infinispan.test.fwk.TestCacheManagerFactory; import org.infinispan.transaction.TransactionMode; import org.infinispan.util.concurrent.IsolationLevel; import org.testng.annotations.AfterClass; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * @author Mircea Markus * @since 5.1 */ @Test(groups = "functional", testName = "notifications.cachelistener.CacheNotifierTest") public class CacheNotifierTest extends AbstractInfinispanTest { protected Cache<Object, Object> cache; protected EmbeddedCacheManager cm; @BeforeMethod public void setUp() throws Exception { ConfigurationBuilder c = new ConfigurationBuilder(); c .transaction().transactionMode(TransactionMode.NON_TRANSACTIONAL) .clustering().cacheMode(CacheMode.LOCAL) .locking().isolationLevel(IsolationLevel.REPEATABLE_READ); cm = TestCacheManagerFactory.createCacheManager(c); cache = getCache(); CacheNotifier mockNotifier = mock(CacheNotifier.class); TestingUtil.replaceComponent(cache, CacheNotifier.class, mockNotifier, true); } protected Cache<Object, Object> getCache() { return cm.getCache(); } @AfterMethod public void tearDown() throws Exception { TestingUtil.killCaches(cache); cm.stop(); } @AfterClass public void destroyManager() { TestingUtil.killCacheManagers(cache.getCacheManager()); } private CacheNotifier getMockNotifier(Cache cache) { return cache.getAdvancedCache().getComponentRegistry().getComponent(CacheNotifier.class); } protected Matcher<FlagAffectedCommand> getFlagMatcher() { Matcher<FlagAffectedCommand> flagAffectedCommandMatcher = new CustomTypeSafeMatcher<FlagAffectedCommand>("SKIP_LISTENER_NOTIFICATION") { @Override protected boolean matchesSafely(FlagAffectedCommand item) { return !item.hasAnyFlag(FlagBitSets.SKIP_LISTENER_NOTIFICATION); } }; return flagAffectedCommandMatcher; } public void testVisit() throws Exception { Matcher<FlagAffectedCommand> matcher = getFlagMatcher(); initCacheData(cache, Collections.singletonMap("key", "value")); cache.get("key"); verify(getMockNotifier(cache)).notifyCacheEntryVisited(eq("key"), eq("value"), eq(true), isA(InvocationContext.class), argThat(matcher)); verify(getMockNotifier(cache)).notifyCacheEntryVisited(eq("key"), eq("value"), eq(false), isA(InvocationContext.class), argThat(matcher)); } public void testRemoveData() throws Exception { Matcher<FlagAffectedCommand> matcher = getFlagMatcher(); Map<String, String> data = new HashMap<>(); data.put("key", "value"); data.put("key2", "value2"); initCacheData(cache, data); cache.remove("key2"); verify(getMockNotifier(cache)).notifyCacheEntryRemoved(eq("key2"), eq("value2"), any(Metadata.class), eq(true), isA(InvocationContext.class), argThat(matcher)); verify(getMockNotifier(cache)).notifyCacheEntryRemoved(eq("key2"), eq("value2"), any(Metadata.class), eq(false), isA(InvocationContext.class), argThat(matcher)); } public void testPutMap() throws Exception { Matcher<FlagAffectedCommand> matcher = getFlagMatcher(); Map<Object, Object> data = new HashMap<Object, Object>(); data.put("key", "value"); data.put("key2", "value2"); cache.putAll(data); expectSingleEntryCreated(cache, "key", "value", matcher); expectSingleEntryCreated(cache, "key2", "value2", matcher); } public void testOnlyModification() throws Exception { Matcher<FlagAffectedCommand> matcher = getFlagMatcher(); initCacheData(cache, Collections.singletonMap("key", "value")); cache.put("key", "value2"); verify(getMockNotifier(cache)).notifyCacheEntryModified(eq("key"), eq("value2"), any(Metadata.class), eq("value"), any(Metadata.class), eq(true), isA(InvocationContext.class), argThat(matcher)); verify(getMockNotifier(cache)).notifyCacheEntryModified(eq("key"), eq("value2"), any(Metadata.class), eq("value"), any(Metadata.class), eq(false), isA(InvocationContext.class), argThat(matcher)); cache.put("key", "value2"); verify(getMockNotifier(cache)).notifyCacheEntryModified(eq("key"), eq("value2"), any(Metadata.class), eq("value2"), any(Metadata.class), eq(true), isA(InvocationContext.class), argThat(matcher)); verify(getMockNotifier(cache)).notifyCacheEntryModified(eq("key"), eq("value2"), any(Metadata.class), eq("value2"), any(Metadata.class), eq(false), isA(InvocationContext.class), argThat(matcher)); } public void testReplaceNotification() throws Exception { Matcher<FlagAffectedCommand> matcher = getFlagMatcher(); initCacheData(cache, Collections.singletonMap("key", "value")); cache.replace("key", "value", "value2"); verify(getMockNotifier(cache)).notifyCacheEntryModified(eq("key"), eq("value2"), any(Metadata.class), eq("value"), any(Metadata.class), eq(true), isA(InvocationContext.class), argThat(matcher)); verify(getMockNotifier(cache)).notifyCacheEntryModified(eq("key"), eq("value2"), any(Metadata.class), eq("value"), any(Metadata.class), eq(false), isA(InvocationContext.class), argThat(matcher)); } public void testReplaceNoNotificationOnNoChange() throws Exception { initCacheData(cache, Collections.singletonMap("key", "value")); cache.replace("key", "value2", "value3"); Matcher<FlagAffectedCommand> matcher = getFlagMatcher(); verify(getMockNotifier(cache), never()).notifyCacheEntryModified(eq("key"), eq("value3"), any(Metadata.class), eq("value3"), any(Metadata.class), eq(true), any(InvocationContext.class), argThat(matcher)); verify(getMockNotifier(cache), never()).notifyCacheEntryModified(eq("key"), eq("value3"), any(Metadata.class), eq("value3"), any(Metadata.class), eq(false), any(InvocationContext.class), argThat(matcher)); } public void testNonexistentVisit() throws Exception { cache.get("doesNotExist"); } public void testNonexistentRemove() throws Exception { cache.remove("doesNotExist"); } public void testCreation() throws Exception { creation(cache, getFlagMatcher()); } private void creation(Cache<Object, Object> cache, Matcher<FlagAffectedCommand> matcher) { cache.put("key", "value"); expectSingleEntryCreated(cache, "key", "value", matcher); } private void initCacheData(Cache cache, Map<String, String> data) { cache.putAll(data); verify(getMockNotifier(cache), atLeastOnce()).notifyCacheEntryCreated(any(), any(), any(Metadata.class), anyBoolean(), isA(InvocationContext.class), getExpectedPutMapCommand()); } protected PutMapCommand getExpectedPutMapCommand() { return isA(PutMapCommand.class); } private void expectSingleEntryCreated(Cache cache, Object key, Object value, Matcher<FlagAffectedCommand> matcher) { verify(getMockNotifier(cache)) .notifyCacheEntryCreated(eq(key), eq(value), any(Metadata.class), eq(true), any(InvocationContext.class), argThat(matcher)); verify(getMockNotifier(cache)) .notifyCacheEntryCreated(eq(key), eq(value), any(Metadata.class), eq(false), any(InvocationContext.class), argThat(matcher)); } }