Jetty 9.4.x 2043 concurrentmodification annotation parsing (#2052)

* Issue #2043 Fix ConcurrentModificationException listing duplicate class scanning

Signed-off-by: Jan Bartel <janb@webtide.com>
This commit is contained in:
Jan Bartel 2017-12-14 09:45:29 +01:00 committed by GitHub
parent 8660055574
commit eebe139494
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 51 additions and 22 deletions

View File

@ -71,7 +71,10 @@ public class AnnotationParser
private static final Logger LOG = Log.getLogger(AnnotationParser.class);
protected static int ASM_OPCODE_VERSION = Opcodes.ASM6; //compatibility of api
protected Map<String, List<String>> _parsedClassNames = new ConcurrentHashMap<>();
/**
* Map of classnames scanned and the first location from which scan occurred
*/
protected Map<String, Resource> _parsedClassNames = new ConcurrentHashMap<>();
private final int _javaPlatform;
private int _asmVersion;
@ -561,17 +564,11 @@ public class AnnotationParser
*/
public void addParsedClass (String classname, Resource location)
{
List<String> list = new ArrayList<>(1);
if (location != null)
list.add(location.toString());
List<String> existing = _parsedClassNames.putIfAbsent(classname, list);
Resource existing = _parsedClassNames.putIfAbsent(classname, location);
if (existing != null)
{
existing.addAll(list);
LOG.warn("{} scanned from multiple locations: {}", classname, existing);
}
LOG.warn("{} scanned from multiple locations: {}, {}", classname, existing, location);
}
/**
* Get the locations of the given classname. There may be more than one
@ -579,13 +576,13 @@ public class AnnotationParser
*
* @param classname the name of the class
* @return an immutable list of locations
*
* @deprecated List of duplicate locations no longer stored
*/
@Deprecated
public List<String> getParsedLocations (String classname)
{
List<String> list = _parsedClassNames.get(classname);
if (list == null)
return Collections.emptyList();
return Collections.unmodifiableList(list);
return Collections.emptyList();
}
/**

View File

@ -35,7 +35,10 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import org.eclipse.jetty.annotations.AnnotationParser.ClassInfo;
import org.eclipse.jetty.annotations.AnnotationParser.FieldInfo;
@ -71,6 +74,33 @@ public class TestAnnotationParser
foundClasses.add(info.getClassName());
}
}
public static class DuplicateClassScanHandler extends AnnotationParser.AbstractHandler
{
private Map<String, List<String>> _classMap = new ConcurrentHashMap();
@Override
public void handle(ClassInfo info)
{
List<String> list = new CopyOnWriteArrayList<>();
Resource r = info.getContainingResource();
list.add((r==null?"":r.toString()));
List<String> existing = _classMap.putIfAbsent(info.getClassName(), list);
if (existing != null)
{
existing.addAll(list);
}
}
public List<String> getParsedList(String classname)
{
return _classMap.get(classname);
}
}
@Rule
public TestingDir testdir = new TestingDir();
@ -219,10 +249,11 @@ public class TestAnnotationParser
Resource testJar = Resource.newResource(MavenTestingUtils.getTestResourceFile("tinytest.jar"));
Resource testJar2 = Resource.newResource(MavenTestingUtils.getTestResourceFile("tinytest_copy.jar"));
AnnotationParser parser = new AnnotationParser();
Set<Handler> emptySet = Collections.emptySet();
parser.parse(emptySet, testJar);
parser.parse(emptySet, testJar2);
List<String> locations = parser.getParsedLocations("org.acme.ClassOne");
DuplicateClassScanHandler handler = new DuplicateClassScanHandler();
Set<Handler> handlers = Collections.singleton(handler);
parser.parse(handlers, testJar);
parser.parse(handlers, testJar2);
List<String> locations = handler.getParsedList("org.acme.ClassOne");
Assert.assertNotNull(locations);
Assert.assertEquals(2, locations.size());
Assert.assertTrue(!(locations.get(0).equals(locations.get(1))));
@ -235,10 +266,11 @@ public class TestAnnotationParser
Resource testJar = Resource.newResource(MavenTestingUtils.getTestResourceFile("tinytest.jar"));
File testClasses = new File(MavenTestingUtils.getTargetDir(), "test-classes");
AnnotationParser parser = new AnnotationParser();
Set<Handler> emptySet = Collections.emptySet();
parser.parse(emptySet, testJar);
parser.parse(emptySet, Resource.newResource(testClasses));
List<String> locations = parser.getParsedLocations("org.acme.ClassOne");
DuplicateClassScanHandler handler = new DuplicateClassScanHandler();
Set<Handler> handlers = Collections.singleton(handler);
parser.parse(handlers, testJar);
parser.parse(handlers, Resource.newResource(testClasses));
List<String>locations = handler.getParsedList("org.acme.ClassOne");
Assert.assertNotNull(locations);
Assert.assertEquals(2, locations.size());
Assert.assertTrue(!(locations.get(0).equals(locations.get(1))));