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;
public class WeavingAdaptor implements IMessageContext {
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 Map<String, IUnwovenClassFile> generatedClasses = new HashMap<String, IUnwovenClassFile>();
public BcelObjectType delegateForCurrentClass;
protected ProtectionDomain activeProtectionDomain;
private boolean haveWarnedOnJavax = false;
protected WeavedClassCache cache;
private int weavingSpecialTypes = 0;
private static final int INITIALIZED = 0x1;
private static final int WEAVE_JAVA_PACKAGE = 0x2;
private static final int WEAVE_JAVAX_PACKAGE = 0x4;
private static Trace trace = TraceFactory.getTraceFactory().getTrace(WeavingAdaptor.class);
protected WeavingAdaptor() {
}
public WeavingAdaptor(WeavingClassLoader loader) {
generatedClassHandler = loader;
init((ClassLoader)loader, getFullClassPath((ClassLoader) loader), getFullAspectPath((ClassLoader) loader));
}
public WeavingAdaptor(GeneratedClassHandler handler, URL[] classURLs, URL[] aspectURLs) {
generatedClassHandler = handler;
init(null, FileUtil.makeClasspath(classURLs), FileUtil.makeClasspath(aspectURLs));
}
protected List<String> getFullClassPath(ClassLoader loader) {
List<String> list = new LinkedList<String>();
for (; loader != null; loader = loader.getParent()) {
if (loader instanceof URLClassLoader) {
URL[] urls = ((URLClassLoader) loader).getURLs();
list.addAll(0, FileUtil.makeClasspath(urls));
} else {
warn("cannot determine classpath");
}
}
if (LangUtil.is19VMOrGreater()) {
list.add(0, LangUtil.getJrtFsFilePath());
List<String> javaClassPathEntries = makeClasspath(System.getProperty("java.class.path"));
for (int i=javaClassPathEntries.size()-1;i>=0;i--) {
String javaClassPathEntry = javaClassPathEntries.get(i);
if (!list.contains(javaClassPathEntry)) {
list.add(0,javaClassPathEntry);
}
}
}
list.addAll(0, makeClasspath(System.getProperty("sun.boot.class.path")));
return list;
}
private List<String> getFullAspectPath(ClassLoader loader) {
List<String> list = new LinkedList<String>();
for (; loader != null; loader = loader.getParent()) {
if (loader instanceof WeavingClassLoader) {
URL[] urls = ((WeavingClassLoader) loader).getAspectURLs();
list.addAll(0, FileUtil.makeClasspath(urls));
}
}
return list;
}
private static boolean getVerbose() {
try {
return Boolean.getBoolean(WEAVING_ADAPTOR_VERBOSE);
} catch (Throwable t) {
return false;
}
}
private void init(ClassLoader loader, List<String> classPath, List<String> aspectPath) {
abortOnError = true;
createMessageHandler();
info("using classpath: " + classPath);
info("using aspectpath: " + aspectPath);
bcelWorld = new BcelWorld(classPath, messageHandler, null);
bcelWorld.setXnoInline(false);
bcelWorld.getLint().loadDefaultProperties();
if (LangUtil.is15VMOrGreater()) {
bcelWorld.setBehaveInJava5Way(true);
}
weaver = new BcelWeaver(bcelWorld);
registerAspectLibraries(aspectPath);
initializeCache(loader, aspectPath, null, getMessageHandler());
enabled = true;
}
protected void initializeCache(ClassLoader loader, List<String> aspects, GeneratedClassHandler existingClassHandler, IMessageHandler myMessageHandler) {
if (WeavedClassCache.isEnabled()) {
cache = WeavedClassCache.createCache(loader, aspects, existingClassHandler, myMessageHandler);
if (cache != null) {
this.generatedClassHandler = cache.getCachingClassHandler();
}
}
}
protected void createMessageHandler() {
messageHolder = new WeavingAdaptorMessageHolder(new PrintWriter(System.err));
messageHandler = messageHolder;
if (verbose) {
messageHandler.dontIgnore(IMessage.INFO);
}
if (Boolean.getBoolean(SHOW_WEAVE_INFO_PROPERTY)) {
messageHandler.dontIgnore(IMessage.WEAVEINFO);
}
info("AspectJ Weaver Version " + Version.getText() + " built on " + Version.getTimeText());
}
protected IMessageHandler getMessageHandler() {
return messageHandler;
}
public IMessageHolder getMessageHolder() {
return messageHolder;
}
protected void setMessageHandler(IMessageHandler mh) {
if (mh instanceof ISupportsMessageContext) {
ISupportsMessageContext smc = (ISupportsMessageContext) mh;
smc.setMessageContext(this);
}
if (mh != messageHolder) {
messageHolder.setDelegate(mh);
}
messageHolder.flushMessages();
}
protected void disable() {
if (trace.isTraceEnabled()) {
trace.enter("disable", this);
}
enabled = false;
messageHolder.flushMessages();
if (trace.isTraceEnabled()) {
trace.exit("disable");
}
}
protected void enable() {
enabled = true;
messageHolder.flushMessages();
}
protected boolean isEnabled() {
return enabled;
}
public void addURL(URL url) {
File libFile = new File(url.getPath());
try {
weaver.addLibraryJarFile(libFile);
} catch (IOException ex) {
warn("bad library: '" + libFile + "'");
}
}
public byte[] weaveClass(String name, byte[] bytes) throws IOException {
return weaveClass(name, bytes, false);
}
private ThreadLocal<Boolean> weaverRunning = new ThreadLocal<Boolean>() {
@Override
protected Boolean initialValue() {
return Boolean.FALSE;
}
};
public byte[] weaveClass(String name, byte[] bytes, boolean mustWeave) throws IOException {
if (trace == null) {
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 bytes;
}
if (weaverRunning.get()) {
return bytes;
}
try {
weaverRunning.set(true);
if (trace.isTraceEnabled()) {
trace.enter("weaveClass", this, new Object[] { name, bytes });
}
if (!enabled) {
if (trace.isTraceEnabled()) {
trace.exit("weaveClass", false);
}
return bytes;
}
boolean debugOn = !messageHandler.isIgnoring(Message.DEBUG);
try {
delegateForCurrentClass = null;
name = name.replace('/', '.');
if (couldWeave(name, bytes)) {
if (accept(name, bytes)) {
CachedClassReference cacheKey = null;
final byte[] original_bytes = bytes;
if (cache != null && !mustWeave) {
cacheKey = cache.createCacheKey(name, original_bytes);
CachedClassEntry entry = cache.get(cacheKey, original_bytes);
if (entry != null) {
if (entry.isIgnored()) {
return bytes;
}
return entry.getBytes();
}
}
if (debugOn) {
debug("weaving '" + name + "'");
}
bytes = getWovenBytes(name, bytes);
if (cacheKey != null) {
if (Arrays.equals(original_bytes, bytes)) {
cache.ignore(cacheKey, original_bytes);
} else {
cache.put(cacheKey, original_bytes, bytes);
}
}
} else if (debugOn) {
debug("not weaving '" + name + "'");
}
} else if (debugOn) {
debug("cannot weave '" + name + "'");
}
} finally {
delegateForCurrentClass = null;
}
if (trace.isTraceEnabled()) {
trace.exit("weaveClass", bytes);
}
return bytes;
} finally {
weaverRunning.remove();
}
}
private boolean couldWeave(String name, byte[] bytes) {
return !generatedClasses.containsKey(name) && shouldWeaveName(name);
}
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;
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.")) {
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;
}
}
}
return true;
}
private boolean shouldWeaveAnnotationStyleAspect(String name, byte[] bytes) {
if (delegateForCurrentClass == null) {
ensureDelegateInitialized(name, bytes);
}
return (delegateForCurrentClass.isAnnotationStyleAspect());
}
protected void ensureDelegateInitialized(String name, byte[] bytes) {
if (delegateForCurrentClass == null) {
BcelWorld world = (BcelWorld) weaver.getWorld();
delegateForCurrentClass = world.addSourceObjectType(name, bytes, false);
}
}
private byte[] getWovenBytes(String name, byte[] bytes) throws IOException {
WeavingClassFileProvider wcp = new WeavingClassFileProvider(name, bytes);
weaver.weave(wcp);
return wcp.getBytes();
}
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(List aspectPath) {
for (Iterator i = aspectPath.iterator(); i.hasNext();) {
String libName = (String) i.next();
addAspectLibrary(libName);
}
weaver.prepareForWeave();
}
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<String> makeClasspath(String cp) {
List<String> ret = new ArrayList<String>();
if (cp != null) {
StringTokenizer tok = new StringTokenizer(cp, File.pathSeparator);
while (tok.hasMoreTokens()) {
ret.add(tok.nextToken());
}
}
return ret;
}
protected boolean debug(String message) {
return MessageUtil.debug(messageHandler, message);
}
protected boolean info(String message) {
return MessageUtil.info(messageHandler, message);
}
protected boolean warn(String message) {
return MessageUtil.warn(messageHandler, message);
}
protected boolean warn(String message, Throwable th) {
return messageHandler.handleMessage(new Message(message, IMessage.WARNING, th, null));
}
protected boolean error(String message) {
return MessageUtil.error(messageHandler, message);
}
protected boolean error(String message, Throwable th) {
return messageHandler.handleMessage(new Message(message, IMessage.ERROR, th, null));
}
public String getContextId() {
return "WeavingAdaptor";
}
protected void dump(String name, byte[] b, boolean before) {
String dirName = getDumpDir();
if (before) {
dirName = dirName + File.separator + "_before";
}
String className = name.replace('.', '/');
final File dir;
if (className.indexOf('/') > 0) {
dir = new File(dirName + File.separator + className.substring(0, className.lastIndexOf('/')));
} else {
dir = new File(dirName);
}
dir.mkdirs();
String fileName = dirName + File.separator + className + ".class";
try {
FileOutputStream os = new FileOutputStream(fileName);
os.write(b);
os.close();
} catch (IOException ex) {
warn("unable to dump class " + name + " in directory " + dirName, ex);
}
}
protected String getDumpDir() {
return "_ajdump";
}
protected class WeavingAdaptorMessageHolder extends MessageHandler {
private IMessageHandler delegate;
private List<IMessage> savedMessages;
protected boolean traceMessages = Boolean.getBoolean(TRACE_MESSAGES_PROPERTY);
public WeavingAdaptorMessageHolder(PrintWriter writer) {
this.delegate = new WeavingAdaptorMessageWriter(writer);
super.dontIgnore(IMessage.WEAVEINFO);
}
private void traceMessage(IMessage message) {
if (message instanceof WeaveMessage) {
trace.debug(render(message));
} else if (message.isDebug()) {
trace.debug(render(message));
} else if (message.isInfo()) {
trace.info(render(message));
} else if (message.isWarning()) {
trace.warn(render(message), message.getThrown());
} else if (message.isError()) {
trace.error(render(message), message.getThrown());
} else if (message.isFailed()) {
trace.fatal(render(message), message.getThrown());
} else if (message.isAbort()) {
trace.fatal(render(message), message.getThrown());
} else {
trace.error(render(message), message.getThrown());
}
}
protected String render(IMessage message) {
return "[" + getContextId() + "] " + message.toString();
}
public void flushMessages() {
if (savedMessages == null) {
savedMessages = new ArrayList<IMessage>();
savedMessages.addAll(super.getUnmodifiableListView());
clearMessages();
for (IMessage message : savedMessages) {
delegate.handleMessage(message);
}
}
}
public void setDelegate(IMessageHandler messageHandler) {
delegate = messageHandler;
}
@Override
public boolean handleMessage(IMessage message) throws AbortException {
if (traceMessages) {
traceMessage(message);
}
super.handleMessage(message);
if (abortOnError && 0 <= message.getKind().compareTo(IMessage.ERROR)) {
throw new AbortException(message);
}
if (savedMessages != null) {
delegate.handleMessage(message);
}
return true;
}
@Override
public boolean isIgnoring(Kind kind) {
return delegate.isIgnoring(kind);
}
@Override
public void dontIgnore(IMessage.Kind kind) {
if (null != kind && delegate != null) {
delegate.dontIgnore(kind);
}
}
@Override
public void ignore(Kind kind) {
if (null != kind && delegate != null) {
delegate.ignore(kind);
}
}
@Override
public List<IMessage> getUnmodifiableListView() {
List<IMessage> allMessages = new ArrayList<IMessage>();
allMessages.addAll(savedMessages);
allMessages.addAll(super.getUnmodifiableListView());
return allMessages;
}
}
protected class WeavingAdaptorMessageWriter extends MessageWriter {
private final Set<IMessage.Kind> ignoring = new HashSet<IMessage.Kind>();
private final IMessage.Kind failKind;
public WeavingAdaptorMessageWriter(PrintWriter writer) {
super(writer, true);
ignore(IMessage.WEAVEINFO);
ignore(IMessage.DEBUG);
ignore(IMessage.INFO);
this.failKind = IMessage.ERROR;
}
@Override
public boolean handleMessage(IMessage message) throws AbortException {
super.handleMessage(message);
if (abortOnError && 0 <= message.getKind().compareTo(failKind)) {
throw new AbortException(message);
}
return true;
}
@Override
public boolean isIgnoring(Kind kind) {
return ((null != kind) && (ignoring.contains(kind)));
}
@Override
public void ignore(IMessage.Kind kind) {
if ((null != kind) && (!ignoring.contains(kind))) {
ignoring.add(kind);
}
}
@Override
public void dontIgnore(IMessage.Kind kind) {
if (null != kind) {
ignoring.remove(kind);
}
}
@Override
protected String render(IMessage message) {
return "[" + getContextId() + "] " + super.render(message);
}
}
private class WeavingClassFileProvider implements IClassFileProvider {
private final UnwovenClassFile unwovenClass;
private final List<UnwovenClassFile> unwovenClasses = new ArrayList<UnwovenClassFile>();
private IUnwovenClassFile wovenClass;
private boolean isApplyAtAspectJMungersOnly = false;
public WeavingClassFileProvider(String name, byte[] bytes) {
ensureDelegateInitialized(name, bytes);
this.unwovenClass = new UnwovenClassFile(name, delegateForCurrentClass.getResolvedTypeX().getName(), bytes);
this.unwovenClasses.add(unwovenClass);
if (shouldDump(name.replace('/', '.'), true)) {
dump(name, bytes, true);
}
}
public void setApplyAtAspectJMungersOnly() {
isApplyAtAspectJMungersOnly = true;
}
public boolean isApplyAtAspectJMungersOnly() {
return isApplyAtAspectJMungersOnly;
}
public byte[] getBytes() {
if (wovenClass != null) {
return wovenClass.getBytes();
} else {
return unwovenClass.getBytes();
}
}
public Iterator<UnwovenClassFile> getClassFileIterator() {
return unwovenClasses.iterator();
}
public IWeaveRequestor getRequestor() {
return new IWeaveRequestor() {
public void acceptResult(IUnwovenClassFile result) {
if (wovenClass == null) {
wovenClass = result;
String name = result.getClassName();
if (shouldDump(name.replace('/', '.'), false)) {
dump(name, result.getBytes(), false);
}
} else {
String className = result.getClassName();
byte[] resultBytes = result.getBytes();
if (SimpleCacheFactory.isEnabled()) {
SimpleCache lacache=SimpleCacheFactory.createSimpleCache();
lacache.put(result.getClassName(), wovenClass.getBytes(), result.getBytes());
lacache.addGeneratedClassesNames(wovenClass.getClassName(), wovenClass.getBytes(), result.getClassName());
}
generatedClasses.put(className, result);
generatedClasses.put(wovenClass.getClassName(), result);
generatedClassHandler.acceptClass(className, null, resultBytes);
}
}
public void processingReweavableState() {
}
public void addingTypeMungers() {
}
public void weavingAspects() {
}
public void weavingClasses() {
}
public void weaveCompleted() {
if (delegateForCurrentClass != null) {
delegateForCurrentClass.weavingCompleted();
}
}
};
}
}
public void setActiveProtectionDomain(ProtectionDomain protectionDomain) {
activeProtectionDomain = protectionDomain;
}
}