You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

WeavingAdaptor.java 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. /* *******************************************************************
  2. * Copyright (c) 2004 IBM Corporation
  3. * All rights reserved.
  4. * This program and the accompanying materials are made available
  5. * under the terms of the Common Public License v1.0
  6. * which accompanies this distribution and is available at
  7. * http://www.eclipse.org/legal/cpl-v10.html
  8. *
  9. * Contributors:
  10. * Matthew Webster, Adrian Colyer,
  11. * Martin Lippert initial implementation
  12. * ******************************************************************/
  13. package org.aspectj.weaver.tools;
  14. import java.io.File;
  15. import java.io.IOException;
  16. import java.io.PrintWriter;
  17. import java.net.URL;
  18. import java.net.URLClassLoader;
  19. import java.util.ArrayList;
  20. import java.util.HashMap;
  21. import java.util.HashSet;
  22. import java.util.Iterator;
  23. import java.util.LinkedList;
  24. import java.util.List;
  25. import java.util.Map;
  26. import java.util.Set;
  27. import java.util.StringTokenizer;
  28. import org.aspectj.bridge.AbortException;
  29. import org.aspectj.bridge.IMessage;
  30. import org.aspectj.bridge.IMessageHandler;
  31. import org.aspectj.bridge.MessageUtil;
  32. import org.aspectj.bridge.MessageWriter;
  33. import org.aspectj.bridge.IMessage.Kind;
  34. import org.aspectj.util.FileUtil;
  35. import org.aspectj.util.LangUtil;
  36. import org.aspectj.weaver.IClassFileProvider;
  37. import org.aspectj.weaver.IWeaveRequestor;
  38. import org.aspectj.weaver.bcel.BcelWeaver;
  39. import org.aspectj.weaver.bcel.BcelWorld;
  40. import org.aspectj.weaver.bcel.UnwovenClassFile;
  41. /**
  42. * This adaptor allows the AspectJ compiler to be embedded in an existing
  43. * system to facilitate load-time weaving. It provides an interface for a
  44. * weaving class loader to provide a classpath to be woven by a set of
  45. * aspects. A callback is supplied to allow a class loader to define classes
  46. * generated by the compiler during the weaving process.
  47. * <p>
  48. * A weaving class loader should create a <code>WeavingAdaptor</code> before
  49. * any classes are defined, typically during construction. The set of aspects
  50. * passed to the adaptor is fixed for the lifetime of the adaptor although the
  51. * classpath can be augmented. A system property can be set to allow verbose
  52. * weaving messages to be written to the console.
  53. *
  54. */
  55. public class WeavingAdaptor {
  56. /**
  57. * System property used to turn on verbose weaving messages
  58. */
  59. public static final String WEAVING_ADAPTOR_VERBOSE = "aj.weaving.verbose";
  60. public static final String SHOW_WEAVE_INFO_PROPERTY = "org.aspectj.weaver.showWeaveInfo";
  61. protected boolean enabled = true;
  62. protected boolean verbose = getVerbose();
  63. protected BcelWorld bcelWorld = null;
  64. protected BcelWeaver weaver = null;
  65. protected IMessageHandler/*WeavingAdaptorMessageHandler*/ messageHandler = null;
  66. protected GeneratedClassHandler generatedClassHandler;
  67. protected Map generatedClasses = new HashMap(); /* String -> UnwovenClassFile */
  68. protected WeavingAdaptor () {
  69. createMessageHandler();
  70. }
  71. /**
  72. * Construct a WeavingAdaptor with a reference to a weaving class loader. The
  73. * adaptor will automatically search the class loader hierarchy to resolve
  74. * classes. The adaptor will also search the hierarchy for WeavingClassLoader
  75. * instances to determine the set of aspects to be used ofr weaving.
  76. * @param loader instance of <code>ClassLoader</code>
  77. */
  78. public WeavingAdaptor (WeavingClassLoader loader) {
  79. // System.err.println("? WeavingAdaptor.<init>(" + loader +"," + aspectURLs.length + ")");
  80. generatedClassHandler = loader;
  81. init(getFullClassPath((ClassLoader)loader),getFullAspectPath((ClassLoader)loader/*,aspectURLs*/));
  82. }
  83. /**
  84. * Construct a WeavingAdator with a reference to a
  85. * <code>GeneratedClassHandler</code>, a full search path for resolving
  86. * classes and a complete set of aspects. The search path must include
  87. * classes loaded by the class loader constructing the WeavingAdaptor and
  88. * all its parents in the hierarchy.
  89. * @param handler <code>GeneratedClassHandler</code>
  90. * @param classURLs the URLs from which to resolve classes
  91. * @param aspectURLs the aspects used to weave classes defined by this class loader
  92. */
  93. public WeavingAdaptor (GeneratedClassHandler handler, URL[] classURLs, URL[] aspectURLs) {
  94. // System.err.println("? WeavingAdaptor.<init>()");
  95. generatedClassHandler = handler;
  96. init(FileUtil.makeClasspath(classURLs),FileUtil.makeClasspath(aspectURLs));
  97. }
  98. private List getFullClassPath (ClassLoader loader) {
  99. List list = new LinkedList();
  100. for (; loader != null; loader = loader.getParent()) {
  101. if (loader instanceof URLClassLoader) {
  102. URL[] urls = ((URLClassLoader)loader).getURLs();
  103. list.addAll(0,FileUtil.makeClasspath(urls));
  104. }
  105. else {
  106. warn("cannot determine classpath");
  107. }
  108. }
  109. list.addAll(0,makeClasspath(System.getProperty("sun.boot.class.path")));
  110. return list;
  111. }
  112. private List getFullAspectPath (ClassLoader loader) {
  113. List list = new LinkedList();
  114. for (; loader != null; loader = loader.getParent()) {
  115. if (loader instanceof WeavingClassLoader) {
  116. URL[] urls = ((WeavingClassLoader)loader).getAspectURLs();
  117. list.addAll(0,FileUtil.makeClasspath(urls));
  118. }
  119. }
  120. return list;
  121. }
  122. private static boolean getVerbose () {
  123. return Boolean.getBoolean(WEAVING_ADAPTOR_VERBOSE);
  124. }
  125. private void init(List classPath, List aspectPath) {
  126. createMessageHandler();
  127. info("using classpath: " + classPath);
  128. info("using aspectpath: " + aspectPath);
  129. bcelWorld = new BcelWorld(classPath,messageHandler,null);
  130. bcelWorld.setXnoInline(false);
  131. bcelWorld.getLint().loadDefaultProperties();
  132. if (LangUtil.is15VMOrGreater()) {
  133. bcelWorld.setBehaveInJava5Way(true);
  134. }
  135. weaver = new BcelWeaver(bcelWorld);
  136. registerAspectLibraries(aspectPath);
  137. }
  138. private void createMessageHandler() {
  139. messageHandler = new WeavingAdaptorMessageHandler(new PrintWriter(System.err));
  140. if (verbose) messageHandler.dontIgnore(IMessage.INFO);
  141. if (Boolean.getBoolean(SHOW_WEAVE_INFO_PROPERTY)) messageHandler.dontIgnore(IMessage.WEAVEINFO);
  142. }
  143. /**
  144. * Appends URL to path used by the WeavingAdptor to resolve classes
  145. * @param url to be appended to search path
  146. */
  147. public void addURL(URL url) {
  148. File libFile = new File(url.getPath());
  149. try {
  150. weaver.addLibraryJarFile(libFile);
  151. }
  152. catch (IOException ex) {
  153. warn("bad library: '" + libFile + "'");
  154. }
  155. }
  156. /**
  157. * Weave a class using aspects previously supplied to the adaptor.
  158. * @param name the name of the class
  159. * @param bytes the class bytes
  160. * @return the woven bytes
  161. * @exception IOException weave failed
  162. */
  163. public byte[] weaveClass (String name, byte[] bytes) throws IOException {
  164. if (shouldWeave(name, bytes)) {
  165. //System.out.println("WeavingAdaptor.weaveClass " + name);
  166. info("weaving '" + name + "'");
  167. bytes = getWovenBytes(name, bytes);
  168. } else if (shouldWeaveAnnotationStyleAspect(name, bytes)) {
  169. // an @AspectJ aspect needs to be at least munged by the aspectOf munger
  170. info("weaving '" + name + "'");
  171. bytes = getAtAspectJAspectBytes(name, bytes);
  172. }
  173. return bytes;
  174. }
  175. /**
  176. * @param name
  177. * @return true if should weave (but maybe we still need to munge it for @AspectJ aspectof support)
  178. */
  179. private boolean shouldWeave (String name, byte[] bytes) {
  180. name = name.replace('/','.');
  181. boolean b = enabled && !generatedClasses.containsKey(name) && shouldWeaveName(name);
  182. return b && accept(name, bytes);
  183. // && shouldWeaveAnnotationStyleAspect(name);
  184. // // we recall shouldWeaveAnnotationStyleAspect as we need to add aspectOf methods for @Aspect anyway
  185. // //FIXME AV - this is half ok as the aspect will be weaved by others. In theory if the aspect
  186. // // is excluded from include/exclude config we should only weave late type mungers for aspectof
  187. // return b && (accept(name) || shouldWeaveAnnotationStyleAspect(name));
  188. }
  189. //ATAJ
  190. protected boolean accept(String name, byte[] bytes) {
  191. return true;
  192. }
  193. public boolean shouldDump(String name) {
  194. return false;
  195. }
  196. private boolean shouldWeaveName (String name) {
  197. return !((/*(name.startsWith("org.apache.bcel.")//FIXME AV why ? bcel is wrapped in org.aspectj.
  198. ||*/ name.startsWith("org.aspectj.")
  199. || name.startsWith("java.")
  200. || name.startsWith("javax."))
  201. //|| name.startsWith("$Proxy")//JDK proxies//FIXME AV is that 1.3 proxy ? fe. ataspect.$Proxy0 is a java5 proxy...
  202. || name.startsWith("sun.reflect."));//JDK reflect
  203. }
  204. /**
  205. * We allow @AJ aspect weaving so that we can add aspectOf() as part of the weaving
  206. * (and not part of the source compilation)
  207. *
  208. * @param name
  209. * @param bytes bytecode (from classloader), allow to NOT lookup stuff on disk again during resolve
  210. * @return true if @Aspect
  211. */
  212. private boolean shouldWeaveAnnotationStyleAspect(String name, byte[] bytes) {
  213. // AV: instead of doing resolve that would lookup stuff on disk thru BCEL ClassLoaderRepository
  214. // we reuse bytes[] here to do a fast lookup for @Aspect annotation
  215. return bcelWorld.isAnnotationStyleAspect(name, bytes);
  216. }
  217. /**
  218. * Weave a set of bytes defining a class.
  219. * @param name the name of the class being woven
  220. * @param bytes the bytes that define the class
  221. * @return byte[] the woven bytes for the class
  222. * @throws IOException
  223. */
  224. private byte[] getWovenBytes(String name, byte[] bytes) throws IOException {
  225. WeavingClassFileProvider wcp = new WeavingClassFileProvider(name,bytes);
  226. weaver.weave(wcp);
  227. return wcp.getBytes();
  228. }
  229. /**
  230. * Weave a set of bytes defining a class for only what is needed to turn @AspectJ aspect
  231. * in a usefull form ie with aspectOf method - see #113587
  232. * @param name the name of the class being woven
  233. * @param bytes the bytes that define the class
  234. * @return byte[] the woven bytes for the class
  235. * @throws IOException
  236. */
  237. private byte[] getAtAspectJAspectBytes(String name, byte[] bytes) throws IOException {
  238. WeavingClassFileProvider wcp = new WeavingClassFileProvider(name,bytes);
  239. wcp.setApplyAtAspectJMungersOnly();
  240. weaver.weave(wcp);
  241. return wcp.getBytes();
  242. }
  243. private void registerAspectLibraries(List aspectPath) {
  244. // System.err.println("? WeavingAdaptor.registerAspectLibraries(" + aspectPath + ")");
  245. for (Iterator i = aspectPath.iterator(); i.hasNext();) {
  246. String libName = (String)i.next();
  247. addAspectLibrary(libName);
  248. }
  249. weaver.prepareForWeave();
  250. }
  251. /*
  252. * Register an aspect library with this classloader for use during
  253. * weaving. This class loader will also return (unmodified) any of the
  254. * classes in the library in response to a <code>findClass()</code> request.
  255. * The library is not required to be on the weavingClasspath given when this
  256. * classloader was constructed.
  257. * @param aspectLibraryJarFile a jar file representing an aspect library
  258. * @throws IOException
  259. */
  260. private void addAspectLibrary(String aspectLibraryName) {
  261. File aspectLibrary = new File(aspectLibraryName);
  262. if (aspectLibrary.isDirectory()
  263. || (aspectLibrary.isFile()
  264. && FileUtil.hasZipSuffix(aspectLibraryName))) {
  265. try {
  266. info("adding aspect library: '" + aspectLibrary + "'");
  267. weaver.addLibraryJarFile(aspectLibrary);
  268. } catch (IOException ex) {
  269. error("exception adding aspect library: '" + ex + "'");
  270. }
  271. } else {
  272. error("bad aspect library: '" + aspectLibrary + "'");
  273. }
  274. }
  275. private static List makeClasspath(String cp) {
  276. List ret = new ArrayList();
  277. if (cp != null) {
  278. StringTokenizer tok = new StringTokenizer(cp,File.pathSeparator);
  279. while (tok.hasMoreTokens()) {
  280. ret.add(tok.nextToken());
  281. }
  282. }
  283. return ret;
  284. }
  285. protected boolean info (String message) {
  286. return MessageUtil.info(messageHandler,message);
  287. }
  288. protected boolean warn (String message) {
  289. return MessageUtil.warn(messageHandler,message);
  290. }
  291. protected boolean error (String message) {
  292. return MessageUtil.error(messageHandler,message);
  293. }
  294. /**
  295. * Processes messages arising from weaver operations.
  296. * Tell weaver to abort on any message more severe than warning.
  297. */
  298. protected class WeavingAdaptorMessageHandler extends MessageWriter {
  299. private Set ignoring = new HashSet();
  300. private IMessage.Kind failKind;
  301. public WeavingAdaptorMessageHandler (PrintWriter writer) {
  302. super(writer,true);
  303. ignore(IMessage.WEAVEINFO);
  304. ignore(IMessage.INFO);
  305. this.failKind = IMessage.ERROR;
  306. }
  307. public boolean handleMessage(IMessage message) throws AbortException {
  308. boolean result = super.handleMessage(message);
  309. if (0 <= message.getKind().compareTo(failKind)) {
  310. throw new AbortException(message);
  311. }
  312. return true;
  313. }
  314. public boolean isIgnoring (Kind kind) {
  315. return ((null != kind) && (ignoring.contains(kind)));
  316. }
  317. /**
  318. * Set a message kind to be ignored from now on
  319. */
  320. public void ignore (IMessage.Kind kind) {
  321. if ((null != kind) && (!ignoring.contains(kind))) {
  322. ignoring.add(kind);
  323. }
  324. }
  325. /**
  326. * Remove a message kind from the list of those ignored from now on.
  327. */
  328. public void dontIgnore (IMessage.Kind kind) {
  329. if (null != kind) {
  330. ignoring.remove(kind);
  331. }
  332. }
  333. }
  334. private class WeavingClassFileProvider implements IClassFileProvider {
  335. private List unwovenClasses = new ArrayList(); /* List<UnovenClassFile> */
  336. private UnwovenClassFile wovenClass;
  337. private boolean isApplyAtAspectJMungersOnly = false;
  338. public WeavingClassFileProvider (String name, byte[] bytes) {
  339. UnwovenClassFile unwoven = new UnwovenClassFile(name,bytes);
  340. unwovenClasses.add(unwoven);
  341. bcelWorld.addSourceObjectType(unwoven.getJavaClass());
  342. }
  343. public void setApplyAtAspectJMungersOnly() {
  344. isApplyAtAspectJMungersOnly = true;
  345. }
  346. public boolean isApplyAtAspectJMungersOnly() {
  347. return isApplyAtAspectJMungersOnly;
  348. }
  349. public byte[] getBytes () {
  350. return wovenClass.getBytes();
  351. }
  352. public Iterator getClassFileIterator() {
  353. return unwovenClasses.iterator();
  354. }
  355. public IWeaveRequestor getRequestor() {
  356. return new IWeaveRequestor() {
  357. public void acceptResult(UnwovenClassFile result) {
  358. if (wovenClass == null) {
  359. wovenClass = result;
  360. }
  361. /* Classes generated by weaver e.g. around closure advice */
  362. else {
  363. String className = result.getClassName();
  364. generatedClasses.put(className,result);
  365. generatedClassHandler.acceptClass(className,result.getBytes());
  366. }
  367. }
  368. public void processingReweavableState() { }
  369. public void addingTypeMungers() {}
  370. public void weavingAspects() {}
  371. public void weavingClasses() {}
  372. public void weaveCompleted() {}
  373. };
  374. }
  375. }
  376. }