]> source.dussan.org Git - aspectj.git/commitdiff
Fixes Bug 525293 - Spring AOP could be faster
authorAndy Clement <aclement@pivotal.io>
Thu, 28 Sep 2017 23:03:17 +0000 (16:03 -0700)
committerAndy Clement <aclement@pivotal.io>
Fri, 20 Oct 2017 20:11:32 +0000 (13:11 -0700)
Multiple changes here:
- annotation unpacking is smarter and if it only needs runtime
  retention annotations it uses reflection and doesn't unpack the
  bytes to discover class level retention annotations.
- Reflection worlds are shared if for the same classloader.

bcel-builder/src/org/aspectj/apache/bcel/util/ClassLoaderRepository.java
org.aspectj.matcher/src/org/aspectj/weaver/reflect/AnnotationFinder.java
org.aspectj.matcher/src/org/aspectj/weaver/reflect/ReflectionBasedResolvedMemberImpl.java
org.aspectj.matcher/src/org/aspectj/weaver/reflect/ReflectionWorld.java
org.aspectj.matcher/src/org/aspectj/weaver/tools/PointcutParser.java
weaver5/java5-src/org/aspectj/weaver/reflect/Java15AnnotationFinder.java
weaver5/java5-testsrc/org/aspectj/weaver/reflect/ReflectionWorldTest.java

