diff --git a/src/main/java/org/testng/internal/annotations/JDK15AnnotationFinder.java b/src/main/java/org/testng/internal/annotations/JDK15AnnotationFinder.java index 2de11c71e6..f533831fa2 100755 --- a/src/main/java/org/testng/internal/annotations/JDK15AnnotationFinder.java +++ b/src/main/java/org/testng/internal/annotations/JDK15AnnotationFinder.java @@ -3,6 +3,7 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Method; +import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -46,10 +47,20 @@ * @author Cedric Beust */ public class JDK15AnnotationFinder implements IAnnotationFinder { + + private static final IAnnotation NULL_MARKER = new IAnnotation() { + @Override + protected Object clone() throws CloneNotSupportedException { + return super.clone(); + } + }; + private JDK15TagFactory m_tagFactory = new JDK15TagFactory(); - private Map, Class> m_annotationMap = - new ConcurrentHashMap<>(); - private Map, IAnnotation> m_annotations = new ConcurrentHashMap<>(); + // readonly map. does not need ConcurrentHashMap + private final Map, Class> m_annotationMap = + new HashMap<>(); + + private Map, IAnnotation> m_annotations = new ConcurrentHashMap<>(); private IAnnotationTransformer m_transformer = null; @@ -106,7 +117,7 @@ public A findAnnotation(Method m, Class annotationCla } Annotation annotation = m.getAnnotation(a); return findAnnotation(m.getDeclaringClass(), annotation, annotationClass, null, null, m, - new Pair<>(annotation, m)); + new Pair<>(new AnnotationWrapper(annotation), m)); } @Override @@ -128,7 +139,7 @@ public A findAnnotation(ITestNGMethod tm, Class annot annotation = testClass.getAnnotation(a); } return findAnnotation(testClass, annotation, annotationClass, null, null, m, - new Pair<>(annotation, m)); + new Pair<>(new AnnotationWrapper(annotation), m)); } private void transform(IAnnotation a, Class testClass, @@ -176,7 +187,7 @@ public A findAnnotation(Class cls, Class annotatio } Annotation annotation = findAnnotationInSuperClasses(cls, a); return findAnnotation(cls, annotation, annotationClass, cls, null, null, - new Pair<>(annotation, annotationClass)); + new Pair<>(new AnnotationWrapper(annotation), annotationClass)); } @Override @@ -188,12 +199,12 @@ public A findAnnotation(Constructor cons, Class an } Annotation annotation = cons.getAnnotation(a); return findAnnotation(cons.getDeclaringClass(), annotation, annotationClass, null, cons, null, - new Pair<>(annotation, cons)); + new Pair<>(new AnnotationWrapper(annotation), cons)); } private A findAnnotation(Class cls, Annotation a, Class annotationClass, Class testClass, - Constructor testConstructor, Method testMethod, Pair p) { + Constructor testConstructor, Method testMethod, Pair p) { if (a == null) { return null; } @@ -201,8 +212,10 @@ private A findAnnotation(Class cls, Annotation a, IAnnotation result = m_annotations.get(p); if (result == null) { result = m_tagFactory.createTag(cls, a, annotationClass, m_transformer); - m_annotations.put(p, result); + m_annotations.put(p, result != null ? result : NULL_MARKER); transform(result, testClass, testConstructor, testMethod); + } else if (result == NULL_MARKER) { + result = null; } //noinspection unchecked return (A) result; @@ -244,4 +257,26 @@ private String[] optionalValues(Annotation[][] annotations) { } return result; } + + /** + * Wrapper class to override {@link Annotation#hashCode()} and {@link Annotation#equals(Object)} + * for performance improvement. + */ + private final static class AnnotationWrapper { + private final Annotation annotation; + + public AnnotationWrapper(Annotation annotation) { + this.annotation = annotation; + } + + @Override + public boolean equals(Object obj) { + return obj instanceof AnnotationWrapper && annotation == ((AnnotationWrapper) obj).annotation; + } + + @Override + public int hashCode() { + return System.identityHashCode(annotation); + } + } }