From a0b24f74a56adc13f986f0770633aacc776dac71 Mon Sep 17 00:00:00 2001 From: Robert von Burg Date: Tue, 19 May 2020 11:16:53 +0200 Subject: [PATCH] [Fix] Fixed concurrent modification exception in SynchronizedCollections --- .../collections/SynchronizedCollections.java | 146 ++++++++-------- .../SynchronizedMapOfListsTest.java | 160 +++++++++++++++++ .../SynchronizedMapOfMapsTest.java | 162 ++++++++++++++++++ .../SynchronizedMapOfSetsTest.java | 160 +++++++++++++++++ 4 files changed, 555 insertions(+), 73 deletions(-) create mode 100644 li.strolch.utils/src/test/java/li/strolch/utils/collections/SynchronizedMapOfListsTest.java create mode 100644 li.strolch.utils/src/test/java/li/strolch/utils/collections/SynchronizedMapOfMapsTest.java create mode 100644 li.strolch.utils/src/test/java/li/strolch/utils/collections/SynchronizedMapOfSetsTest.java diff --git a/li.strolch.utils/src/main/java/li/strolch/utils/collections/SynchronizedCollections.java b/li.strolch.utils/src/main/java/li/strolch/utils/collections/SynchronizedCollections.java index bb83ef860..acb4c4116 100644 --- a/li.strolch.utils/src/main/java/li/strolch/utils/collections/SynchronizedCollections.java +++ b/li.strolch.utils/src/main/java/li/strolch/utils/collections/SynchronizedCollections.java @@ -29,7 +29,7 @@ public class SynchronizedCollections { SynchronizedMapOfLists(MapOfLists m) { this.m = m; - this.mutex = new Object(); + this.mutex = this; } @Override @@ -174,7 +174,7 @@ public class SynchronizedCollections { SynchronizedMapOfMaps(MapOfMaps m) { this.m = m; - this.mutex = new Object(); + this.mutex = this; } @Override @@ -341,7 +341,7 @@ public class SynchronizedCollections { public SynchronizedMapOfSets(MapOfSets m) { this.m = m; - this.mutex = new Object(); + this.mutex = this; } @Override @@ -507,35 +507,35 @@ public class SynchronizedCollections { @Override public int size() { - synchronized (mutex) { + synchronized (this.mutex) { return c.size(); } } @Override public boolean isEmpty() { - synchronized (mutex) { + synchronized (this.mutex) { return c.isEmpty(); } } @Override public boolean contains(Object o) { - synchronized (mutex) { + synchronized (this.mutex) { return c.contains(o); } } @Override public Object[] toArray() { - synchronized (mutex) { + synchronized (this.mutex) { return c.toArray(); } } @Override public T[] toArray(T[] a) { - synchronized (mutex) { + synchronized (this.mutex) { return c.toArray(a); } } @@ -547,70 +547,70 @@ public class SynchronizedCollections { @Override public boolean add(E e) { - synchronized (mutex) { + synchronized (this.mutex) { return c.add(e); } } @Override public boolean remove(Object o) { - synchronized (mutex) { + synchronized (this.mutex) { return c.remove(o); } } @Override public boolean containsAll(Collection coll) { - synchronized (mutex) { + synchronized (this.mutex) { return c.containsAll(coll); } } @Override public boolean addAll(Collection coll) { - synchronized (mutex) { + synchronized (this.mutex) { return c.addAll(coll); } } @Override public boolean removeAll(Collection coll) { - synchronized (mutex) { + synchronized (this.mutex) { return c.removeAll(coll); } } @Override public boolean retainAll(Collection coll) { - synchronized (mutex) { + synchronized (this.mutex) { return c.retainAll(coll); } } @Override public void clear() { - synchronized (mutex) { + synchronized (this.mutex) { c.clear(); } } @Override public String toString() { - synchronized (mutex) { + synchronized (this.mutex) { return c.toString(); } } @Override public void forEach(Consumer consumer) { - synchronized (mutex) { + synchronized (this.mutex) { c.forEach(consumer); } } @Override public boolean removeIf(Predicate filter) { - synchronized (mutex) { + synchronized (this.mutex) { return c.removeIf(filter); } } @@ -631,7 +631,7 @@ public class SynchronizedCollections { } private void writeObject(ObjectOutputStream s) throws IOException { - synchronized (mutex) { + synchronized (this.mutex) { s.defaultWriteObject(); } } @@ -650,63 +650,63 @@ public class SynchronizedCollections { public boolean equals(Object o) { if (this == o) return true; - synchronized (mutex) { + synchronized (this.mutex) { return list.equals(o); } } @Override public int hashCode() { - synchronized (mutex) { + synchronized (this.mutex) { return list.hashCode(); } } @Override public E get(int index) { - synchronized (mutex) { + synchronized (this.mutex) { return list.get(index); } } @Override public E set(int index, E element) { - synchronized (mutex) { + synchronized (this.mutex) { return list.set(index, element); } } @Override public void add(int index, E element) { - synchronized (mutex) { + synchronized (this.mutex) { list.add(index, element); } } @Override public E remove(int index) { - synchronized (mutex) { + synchronized (this.mutex) { return list.remove(index); } } @Override public int indexOf(Object o) { - synchronized (mutex) { + synchronized (this.mutex) { return list.indexOf(o); } } @Override public int lastIndexOf(Object o) { - synchronized (mutex) { + synchronized (this.mutex) { return list.lastIndexOf(o); } } @Override public boolean addAll(int index, Collection c) { - synchronized (mutex) { + synchronized (this.mutex) { return list.addAll(index, c); } } @@ -723,21 +723,21 @@ public class SynchronizedCollections { @Override public List subList(int fromIndex, int toIndex) { - synchronized (mutex) { - return new SynchronizedList<>(list.subList(fromIndex, toIndex), mutex); + synchronized (this.mutex) { + return new SynchronizedList<>(list.subList(fromIndex, toIndex), this.mutex); } } @Override public void replaceAll(UnaryOperator operator) { - synchronized (mutex) { + synchronized (this.mutex) { list.replaceAll(operator); } } @Override public void sort(Comparator c) { - synchronized (mutex) { + synchronized (this.mutex) { list.sort(c); } } @@ -753,14 +753,14 @@ public class SynchronizedCollections { public boolean equals(Object o) { if (this == o) return true; - synchronized (mutex) { + synchronized (this.mutex) { return c.equals(o); } } @Override public int hashCode() { - synchronized (mutex) { + synchronized (this.mutex) { return c.hashCode(); } } @@ -777,42 +777,42 @@ public class SynchronizedCollections { @Override public Comparator comparator() { - synchronized (mutex) { + synchronized (this.mutex) { return ss.comparator(); } } @Override public SortedSet subSet(E fromElement, E toElement) { - synchronized (mutex) { - return new SynchronizedSortedSet<>(ss.subSet(fromElement, toElement), mutex); + synchronized (this.mutex) { + return new SynchronizedSortedSet<>(ss.subSet(fromElement, toElement), this.mutex); } } @Override public SortedSet headSet(E toElement) { - synchronized (mutex) { - return new SynchronizedSortedSet<>(ss.headSet(toElement), mutex); + synchronized (this.mutex) { + return new SynchronizedSortedSet<>(ss.headSet(toElement), this.mutex); } } @Override public SortedSet tailSet(E fromElement) { - synchronized (mutex) { - return new SynchronizedSortedSet<>(ss.tailSet(fromElement), mutex); + synchronized (this.mutex) { + return new SynchronizedSortedSet<>(ss.tailSet(fromElement), this.mutex); } } @Override public E first() { - synchronized (mutex) { + synchronized (this.mutex) { return ss.first(); } } @Override public E last() { - synchronized (mutex) { + synchronized (this.mutex) { return ss.last(); } } @@ -829,55 +829,55 @@ public class SynchronizedCollections { } public int size() { - synchronized (mutex) { + synchronized (this.mutex) { return m.size(); } } public boolean isEmpty() { - synchronized (mutex) { + synchronized (this.mutex) { return m.isEmpty(); } } public boolean containsKey(Object key) { - synchronized (mutex) { + synchronized (this.mutex) { return m.containsKey(key); } } public boolean containsValue(Object value) { - synchronized (mutex) { + synchronized (this.mutex) { return m.containsValue(value); } } public V get(Object key) { - synchronized (mutex) { + synchronized (this.mutex) { return m.get(key); } } public V put(K key, V value) { - synchronized (mutex) { + synchronized (this.mutex) { return m.put(key, value); } } public V remove(Object key) { - synchronized (mutex) { + synchronized (this.mutex) { return m.remove(key); } } public void putAll(Map map) { - synchronized (mutex) { + synchronized (this.mutex) { m.putAll(map); } } public void clear() { - synchronized (mutex) { + synchronized (this.mutex) { m.clear(); } } @@ -887,25 +887,25 @@ public class SynchronizedCollections { private transient Collection values; public Set keySet() { - synchronized (mutex) { + synchronized (this.mutex) { if (keySet == null) - keySet = new SynchronizedSet<>(m.keySet(), mutex); + keySet = new SynchronizedSet<>(m.keySet(), this.mutex); return keySet; } } public Set> entrySet() { - synchronized (mutex) { + synchronized (this.mutex) { if (entrySet == null) - entrySet = new SynchronizedSet<>(m.entrySet(), mutex); + entrySet = new SynchronizedSet<>(m.entrySet(), this.mutex); return entrySet; } } public Collection values() { - synchronized (mutex) { + synchronized (this.mutex) { if (values == null) - values = new SynchronizedCollection<>(m.values(), mutex); + values = new SynchronizedCollection<>(m.values(), this.mutex); return values; } } @@ -913,102 +913,102 @@ public class SynchronizedCollections { public boolean equals(Object o) { if (this == o) return true; - synchronized (mutex) { + synchronized (this.mutex) { return m.equals(o); } } public int hashCode() { - synchronized (mutex) { + synchronized (this.mutex) { return m.hashCode(); } } public String toString() { - synchronized (mutex) { + synchronized (this.mutex) { return m.toString(); } } @Override public V getOrDefault(Object k, V defaultValue) { - synchronized (mutex) { + synchronized (this.mutex) { return m.getOrDefault(k, defaultValue); } } @Override public void forEach(BiConsumer action) { - synchronized (mutex) { + synchronized (this.mutex) { m.forEach(action); } } @Override public void replaceAll(BiFunction function) { - synchronized (mutex) { + synchronized (this.mutex) { m.replaceAll(function); } } @Override public V putIfAbsent(K key, V value) { - synchronized (mutex) { + synchronized (this.mutex) { return m.putIfAbsent(key, value); } } @Override public boolean remove(Object key, Object value) { - synchronized (mutex) { + synchronized (this.mutex) { return m.remove(key, value); } } @Override public boolean replace(K key, V oldValue, V newValue) { - synchronized (mutex) { + synchronized (this.mutex) { return m.replace(key, oldValue, newValue); } } @Override public V replace(K key, V value) { - synchronized (mutex) { + synchronized (this.mutex) { return m.replace(key, value); } } @Override public V computeIfAbsent(K key, Function mappingFunction) { - synchronized (mutex) { + synchronized (this.mutex) { return m.computeIfAbsent(key, mappingFunction); } } @Override public V computeIfPresent(K key, BiFunction remappingFunction) { - synchronized (mutex) { + synchronized (this.mutex) { return m.computeIfPresent(key, remappingFunction); } } @Override public V compute(K key, BiFunction remappingFunction) { - synchronized (mutex) { + synchronized (this.mutex) { return m.compute(key, remappingFunction); } } @Override public V merge(K key, V value, BiFunction remappingFunction) { - synchronized (mutex) { + synchronized (this.mutex) { return m.merge(key, value, remappingFunction); } } private void writeObject(ObjectOutputStream s) throws IOException { - synchronized (mutex) { + synchronized (this.mutex) { s.defaultWriteObject(); } } diff --git a/li.strolch.utils/src/test/java/li/strolch/utils/collections/SynchronizedMapOfListsTest.java b/li.strolch.utils/src/test/java/li/strolch/utils/collections/SynchronizedMapOfListsTest.java new file mode 100644 index 000000000..b2b38b4aa --- /dev/null +++ b/li.strolch.utils/src/test/java/li/strolch/utils/collections/SynchronizedMapOfListsTest.java @@ -0,0 +1,160 @@ +package li.strolch.utils.collections; + +import static li.strolch.utils.collections.SynchronizedCollections.synchronizedMapOfLists; +import static org.junit.Assert.*; + +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.junit.Before; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SynchronizedMapOfListsTest { + + private static final Logger logger = LoggerFactory.getLogger(SynchronizedMapOfListsTest.class); + + private ExecutorService executorService; + + @Before + public void before() { + this.executorService = Executors.newCachedThreadPool(); + } + + @Test + public void shouldForEach() throws ExecutionException, InterruptedException { + + MapOfLists mapOfLists = buildMapOfLists(); + AtomicBoolean run = new AtomicBoolean(false); + Callable addTask = () -> addToMap(mapOfLists, run); + + Callable iterateTask = () -> { + for (; ; ) { + if (run.get()) + break; + } + + while (run.get()) { + mapOfLists.forEach((s, list) -> list.forEach(s1 -> logger.info(s + " " + s1))); + mapOfLists.getList("Resource").forEach(s1 -> logger.info(" " + s1)); + } + + return true; + }; + + runTest(addTask, iterateTask, run); + } + + @Test + public void shouldIterate() throws ExecutionException, InterruptedException { + + MapOfLists mapOfLists = buildMapOfLists(); + AtomicBoolean run = new AtomicBoolean(false); + Callable addTask = () -> addToMap(mapOfLists, run); + Callable iterateTask = () -> { + for (; ; ) { + if (run.get()) + break; + } + + while (run.get()) { + Set types = mapOfLists.keySet(); + for (String type : types) { + synchronized (mapOfLists) { + for (String id : mapOfLists.getList(type)) { + logger.info(type + " " + id); + } + } + } + + synchronized (mapOfLists) { + List resources = mapOfLists.getList("Resource"); + for (String value : resources) { + logger.info("Resource: value: " + value); + } + } + } + + return true; + }; + + runTest(addTask, iterateTask, run); + } + + private void runTest(Callable addTask, Callable iterateTask, AtomicBoolean run) + throws InterruptedException, ExecutionException { + + Future task0 = this.executorService.submit(addTask); + Future task1 = this.executorService.submit(iterateTask); + Future task2 = this.executorService.submit(iterateTask); + Future task3 = this.executorService.submit(iterateTask); + Future task4 = this.executorService.submit(iterateTask); + Future task5 = this.executorService.submit(iterateTask); + + run.set(true); + Thread.sleep(1000L); + run.set(false); + + Boolean result0 = task0.get(); + Boolean result1 = task1.get(); + Boolean result2 = task2.get(); + Boolean result3 = task3.get(); + Boolean result4 = task4.get(); + Boolean result5 = task5.get(); + + assertTrue(result0); + assertTrue(result1); + assertTrue(result2); + assertTrue(result3); + assertTrue(result4); + assertTrue(result5); + } + + private Boolean addToMap(MapOfLists mapOfLists, AtomicBoolean run) { + for (; ; ) { + if (run.get()) + break; + } + + while (run.get()) { + addElement(mapOfLists, "Resource", UUID.randomUUID().toString()); + addElement(mapOfLists, "Resource", UUID.randomUUID().toString()); + addElement(mapOfLists, "Order", UUID.randomUUID().toString()); + addElement(mapOfLists, "Order", UUID.randomUUID().toString()); + addElement(mapOfLists, "Order", UUID.randomUUID().toString()); + addElement(mapOfLists, "Activity", UUID.randomUUID().toString()); + addElement(mapOfLists, "Activity", UUID.randomUUID().toString()); + + mapOfLists.removeElement("Resource", "Ball"); + mapOfLists.removeElement("Order", "ToStock"); + mapOfLists.removeElement("Activity", "ToStock"); + } + + return true; + } + + private void addElement(MapOfLists mapOfLists, String type, String id) { + logger.info("Adding " + type + " " + id); + mapOfLists.addElement(type, id); + } + + private MapOfLists buildMapOfLists() { + MapOfLists mapOfLists = synchronizedMapOfLists(new MapOfLists<>(true)); + mapOfLists.addElement("Resource", "Ball"); + mapOfLists.addElement("Resource", "Car"); + mapOfLists.addElement("Order", "StockOrder"); + mapOfLists.addElement("Order", "ToStock"); + mapOfLists.addElement("Order", "FromStock"); + mapOfLists.addElement("Activity", "FromStock"); + mapOfLists.addElement("Activity", "ToStock"); + + assertEquals(Arrays.asList("Ball", "Car"), mapOfLists.getList("Resource")); + assertNull(mapOfLists.getList("xxx")); + return mapOfLists; + } +} diff --git a/li.strolch.utils/src/test/java/li/strolch/utils/collections/SynchronizedMapOfMapsTest.java b/li.strolch.utils/src/test/java/li/strolch/utils/collections/SynchronizedMapOfMapsTest.java new file mode 100644 index 000000000..a43877417 --- /dev/null +++ b/li.strolch.utils/src/test/java/li/strolch/utils/collections/SynchronizedMapOfMapsTest.java @@ -0,0 +1,162 @@ +package li.strolch.utils.collections; + +import static li.strolch.utils.collections.SynchronizedCollections.synchronizedMapOfMaps; +import static org.junit.Assert.*; + +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.junit.Before; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SynchronizedMapOfMapsTest { + + private static final Logger logger = LoggerFactory.getLogger(SynchronizedMapOfMapsTest.class); + + private ExecutorService executorService; + + @Before + public void before() { + this.executorService = Executors.newCachedThreadPool(); + } + + @Test + public void shouldForEach() throws ExecutionException, InterruptedException { + + MapOfMaps mapOfMaps = buildMapOfMaps(); + AtomicBoolean run = new AtomicBoolean(false); + Callable addTask = () -> addToMap(mapOfMaps, run); + + Callable iterateTask = () -> { + for (; ; ) { + if (run.get()) + break; + } + + while (run.get()) { + mapOfMaps.forEach( + (s, subTypeMap) -> subTypeMap.forEach((s1, s2) -> logger.info(s + " " + s1 + " " + s2))); + mapOfMaps.getMap("Resource").forEach((s1, s2) -> logger.info(" " + s1 + " " + s2)); + } + + return true; + }; + + runTest(addTask, iterateTask, run); + } + + @Test + public void shouldIterate() throws ExecutionException, InterruptedException { + + MapOfMaps mapOfMaps = buildMapOfMaps(); + AtomicBoolean run = new AtomicBoolean(false); + Callable addTask = () -> addToMap(mapOfMaps, run); + Callable iterateTask = () -> { + for (; ; ) { + if (run.get()) + break; + } + + while (run.get()) { + Set types = mapOfMaps.keySet(); + for (String type : types) { + Map subTypeMap = mapOfMaps.getMap(type); + Set subTypes = subTypeMap.keySet(); + synchronized (mapOfMaps) { + for (String subType : subTypes) { + logger.info(type + " " + subType + " " + mapOfMaps.getElement(type, subType)); + } + } + } + + synchronized (mapOfMaps) { + Map resources = mapOfMaps.getMap("Resource"); + for (String value : resources.values()) { + logger.info("Resource: value: " + value); + } + } + } + + return true; + }; + + runTest(addTask, iterateTask, run); + } + + private void runTest(Callable addTask, Callable iterateTask, AtomicBoolean run) + throws InterruptedException, ExecutionException { + + Future task0 = this.executorService.submit(addTask); + Future task1 = this.executorService.submit(iterateTask); + Future task2 = this.executorService.submit(iterateTask); + Future task3 = this.executorService.submit(iterateTask); + Future task4 = this.executorService.submit(iterateTask); + Future task5 = this.executorService.submit(iterateTask); + + run.set(true); + Thread.sleep(1000L); + run.set(false); + + Boolean result0 = task0.get(); + Boolean result1 = task1.get(); + Boolean result2 = task2.get(); + Boolean result3 = task3.get(); + Boolean result4 = task4.get(); + Boolean result5 = task5.get(); + + assertTrue(result0); + assertTrue(result1); + assertTrue(result2); + assertTrue(result3); + assertTrue(result4); + assertTrue(result5); + } + + private Boolean addToMap(MapOfMaps mapOfMaps, AtomicBoolean run) { + for (; ; ) { + if (run.get()) + break; + } + + while (run.get()) { + addElement(mapOfMaps, "Resource", "Ball", UUID.randomUUID().toString()); + addElement(mapOfMaps, "Resource", "Car", UUID.randomUUID().toString()); + addElement(mapOfMaps, "Order", "StockOrder", UUID.randomUUID().toString()); + addElement(mapOfMaps, "Order", "ToStock", UUID.randomUUID().toString()); + addElement(mapOfMaps, "Order", "FromStock", UUID.randomUUID().toString()); + addElement(mapOfMaps, "Activity", "FromStock", UUID.randomUUID().toString()); + addElement(mapOfMaps, "Activity", "ToStock", UUID.randomUUID().toString()); + + mapOfMaps.removeElement("Resource", "Ball"); + mapOfMaps.removeElement("Order", "ToStock"); + mapOfMaps.removeElement("Activity", "ToStock"); + } + + return true; + } + + private void addElement(MapOfMaps mapOfMaps, String type, String subType, String id) { + logger.info("Adding " + type + " " + subType + " " + id); + mapOfMaps.addElement(type, subType, id); + } + + private MapOfMaps buildMapOfMaps() { + MapOfMaps mapOfMaps = synchronizedMapOfMaps(new MapOfMaps<>(true)); + mapOfMaps.addElement("Resource", "Ball", "yellow"); + mapOfMaps.addElement("Resource", "Car", "car1"); + mapOfMaps.addElement("Order", "StockOrder", "stockOrder1"); + mapOfMaps.addElement("Order", "ToStock", "toStock1"); + mapOfMaps.addElement("Order", "FromStock", "fromStock1"); + mapOfMaps.addElement("Activity", "FromStock", "fromStock1"); + mapOfMaps.addElement("Activity", "ToStock", "toStock1"); + + assertEquals("yellow", mapOfMaps.getElement("Resource", "Ball")); + assertNull(mapOfMaps.getMap("xxx")); + return mapOfMaps; + } +} diff --git a/li.strolch.utils/src/test/java/li/strolch/utils/collections/SynchronizedMapOfSetsTest.java b/li.strolch.utils/src/test/java/li/strolch/utils/collections/SynchronizedMapOfSetsTest.java new file mode 100644 index 000000000..82f13de37 --- /dev/null +++ b/li.strolch.utils/src/test/java/li/strolch/utils/collections/SynchronizedMapOfSetsTest.java @@ -0,0 +1,160 @@ +package li.strolch.utils.collections; + +import static li.strolch.utils.collections.SynchronizedCollections.synchronizedMapOfSets; +import static org.junit.Assert.*; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.junit.Before; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SynchronizedMapOfSetsTest { + + private static final Logger logger = LoggerFactory.getLogger(SynchronizedMapOfSetsTest.class); + + private ExecutorService executorService; + + @Before + public void before() { + this.executorService = Executors.newCachedThreadPool(); + } + + @Test + public void shouldForEach() throws ExecutionException, InterruptedException { + + MapOfSets mapOfSets = buildMapOfSets(); + AtomicBoolean run = new AtomicBoolean(false); + Callable addTask = () -> addToMap(mapOfSets, run); + + Callable iterateTask = () -> { + for (; ; ) { + if (run.get()) + break; + } + + while (run.get()) { + mapOfSets.forEach((s, list) -> list.forEach(s1 -> logger.info(s + " " + s1))); + mapOfSets.getSet("Resource").forEach(s1 -> logger.info(" " + s1)); + } + + return true; + }; + + runTest(addTask, iterateTask, run); + } + + @Test + public void shouldIterate() throws ExecutionException, InterruptedException { + + MapOfSets mapOfSets = buildMapOfSets(); + AtomicBoolean run = new AtomicBoolean(false); + Callable addTask = () -> addToMap(mapOfSets, run); + Callable iterateTask = () -> { + for (; ; ) { + if (run.get()) + break; + } + + while (run.get()) { + Set types = mapOfSets.keySet(); + for (String type : types) { + synchronized (mapOfSets) { + for (String id : mapOfSets.getSet(type)) { + logger.info(type + " " + id); + } + } + } + + synchronized (mapOfSets) { + Set resources = mapOfSets.getSet("Resource"); + for (String value : resources) { + logger.info("Resource: value: " + value); + } + } + } + + return true; + }; + + runTest(addTask, iterateTask, run); + } + + private void runTest(Callable addTask, Callable iterateTask, AtomicBoolean run) + throws InterruptedException, ExecutionException { + + Future task0 = this.executorService.submit(addTask); + Future task1 = this.executorService.submit(iterateTask); + Future task2 = this.executorService.submit(iterateTask); + Future task3 = this.executorService.submit(iterateTask); + Future task4 = this.executorService.submit(iterateTask); + Future task5 = this.executorService.submit(iterateTask); + + run.set(true); + Thread.sleep(1000L); + run.set(false); + + Boolean result0 = task0.get(); + Boolean result1 = task1.get(); + Boolean result2 = task2.get(); + Boolean result3 = task3.get(); + Boolean result4 = task4.get(); + Boolean result5 = task5.get(); + + assertTrue(result0); + assertTrue(result1); + assertTrue(result2); + assertTrue(result3); + assertTrue(result4); + assertTrue(result5); + } + + private Boolean addToMap(MapOfSets mapOfSets, AtomicBoolean run) { + for (; ; ) { + if (run.get()) + break; + } + + while (run.get()) { + addElement(mapOfSets, "Resource", UUID.randomUUID().toString()); + addElement(mapOfSets, "Resource", UUID.randomUUID().toString()); + addElement(mapOfSets, "Order", UUID.randomUUID().toString()); + addElement(mapOfSets, "Order", UUID.randomUUID().toString()); + addElement(mapOfSets, "Order", UUID.randomUUID().toString()); + addElement(mapOfSets, "Activity", UUID.randomUUID().toString()); + addElement(mapOfSets, "Activity", UUID.randomUUID().toString()); + + mapOfSets.removeElement("Resource", "Ball"); + mapOfSets.removeElement("Order", "ToStock"); + mapOfSets.removeElement("Activity", "ToStock"); + } + + return true; + } + + private void addElement(MapOfSets mapOfSets, String type, String id) { + logger.info("Adding " + type + " " + id); + mapOfSets.addElement(type, id); + } + + private MapOfSets buildMapOfSets() { + MapOfSets mapOfSets = synchronizedMapOfSets(new MapOfSets<>(true)); + mapOfSets.addElement("Resource", "Ball"); + mapOfSets.addElement("Resource", "Car"); + mapOfSets.addElement("Order", "StockOrder"); + mapOfSets.addElement("Order", "ToStock"); + mapOfSets.addElement("Order", "FromStock"); + mapOfSets.addElement("Activity", "FromStock"); + mapOfSets.addElement("Activity", "ToStock"); + + assertEquals(new HashSet<>(Arrays.asList("Ball", "Car")), mapOfSets.getSet("Resource")); + assertNull(mapOfSets.getSet("xxx")); + return mapOfSets; + } +}