Author: asanso Date: Mon Jul 27 07:17:54 2015 New Revision: 1692825 URL: http://svn.apache.org/r1692825 Log: SLING-4903 - ConcurrentModificationException in MapEntries
Modified: sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/mapping/MapEntries.java sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/mapping/MapEntriesTest.java Modified: sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/mapping/MapEntries.java URL: http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/mapping/MapEntries.java?rev=1692825&r1=1692824&r2=1692825&view=diff ============================================================================== --- sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/mapping/MapEntries.java (original) +++ sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/mapping/MapEntries.java Mon Jul 27 07:17:54 2015 @@ -1020,17 +1020,25 @@ public class MapEntries implements Event * Add an entry to the resolve map. */ private boolean addEntry(final Map<String, List<MapEntry>> entryMap, final String key, final MapEntry entry) { + if (entry==null){ return false; } + List<MapEntry> entries = entryMap.get(key); if (entries == null) { entries = new ArrayList<MapEntry>(); + entries.add(entry); + // and finally sort list + Collections.sort(entries); entryMap.put(key, entries); + } else { + List<MapEntry> entriesCopy =new ArrayList<MapEntry>(entries); + entriesCopy.add(entry); + // and finally sort list + Collections.sort( entriesCopy); + entryMap.put(key, entriesCopy); } - entries.add(entry); - // and finally sort list - Collections.sort(entries); return true; } Modified: sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/mapping/MapEntriesTest.java URL: http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/mapping/MapEntriesTest.java?rev=1692825&r1=1692824&r2=1692825&view=diff ============================================================================== --- sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/mapping/MapEntriesTest.java (original) +++ sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/mapping/MapEntriesTest.java Mon Jul 27 07:17:54 2015 @@ -40,7 +40,12 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicLong; + import org.apache.sling.api.resource.Resource; import org.apache.sling.api.resource.ResourceResolver; import org.apache.sling.api.resource.ResourceUtil; @@ -1801,4 +1806,66 @@ public class MapEntriesTest { assertEquals(2, counter.longValue()); } + @Test + //SLING-4883 + public void test_concutrrent_getResolveMapsIterator() throws Exception { + ExecutorService pool = Executors.newFixedThreadPool(10); + + final Resource justVanityPath = mock(Resource.class, "justVanityPath"); + when(resourceResolver.getResource("/justVanityPath")).thenReturn(justVanityPath); + when(justVanityPath.getPath()).thenReturn("/justVanityPath"); + when(justVanityPath.getName()).thenReturn("justVanityPath"); + when(justVanityPath.adaptTo(ValueMap.class)).thenReturn(buildValueMap("sling:vanityPath", "/target/justVanityPath")); + + + when(resourceResolver.findResources(anyString(), eq("sql"))).thenAnswer(new Answer<Iterator<Resource>>() { + + public Iterator<Resource> answer(InvocationOnMock invocation) throws Throwable { + if (invocation.getArguments()[0].toString().contains("sling:vanityPath")) { + return Collections.singleton(justVanityPath).iterator(); + } else { + return Collections.<Resource> emptySet().iterator(); + } + } + }); + + + Field field1 = MapEntries.class.getDeclaredField("maxCachedVanityPathEntries"); + field1.setAccessible(true); + field1.set(mapEntries, 2); + + ArrayList<DataFuture> list = new ArrayList<DataFuture>(); + for (int i =0;i<10;i++) { + list.add(createDataFuture(pool, mapEntries)); + + } + + for (DataFuture df : list) { + df.future.get(); + } + + } + + // -------------------------- private methods ---------- + private DataFuture createDataFuture(ExecutorService pool, final MapEntries mapEntries) { + + Future<Iterator> future = pool.submit(new Callable<Iterator>() { + @Override + public Iterator call() throws Exception { + return mapEntries.getResolveMapsIterator("http/localhost.8080/target/justVanityPath"); + } + }); + return new DataFuture(future); + } + + // -------------------------- inner classes ------------ + + private static class DataFuture { + public Future<Iterator> future; + + public DataFuture(Future<Iterator> future) { + super(); + this.future = future; + } + } }