index eb33d8b4e2d79b10ce63468af1786b35dc7b7293..e3c59556ba805f1825ca649d56c8cd59c1a03a81 100644 (file)
@@ -88,15 +88,13 @@ public class ClassLoaderRepository implements Repository {
        private ClassLoaderReference loaderRef;
 
        // Choice of cache...
-       private WeakHashMap /* <URL,SoftRef(JavaClass)> */<URL, SoftReference<JavaClass>> localCache = new WeakHashMap<URL, SoftReference<JavaClass>>();
-       private static SoftHashMap /* <URL,JavaClass> */sharedCache = new SoftHashMap(Collections
-                       .synchronizedMap(new HashMap<Object, SpecialValue>()));
+       private WeakHashMap<URL, SoftReference<JavaClass>> localCache = new WeakHashMap<URL, SoftReference<JavaClass>>();
+       private static SoftHashMap /* <URL,JavaClass> */sharedCache = new SoftHashMap(Collections.synchronizedMap(new HashMap<Object, SpecialValue>()));
 
        // For fast translation of the classname *intentionally not static*
        private SoftHashMap /* <String,URL> */nameMap = new SoftHashMap(new HashMap(), false);
 
-       public static boolean useSharedCache = System.getProperty("org.aspectj.apache.bcel.useSharedCache", "true").equalsIgnoreCase(
-                       "true");
+       public static boolean useSharedCache = System.getProperty("org.aspectj.apache.bcel.useSharedCache", "true").equalsIgnoreCase("true");
 
        private static int cacheHitsShared = 0;
        private static int missSharedEvicted = 0; // Misses in shared cache access due to reference GC
index 7efeac9aec0ad634bcd8ab66c78005d25463929a..90ce368d96820551921bb0c27d61a802d8920229 100644 (file)
@@ -1,18 +1,16 @@
 /* *******************************************************************
- * Copyright (c) 2005 Contributors.
+ * Copyright (c) 2005, 2017 Contributors.
+ * 
  * All rights reserved. 
  * This program and the accompanying materials are made available 
  * under the terms of the Eclipse Public License v1.0 
  * which accompanies this distribution and is available at 
  * http://eclipse.org/legal/epl-v10.html 
  *  
- * Contributors: 
- *   Adrian Colyer                     Initial implementation
  * ******************************************************************/
 package org.aspectj.weaver.reflect;
 
 import java.lang.reflect.Member;
-import java.util.Set;
 
 import org.aspectj.weaver.AnnotationAJ;
 import org.aspectj.weaver.ResolvedType;
@@ -20,7 +18,8 @@ import org.aspectj.weaver.UnresolvedType;
 import org.aspectj.weaver.World;
 
 /**
- * @author colyer Used in 1.4 code to access annotations safely
+ * @author Adrian Colyer
+ * @author Andy Clement
  */
 public interface AnnotationFinder {
 
@@ -32,14 +31,13 @@ public interface AnnotationFinder {
 
        Object getAnnotationFromMember(ResolvedType annotationType, Member aMember);
 
-       public AnnotationAJ getAnnotationOfType(UnresolvedType ofType,
-                       Member onMember);
+       public AnnotationAJ getAnnotationOfType(UnresolvedType ofType, Member onMember);
 
        public String getAnnotationDefaultValue(Member onMember);
 
-       Object getAnnotationFromClass(ResolvedType annotationType, Class aClass);
+       Object getAnnotationFromClass(ResolvedType annotationType, Class<?> aClass);
 
-       Set/* ResolvedType */getAnnotations(Member onMember);
+       ResolvedType[] getAnnotations(Member onMember, boolean runtimeAnnotationsOnly);
 
        ResolvedType[][] getParameterAnnotationTypes(Member onMember);
 }
index 28c8aacf67f3645e018de23269e5051233748483..ba8f1330e267a07b5dd2de6ebed9b7a1e10eee99 100644 (file)
@@ -1,18 +1,15 @@
 /* *******************************************************************
- * Copyright (c) 2005 Contributors.
+ * Copyright (c) 2005, 2017 Contributors.
  * All rights reserved. 
  * This program and the accompanying materials are made available 
  * under the terms of the Eclipse Public License v1.0 
  * which accompanies this distribution and is available at 
  * http://eclipse.org/legal/epl-v10.html 
  *  
- * Contributors: 
- *   Adrian Colyer                     Initial implementation
  * ******************************************************************/
 package org.aspectj.weaver.reflect;
 
 import java.lang.reflect.Member;
-import java.util.Set;
 
 import org.aspectj.weaver.AnnotationAJ;
 import org.aspectj.weaver.MemberKind;
@@ -24,37 +21,29 @@ import org.aspectj.weaver.UnresolvedType;
 /**
  * Subtype of ResolvedMemberImpl used in reflection world. Knows how to get annotations from a java.lang.reflect.Member
  * 
+ * @author Adrian Colyer
+ * @author Andy Clement
  */
 public class ReflectionBasedResolvedMemberImpl extends ResolvedMemberImpl {
 
        private AnnotationFinder annotationFinder = null;
        private GenericSignatureInformationProvider gsigInfoProvider = new Java14GenericSignatureInformationProvider();
+       
+       /**
+        * If true then only runtime visible annotations have been resolved via reflection. If class retention
+        * annotations are also required (later) then the cache will have to be rebuilt using a more detailed
+        * dig into the class file.
+        */
+       private boolean onlyRuntimeAnnotationsCached;
 
        private Member reflectMember;
 
-       /**
-        * @param kind
-        * @param declaringType
-        * @param modifiers
-        * @param returnType
-        * @param name
-        * @param parameterTypes
-        */
        public ReflectionBasedResolvedMemberImpl(MemberKind kind, UnresolvedType declaringType, int modifiers,
                        UnresolvedType returnType, String name, UnresolvedType[] parameterTypes, Member reflectMember) {
                super(kind, declaringType, modifiers, returnType, name, parameterTypes);
                this.reflectMember = reflectMember;
        }
 
-       /**
-        * @param kind
-        * @param declaringType
-        * @param modifiers
-        * @param returnType
-        * @param name
-        * @param parameterTypes
-        * @param checkedExceptions
-        */
        public ReflectionBasedResolvedMemberImpl(MemberKind kind, UnresolvedType declaringType, int modifiers,
                        UnresolvedType returnType, String name, UnresolvedType[] parameterTypes, UnresolvedType[] checkedExceptions,
                        Member reflectMember) {
@@ -62,16 +51,6 @@ public class ReflectionBasedResolvedMemberImpl extends ResolvedMemberImpl {
                this.reflectMember = reflectMember;
        }
 
-       /**
-        * @param kind
-        * @param declaringType
-        * @param modifiers
-        * @param returnType
-        * @param name
-        * @param parameterTypes
-        * @param checkedExceptions
-        * @param backingGenericMember
-        */
        public ReflectionBasedResolvedMemberImpl(MemberKind kind, UnresolvedType declaringType, int modifiers,
                        UnresolvedType returnType, String name, UnresolvedType[] parameterTypes, UnresolvedType[] checkedExceptions,
                        ResolvedMember backingGenericMember, Member reflectMember) {
@@ -79,13 +58,6 @@ public class ReflectionBasedResolvedMemberImpl extends ResolvedMemberImpl {
                this.reflectMember = reflectMember;
        }
 
-       /**
-        * @param kind
-        * @param declaringType
-        * @param modifiers
-        * @param name
-        * @param signature
-        */
        public ReflectionBasedResolvedMemberImpl(MemberKind kind, UnresolvedType declaringType, int modifiers, String name,
                        String signature, Member reflectMember) {
                super(kind, declaringType, modifiers, name, signature);
@@ -96,89 +68,64 @@ public class ReflectionBasedResolvedMemberImpl extends ResolvedMemberImpl {
                return this.reflectMember;
        }
 
-       // generic signature support
-
        public void setGenericSignatureInformationProvider(GenericSignatureInformationProvider gsigProvider) {
                this.gsigInfoProvider = gsigProvider;
        }
 
-       /*
-        * (non-Javadoc)
-        * 
-        * @see org.aspectj.weaver.ResolvedMemberImpl#getGenericParameterTypes()
-        */
        @Override
        public UnresolvedType[] getGenericParameterTypes() {
                return this.gsigInfoProvider.getGenericParameterTypes(this);
        }
 
-       /*
-        * (non-Javadoc)
-        * 
-        * @see org.aspectj.weaver.ResolvedMemberImpl#getGenericReturnType()
-        */
        @Override
        public UnresolvedType getGenericReturnType() {
                return this.gsigInfoProvider.getGenericReturnType(this);
        }
 
-       /*
-        * (non-Javadoc)
-        * 
-        * @see org.aspectj.weaver.ResolvedMemberImpl#isSynthetic()
-        */
        @Override
        public boolean isSynthetic() {
                return this.gsigInfoProvider.isSynthetic(this);
        }
 
-       /*
-        * (non-Javadoc)
-        * 
-        * @see org.aspectj.weaver.ResolvedMemberImpl#isVarargsMethod()
-        */
        @Override
        public boolean isVarargsMethod() {
                return this.gsigInfoProvider.isVarArgs(this);
        }
 
-       /*
-        * (non-Javadoc)
-        * 
-        * @see org.aspectj.weaver.ResolvedMemberImpl#isBridgeMethod()
-        */
        @Override
        public boolean isBridgeMethod() {
                return this.gsigInfoProvider.isBridge(this);
        }
 
-       // annotation support
-
        public void setAnnotationFinder(AnnotationFinder finder) {
                this.annotationFinder = finder;
        }
 
        @Override
        public boolean hasAnnotation(UnresolvedType ofType) {
-               unpackAnnotations();
+               boolean areRuntimeRetentionAnnotationsSufficient = false;
+               if (ofType instanceof ResolvedType) {
+                       areRuntimeRetentionAnnotationsSufficient = ((ResolvedType)ofType).isAnnotationWithRuntimeRetention();
+               }
+               unpackAnnotations(areRuntimeRetentionAnnotationsSufficient);
                return super.hasAnnotation(ofType);
        }
 
        @Override
        public boolean hasAnnotations() {
-               unpackAnnotations();
+               unpackAnnotations(false);
                return super.hasAnnotations();
        }
 
        @Override
        public ResolvedType[] getAnnotationTypes() {
-               unpackAnnotations();
+               unpackAnnotations(false);
                return super.getAnnotationTypes();
        }
 
        @Override
        public AnnotationAJ getAnnotationOfType(UnresolvedType ofType) {
-               unpackAnnotations();
+               unpackAnnotations(false);
                if (annotationFinder == null || annotationTypes == null) {
                        return null;
                }
@@ -206,18 +153,10 @@ public class ReflectionBasedResolvedMemberImpl extends ResolvedMemberImpl {
                return parameterAnnotationTypes;
        }
 
-       private void unpackAnnotations() {
-               if (annotationTypes == null && annotationFinder != null) {
-                       Set<?> s = annotationFinder.getAnnotations(reflectMember);
-                       if (s.size() == 0) {
-                               annotationTypes = ResolvedType.EMPTY_ARRAY;
-                       } else {
-                               annotationTypes = new ResolvedType[s.size()];
-                               int i = 0;
-                               for (Object o : s) {
-                                       annotationTypes[i++] = (ResolvedType) o;
-                               }
-                       }
+       private void unpackAnnotations(boolean areRuntimeRetentionAnnotationsSufficient) {
+               if (annotationFinder != null && (annotationTypes == null || (!areRuntimeRetentionAnnotationsSufficient && onlyRuntimeAnnotationsCached))) {
+                       annotationTypes = annotationFinder.getAnnotations(reflectMember, areRuntimeRetentionAnnotationsSufficient);
+                       onlyRuntimeAnnotationsCached = areRuntimeRetentionAnnotationsSufficient;
                }
        }
 }
index 98e800222b9da04c81c513637dc894bed4914cd6..d06db52d1813dc4882bf3c9e35f71b53c88f106a 100644 (file)
@@ -1,17 +1,16 @@
 /* *******************************************************************
- * Copyright (c) 2005 Contributors.
+ * Copyright (c) 2005-2017 Contributors.
  * All rights reserved. 
  * This program and the accompanying materials are made available 
  * under the terms of the Eclipse Public License v1.0 
  * which accompanies this distribution and is available at 
  * http://eclipse.org/legal/epl-v10.html 
- *  
- * Contributors: 
- *   Adrian Colyer                     Initial implementation
  * ******************************************************************/
 package org.aspectj.weaver.reflect;
 
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.Map;
 
 import org.aspectj.bridge.AbortException;
@@ -31,14 +30,46 @@ import org.aspectj.weaver.World;
  * A ReflectionWorld is used solely for purposes of type resolution based on the runtime classpath (java.lang.reflect). It does not
  * support weaving operations (creation of mungers etc..).
  * 
+ * @author Adrian Colyer
+ * @author Andy Clement
  */
 public class ReflectionWorld extends World implements IReflectionWorld {
 
+       private static Map<WeakClassLoaderReference, ReflectionWorld> rworlds = Collections.synchronizedMap(new HashMap<WeakClassLoaderReference, ReflectionWorld>());
+
        private WeakClassLoaderReference classLoaderReference;
        private AnnotationFinder annotationFinder;
        private boolean mustUseOneFourDelegates = false; // for testing
        private Map<String,Class<?>> inProgressResolutionClasses = new HashMap<String,Class<?>>();
-
+       
+       public static ReflectionWorld getReflectionWorldFor(WeakClassLoaderReference classLoaderReference) {
+               synchronized (rworlds) {
+                       // Tidyup any no longer relevant entries...
+                       for (Iterator<Map.Entry<WeakClassLoaderReference, ReflectionWorld>> it = rworlds.entrySet().iterator();
+                                       it.hasNext();) {
+                               Map.Entry<WeakClassLoaderReference, ReflectionWorld> entry = it.next();
+                               if (entry.getKey().getClassLoader() == null) {
+                                       it.remove();
+                               }
+                       }
+                       ReflectionWorld rworld = null;
+                       if (classLoaderReference.getClassLoader() != null) {
+                               rworld = rworlds.get(classLoaderReference);
+                               if (rworld == null) {
+                                       rworld = new ReflectionWorld(classLoaderReference);
+                                       rworlds.put(classLoaderReference, rworld);
+                               }
+                       }
+                       return rworld;
+               }
+       }
+       
+       public static void cleanUpWorlds() {
+               synchronized (rworlds) {
+                       rworlds.clear();
+               }
+       }
+       
        private ReflectionWorld() {
                // super();
                // this.setMessageHandler(new ExceptionBasedMessageHandler());
@@ -49,6 +80,13 @@ public class ReflectionWorld extends World implements IReflectionWorld {
                // makeAnnotationFinderIfAny(classLoaderReference.getClassLoader(),
                // this);
        }
+       
+       public ReflectionWorld(WeakClassLoaderReference classloaderRef) {
+               this.setMessageHandler(new ExceptionBasedMessageHandler());
+               setBehaveInJava5Way(LangUtil.is15VMOrGreater());
+               classLoaderReference = classloaderRef;
+               annotationFinder = makeAnnotationFinderIfAny(classLoaderReference.getClassLoader(), this);
+       }
 
        public ReflectionWorld(ClassLoader aClassLoader) {
                super();
@@ -71,7 +109,7 @@ public class ReflectionWorld extends World implements IReflectionWorld {
                AnnotationFinder annotationFinder = null;
                try {
                        if (LangUtil.is15VMOrGreater()) {
-                               Class java15AnnotationFinder = Class.forName("org.aspectj.weaver.reflect.Java15AnnotationFinder");
+                               Class<?> java15AnnotationFinder = Class.forName("org.aspectj.weaver.reflect.Java15AnnotationFinder");
                                annotationFinder = (AnnotationFinder) java15AnnotationFinder.newInstance();
                                annotationFinder.setClassLoader(loader);
                                annotationFinder.setWorld(world);
@@ -99,7 +137,7 @@ public class ReflectionWorld extends World implements IReflectionWorld {
                return resolve(this, aClass);
        }
 
-       public static ResolvedType resolve(World world, Class aClass) {
+       public static ResolvedType resolve(World world, Class<?> aClass) {
                // classes that represent arrays return a class name that is the
                // signature of the array type, ho-hum...
                String className = aClass.getName();
index 649aa0ba9585ed6cf4838a00e71bac4b2e09dd38..4e3b2bd595d6c2e529e323485510a9bd51640680 100644 (file)
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2004 IBM Corporation and others.
+ * Copyright (c) 2004, 2017 Contributors
  * All rights reserved. This program and the accompanying materials 
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -51,6 +51,9 @@ import org.aspectj.weaver.reflect.ReflectionWorld;
 
 /**
  * A PointcutParser can be used to build PointcutExpressions for a user-defined subset of AspectJ's pointcut language
+ * 
+ * @author Adrian Colyer
+ * @author Andy Clement
  */
 public class PointcutParser {
 
@@ -123,7 +126,7 @@ public class PointcutParser {
         * @throws UnsupportedOperationException if the set contains if, cflow, or cflow below
         */
        public static PointcutParser getPointcutParserSupportingSpecifiedPrimitivesAndUsingContextClassloaderForResolution(
-                       Set supportedPointcutKinds) {
+                       Set<PointcutPrimitive> supportedPointcutKinds) {
                PointcutParser p = new PointcutParser(supportedPointcutKinds);
                p.setClassLoader(Thread.currentThread().getContextClassLoader());
                return p;
@@ -163,7 +166,7 @@ public class PointcutParser {
         * @throws UnsupportedOperationException if the set contains if, cflow, or cflow below
         */
        public static PointcutParser getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(
-                       Set supportedPointcutKinds, ClassLoader classLoader) {
+                       Set<PointcutPrimitive> supportedPointcutKinds, ClassLoader classLoader) {
                PointcutParser p = new PointcutParser(supportedPointcutKinds);
                p.setClassLoader(classLoader);
                return p;
@@ -216,7 +219,22 @@ public class PointcutParser {
         */
        protected void setClassLoader(ClassLoader aLoader) {
                this.classLoaderReference = new WeakClassLoaderReference(aLoader);
-               world = new ReflectionWorld(this.classLoaderReference.getClassLoader());
+               world = ReflectionWorld.getReflectionWorldFor(this.classLoaderReference);
+       }
+       
+       /**
+        * Set the classloader that this parser should use for type resolution.
+        * 
+        * @param aLoader
+        * @param shareWorlds if true then two PointcutParsers operating using the same classloader will share a ReflectionWorld
+        */
+       protected void setClassLoader(ClassLoader aLoader, boolean shareWorlds) {
+               this.classLoaderReference = new WeakClassLoaderReference(aLoader);
+               if (shareWorlds) {
+                       world = ReflectionWorld.getReflectionWorldFor(this.classLoaderReference);
+               } else {
+                       world = new ReflectionWorld(classLoaderReference);
+               }
        }
 
        /**
@@ -261,7 +279,7 @@ public class PointcutParser {
         * @param type
         * @return
         */
-       public PointcutParameter createPointcutParameter(String name, Class type) {
+       public PointcutParameter createPointcutParameter(String name, Class<?> type) {
                return new PointcutParameterImpl(name, type);
        }
 
@@ -287,7 +305,7 @@ public class PointcutParser {
         *         supported by this PointcutParser.
         * @throws IllegalArgumentException if the expression is not a well-formed pointcut expression
         */
-       public PointcutExpression parsePointcutExpression(String expression, Class inScope, PointcutParameter[] formalParameters)
+       public PointcutExpression parsePointcutExpression(String expression, Class<?> inScope, PointcutParameter[] formalParameters)
                        throws UnsupportedPointcutPrimitiveException, IllegalArgumentException {
                PointcutExpressionImpl pcExpr = null;
                try {
@@ -303,7 +321,7 @@ public class PointcutParser {
                return pcExpr;
        }
 
-       protected Pointcut resolvePointcutExpression(String expression, Class inScope, PointcutParameter[] formalParameters) {
+       protected Pointcut resolvePointcutExpression(String expression, Class<?> inScope, PointcutParameter[] formalParameters) {
                try {
                        PatternParser parser = new PatternParser(expression);
                        parser.setPointcutDesignatorHandlers(pointcutDesignators, world);
@@ -317,7 +335,7 @@ public class PointcutParser {
                }
        }
 
-       protected Pointcut concretizePointcutExpression(Pointcut pc, Class inScope, PointcutParameter[] formalParameters) {
+       protected Pointcut concretizePointcutExpression(Pointcut pc, Class<?> inScope, PointcutParameter[] formalParameters) {
                ResolvedType declaringTypeForResolution = null;
                if (inScope != null) {
                        declaringTypeForResolution = getWorld().resolve(inScope.getName());
@@ -355,7 +373,7 @@ public class PointcutParser {
        }
 
        /* for testing */
-       Set getSupportedPrimitives() {
+       Set<PointcutPrimitive> getSupportedPrimitives() {
                return supportedPrimitives;
        }
 
@@ -366,7 +384,7 @@ public class PointcutParser {
                return current;
        }
 
-       private IScope buildResolutionScope(Class inScope, PointcutParameter[] formalParameters) {
+       private IScope buildResolutionScope(Class<?> inScope, PointcutParameter[] formalParameters) {
                if (formalParameters == null) {
                        formalParameters = new PointcutParameter[0];
                }
@@ -398,7 +416,7 @@ public class PointcutParser {
                }
        }
 
-       private UnresolvedType toUnresolvedType(Class clazz) {
+       private UnresolvedType toUnresolvedType(Class<?> clazz) {
                if (clazz.isArray()) {
                        return UnresolvedType.forSignature(clazz.getName().replace('.', '/'));
                } else {
@@ -560,4 +578,5 @@ public class PointcutParser {
                msg.append("\n");
                return msg.toString();
        }
+       
 }
index a978d9605da63726478441e999048519df1ab988..016becf8728597612cd0ea28f98000aa426ede0b 100644 (file)
@@ -1,13 +1,10 @@
 /* *******************************************************************
- * Copyright (c) 2005 Contributors.
+ * Copyright (c) 2005, 2017 Contributors.
  * All rights reserved. 
  * This program and the accompanying materials are made available 
  * under the terms of the Eclipse Public License v1.0 
  * which accompanies this distribution and is available at 
  * http://eclipse.org/legal/epl-v10.html 
- *  
- * Contributors: 
- *   Adrian Colyer                     Initial implementation
  * ******************************************************************/
 package org.aspectj.weaver.reflect;
 
@@ -17,15 +14,13 @@ import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.Member;
 import java.lang.reflect.Method;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
 
 import org.aspectj.apache.bcel.classfile.AnnotationDefault;
 import org.aspectj.apache.bcel.classfile.Attribute;
 import org.aspectj.apache.bcel.classfile.JavaClass;
 import org.aspectj.apache.bcel.classfile.LocalVariable;
 import org.aspectj.apache.bcel.classfile.LocalVariableTable;
+import org.aspectj.apache.bcel.util.ClassLoaderRepository;
 import org.aspectj.apache.bcel.util.NonCachingClassLoaderRepository;
 import org.aspectj.apache.bcel.util.Repository;
 import org.aspectj.weaver.AnnotationAJ;
@@ -36,36 +31,44 @@ import org.aspectj.weaver.bcel.BcelAnnotation;
 import org.aspectj.weaver.bcel.BcelWeakClassLoaderReference;
 
 /**
- * Find the given annotation (if present) on the given object
  * 
+ * @author Adrian Colyer
+ * @author Andy Clement
  */
 public class Java15AnnotationFinder implements AnnotationFinder, ArgNameFinder {
 
+       public static final ResolvedType[][] NO_PARAMETER_ANNOTATIONS = new ResolvedType[][] {};
+
        private Repository bcelRepository;
        private BcelWeakClassLoaderReference classLoaderRef;
        private World world;
+       private static boolean useCachingClassLoaderRepository;
+       
+       static {
+               try {
+                       useCachingClassLoaderRepository = System.getProperty("Xset:bcelRepositoryCaching","true").equalsIgnoreCase("true");
+               } catch (Throwable t) {
+                       useCachingClassLoaderRepository = false;
+               }
+       }
 
        // must have no-arg constructor for reflective construction
        public Java15AnnotationFinder() {
        }
 
        public void setClassLoader(ClassLoader aLoader) {
-               // TODO: No easy way to ask the world factory for the right kind of
-               // repository so
-               // default to the safe one! (pr160674)
                this.classLoaderRef = new BcelWeakClassLoaderReference(aLoader);
-               this.bcelRepository = new NonCachingClassLoaderRepository(classLoaderRef);
+               if (useCachingClassLoaderRepository) {
+                       this.bcelRepository = new ClassLoaderRepository(classLoaderRef);
+               } else {
+                       this.bcelRepository = new NonCachingClassLoaderRepository(classLoaderRef);
+               }
        }
 
        public void setWorld(World aWorld) {
                this.world = aWorld;
        }
 
-       /*
-        * (non-Javadoc)
-        * 
-        * @see org.aspectj.weaver.reflect.AnnotationFinder#getAnnotation(org.aspectj .weaver.ResolvedType, java.lang.Object)
-        */
        public Object getAnnotation(ResolvedType annotationType, Object onObject) {
                try {
                        Class<? extends Annotation> annotationClass = (Class<? extends Annotation>) Class.forName(annotationType.getName(),
@@ -155,7 +158,6 @@ public class Java15AnnotationFinder implements AnnotationFinder, ArgNameFinder {
                } catch (ClassNotFoundException cnfEx) {
                        // just use reflection then
                }
-
                return null;
        }
 
@@ -186,80 +188,76 @@ public class Java15AnnotationFinder implements AnnotationFinder, ArgNameFinder {
                } catch (ClassNotFoundException cnfEx) {
                        // just use reflection then
                }
-
                return null;
        }
 
-       public Set getAnnotations(Member onMember) {
-               if (!(onMember instanceof AccessibleObject))
-                       return Collections.EMPTY_SET;
-               // here we really want both the runtime visible AND the class visible
-               // annotations
-               // so we bail out to Bcel and then chuck away the JavaClass so that we
-               // don't hog
-               // memory.
-               try {
-                       JavaClass jc = bcelRepository.loadClass(onMember.getDeclaringClass());
-                       org.aspectj.apache.bcel.classfile.annotation.AnnotationGen[] anns = new org.aspectj.apache.bcel.classfile.annotation.AnnotationGen[0];
-                       if (onMember instanceof Method) {
-                               org.aspectj.apache.bcel.classfile.Method bcelMethod = jc.getMethod((Method) onMember);
-                               if (bcelMethod == null) {
-                                       // fallback on reflection - see pr220430
-                                       // System.err.println(
-                                       // "Unexpected problem in Java15AnnotationFinder: cannot retrieve annotations on method '"
-                                       // +
-                                       // onMember.getName()+"' in class '"+jc.getClassName()+"'");
-                               } else {
-                                       anns = bcelMethod.getAnnotations();
+       public ResolvedType[] getAnnotations(Member onMember, boolean areRuntimeAnnotationsSufficient) {
+               if (!(onMember instanceof AccessibleObject)) {
+                       return ResolvedType.NONE;
+               }
+               // If annotations with class level retention are required then we need to open
+               // open the class file. If only runtime retention annotations are required
+               // we can just use reflection.
+               if (!areRuntimeAnnotationsSufficient) {
+                       try {
+                               JavaClass jc = bcelRepository.loadClass(onMember.getDeclaringClass());
+                               org.aspectj.apache.bcel.classfile.annotation.AnnotationGen[] anns = null;
+                               if (onMember instanceof Method) {
+                                       org.aspectj.apache.bcel.classfile.Method bcelMethod = jc.getMethod((Method) onMember);
+                                       if (bcelMethod != null) {
+                                               anns = bcelMethod.getAnnotations();
+                                       }
+                               } else if (onMember instanceof Constructor) {
+                                       org.aspectj.apache.bcel.classfile.Method bcelCons = jc.getMethod((Constructor) onMember);
+                                       anns = bcelCons.getAnnotations();
+                               } else if (onMember instanceof Field) {
+                                       org.aspectj.apache.bcel.classfile.Field bcelField = jc.getField((Field) onMember);
+                                       anns = bcelField.getAnnotations();
                                }
-                       } else if (onMember instanceof Constructor) {
-                               org.aspectj.apache.bcel.classfile.Method bcelCons = jc.getMethod((Constructor) onMember);
-                               anns = bcelCons.getAnnotations();
-                       } else if (onMember instanceof Field) {
-                               org.aspectj.apache.bcel.classfile.Field bcelField = jc.getField((Field) onMember);
-                               anns = bcelField.getAnnotations();
-                       }
-                       // the answer is cached and we don't want to hold on to memory
-                       bcelRepository.clear();
-                       // OPTIMIZE make this a constant 0 size array
-                       if (anns == null)
-                               anns = new org.aspectj.apache.bcel.classfile.annotation.AnnotationGen[0];
-                       // convert to our Annotation type
-                       Set<ResolvedType> annSet = new HashSet<ResolvedType>();
-                       for (int i = 0; i < anns.length; i++) {
-                               annSet.add(world.resolve(UnresolvedType.forSignature(anns[i].getTypeSignature())));
+                               // the answer is cached and we don't want to hold on to memory
+                               bcelRepository.clear();
+                               if (anns == null || anns.length == 0) {
+                                       return ResolvedType.NONE;
+                               }
+                               ResolvedType[] annotationTypes = new ResolvedType[anns.length];
+                               for (int i = 0; i < anns.length; i++) {
+                                       annotationTypes[i] = world.resolve(UnresolvedType.forSignature(anns[i].getTypeSignature()));
+                               }
+                               return annotationTypes;
+                       } catch (ClassNotFoundException cnfEx) {
+                               // just use reflection then
                        }
-                       return annSet;
-               } catch (ClassNotFoundException cnfEx) {
-                       // just use reflection then
                }
 
                AccessibleObject ao = (AccessibleObject) onMember;
                Annotation[] anns = ao.getDeclaredAnnotations();
-               Set<UnresolvedType> annSet = new HashSet<UnresolvedType>();
+               if (anns.length == 0) {
+                       return ResolvedType.NONE;
+               }
+               ResolvedType[] annotationTypes = new ResolvedType[anns.length];
                for (int i = 0; i < anns.length; i++) {
-                       annSet.add(UnresolvedType.forName(anns[i].annotationType().getName()).resolve(world));
+                       annotationTypes[i] = UnresolvedType.forName(anns[i].annotationType().getName()).resolve(world);
                }
-               return annSet;
+               return annotationTypes;
        }
 
        public ResolvedType[] getAnnotations(Class forClass, World inWorld) {
                // here we really want both the runtime visible AND the class visible
-               // annotations
-               // so we bail out to Bcel and then chuck away the JavaClass so that we
-               // don't hog
-               // memory.
+               // annotations so we bail out to Bcel and then chuck away the JavaClass so that we
+               // don't hog memory.
                try {
                        JavaClass jc = bcelRepository.loadClass(forClass);
                        org.aspectj.apache.bcel.classfile.annotation.AnnotationGen[] anns = jc.getAnnotations();
                        bcelRepository.clear();
-                       if (anns == null)
+                       if (anns == null) {
                                return ResolvedType.NONE;
-                       ResolvedType[] ret = new ResolvedType[anns.length];
-                       for (int i = 0; i < ret.length; i++) {
-                               ret[i] = inWorld.resolve(UnresolvedType.forSignature(anns[i].getTypeSignature()));
+                       } else {
+                               ResolvedType[] ret = new ResolvedType[anns.length];
+                               for (int i = 0; i < ret.length; i++) {
+                                       ret[i] = inWorld.resolve(UnresolvedType.forSignature(anns[i].getTypeSignature()));
+                               }
+                               return ret;
                        }
-                       return ret;
                } catch (ClassNotFoundException cnfEx) {
                        // just use reflection then
                }
@@ -313,8 +311,6 @@ public class Java15AnnotationFinder implements AnnotationFinder, ArgNameFinder {
                return ret;
        }
 
-       public static final ResolvedType[][] NO_PARAMETER_ANNOTATIONS = new ResolvedType[][] {};
-
        public ResolvedType[][] getParameterAnnotationTypes(Member onMember) {
                if (!(onMember instanceof AccessibleObject))
                        return NO_PARAMETER_ANNOTATIONS;
index 3dcbf45b2c1f2fde749805392c53b3521b786e40..dd63c858ea416b450316e2d3f06def16d9949227 100644 (file)
  * ******************************************************************/
 package org.aspectj.weaver.reflect;
 
+import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.lang.reflect.Type;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.List;
+import java.util.Map;
 
 import org.aspectj.bridge.IMessageHandler;
 import org.aspectj.weaver.ReferenceType;
 import org.aspectj.weaver.ResolvedMember;
 import org.aspectj.weaver.ResolvedType;
 import org.aspectj.weaver.UnresolvedType;
+import org.aspectj.weaver.WeakClassLoaderReference;
 import org.aspectj.weaver.World;
 import org.aspectj.weaver.bcel.BcelWorld;
 
@@ -32,6 +38,64 @@ public class ReflectionWorldTest extends TestCase {
                assertNotNull(rt);
                assertEquals("Ljava/lang/Object;", rt.getSignature());
        }
+       
+       public void testReflectionWorldFactory() throws Exception {
+               ClassLoader parent = getClass().getClassLoader();
+               ClassLoader cl1 = new URLClassLoader(new URL[] {}, parent);
+               ClassLoader cl2 = new URLClassLoader(new URL[] {}, parent);
+
+               WeakClassLoaderReference wcl1 = new WeakClassLoaderReference(cl1);
+               WeakClassLoaderReference wcl2 = new WeakClassLoaderReference(cl2);
+               ReflectionWorld a = ReflectionWorld.getReflectionWorldFor(wcl1);
+
+               ResolvedType stringClass1 = a.resolve(String.class);
+               assertNotNull(stringClass1);
+               
+               ReflectionWorld b = ReflectionWorld.getReflectionWorldFor(wcl1);
+               
+               // They should be the same because the classloader has not gone away
+               assertTrue(a==b);
+               
+               cl1 = null;
+               for (int i=0;i<100;i++) {
+                       System.gc(); // How robust is it that this should be causing the reference to be collected?
+               }
+               b = ReflectionWorld.getReflectionWorldFor(wcl1);
+               
+               assertFalse(a==b);
+               
+               cl1 = new URLClassLoader(new URL[] {}, parent);
+               wcl1 = new WeakClassLoaderReference(cl1);
+               a = ReflectionWorld.getReflectionWorldFor(wcl1);
+               b = ReflectionWorld.getReflectionWorldFor(wcl2);
+               assertFalse(a==b);
+               
+               Field declaredField = ReflectionWorld.class.getDeclaredField("rworlds");
+               declaredField.setAccessible(true);
+               Map worlds = (Map)declaredField.get(null);
+               assertEquals(2, worlds.size());
+               
+               cl2 = null;
+               for (int i=0;i<100;i++) {
+                       System.gc(); // How robust is it that this should be causing the reference to be collected?
+               }
+               ReflectionWorld.getReflectionWorldFor(wcl1); // need to call this to trigger tidyup
+               assertEquals(1, worlds.size());
+
+               cl1 = null;
+               for (int i=0;i<100;i++) {
+                       System.gc(); // How robust is it that this should be causing the reference to be collected?
+               }
+               ReflectionWorld.getReflectionWorldFor(wcl1); // need to call this to trigger tidyup
+               assertEquals(0, worlds.size());
+               
+               cl1 = new URLClassLoader(new URL[] {}, parent);
+               wcl1 = new WeakClassLoaderReference(cl1);
+               ReflectionWorld reflectionWorldFor = ReflectionWorld.getReflectionWorldFor(wcl1);
+               assertEquals(1, worlds.size());
+               ReflectionWorld.cleanUpWorlds();                
+               assertEquals(0, worlds.size());
+       }
 
        public void testArrayTypes() {
                IReflectionWorld world = new ReflectionWorld(getClass().getClassLoader());