| /* ******************************************************************* |
| * 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 v1.0 |
| * which accompanies this distribution and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * 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. |
| * <p> |
| * A weaving class loader should create a <code>WeavingAdaptor</code> 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 Map<String, IUnwovenClassFile> generatedClasses = new HashMap<String, IUnwovenClassFile>(); |
| public BcelObjectType delegateForCurrentClass; // lazily initialized, should be used to prevent parsing bytecode multiple |
| // times |
| 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() { |
| } |
| |
| /** |
| * Construct a WeavingAdaptor with a reference to a weaving class loader. The adaptor will automatically search the class loader |
| * hierarchy to resolve classes. The adaptor will also search the hierarchy for WeavingClassLoader instances to determine the |
| * set of aspects to be used for weaving. |
| * |
| * @param loader instance of <code>ClassLoader</code> |
| */ |
| public WeavingAdaptor(WeavingClassLoader loader) { |
| // System.err.println("? WeavingAdaptor.<init>(" + loader +"," + aspectURLs.length + ")"); |
| generatedClassHandler = loader; |
| init((ClassLoader)loader, getFullClassPath((ClassLoader) loader), getFullAspectPath((ClassLoader) loader/* ,aspectURLs */)); |
| } |
| |
| /** |
| * Construct a WeavingAdaptor with a reference to a <code>GeneratedClassHandler</code>, 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 <code>GeneratedClassHandler</code> |
| * @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.<init>()"); |
| 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"); |
| } |
| } |
| // On Java9 it is possible to fail to find a URLClassLoader from which to derive a suitable classpath |
| // For now we can determine it from the java.class.path: |
| 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); |
| } |
| } |
| } |
| // On Java9 the sun.boot.class.path won't be set. System classes accessible through JRT filesystem |
| 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) { |
| // security exception |
| return false; |
| } |
| } |
| |
| /** |
| * Initialize the WeavingAdapter |
| * @param loader ClassLoader used by this adapter; which can be null |
| * @param classPath classpath of this adapter |
| * @param aspectPath list of aspect paths |
| */ |
| 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; |
| } |
| |
| /** |
| * If the cache is enabled, initialize it and swap out the existing classhandler |
| * for the caching one - |
| * |
| * @param loader classloader for this adapter, may be null |
| * @param aspects List of strings representing aspects managed by the adapter; these could be urls or classnames |
| * @param existingClassHandler current class handler |
| * @param myMessageHandler current message handler |
| */ |
| protected void initializeCache(ClassLoader loader, List<String> aspects, GeneratedClassHandler existingClassHandler, IMessageHandler myMessageHandler) { |
| if (WeavedClassCache.isEnabled()) { |
| cache = WeavedClassCache.createCache(loader, aspects, existingClassHandler, myMessageHandler); |
| // Wrap the existing class handler so that any generated classes are also cached |
| 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()); //$NON-NLS-1$ |
| } |
| |
| 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; |
| } |
| |
| /** |
| * Appends URL to path used by the WeavingAdptor to resolve classes |
| * |
| * @param url to be appended to search path |
| */ |
| public void addURL(URL url) { |
| File libFile = new File(url.getPath()); |
| try { |
| weaver.addLibraryJarFile(libFile); |
| } catch (IOException ex) { |
| warn("bad library: '" + libFile + "'"); |
| } |
| } |
| |
| /** |
| * Weave a class using aspects previously supplied to the adaptor. |
| * |
| * @param name the name of the class |
| * @param bytes the class bytes |
| * @return the woven bytes |
| * @exception IOException weave failed |
| */ |
| public byte[] weaveClass(String name, byte[] bytes) throws IOException { |
| return weaveClass(name, bytes, false); |
| } |
| |
| // Track if the weaver is already running on this thread - don't allow re-entrant calls |
| private ThreadLocal<Boolean> weaverRunning = new ThreadLocal<Boolean>() { |
| @Override |
| protected Boolean initialValue() { |
| return Boolean.FALSE; |
| } |
| }; |
| |
| /** |
| * Weave a class using aspects previously supplied to the adaptor. |
| * |
| * @param name the name of the class |
| * @param bytes the class bytes |
| * @param mustWeave if true then this class *must* get woven (used for concrete aspects generated from XML) |
| * @return the woven bytes |
| * @exception IOException weave failed |
| */ |
| public byte[] weaveClass(String name, 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 bytes; |
| } |
| if (weaverRunning.get()) { |
| // System.out.println("AJC: avoiding re-entrant call to transform " + name); |
| 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)) { |
| |
| // Determine if we have the weaved class cached |
| 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 the entry has been explicitly ignored |
| // return the original bytes |
| if (entry.isIgnored()) { |
| return bytes; |
| } |
| 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 + "'"); |
| } |
| bytes = getWovenBytes(name, bytes); |
| // 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 (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.set(false); |
| } |
| } |
| |
| /** |
| * @param name |
| * @return true if even valid to weave: either with an accept check or to munge it for @AspectJ aspectof support |
| */ |
| private boolean couldWeave(String name, byte[] bytes) { |
| return !generatedClasses.containsKey(name) && shouldWeaveName(name); |
| } |
| |
| // 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(List aspectPath) { |
| // System.err.println("? WeavingAdaptor.registerAspectLibraries(" + aspectPath + ")"); |
| for (Iterator i = aspectPath.iterator(); i.hasNext();) { |
| String libName = (String) i.next(); |
| addAspectLibrary(libName); |
| } |
| |
| weaver.prepareForWeave(); |
| } |
| |
| /* |
| * Register an aspect library with this classloader for use during weaving. This class loader will also return (unmodified) any |
| * of the classes in the library in response to a <code>findClass()</code> 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<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"; |
| } |
| |
| /** |
| * Dump the given bytcode in _dump/... (dev mode) |
| * |
| * @param name |
| * @param b |
| * @param before whether we are dumping before weaving |
| * @throws Throwable |
| */ |
| 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 { |
| // System.out.println("WeavingAdaptor.dump() fileName=" + new File(fileName).getAbsolutePath()); |
| FileOutputStream os = new FileOutputStream(fileName); |
| os.write(b); |
| os.close(); |
| } catch (IOException ex) { |
| warn("unable to dump class " + name + " in directory " + dirName, ex); |
| } |
| } |
| |
| /** |
| * @return the directory in which to dump - default is _ajdump but it |
| */ |
| protected String getDumpDir() { |
| return "_ajdump"; |
| } |
| |
| /** |
| * Processes messages arising from weaver operations. Tell weaver to abort on any message more severe than warning. |
| */ |
| 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); |
| } |
| } |
| // accumulating = false; |
| // messages.clear(); |
| } |
| |
| public void setDelegate(IMessageHandler messageHandler) { |
| delegate = messageHandler; |
| } |
| |
| /* |
| * IMessageHandler |
| */ |
| |
| @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 (accumulating) { |
| // boolean result = addMessage(message); |
| // if (abortOnError && 0 <= message.getKind().compareTo(IMessage.ERROR)) { |
| // throw new AbortException(message); |
| // } |
| // return result; |
| // } |
| // else return delegate.handleMessage(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); |
| } |
| } |
| |
| /* |
| * IMessageHolder |
| */ |
| |
| @Override |
| public List<IMessage> getUnmodifiableListView() { |
| // System.err.println("? WeavingAdaptorMessageHolder.getUnmodifiableListView() savedMessages=" + savedMessages); |
| 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 { |
| // boolean result = |
| 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))); |
| } |
| |
| /** |
| * Set a message kind to be ignored from now on |
| */ |
| @Override |
| public void ignore(IMessage.Kind kind) { |
| if ((null != kind) && (!ignoring.contains(kind))) { |
| ignoring.add(kind); |
| } |
| } |
| |
| /** |
| * Remove a message kind from the list of those ignored from now on. |
| */ |
| @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 { |
| // Classes generated by weaver e.g. around closure advice |
| 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() { |
| // ResolvedType.resetPrimitives(); |
| if (delegateForCurrentClass != null) { |
| delegateForCurrentClass.weavingCompleted(); |
| } |
| // ResolvedType.resetPrimitives(); |
| // bcelWorld.discardType(typeBeingProcessed.getResolvedTypeX()); // work in progress |
| } |
| }; |
| } |
| } |
| |
| public void setActiveProtectionDomain(ProtectionDomain protectionDomain) { |
| activeProtectionDomain = protectionDomain; |
| } |
| } |