/* ******************************************************************* * 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 v 2.0 * which accompanies this distribution and is available at * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt * ******************************************************************/ package org.aspectj.weaver.reflect; import java.lang.reflect.InvocationTargetException; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.aspectj.bridge.AbortException; import org.aspectj.bridge.IMessage; import org.aspectj.bridge.IMessageHandler; import org.aspectj.weaver.BCException; import org.aspectj.weaver.IWeavingSupport; import org.aspectj.weaver.ReferenceType; import org.aspectj.weaver.ReferenceTypeDelegate; import org.aspectj.weaver.ResolvedType; import org.aspectj.weaver.UnresolvedType; import org.aspectj.weaver.WeakClassLoaderReference; 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 rworlds = Collections.synchronizedMap(new HashMap<>()); private WeakClassLoaderReference classLoaderReference; private AnnotationFinder annotationFinder; private boolean mustUseOneFourDelegates = false; // for testing private Map> inProgressResolutionClasses = new HashMap<>(); public static ReflectionWorld getReflectionWorldFor(WeakClassLoaderReference classLoaderReference) { // Temporarily do as before. Although the cache makes things faster it needs a bit more thought because // if the world has pointcutdesignators registered then someone may inadvertently register additional // ones on reusing a world (when they would be expecting a clean world). We can't automatically // clear them because we don't know when they are finished with. return new ReflectionWorld(classLoaderReference); /* synchronized (rworlds) { // Tidyup any no longer relevant entries... for (Iterator> it = rworlds.entrySet().iterator(); it.hasNext();) { Map.Entry 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()); // setBehaveInJava5Way(LangUtil.isVMGreaterOrEqual(15)); // this.classLoaderReference = new // WeakClassLoaderReference(ReflectionWorld.class.getClassLoader()); // this.annotationFinder = // makeAnnotationFinderIfAny(classLoaderReference.getClassLoader(), // this); } public ReflectionWorld(WeakClassLoaderReference classloaderRef) { this.setMessageHandler(new ExceptionBasedMessageHandler()); setBehaveInJava5Way(true); classLoaderReference = classloaderRef; annotationFinder = makeAnnotationFinderIfAny(classLoaderReference.getClassLoader(), this); } public ReflectionWorld(ClassLoader aClassLoader) { super(); this.setMessageHandler(new ExceptionBasedMessageHandler()); setBehaveInJava5Way(true); classLoaderReference = new WeakClassLoaderReference(aClassLoader); annotationFinder = makeAnnotationFinderIfAny(classLoaderReference.getClassLoader(), this); } public ReflectionWorld(boolean forceUseOf14Delegates, ClassLoader aClassLoader) { this(aClassLoader); this.mustUseOneFourDelegates = forceUseOf14Delegates; if (forceUseOf14Delegates) { // Dont use 1.4 delegates and yet allow autoboxing this.setBehaveInJava5Way(false); } } public static AnnotationFinder makeAnnotationFinderIfAny(ClassLoader loader, World world) { AnnotationFinder annotationFinder = null; try { Class java15AnnotationFinder = Class.forName("org.aspectj.weaver.reflect.Java15AnnotationFinder"); annotationFinder = (AnnotationFinder) java15AnnotationFinder.getDeclaredConstructor().newInstance(); annotationFinder.setClassLoader(loader); annotationFinder.setWorld(world); } catch (ClassNotFoundException ex) { // must be on 1.4 or earlier } catch (IllegalAccessException | InstantiationException | NoSuchMethodException ex) { // not so good throw new BCException("AspectJ internal error", ex); } catch (InvocationTargetException e) { e.printStackTrace(); } return annotationFinder; } public ClassLoader getClassLoader() { return classLoaderReference.getClassLoader(); } public AnnotationFinder getAnnotationFinder() { return annotationFinder; } public ResolvedType resolve(Class aClass) { return resolve(this, 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(); if (aClass.isArray()) { return world.resolve(UnresolvedType.forSignature(className.replace('.', '/'))); } else { return world.resolve(className); } } /** * Resolve a type using the specified class. Normal resolution in a reflection * world uses Class.forName() via the classloader (attached to this world) * in order to find a named type then builds a reference type and a reference * type delegate based on that. For some classes generated at runtime (e.g. * proxy or lambda representation) the forName() call will not work. In those * situations we should just use the clazz we have. * * Should the whole thing switch from using forName() to using the clazz objects? * Possibly but that introduces a lot of change and we don't have a lot * of test coverage for this scenario (reflection world). What we are doing * right now is that this can optionally be used if the regular resolution * scheme did not work. * * Although AspectJ is *not* multi threaded or re-entrant, Spring doesn't * always respect that. There might be an issue here if two attempts are * made to resolve the same thing at the same time via this method. * * @param clazz the class to use as the delegate for the resolved type */ public ResolvedType resolveUsingClass(Class clazz) { String signature = UnresolvedType.forName(clazz.getName()).getSignature(); try { inProgressResolutionClasses.put(signature, clazz); return resolve(clazz.getName()); } finally { inProgressResolutionClasses.remove(signature); } } protected ReferenceTypeDelegate resolveDelegate(ReferenceType ty) { ReferenceTypeDelegate result; if (mustUseOneFourDelegates) { result = ReflectionBasedReferenceTypeDelegateFactory.create14Delegate(ty, this, classLoaderReference.getClassLoader()); } else { result = ReflectionBasedReferenceTypeDelegateFactory.createDelegate(ty, this, classLoaderReference.getClassLoader()); } if (result == null && inProgressResolutionClasses.size() != 0) { // Is it a class that cannot be loaded (i.e. it was generated) but we already know about? Class clazz = inProgressResolutionClasses.get(ty.getSignature()); if (clazz != null) { result = ReflectionBasedReferenceTypeDelegateFactory.createDelegate(ty,this,clazz); } } return result; } public static class ReflectionWorldException extends RuntimeException { private static final long serialVersionUID = -3432261918302793005L; public ReflectionWorldException(String message) { super(message); } } private static class ExceptionBasedMessageHandler implements IMessageHandler { public boolean handleMessage(IMessage message) throws AbortException { throw new ReflectionWorldException(message.toString()); } public boolean isIgnoring(org.aspectj.bridge.IMessage.Kind kind) { if (kind == IMessage.INFO) { return true; } else { return false; } } public void dontIgnore(org.aspectj.bridge.IMessage.Kind kind) { // empty } public void ignore(org.aspectj.bridge.IMessage.Kind kind) { // empty } } public IWeavingSupport getWeavingSupport() { return null; } public boolean isLoadtimeWeaving() { return true; } }