/* * Copyright (c) 2008-2017, Hazelcast, Inc. All Rights Reserved. * * 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 com.hazelcast.map; import com.hazelcast.config.Config; import com.hazelcast.config.InMemoryFormat; import com.hazelcast.config.MapConfig; import com.hazelcast.config.MapIndexConfig; import com.hazelcast.config.MapStoreConfig; import com.hazelcast.core.EntryEvent; import com.hazelcast.core.ExecutionCallback; import com.hazelcast.core.HazelcastInstance; import com.hazelcast.core.HazelcastInstanceAware; import com.hazelcast.core.IMap; import com.hazelcast.core.MapLoader; import com.hazelcast.core.MapStoreAdapter; import com.hazelcast.core.PostProcessingMapStore; import com.hazelcast.map.listener.EntryAddedListener; import com.hazelcast.map.listener.EntryRemovedListener; import com.hazelcast.map.listener.EntryUpdatedListener; import com.hazelcast.monitor.LocalMapStats; import com.hazelcast.nio.ObjectDataInput; import com.hazelcast.nio.ObjectDataOutput; import com.hazelcast.nio.serialization.DataSerializable; import com.hazelcast.query.EntryObject; import com.hazelcast.query.IndexAwarePredicate; import com.hazelcast.query.Predicate; import com.hazelcast.query.PredicateBuilder; import com.hazelcast.query.Predicates; import com.hazelcast.query.SampleObjects; import com.hazelcast.query.SampleObjects.Employee; import com.hazelcast.query.SqlPredicate; import com.hazelcast.query.impl.Index; import com.hazelcast.query.impl.QueryContext; import com.hazelcast.query.impl.QueryableEntry; import com.hazelcast.test.AssertTask; import com.hazelcast.test.HazelcastParametersRunnerFactory; import com.hazelcast.test.HazelcastTestSupport; import com.hazelcast.test.TestHazelcastInstanceFactory; import com.hazelcast.test.annotation.ParallelTest; import com.hazelcast.test.annotation.QuickTest; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import java.io.IOException; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import static com.hazelcast.config.InMemoryFormat.BINARY; import static com.hazelcast.config.InMemoryFormat.NATIVE; import static com.hazelcast.config.InMemoryFormat.OBJECT; import static com.hazelcast.map.EntryProcessorTest.ApplyCountAwareIndexedTestPredicate.PREDICATE_APPLY_COUNT; import static java.util.Arrays.asList; import static java.util.Collections.emptyMap; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @RunWith(Parameterized.class) @Parameterized.UseParametersRunnerFactory(HazelcastParametersRunnerFactory.class) @Category({QuickTest.class, ParallelTest.class}) public class EntryProcessorTest extends HazelcastTestSupport { public static final String MAP_NAME = "EntryProcessorTest"; @Parameterized.Parameter public InMemoryFormat inMemoryFormat; @Parameterized.Parameters(name = "{index}: {0}") public static Collection<Object[]> data() { return asList(new Object[][]{ {BINARY}, {OBJECT} }); } @Override public Config getConfig() { Config config = super.getConfig(); MapConfig mapConfig = new MapConfig(MAP_NAME); mapConfig.setInMemoryFormat(inMemoryFormat); config.addMapConfig(mapConfig); return config; } @Test public void testExecuteOnEntriesWithEntryListener() { HazelcastInstance instance = createHazelcastInstance(getConfig()); IMap<String, String> map = instance.getMap(MAP_NAME); map.put("key", "value"); final CountDownLatch latch = new CountDownLatch(1); map.addEntryListener(new EntryUpdatedListener<String, String>() { @Override public void entryUpdated(EntryEvent<String, String> event) { String val = event.getValue(); String oldValue = event.getOldValue(); if ("newValue".equals(val) // contract difference && ((inMemoryFormat == BINARY || inMemoryFormat == NATIVE) && "value".equals(oldValue) || inMemoryFormat == OBJECT && null == oldValue)) { latch.countDown(); } } }, true); map.executeOnEntries(new AbstractEntryProcessor<String, String>() { @Override public Object process(Map.Entry<String, String> entry) { entry.setValue("newValue"); return 5; } }); assertOpenEventually(latch, 5); } @Test public void testExecuteOnKeysWithEntryListener() { HazelcastInstance instance = createHazelcastInstance(getConfig()); IMap<String, String> map = instance.getMap(MAP_NAME); map.put("key", "value"); final CountDownLatch latch = new CountDownLatch(1); map.addEntryListener(new EntryUpdatedListener<String, String>() { @Override public void entryUpdated(EntryEvent<String, String> event) { String val = event.getValue(); String oldValue = event.getOldValue(); if ("newValue".equals(val) // contract difference && ((inMemoryFormat == BINARY || inMemoryFormat == NATIVE) && "value".equals(oldValue) || inMemoryFormat == OBJECT && null == oldValue)) { latch.countDown(); } } }, true); HashSet<String> keys = new HashSet<String>(); keys.add("key"); map.executeOnKeys(keys, new AbstractEntryProcessor<String, String>() { @Override public Object process(Map.Entry<String, String> entry) { entry.setValue("newValue"); return 5; } }); assertOpenEventually(latch, 5); } @Test public void testUpdate_Issue_1764() { Config cfg = getConfig(); TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(1); HazelcastInstance instance = factory.newHazelcastInstance(cfg); try { IMap<String, Issue1764Data> map = instance.getMap(MAP_NAME); map.put("a", new Issue1764Data("foo", "bar")); map.put("b", new Issue1764Data("abc", "123")); Set<String> keys = new HashSet<String>(); keys.add("a"); map.executeOnKeys(keys, new Issue1764UpdatingEntryProcessor(MAP_NAME)); } catch (ClassCastException e) { e.printStackTrace(); fail("ClassCastException must not happen!"); } finally { instance.shutdown(); } } @SuppressWarnings("unused") private static class Issue1764Data implements DataSerializable { private static AtomicInteger serializationCount = new AtomicInteger(); private static AtomicInteger deserializationCount = new AtomicInteger(); private String attr1; private String attr2; Issue1764Data() { } Issue1764Data(String attr1, String attr2) { this.attr1 = attr1; this.attr2 = attr2; } String getAttr1() { return attr1; } void setAttr1(String attr1) { this.attr1 = attr1; } String getAttr2() { return attr2; } void setAttr2(String attr2) { this.attr2 = attr2; } @Override public String toString() { return "[" + attr1 + " " + attr2 + "]"; } @Override public void writeData(ObjectDataOutput out) throws IOException { serializationCount.incrementAndGet(); out.writeObject(attr1); out.writeObject(attr2); } @Override public void readData(ObjectDataInput in) throws IOException { attr1 = in.readObject(); attr2 = in.readObject(); deserializationCount.incrementAndGet(); } } private static class Issue1764UpdatingEntryProcessor extends AbstractEntryProcessor<String, Issue1764Data> { private static final long serialVersionUID = 1L; private String newValue; Issue1764UpdatingEntryProcessor(String newValue) { this.newValue = newValue; } @Override public Object process(Map.Entry<String, Issue1764Data> entry) { Issue1764Data data = entry.getValue(); data.setAttr1(newValue); entry.setValue(data); return true; } } @Test public void testIndexAware_Issue_1719() { Config cfg = getConfig(); cfg.getMapConfig(MAP_NAME).addMapIndexConfig(new MapIndexConfig("attr1", false)); HazelcastInstance instance = createHazelcastInstance(cfg); IMap<String, TestData> map = instance.getMap(MAP_NAME); map.put("a", new TestData("foo", "bar")); map.put("b", new TestData("abc", "123")); TestPredicate predicate = new TestPredicate("foo"); Map<String, Object> entries = map.executeOnEntries(new TestLoggingEntryProcessor(), predicate); assertEquals("The predicate should only relate to one entry!", 1, entries.size()); assertEquals("The predicate's apply method should only be invoked once!", 1, predicate.getApplied()); assertTrue("The predicate should only be used via index service!", predicate.isFilteredAndAppliedOnlyOnce()); } /** * Reproducer for https://github.com/hazelcast/hazelcast/issues/1854 * Similar to above tests but with executeOnKeys instead. */ @Test public void testExecuteOnKeysBackupOperation() { Config cfg = getConfig(); cfg.getMapConfig(MAP_NAME).setBackupCount(1); TestHazelcastInstanceFactory nodeFactory = createHazelcastInstanceFactory(2); HazelcastInstance instance1 = nodeFactory.newHazelcastInstance(cfg); HazelcastInstance instance2 = nodeFactory.newHazelcastInstance(cfg); IMap<String, TestData> map = instance1.getMap(MAP_NAME); map.put("a", new TestData("foo", "bar")); map.put("b", new TestData("foo", "bar")); map.executeOnKeys(map.keySet(), new TestDeleteEntryProcessor()); // the entry has been removed from the primary store but not the backup, // so let's kill the primary and execute the logging processor again HazelcastInstance newPrimary; String aMemberUiid = instance1.getPartitionService().getPartition("a").getOwner().getUuid(); if (aMemberUiid.equals(instance1.getCluster().getLocalMember().getUuid())) { instance1.shutdown(); newPrimary = instance2; } else { instance2.shutdown(); newPrimary = instance1; } // make sure there are no entries left IMap<String, TestData> map2 = newPrimary.getMap("test"); Map<String, Object> executedEntries = map2.executeOnEntries(new TestLoggingEntryProcessor()); assertEquals(0, executedEntries.size()); } /** * Reproducer for https://github.com/hazelcast/hazelcast/issues/1854 * This one with index which results in an exception. */ @Test public void testExecuteOnKeysBackupOperationIndexed() { Config cfg = getConfig(); cfg.getMapConfig(MAP_NAME).setBackupCount(1).addMapIndexConfig(new MapIndexConfig("attr1", false)); TestHazelcastInstanceFactory nodeFactory = createHazelcastInstanceFactory(2); HazelcastInstance instance1 = nodeFactory.newHazelcastInstance(cfg); HazelcastInstance instance2 = nodeFactory.newHazelcastInstance(cfg); IMap<String, TestData> map = instance1.getMap(MAP_NAME); map.put("a", new TestData("foo", "bar")); map.put("b", new TestData("abc", "123")); map.executeOnKeys(map.keySet(), new TestDeleteEntryProcessor()); // the entry has been removed from the primary store but not the backup, // so let's kill the primary and execute the logging processor again HazelcastInstance newPrimary; String aMemberUiid = instance1.getPartitionService().getPartition("a").getOwner().getUuid(); if (aMemberUiid.equals(instance1.getCluster().getLocalMember().getUuid())) { instance1.shutdown(); newPrimary = instance2; } else { instance2.shutdown(); newPrimary = instance1; } // make sure there are no entries left IMap<String, TestData> map2 = newPrimary.getMap("test"); Map<String, Object> executedEntries = map2.executeOnEntries(new TestLoggingEntryProcessor()); assertEquals(0, executedEntries.size()); } @Test public void testEntryProcessorDeleteWithPredicate() { TestHazelcastInstanceFactory nodeFactory = createHazelcastInstanceFactory(2); Config cfg = getConfig(); cfg.getMapConfig(MAP_NAME).setBackupCount(1); HazelcastInstance instance1 = nodeFactory.newHazelcastInstance(cfg); HazelcastInstance instance2 = nodeFactory.newHazelcastInstance(cfg); try { IMap<String, TestData> map = instance1.getMap(MAP_NAME); map.put("a", new TestData("foo", "bar")); map.executeOnEntries(new TestLoggingEntryProcessor(), Predicates.equal("attr1", "foo")); map.executeOnEntries(new TestDeleteEntryProcessor(), Predicates.equal("attr1", "foo")); // now the entry has been removed from the primary store but not the backup, // so let's kill the primary and execute the logging processor again HazelcastInstance newPrimary; String a_member_uiid = instance1.getPartitionService().getPartition("a").getOwner().getUuid(); if (a_member_uiid.equals(instance1.getCluster().getLocalMember().getUuid())) { instance1.shutdown(); newPrimary = instance2; } else { instance2.shutdown(); newPrimary = instance1; } IMap<String, TestData> map2 = newPrimary.getMap(MAP_NAME); map2.executeOnEntries(new TestLoggingEntryProcessor(), Predicates.equal("attr1", "foo")); } finally { instance1.shutdown(); instance2.shutdown(); } } @Test public void testEntryProcessorWithKey() { Config cfg = getConfig(); TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(2); HazelcastInstance instance1 = factory.newHazelcastInstance(cfg); HazelcastInstance instance2 = factory.newHazelcastInstance(cfg); String key = generateKeyOwnedBy(instance1); SimpleValue simpleValue = new SimpleValue(1); // EntryProcessor contract difference between OBJECT and BINARY SimpleValue expectedValue = inMemoryFormat == OBJECT ? new SimpleValue(2) : new SimpleValue(1); IMap<Object, Object> map = instance2.getMap(MAP_NAME); map.put(key, simpleValue); map.executeOnKey(key, new EntryInc()); assertEquals(expectedValue, map.get(key)); instance1.shutdown(); assertEquals(expectedValue, map.get(key)); } @Test public void testEntryProcessorWithKeys() { Config cfg = getConfig(); TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(2); HazelcastInstance instance1 = factory.newHazelcastInstance(cfg); HazelcastInstance instance2 = factory.newHazelcastInstance(cfg); IMap<Object, Object> map = instance2.getMap(MAP_NAME); Set<Object> keys = new HashSet<Object>(); for (int i = 0; i < 4; i++) { final String key = generateKeyOwnedBy(instance1); keys.add(key); } SimpleValue simpleValue = new SimpleValue(1); for (Object key : keys) { map.put(key, simpleValue); } map.executeOnKeys(keys, new EntryInc()); // EntryProcessor contract difference between OBJECT and BINARY SimpleValue expectedValue = inMemoryFormat == OBJECT ? new SimpleValue(2) : new SimpleValue(1); for (Object key : keys) { assertEquals(expectedValue, map.get(key)); } instance1.shutdown(); for (Object key : keys) { assertEquals(expectedValue, map.get(key)); } } @Test public void testIssue2754() { Config cfg = getConfig(); TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(2); HazelcastInstance instance1 = factory.newHazelcastInstance(cfg); HazelcastInstance instance2 = factory.newHazelcastInstance(cfg); IMap<Object, Object> map = instance2.getMap(MAP_NAME); Set<Object> keys = new HashSet<Object>(); for (int i = 0; i < 4; i++) { String key = generateKeyOwnedBy(instance1); keys.add(key); } map.executeOnKeys(keys, new EntryCreate()); for (Object key : keys) { assertEquals(6, map.get(key)); } instance1.shutdown(); for (Object key : keys) { assertEquals(6, map.get(key)); } } @Test public void testEntryProcessorDelete() { TestHazelcastInstanceFactory nodeFactory = createHazelcastInstanceFactory(2); Config cfg = getConfig(); cfg.getMapConfig(MAP_NAME).setBackupCount(1); HazelcastInstance instance1 = nodeFactory.newHazelcastInstance(cfg); HazelcastInstance instance2 = nodeFactory.newHazelcastInstance(cfg); try { IMap<String, TestData> map = instance1.getMap(MAP_NAME); map.put("a", new TestData("foo", "bar")); map.executeOnKey("a", new TestLoggingEntryProcessor()); map.executeOnKey("a", new TestDeleteEntryProcessor()); // now the entry has been removed from the primary store but not the backup, // so let's kill the primary and execute the logging processor again HazelcastInstance newPrimary; String aMemberUiid = instance1.getPartitionService().getPartition("a").getOwner().getUuid(); if (aMemberUiid.equals(instance1.getCluster().getLocalMember().getUuid())) { instance1.shutdown(); newPrimary = instance2; } else { instance2.shutdown(); newPrimary = instance1; } IMap<String, TestData> map2 = newPrimary.getMap("test"); assertFalse(map2.containsKey("a")); } finally { instance1.shutdown(); instance2.shutdown(); } } @Test public void testMapEntryProcessor() { Config cfg = getConfig(); TestHazelcastInstanceFactory nodeFactory = createHazelcastInstanceFactory(2); HazelcastInstance instance1 = nodeFactory.newHazelcastInstance(cfg); HazelcastInstance instance2 = nodeFactory.newHazelcastInstance(cfg); String instance1Key = generateKeyOwnedBy(instance1); String instance2Key = generateKeyOwnedBy(instance2); IMap<String, Integer> map = instance1.getMap(MAP_NAME); map.put(instance1Key, 23); map.put(instance2Key, 42); EntryProcessor entryProcessor = new IncrementorEntryProcessor(); assertEquals(24, map.executeOnKey(instance1Key, entryProcessor)); assertEquals(43, map.executeOnKey(instance2Key, entryProcessor)); assertEquals((Integer) 24, map.get(instance1Key)); assertEquals((Integer) 43, map.get(instance2Key)); } @Test public void testMapEntryProcessorCallback() throws Exception { TestHazelcastInstanceFactory nodeFactory = createHazelcastInstanceFactory(2); Config cfg = getConfig(); HazelcastInstance instance1 = nodeFactory.newHazelcastInstance(cfg); nodeFactory.newHazelcastInstance(cfg); IMap<Integer, Integer> map = instance1.getMap(MAP_NAME); map.put(1, 1); EntryProcessor entryProcessor = new IncrementorEntryProcessor(); final AtomicInteger result = new AtomicInteger(0); final CountDownLatch latch = new CountDownLatch(1); map.submitToKey(1, entryProcessor, new ExecutionCallback<Integer>() { @Override public void onResponse(Integer response) { result.set(response); latch.countDown(); } @Override public void onFailure(Throwable t) { latch.countDown(); } }); latch.await(10, TimeUnit.SECONDS); assertEquals(2, result.get()); } @Test public void testNotExistingEntryProcessor() { TestHazelcastInstanceFactory nodeFactory = createHazelcastInstanceFactory(2); Config cfg = getConfig(); HazelcastInstance instance1 = nodeFactory.newHazelcastInstance(cfg); nodeFactory.newHazelcastInstance(cfg); IMap<Integer, Integer> map = instance1.getMap(MAP_NAME); EntryProcessor entryProcessor = new IncrementorEntryProcessor(); assertEquals(1, map.executeOnKey(1, entryProcessor)); assertEquals((Integer) 1, map.get(1)); } @Test public void testMapEntryProcessorAllKeys() { TestHazelcastInstanceFactory nodeFactory = createHazelcastInstanceFactory(2); Config cfg = getConfig(); HazelcastInstance instance1 = nodeFactory.newHazelcastInstance(cfg); HazelcastInstance instance2 = nodeFactory.newHazelcastInstance(cfg); try { IMap<Integer, Integer> map = instance1.getMap(MAP_NAME); int size = 100; for (int i = 0; i < size; i++) { map.put(i, i); } EntryProcessor entryProcessor = new IncrementorEntryProcessor(); Map<Integer, Object> res = map.executeOnEntries(entryProcessor); for (int i = 0; i < size; i++) { assertEquals(map.get(i), (Object) (i + 1)); } for (int i = 0; i < size; i++) { assertEquals(map.get(i), res.get(i)); } } finally { instance1.shutdown(); instance2.shutdown(); } } @Test public void testBackupMapEntryProcessorAllKeys() { TestHazelcastInstanceFactory nodeFactory = createHazelcastInstanceFactory(3); Config cfg = getConfig(); HazelcastInstance instance1 = nodeFactory.newHazelcastInstance(cfg); HazelcastInstance instance2 = nodeFactory.newHazelcastInstance(cfg); HazelcastInstance instance3 = nodeFactory.newHazelcastInstance(cfg); assertClusterSize(3, instance1, instance3); assertClusterSizeEventually(3, instance2); IMap<Integer, Integer> map = instance1.getMap(MAP_NAME); int size = 100; for (int i = 0; i < size; i++) { map.put(i, i); } EntryProcessor entryProcessor = new IncrementorEntryProcessor(); map.executeOnEntries(entryProcessor); for (int i = 0; i < size; i++) { assertEquals(map.get(i), (Object) (i + 1)); } instance1.shutdown(); assertClusterSizeEventually(2, instance2, instance3); IMap<Integer, Integer> map2 = instance2.getMap(MAP_NAME); for (int i = 0; i < size; i++) { assertEquals(map2.get(i), (Object) (i + 1)); } } @Test public void testMapEntryProcessorWithPredicate() { TestHazelcastInstanceFactory nodeFactory = createHazelcastInstanceFactory(2); Config cfg = getConfig(); HazelcastInstance instance1 = nodeFactory.newHazelcastInstance(cfg); HazelcastInstance instance2 = nodeFactory.newHazelcastInstance(cfg); try { IMap<Integer, Employee> map = instance1.getMap(MAP_NAME); int size = 10; for (int i = 0; i < size; i++) { map.put(i, new Employee(i, "", 0, false, 0D, SampleObjects.State.STATE1)); } EntryProcessor entryProcessor = new ChangeStateEntryProcessor(); EntryObject entryObject = new PredicateBuilder().getEntryObject(); Predicate predicate = entryObject.get("id").lessThan(5); Map<Integer, Object> res = map.executeOnEntries(entryProcessor, predicate); for (int i = 0; i < 5; i++) { assertEquals(SampleObjects.State.STATE2, map.get(i).getState()); } for (int i = 5; i < size; i++) { assertEquals(SampleObjects.State.STATE1, map.get(i).getState()); } for (int i = 0; i < 5; i++) { assertEquals(((Employee) res.get(i)).getState(), SampleObjects.State.STATE2); } } finally { instance1.shutdown(); instance2.shutdown(); } } private static class ChangeStateEntryProcessor implements EntryProcessor<Integer, Employee>, EntryBackupProcessor<Integer, Employee> { ChangeStateEntryProcessor() { } @Override public Object process(Map.Entry<Integer, Employee> entry) { Employee value = entry.getValue(); value.setState(SampleObjects.State.STATE2); entry.setValue(value); return value; } @Override public EntryBackupProcessor<Integer, Employee> getBackupProcessor() { return ChangeStateEntryProcessor.this; } @Override public void processBackup(Map.Entry<Integer, Employee> entry) { Employee value = entry.getValue(); value.setState(SampleObjects.State.STATE2); entry.setValue(value); } } @Test public void testBackups() { TestHazelcastInstanceFactory nodeFactory = createHazelcastInstanceFactory(3); Config cfg = getConfig(); HazelcastInstance instance1 = nodeFactory.newHazelcastInstance(cfg); HazelcastInstance instance2 = nodeFactory.newHazelcastInstance(cfg); HazelcastInstance instance3 = nodeFactory.newHazelcastInstance(cfg); IMap<Integer, Integer> map = instance1.getMap(MAP_NAME); for (int i = 0; i < 1000; i++) { map.put(i, i); } EntryProcessor entryProcessor = new IncrementorEntryProcessor(); for (int i = 0; i < 1000; i++) { map.executeOnKey(i, entryProcessor); } instance1.shutdown(); waitAllForSafeState(instance2, instance3); IMap<Integer, Integer> map3 = instance3.getMap(MAP_NAME); for (int i = 0; i < 1000; i++) { assertEquals((Object) (i + 1), map3.get(i)); } } @Test public void testIssue825MapEntryProcessorDeleteSettingNull() { TestHazelcastInstanceFactory nodeFactory = createHazelcastInstanceFactory(2); Config cfg = getConfig(); HazelcastInstance instance1 = nodeFactory.newHazelcastInstance(cfg); nodeFactory.newHazelcastInstance(cfg); IMap<Integer, Integer> map = instance1.getMap(MAP_NAME); map.put(1, -1); map.put(2, -1); map.put(3, 1); EntryProcessor entryProcessor = new IncrementorEntryProcessor(); map.executeOnKey(2, entryProcessor); map.executeOnEntries(entryProcessor); assertNull(map.get(1)); assertNull(map.get(2)); assertEquals(1, map.size()); } @Test public void testMapEntryProcessorEntryListeners() throws Exception { TestHazelcastInstanceFactory nodeFactory = createHazelcastInstanceFactory(3); Config cfg = getConfig(); HazelcastInstance instance1 = nodeFactory.newHazelcastInstance(cfg); nodeFactory.newHazelcastInstance(cfg); nodeFactory.newHazelcastInstance(cfg); IMap<Integer, Integer> map = instance1.getMap(MAP_NAME); final AtomicInteger addCount = new AtomicInteger(0); final AtomicInteger updateCount = new AtomicInteger(0); final AtomicInteger removeCount = new AtomicInteger(0); final AtomicInteger addKey1Sum = new AtomicInteger(0); final AtomicInteger updateKey1Sum = new AtomicInteger(0); final AtomicInteger removeKey1Sum = new AtomicInteger(0); final CountDownLatch latch = new CountDownLatch(6); map.addEntryListener(new EntryAddedListener<Integer, Integer>() { @Override public void entryAdded(EntryEvent<Integer, Integer> event) { addCount.incrementAndGet(); if (event.getKey() == 1) { addKey1Sum.addAndGet(event.getValue()); } latch.countDown(); } }, true); map.addEntryListener(new EntryRemovedListener<Integer, Integer>() { @Override public void entryRemoved(EntryEvent<Integer, Integer> event) { removeCount.incrementAndGet(); if (event.getKey() == 1) { removeKey1Sum.addAndGet(event.getOldValue()); } latch.countDown(); } }, true); map.addEntryListener(new EntryUpdatedListener<Integer, Integer>() { @Override public void entryUpdated(EntryEvent<Integer, Integer> event) { updateCount.incrementAndGet(); if (event.getKey() == 1) { updateKey1Sum.addAndGet(event.getValue()); } latch.countDown(); } }, true); map.executeOnKey(1, new ValueSetterEntryProcessor(5)); map.executeOnKey(2, new ValueSetterEntryProcessor(7)); map.executeOnKey(2, new ValueSetterEntryProcessor(1)); map.executeOnKey(1, new ValueSetterEntryProcessor(3)); map.executeOnKey(1, new ValueSetterEntryProcessor(1)); map.executeOnKey(1, new ValueSetterEntryProcessor(null)); assertEquals((Integer) 1, map.get(2)); assertNull(map.get(1)); assertTrue(latch.await(100, TimeUnit.SECONDS)); assertEquals(2, addCount.get()); assertEquals(3, updateCount.get()); assertEquals(1, removeCount.get()); assertEquals(5, addKey1Sum.get()); assertEquals(4, updateKey1Sum.get()); assertEquals(1, removeKey1Sum.get()); } private static class ValueSetterEntryProcessor extends AbstractEntryProcessor<Integer, Integer> { Integer value; ValueSetterEntryProcessor(Integer v) { this.value = v; } @Override public Object process(Map.Entry<Integer, Integer> entry) { entry.setValue(value); return value; } } @Test public void testIssue969() throws Exception { TestHazelcastInstanceFactory nodeFactory = createHazelcastInstanceFactory(3); Config cfg = getConfig(); HazelcastInstance instance1 = nodeFactory.newHazelcastInstance(cfg); IMap<Integer, Integer> map = instance1.getMap(MAP_NAME); final AtomicInteger addCount = new AtomicInteger(0); final AtomicInteger updateCount = new AtomicInteger(0); final AtomicInteger removeCount = new AtomicInteger(0); final CountDownLatch latch = new CountDownLatch(3); map.addEntryListener(new EntryAddedListener<Integer, Integer>() { @Override public void entryAdded(EntryEvent<Integer, Integer> event) { addCount.incrementAndGet(); latch.countDown(); } }, true); map.addEntryListener(new EntryRemovedListener<Integer, Integer>() { @Override public void entryRemoved(EntryEvent<Integer, Integer> event) { removeCount.incrementAndGet(); latch.countDown(); } }, true); map.addEntryListener(new EntryUpdatedListener<Integer, Integer>() { @Override public void entryUpdated(EntryEvent<Integer, Integer> event) { updateCount.incrementAndGet(); latch.countDown(); } }, true); map.executeOnKey(1, new ValueReaderEntryProcessor()); assertNull(map.get(1)); map.executeOnKey(1, new ValueReaderEntryProcessor()); map.put(1, 3); assertNotNull(map.get(1)); map.executeOnKey(1, new ValueReaderEntryProcessor()); map.put(2, 2); ValueReaderEntryProcessor valueReaderEntryProcessor = new ValueReaderEntryProcessor(); map.executeOnKey(2, valueReaderEntryProcessor); assertEquals(2, valueReaderEntryProcessor.getValue().intValue()); map.put(2, 5); map.executeOnKey(2, valueReaderEntryProcessor); assertEquals(5, valueReaderEntryProcessor.getValue().intValue()); assertTrue(latch.await(1, TimeUnit.MINUTES)); assertEquals(2, addCount.get()); assertEquals(0, removeCount.get()); assertEquals(1, updateCount.get()); } private static class ValueReaderEntryProcessor extends AbstractEntryProcessor<Integer, Integer> { Integer value; ValueReaderEntryProcessor() { } @Override public Integer process(Map.Entry<Integer, Integer> entry) { value = entry.getValue(); return value; } public Integer getValue() { return value; } } @Test public void testIssue969MapEntryProcessorAllKeys() throws Exception { TestHazelcastInstanceFactory nodeFactory = createHazelcastInstanceFactory(2); Config cfg = getConfig(); HazelcastInstance instance1 = nodeFactory.newHazelcastInstance(cfg); HazelcastInstance instance2 = nodeFactory.newHazelcastInstance(cfg); try { IMap<Integer, Integer> map = instance1.getMap(MAP_NAME); final AtomicInteger addCount = new AtomicInteger(0); final AtomicInteger updateCount = new AtomicInteger(0); final AtomicInteger removeCount = new AtomicInteger(0); final CountDownLatch latch = new CountDownLatch(300); map.addEntryListener(new EntryAddedListener<Integer, Integer>() { @Override public void entryAdded(EntryEvent<Integer, Integer> event) { addCount.incrementAndGet(); latch.countDown(); } }, true); map.addEntryListener(new EntryRemovedListener<Integer, Integer>() { @Override public void entryRemoved(EntryEvent<Integer, Integer> event) { removeCount.incrementAndGet(); latch.countDown(); } }, true); map.addEntryListener(new EntryUpdatedListener<Integer, Integer>() { @Override public void entryUpdated(EntryEvent<Integer, Integer> event) { updateCount.incrementAndGet(); latch.countDown(); } }, true); int size = 100; for (int i = 0; i < size; i++) { map.put(i, i); } EntryProcessor entryProcessor = new IncrementorEntryProcessor(); Map<Integer, Object> res = map.executeOnEntries(entryProcessor); for (int i = 0; i < size; i++) { assertEquals(map.get(i), (Object) (i + 1)); } for (int i = 0; i < size; i++) { assertEquals(map.get(i), res.get(i)); } RemoveEntryProcessor removeEntryProcessor = new RemoveEntryProcessor(); map.executeOnEntries(removeEntryProcessor); assertEquals(0, map.size()); assertTrue(latch.await(100, TimeUnit.SECONDS)); assertEquals(100, addCount.get()); assertEquals(100, removeCount.get()); assertEquals(100, updateCount.get()); } finally { instance1.shutdown(); instance2.shutdown(); } } private static class RemoveEntryProcessor extends AbstractEntryProcessor<Integer, Integer> { RemoveEntryProcessor() { } @Override public Object process(Map.Entry<Integer, Integer> entry) { entry.setValue(null); return null; } } @Test public void testMapEntryProcessorPartitionAware() { String mapName1 = "default"; String mapName2 = "default-2"; TestHazelcastInstanceFactory nodeFactory = createHazelcastInstanceFactory(2); Config cfg = getConfig(); HazelcastInstance instance1 = nodeFactory.newHazelcastInstance(cfg); HazelcastInstance instance2 = nodeFactory.newHazelcastInstance(cfg); IMap<Integer, Integer> map = instance1.getMap(mapName1); map.put(1, 1); EntryProcessor entryProcessor = new PartitionAwareTestEntryProcessor(mapName2); assertNull(map.executeOnKey(1, entryProcessor)); assertEquals(1, instance2.getMap(mapName2).get(1)); } private static class PartitionAwareTestEntryProcessor implements EntryProcessor<Object, Object>, HazelcastInstanceAware { private String name; private transient HazelcastInstance hz; private PartitionAwareTestEntryProcessor(String name) { this.name = name; } @Override public Object process(Map.Entry<Object, Object> entry) { hz.getMap(name).put(entry.getKey(), entry.getValue()); return null; } @Override public EntryBackupProcessor<Object, Object> getBackupProcessor() { return null; } @Override public void setHazelcastInstance(HazelcastInstance hazelcastInstance) { this.hz = hazelcastInstance; } } @Test public void testInstanceAwareness_onOwnerAndBackup() { Config cfg = getConfig(); cfg.getMapConfig(MAP_NAME).setReadBackupData(true); TestHazelcastInstanceFactory nodeFactory = createHazelcastInstanceFactory(2); HazelcastInstance instance1 = nodeFactory.newHazelcastInstance(cfg); HazelcastInstance instance2 = nodeFactory.newHazelcastInstance(cfg); IMap<String, String> map1 = instance1.getMap(MAP_NAME); IMap<String, String> map2 = instance2.getMap(MAP_NAME); String keyOnInstance1 = generateKeyNotOwnedBy(instance1); map1.executeOnKey(keyOnInstance1, new UuidSetterEntryProcessor()); assertEquals(instance1.getCluster().getLocalMember().getUuid(), map1.get(keyOnInstance1)); assertEquals(instance2.getCluster().getLocalMember().getUuid(), map2.get(keyOnInstance1)); } private static class UuidSetterEntryProcessor implements EntryProcessor<String, String>, EntryBackupProcessor<String, String>, HazelcastInstanceAware { private transient HazelcastInstance hz; private UuidSetterEntryProcessor() { } @Override public Object process(Map.Entry<String, String> entry) { String uuid = hz.getCluster().getLocalMember().getUuid(); return entry.setValue(uuid); } @Override public EntryBackupProcessor<String, String> getBackupProcessor() { return this; } @Override public void processBackup(Map.Entry<String, String> entry) { process(entry); } @Override public void setHazelcastInstance(HazelcastInstance hazelcastInstance) { this.hz = hazelcastInstance; } } @Test public void testIssue1022() { TestHazelcastInstanceFactory nodeFactory = createHazelcastInstanceFactory(2); Config cfg = getConfig(); MapStoreConfig mapStoreConfig = new MapStoreConfig(); mapStoreConfig.setEnabled(true); mapStoreConfig.setImplementation(new MapLoader<Integer, Integer>() { public Integer load(Integer key) { return 123; } public Map<Integer, Integer> loadAll(Collection<Integer> keys) { return null; } public Set<Integer> loadAllKeys() { return null; } }); cfg.getMapConfig(MAP_NAME).setMapStoreConfig(mapStoreConfig); HazelcastInstance instance = nodeFactory.newHazelcastInstance(cfg); EntryProcessor entryProcessor = new IncrementorEntryProcessor(); instance.getMap(MAP_NAME).executeOnKey(1, entryProcessor); assertEquals(124, instance.getMap(MAP_NAME).get(1)); instance.shutdown(); } @Test public void testIssue7631_emptyKeysSupported() { TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(); IMap<Object, Object> map = factory.newHazelcastInstance().getMap(MAP_NAME); assertEquals(emptyMap(), map.executeOnEntries(new NoOpEntryProcessor())); } private static class NoOpEntryProcessor implements EntryProcessor<Object, Object> { @Override public Object process(final Map.Entry entry) { return null; } @Override public EntryBackupProcessor<Object, Object> getBackupProcessor() { return null; } } @Test public void testSubmitToKey() throws Exception { HazelcastInstance instance1 = createHazelcastInstance(getConfig()); IMap<Integer, Integer> map = instance1.getMap(MAP_NAME); map.put(1, 1); Future future = map.submitToKey(1, new IncrementorEntryProcessor()); assertEquals(2, future.get()); assertEquals(2, (int) map.get(1)); } @Test public void testSubmitToNonExistentKey() throws Exception { HazelcastInstance instance1 = createHazelcastInstance(getConfig()); IMap<Integer, Integer> map = instance1.getMap(MAP_NAME); Future future = map.submitToKey(11, new IncrementorEntryProcessor()); assertEquals(1, future.get()); assertEquals(1, (int) map.get(11)); } @Test public void testSubmitToKeyWithCallback() throws Exception { HazelcastInstance instance1 = createHazelcastInstance(getConfig()); IMap<Integer, Integer> map = instance1.getMap(MAP_NAME); map.put(1, 1); final CountDownLatch latch = new CountDownLatch(1); ExecutionCallback executionCallback = new ExecutionCallback() { @Override public void onResponse(Object response) { latch.countDown(); } @Override public void onFailure(Throwable t) { } }; map.submitToKey(1, new IncrementorEntryProcessor(), executionCallback); assertTrue(latch.await(5, TimeUnit.SECONDS)); assertEquals(2, (int) map.get(1)); } @Test public void testExecuteOnKeys() { Config config = getConfig(); TestHazelcastInstanceFactory nodeFactory = createHazelcastInstanceFactory(2); HazelcastInstance instance1 = nodeFactory.newHazelcastInstance(config); HazelcastInstance instance2 = nodeFactory.newHazelcastInstance(config); IMap<Integer, Integer> map = instance1.getMap(MAP_NAME); IMap<Integer, Integer> map2 = instance2.getMap(MAP_NAME); for (int i = 0; i < 10; i++) { map.put(i, 0); } Set<Integer> keys = new HashSet<Integer>(); keys.add(1); keys.add(4); keys.add(7); keys.add(9); Map<Integer, Object> resultMap = map2.executeOnKeys(keys, new IncrementorEntryProcessor()); assertEquals(1, resultMap.get(1)); assertEquals(1, resultMap.get(4)); assertEquals(1, resultMap.get(7)); assertEquals(1, resultMap.get(9)); assertEquals(1, (int) map.get(1)); assertEquals(0, (int) map.get(2)); assertEquals(0, (int) map.get(3)); assertEquals(1, (int) map.get(4)); assertEquals(0, (int) map.get(5)); assertEquals(0, (int) map.get(6)); assertEquals(1, (int) map.get(7)); assertEquals(0, (int) map.get(8)); assertEquals(1, (int) map.get(9)); } /** * Expected serialization count is 0 in Object format when there is no registered event listener. * If there is an event listener serialization count should be 1. */ @Test public void testEntryProcessorSerializationCountWithObjectFormat() { // EntryProcessor contract difference between OBJECT and BINARY int expectedSerializationCount = inMemoryFormat == OBJECT ? 0 : 1; Config cfg = getConfig(); TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(1); HazelcastInstance instance = factory.newHazelcastInstance(cfg); IMap<String, MyObject> map = instance.getMap(MAP_NAME); map.executeOnKey("key", new StoreOperation()); Integer serialized = (Integer) map.executeOnKey("key", new FetchSerializedCount()); assertEquals(expectedSerializationCount, serialized.intValue()); instance.shutdown(); } @Test public void testEntryProcessorNoDeserializationWithObjectFormat() { // EntryProcessor contract difference between OBJECT and BINARY int expectedDeserializationCount = inMemoryFormat == OBJECT ? 0 : 1; Config cfg = getConfig(); TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(1); HazelcastInstance instance = factory.newHazelcastInstance(cfg); IMap<String, MyObject> map = instance.getMap(MAP_NAME); map.executeOnKey("key", new StoreOperation()); Integer serialized = (Integer) map.executeOnKey("key", new FetchDeSerializedCount()); assertEquals(expectedDeserializationCount, serialized.intValue()); instance.shutdown(); } private static class MyObject implements DataSerializable { int serializedCount = 0; int deserializedCount = 0; MyObject() { } @Override public void writeData(ObjectDataOutput out) throws IOException { out.writeInt(++serializedCount); out.writeInt(deserializedCount); } @Override public void readData(ObjectDataInput in) throws IOException { serializedCount = in.readInt(); deserializedCount = in.readInt() + 1; } } private static class StoreOperation implements EntryProcessor<Object, MyObject> { @Override public Object process(Map.Entry<Object, MyObject> entry) { MyObject myObject = new MyObject(); entry.setValue(myObject); return 1; } @Override public EntryBackupProcessor<Object, MyObject> getBackupProcessor() { return null; } } private static class FetchSerializedCount implements EntryProcessor<String, MyObject> { @Override public Object process(Map.Entry<String, MyObject> entry) { return entry.getValue().serializedCount; } @Override public EntryBackupProcessor<String, MyObject> getBackupProcessor() { return null; } } private static class FetchDeSerializedCount implements EntryProcessor<String, MyObject> { @Override public Object process(Map.Entry<String, MyObject> entry) { return entry.getValue().deserializedCount; } @Override public EntryBackupProcessor<String, MyObject> getBackupProcessor() { return null; } } @Test public void executionOrderTest() { Config cfg = getConfig(); TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(1); HazelcastInstance instance1 = factory.newHazelcastInstance(cfg); final int maxTasks = 20; final Object key = "key"; final IMap<Object, List<Integer>> processorMap = instance1.getMap(MAP_NAME); processorMap.put(key, new ArrayList<Integer>()); for (int i = 0; i < maxTasks; i++) { processorMap.submitToKey(key, new SimpleEntryProcessor(i)); } List<Integer> expectedOrder = new ArrayList<Integer>(); for (int i = 0; i < maxTasks; i++) { expectedOrder.add(i); } assertTrueEventually(new AssertTask() { public void run() throws Exception { List<Integer> actualOrder = processorMap.get(key); assertEquals("failed to execute all entry processor tasks", maxTasks, actualOrder.size()); } }); List<Integer> actualOrder = processorMap.get(key); assertEquals("entry processor tasks executed in unexpected order", expectedOrder, actualOrder); } private static class SimpleEntryProcessor implements DataSerializable, EntryProcessor<Object, List<Integer>>, EntryBackupProcessor<Object, List<Integer>> { private Integer id; SimpleEntryProcessor() { } SimpleEntryProcessor(Integer id) { this.id = id; } @Override public Object process(Map.Entry<Object, List<Integer>> entry) { List<Integer> list = entry.getValue(); list.add(id); entry.setValue(list); return id; } @Override public void processBackup(Map.Entry<Object, List<Integer>> entry) { process(entry); } @Override public void writeData(ObjectDataOutput out) throws IOException { out.writeObject(id); } @Override public void readData(ObjectDataInput in) throws IOException { id = in.readObject(); } @Override public EntryBackupProcessor<Object, List<Integer>> getBackupProcessor() { return this; } } @Test public void test_executeOnEntriesWithPredicate_withIndexes() { Config config = getConfig(); TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(); HazelcastInstance node = factory.newHazelcastInstance(config); factory.newHazelcastInstance(config); factory.newHazelcastInstance(config); IMap<Integer, Integer> map = node.getMap(MAP_NAME); map.addIndex("__key", true); for (int i = 0; i < 1000; i++) { map.put(i, i); } map.executeOnEntries(new DeleteEntryProcessor(), new SqlPredicate("__key >=0")); assertSizeEventually(0, map); } /** * In this test, map is cleared via entry processor. Entries to be cleared is found by a predicate. * That predicate uses indexes on a value attribute to find eligible entries. */ @Test public void entry_processor_with_predicate_clears_map_when_value_attributes_are_indexed() { Config config = getConfig(); TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(); HazelcastInstance node = factory.newHazelcastInstance(config); factory.newHazelcastInstance(config); factory.newHazelcastInstance(config); IMap<Integer, SampleObjects.ObjectWithInteger> map = node.getMap(MAP_NAME); map.addIndex("attribute", true); for (int i = 0; i < 1000; i++) { map.put(i, new SampleObjects.ObjectWithInteger(i)); } map.executeOnEntries(new DeleteEntryProcessor(), new SqlPredicate("attribute >=0")); assertSizeEventually(0, map); } @Test public void test_executeOnEntriesWithPredicate_usesIndexes_whenIndexesAvailable() { HazelcastInstance node = createHazelcastInstance(getConfig()); IMap<Integer, Integer> map = node.getMap(MAP_NAME); map.addIndex("__key", true); for (int i = 0; i < 10; i++) { map.put(i, i); } AtomicBoolean indexCalled = new AtomicBoolean(false); map.executeOnEntries(new AbstractEntryProcessor() { @Override public Object process(Map.Entry entry) { return null; } }, new IndexedTestPredicate(indexCalled)); assertTrue("isIndexed method of IndexAwarePredicate should be called", indexCalled.get()); } @Test public void test_executeOnEntriesWithPredicate_notTriesToUseIndexes_whenNoIndexAvailable() { HazelcastInstance node = createHazelcastInstance(getConfig()); IMap<Integer, Integer> map = node.getMap(MAP_NAME); for (int i = 0; i < 10; i++) { map.put(i, i); } AtomicBoolean indexCalled = new AtomicBoolean(false); map.executeOnEntries(new AbstractEntryProcessor() { @Override public Object process(Map.Entry entry) { return null; } }, new IndexedTestPredicate(indexCalled)); assertFalse("isIndexed method of IndexAwarePredicate should not be called", indexCalled.get()); } @Test public void test_executeOnEntriesWithPredicate_runsOnBackup_whenIndexesAvailable() { TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(); Config config = getConfig(); HazelcastInstance instance1 = factory.newHazelcastInstance(config); factory.newHazelcastInstance(config); final IMap<Integer, Integer> map = instance1.getMap(MAP_NAME); map.addIndex("__key", true); AssertTask task = new AssertTask() { @Override public void run() throws Exception { try { map.set(1, 1); ApplyCountAwareIndexedTestPredicate predicate = new ApplyCountAwareIndexedTestPredicate("__key", 1); map.executeOnEntries(new DeleteEntryProcessor(), predicate); assertEquals("Expecting two predicate#apply method call one on owner, other one on backup", 2, PREDICATE_APPLY_COUNT.get()); } finally { // set predicateApplyCount to zero, in case we repeat this test PREDICATE_APPLY_COUNT.set(0); } } }; assertTrueEventually(task); } static class ApplyCountAwareIndexedTestPredicate implements IndexAwarePredicate { static final AtomicInteger PREDICATE_APPLY_COUNT = new AtomicInteger(0); private Comparable key; private String attributeName; ApplyCountAwareIndexedTestPredicate() { } ApplyCountAwareIndexedTestPredicate(String attributeName, Comparable key) { this.key = key; this.attributeName = attributeName; } @Override public Set<QueryableEntry> filter(QueryContext queryContext) { Index index = queryContext.getIndex(attributeName); return index.getRecords(key); } @Override public boolean isIndexed(QueryContext queryContext) { return true; } @Override public boolean apply(Map.Entry mapEntry) { PREDICATE_APPLY_COUNT.incrementAndGet(); return true; } } @Test public void test_entryProcessorRuns_onAsyncBackup() { Config config = getConfig(); config.getMapConfig(MAP_NAME).setBackupCount(0).setAsyncBackupCount(1); TestHazelcastInstanceFactory factory = createHazelcastInstanceFactory(); final HazelcastInstance instance1 = factory.newHazelcastInstance(config); final HazelcastInstance instance2 = factory.newHazelcastInstance(config); IMap<Integer, Integer> map = instance1.getMap(MAP_NAME); map.executeOnKey(1, new EntryCreate()); AssertTask task = new AssertTask() { @Override public void run() throws Exception { long entryCountOnNode1 = getTotalOwnedAndBackupEntryCount(instance1.getMap(MAP_NAME)); long entryCountOnNode2 = getTotalOwnedAndBackupEntryCount(instance2.getMap(MAP_NAME)); assertEquals("EntryProcess should run on async backup and should create entry there", entryCountOnNode1, entryCountOnNode2); } }; assertTrueEventually(task); } @Test public void receivesEntryRemovedEvent_onPostProcessingMapStore_after_executeOnKey() throws Exception { Config config = getConfig(); config.getMapConfig(MAP_NAME) .getMapStoreConfig().setEnabled(true).setImplementation(new TestPostProcessingMapStore()); HazelcastInstance node = createHazelcastInstance(config); IMap<Integer, Integer> map = node.getMap(MAP_NAME); final CountDownLatch latch = new CountDownLatch(1); map.addEntryListener(new EntryRemovedListener<Integer, Integer>() { @Override public void entryRemoved(EntryEvent<Integer, Integer> event) { latch.countDown(); } }, true); map.put(1, 1); map.executeOnKey(1, new AbstractEntryProcessor<Integer, Integer>() { @Override public Integer process(Map.Entry<Integer, Integer> entry) { entry.setValue(null); return null; } }); assertOpenEventually(latch); } @Test public void receivesEntryRemovedEvent_onPostProcessingMapStore_after_executeOnEntries() throws Exception { Config config = getConfig(); config.getMapConfig(MAP_NAME) .getMapStoreConfig().setEnabled(true).setImplementation(new TestPostProcessingMapStore()); HazelcastInstance node = createHazelcastInstance(config); IMap<Integer, Integer> map = node.getMap(MAP_NAME); final CountDownLatch latch = new CountDownLatch(1); map.addEntryListener(new EntryRemovedListener<Integer, Integer>() { @Override public void entryRemoved(EntryEvent<Integer, Integer> event) { latch.countDown(); } }, true); map.put(1, 1); map.executeOnEntries(new AbstractEntryProcessor<Integer, Integer>() { @Override public Integer process(Map.Entry<Integer, Integer> entry) { entry.setValue(null); return null; } }); assertOpenEventually(latch); } private static class TestPostProcessingMapStore extends MapStoreAdapter implements PostProcessingMapStore { } private static long getTotalOwnedAndBackupEntryCount(IMap map) { LocalMapStats localMapStats = map.getLocalMapStats(); return localMapStats.getOwnedEntryCount() + localMapStats.getBackupEntryCount(); } private static class IncrementorEntryProcessor extends AbstractEntryProcessor<Integer, Integer> implements DataSerializable { IncrementorEntryProcessor() { super(true); } @Override public Object process(Map.Entry<Integer, Integer> entry) { Integer value = entry.getValue(); if (value == null) { value = 0; } if (value == -1) { entry.setValue(null); return null; } value++; entry.setValue(value); return value; } @Override public void writeData(ObjectDataOutput out) throws IOException { } @Override public void readData(ObjectDataInput in) throws IOException { } } private static class DeleteEntryProcessor extends AbstractEntryProcessor<Integer, Integer> { @Override public Object process(Map.Entry<Integer, Integer> entry) { entry.setValue(null); return null; } } /** * This predicate is used to check whether or not {@link IndexAwarePredicate#isIndexed} method is called. */ private static class IndexedTestPredicate implements IndexAwarePredicate { private final AtomicBoolean indexCalled; IndexedTestPredicate(AtomicBoolean indexCalled) { this.indexCalled = indexCalled; } @Override public Set<QueryableEntry> filter(QueryContext queryContext) { return null; } @Override public boolean isIndexed(QueryContext queryContext) { indexCalled.set(true); return true; } @Override public boolean apply(Map.Entry mapEntry) { return false; } } private static class EntryInc extends AbstractEntryProcessor<String, SimpleValue> { @Override public Object process(final Map.Entry<String, SimpleValue> entry) { final SimpleValue value = entry.getValue(); value.i++; return null; } } private static class SimpleValue implements Serializable { public int i; SimpleValue() { } SimpleValue(final int i) { this.i = i; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } SimpleValue that = (SimpleValue) o; if (i != that.i) { return false; } return true; } @Override public String toString() { return "value: " + i; } } private static class EntryCreate extends AbstractEntryProcessor<String, Integer> { @Override public Object process(final Map.Entry<String, Integer> entry) { entry.setValue(6); return null; } } private IMap<Long, MyData> setupImapForEntryProcessorWithIndex() { Config config = getConfig(); MapConfig testMapConfig = config.getMapConfig(MAP_NAME); testMapConfig.setInMemoryFormat(inMemoryFormat); testMapConfig.getMapIndexConfigs().add(new MapIndexConfig("lastValue", true)); HazelcastInstance instance = createHazelcastInstance(config); return instance.getMap(MAP_NAME); } @Test public void issue9798_indexNotUpdatedWithObjectFormat_onKey() throws Exception { IMap<Long, MyData> testMap = setupImapForEntryProcessorWithIndex(); testMap.set(1L, new MyData(10)); testMap.executeOnKey(1L, new MyProcessor()); Predicate betweenPredicate = Predicates.between("lastValue", 0, 10); Collection<MyData> values = testMap.values(betweenPredicate); assertEquals(0, values.size()); assertEquals(11, testMap.get(1L).getLastValue()); } @Test public void issue9798_indexNotUpdatedWithObjectFormat_submitToKey() throws Exception { IMap<Long, MyData> testMap = setupImapForEntryProcessorWithIndex(); testMap.set(1L, new MyData(10)); testMap.submitToKey(1L, new MyProcessor()).get(); Predicate betweenPredicate = Predicates.between("lastValue", 0, 10); Collection<MyData> values = testMap.values(betweenPredicate); assertEquals(0, values.size()); assertEquals(11, testMap.get(1L).getLastValue()); } @Test public void issue9798_indexNotUpdatedWithObjectFormat_onKeys() throws Exception { IMap<Long, MyData> testMap = setupImapForEntryProcessorWithIndex(); testMap.set(1L, new MyData(10)); testMap.set(2L, new MyData(20)); testMap.executeOnKeys(new HashSet(asList(1L, 2L)), new MyProcessor()); Predicate betweenPredicate = Predicates.between("lastValue", 0, 10); Collection<MyData> values = testMap.values(betweenPredicate); assertEquals(0, values.size()); assertEquals(11, testMap.get(1L).getLastValue()); assertEquals(21, testMap.get(2L).getLastValue()); } @Test public void issue9798_indexNotUpdatedWithObjectFormat_onEntries() throws Exception { IMap<Long, MyData> testMap = setupImapForEntryProcessorWithIndex(); testMap.set(1L, new MyData(10)); testMap.set(2L, new MyData(20)); testMap.executeOnEntries(new MyProcessor()); Predicate betweenPredicate = Predicates.between("lastValue", 0, 10); Collection<MyData> values = testMap.values(betweenPredicate); assertEquals(0, values.size()); assertEquals(11, testMap.get(1L).getLastValue()); assertEquals(21, testMap.get(2L).getLastValue()); } @Test public void issue9798_indexNotUpdatedWithObjectFormat_onEntriesWithPredicate() throws Exception { IMap<Long, MyData> testMap = setupImapForEntryProcessorWithIndex(); testMap.set(1L, new MyData(10)); testMap.set(2L, new MyData(20)); Predicate betweenPredicate = Predicates.between("lastValue", 0, 10); testMap.executeOnEntries(new MyProcessor(), betweenPredicate); Collection<MyData> values = testMap.values(betweenPredicate); assertEquals(0, values.size()); assertEquals(11, testMap.get(1L).getLastValue()); assertEquals(20, testMap.get(2L).getLastValue()); } static class MyData implements Serializable { private long lastValue; public MyData(long lastValue) { this.lastValue = lastValue; } public long getLastValue() { return lastValue; } public void setLastValue(long lastValue) { this.lastValue = lastValue; } } static class MyProcessor extends AbstractEntryProcessor<Long, MyData> { public MyProcessor() { } @Override public Object process(Map.Entry<Long, MyData> entry) { MyData data = entry.getValue(); data.setLastValue(data.getLastValue() + 1); entry.setValue(data); return null; } } }