/* ******************************************************************* * Copyright (c) 2004 IBM Corporation * 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 * * Contributors: * Matthew Webster, Adrian Colyer, John Kew + Lyor Goldstein (caching) * Martin Lippert initial implementation * ******************************************************************/ package org.aspectj.weaver.tools; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.net.URL; import java.net.URLClassLoader; import java.security.ProtectionDomain; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.StringTokenizer; import org.aspectj.bridge.AbortException; import org.aspectj.bridge.IMessage; import org.aspectj.bridge.IMessage.Kind; import org.aspectj.bridge.IMessageContext; import org.aspectj.bridge.IMessageHandler; import org.aspectj.bridge.IMessageHolder; import org.aspectj.bridge.Message; import org.aspectj.bridge.MessageHandler; import org.aspectj.bridge.MessageUtil; import org.aspectj.bridge.MessageWriter; import org.aspectj.bridge.Version; import org.aspectj.bridge.WeaveMessage; import org.aspectj.util.FileUtil; import org.aspectj.util.LangUtil; import org.aspectj.weaver.IClassFileProvider; import org.aspectj.weaver.IUnwovenClassFile; import org.aspectj.weaver.IWeaveRequestor; import org.aspectj.weaver.World; import org.aspectj.weaver.bcel.BcelObjectType; import org.aspectj.weaver.bcel.BcelWeaver; import org.aspectj.weaver.bcel.BcelWorld; import org.aspectj.weaver.bcel.UnwovenClassFile; import org.aspectj.weaver.tools.cache.CachedClassEntry; import org.aspectj.weaver.tools.cache.CachedClassReference; import org.aspectj.weaver.tools.cache.SimpleCache; import org.aspectj.weaver.tools.cache.SimpleCacheFactory; import org.aspectj.weaver.tools.cache.WeavedClassCache; // OPTIMIZE add guards for all the debug/info/etc /** * This adaptor allows the AspectJ compiler to be embedded in an existing system to facilitate load-time weaving. It provides an * interface for a weaving class loader to provide a classpath to be woven by a set of aspects. A callback is supplied to allow a * class loader to define classes generated by the compiler during the weaving process. *
* A weaving class loader should create a WeavingAdaptor
before any classes are defined, typically during construction.
* The set of aspects passed to the adaptor is fixed for the lifetime of the adaptor although the classpath can be augmented. A
* system property can be set to allow verbose weaving messages to be written to the console.
*
*/
public class WeavingAdaptor implements IMessageContext {
/**
* System property used to turn on verbose weaving messages
*/
public static final String WEAVING_ADAPTOR_VERBOSE = "aj.weaving.verbose";
public static final String SHOW_WEAVE_INFO_PROPERTY = "org.aspectj.weaver.showWeaveInfo";
public static final String TRACE_MESSAGES_PROPERTY = "org.aspectj.tracing.messages";
private final static String ASPECTJ_BASE_PACKAGE = "org.aspectj.";
private final static String PACKAGE_INITIAL_CHARS = ASPECTJ_BASE_PACKAGE.charAt(0) + "sj";
private boolean enabled = false;
protected boolean verbose = getVerbose();
protected BcelWorld bcelWorld;
protected BcelWeaver weaver;
private IMessageHandler messageHandler;
private WeavingAdaptorMessageHolder messageHolder;
private boolean abortOnError = false;
protected GeneratedClassHandler generatedClassHandler;
protected MapClassLoader
*/
public WeavingAdaptor(WeavingClassLoader loader) {
// System.err.println("? WeavingAdaptor.GeneratedClassHandler
, a full search path for resolving classes
* and a complete set of aspects. The search path must include classes loaded by the class loader constructing the
* WeavingAdaptor and all its parents in the hierarchy.
*
* @param handler GeneratedClassHandler
* @param classURLs the URLs from which to resolve classes
* @param aspectURLs the aspects used to weave classes defined by this class loader
*/
public WeavingAdaptor(GeneratedClassHandler handler, URL[] classURLs, URL[] aspectURLs) {
// System.err.println("? WeavingAdaptor."java/util/List"
.
* @param bytes the input byte buffer in class file format - must not be modified
* @param mustWeave if true then this class must get woven (used for concrete aspects generated from XML)
*
* @return a well-formed class file buffer (the weaving result), or {@code null} if no weaving was performed
*
* @throws IOException weave failed
*
* @see java.lang.instrument.ClassFileTransformer#transform(ClassLoader, String, Class, ProtectionDomain, byte[])
*/
public byte[] weaveClass(String name, final byte[] bytes, boolean mustWeave) throws IOException {
if (trace == null) {
// Pr231945: we are likely to be under tomcat and ENABLE_CLEAR_REFERENCES hasn't been set
System.err
.println("AspectJ Weaver cannot continue to weave, static state has been cleared. Are you under Tomcat? In order to weave '"
+ name
+ "' during shutdown, 'org.apache.catalina.loader.WebappClassLoader.ENABLE_CLEAR_REFERENCES=false' must be set (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=231945).");
return null;
}
if (weaverRunning.get()) {
// System.out.println("AJC: avoiding re-entrant call to transform " + name);
return null;
}
try {
byte[] newBytes = null;
weaverRunning.set(true);
if (trace.isTraceEnabled()) {
trace.enter("weaveClass", this, new Object[] { name, bytes });
}
if (!enabled) {
if (trace.isTraceEnabled()) {
trace.exit("weaveClass", false);
}
return null;
}
boolean debugOn = !messageHandler.isIgnoring(Message.DEBUG);
name = name.replace('/', '.');
byte[] wovenBytes = wovenWithGeneratedClass(name);
if (wovenBytes != null) {
if (debugOn) {
debug("returning woven bytes for '" + name + "' that were generated by a previous weaving process");
}
return wovenBytes;
}
try {
delegateForCurrentClass = null;
if (shouldWeaveName(name)) {
if (accept(name, bytes)) {
// Determine if we have the weaved class cached
CachedClassReference cacheKey = null;
if (cache != null && !mustWeave) {
cacheKey = cache.createCacheKey(name, bytes);
CachedClassEntry entry = cache.get(cacheKey, bytes);
if (entry != null) {
// If the entry has been explicitly ignored
// return the original bytes
if (entry.isIgnored()) {
return null;
}
return entry.getBytes();
}
}
// TODO @AspectJ problem
// Annotation style aspects need to be included regardless in order to get
// a valid aspectOf()/hasAspect() generated in them. However - if they are excluded
// (via include/exclude in aop.xml) they really should only get aspectOf()/hasAspect()
// and not be included in the full set of aspects being applied by 'this' weaver
if (debugOn) {
debug("weaving '" + name + "'");
}
newBytes = getWovenBytes(name, bytes);
// TODO: Is this OK performance-wise?
if (Arrays.equals(bytes, newBytes)) {
// null means unchanged in java.lang.instrument.ClassFileTransformer::transform
newBytes = null;
}
// temporarily out - searching for @Aspect annotated types is a slow thing to do - we should
// expect the user to name them if they want them woven - just like code style
// } else if (shouldWeaveAnnotationStyleAspect(name, bytes)) {
// if (mustWeave) {
// if (bcelWorld.getLint().mustWeaveXmlDefinedAspects.isEnabled()) {
// bcelWorld.getLint().mustWeaveXmlDefinedAspects.signal(name, null);
// }
// }
// // an @AspectJ aspect needs to be at least munged by the aspectOf munger
// if (debugOn) {
// debug("weaving '" + name + "'");
// }
// bytes = getAtAspectJAspectBytes(name, bytes);
// Add the weaved class to the cache only if there
// has been an actual change
// JVK: Is there a better way to check if the class has
// been transformed without carrying up some value
// from the depths?
if (cacheKey != null) {
// If no transform has been applied, mark the class
// as ignored.
if (newBytes == null) {
cache.ignore(cacheKey, bytes);
} else {
cache.put(cacheKey, bytes, newBytes);
}
}
} else if (debugOn) {
debug("not weaving '" + name + "'");
}
} else if (debugOn) {
debug("cannot weave '" + name + "'");
}
} finally {
delegateForCurrentClass = null;
}
if (trace.isTraceEnabled()) {
trace.exit("weaveClass", newBytes);
}
return newBytes;
} finally {
weaverRunning.remove();
}
}
/**
* Return the bytes from a (parallel?) weaving process that generated an inner class, e.g. to support Around closures.
* This is done instead of weaving again, as weaving would generate another inner class.
* @param name
* @return the cached bytes of a previously woven class, or null if not found
*/
private byte[] wovenWithGeneratedClass(String name) {
IUnwovenClassFile woven = generatedClasses.get(name);
if (woven == null) {
return null;
}
return woven.getBytes();
}
// ATAJ
protected boolean accept(String name, byte[] bytes) {
return true;
}
protected boolean shouldDump(String name, boolean before) {
return false;
}
private boolean shouldWeaveName(String name) {
if (PACKAGE_INITIAL_CHARS.indexOf(name.charAt(0)) != -1) {
if ((weavingSpecialTypes & INITIALIZED) == 0) {
weavingSpecialTypes |= INITIALIZED;
// initialize it
Properties p = weaver.getWorld().getExtraConfiguration();
if (p != null) {
boolean b = p.getProperty(World.xsetWEAVE_JAVA_PACKAGES, "false").equalsIgnoreCase("true");
if (b) {
weavingSpecialTypes |= WEAVE_JAVA_PACKAGE;
}
b = p.getProperty(World.xsetWEAVE_JAVAX_PACKAGES, "false").equalsIgnoreCase("true");
if (b) {
weavingSpecialTypes |= WEAVE_JAVAX_PACKAGE;
}
}
}
if (name.startsWith(ASPECTJ_BASE_PACKAGE)) {
return false;
}
if (name.startsWith("sun.reflect.")) {// JDK reflect
return false;
}
if (name.startsWith("javax.")) {
if ((weavingSpecialTypes & WEAVE_JAVAX_PACKAGE) != 0) {
return true;
} else {
if (!haveWarnedOnJavax) {
haveWarnedOnJavax = true;
warn("javax.* types are not being woven because the weaver option '-Xset:weaveJavaxPackages=true' has not been specified");
}
return false;
}
}
if (name.startsWith("java.")) {
if ((weavingSpecialTypes & WEAVE_JAVA_PACKAGE) != 0) {
return true;
} else {
return false;
}
}
}
// boolean should = !(name.startsWith("org.aspectj.")
// || (name.startsWith("java.") && (weavingSpecialTypes & WEAVE_JAVA_PACKAGE) == 0)
// || (name.startsWith("javax.") && (weavingSpecialTypes & WEAVE_JAVAX_PACKAGE) == 0)
// // || name.startsWith("$Proxy")//JDK proxies//FIXME AV is that 1.3 proxy ? fe. ataspect.$Proxy0 is a java5 proxy...
// || name.startsWith("sun.reflect."));
return true;
}
/**
* We allow @AJ aspect weaving so that we can add aspectOf() as part of the weaving (and not part of the source compilation)
*
* @param name
* @param bytes bytecode (from classloader), allow to NOT lookup stuff on disk again during resolve
* @return true if @Aspect
*/
private boolean shouldWeaveAnnotationStyleAspect(String name, byte[] bytes) {
if (delegateForCurrentClass == null) {
// if (weaver.getWorld().isASMAround()) return asmCheckAnnotationStyleAspect(bytes);
// else
ensureDelegateInitialized(name, bytes);
}
return (delegateForCurrentClass.isAnnotationStyleAspect());
}
// private boolean asmCheckAnnotationStyleAspect(byte[] bytes) {
// IsAtAspectAnnotationVisitor detector = new IsAtAspectAnnotationVisitor();
//
// ClassReader cr = new ClassReader(bytes);
// try {
// cr.accept(detector, true);//, ClassReader.SKIP_DEBUG | ClassReader.SKIP_CODE | ClassReader.SKIP_FRAMES);
// } catch (Exception spe) {
// // if anything goes wrong, e.g., an NPE, then assume it's NOT an @AspectJ aspect...
// System.err.println("Unexpected problem parsing bytes to discover @Aspect annotation");
// spe.printStackTrace();
// return false;
// }
//
// return detector.isAspect();
// }
protected void ensureDelegateInitialized(String name, byte[] bytes) {
if (delegateForCurrentClass == null) {
BcelWorld world = (BcelWorld) weaver.getWorld();
delegateForCurrentClass = world.addSourceObjectType(name, bytes, false);
}
}
/**
* Weave a set of bytes defining a class.
*
* @param name the name of the class being woven
* @param bytes the bytes that define the class
* @return byte[] the woven bytes for the class
* @throws IOException
*/
private byte[] getWovenBytes(String name, byte[] bytes) throws IOException {
WeavingClassFileProvider wcp = new WeavingClassFileProvider(name, bytes);
weaver.weave(wcp);
return wcp.getBytes();
}
/**
* Weave a set of bytes defining a class for only what is needed to turn @AspectJ aspect in a usefull form ie with aspectOf
* method - see #113587
*
* @param name the name of the class being woven
* @param bytes the bytes that define the class
* @return byte[] the woven bytes for the class
* @throws IOException
*/
private byte[] getAtAspectJAspectBytes(String name, byte[] bytes) throws IOException {
WeavingClassFileProvider wcp = new WeavingClassFileProvider(name, bytes);
wcp.setApplyAtAspectJMungersOnly();
weaver.weave(wcp);
return wcp.getBytes();
}
private void registerAspectLibraries(ListfindClass()
request. The library is not required to be on the
* weavingClasspath given when this classloader was constructed.
*
* @param aspectLibraryJarFile a jar file representing an aspect library
*
* @throws IOException
*/
private void addAspectLibrary(String aspectLibraryName) {
File aspectLibrary = new File(aspectLibraryName);
if (aspectLibrary.isDirectory() || (FileUtil.isZipFile(aspectLibrary))) {
try {
info("adding aspect library: '" + aspectLibrary + "'");
weaver.addLibraryJarFile(aspectLibrary);
} catch (IOException ex) {
error("exception adding aspect library: '" + ex + "'");
}
} else {
error("bad aspect library: '" + aspectLibrary + "'");
}
}
private static List