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 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  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 Eclipse Public License v1.0
  6. * which accompanies this distribution and is available at
  7. * http://www.eclipse.org/legal/epl-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.FileOutputStream;
  16. import java.io.IOException;
  17. import java.io.PrintWriter;
  18. import java.net.URL;
  19. import java.net.URLClassLoader;
  20. import java.util.ArrayList;
  21. import java.util.HashMap;
  22. import java.util.HashSet;
  23. import java.util.Iterator;
  24. import java.util.LinkedList;
  25. import java.util.List;
  26. import java.util.Map;
  27. import java.util.Set;
  28. import java.util.StringTokenizer;
  29. import org.aspectj.bridge.AbortException;
  30. import org.aspectj.bridge.IMessage;
  31. import org.aspectj.bridge.IMessageHandler;
  32. import org.aspectj.bridge.Message;
  33. import org.aspectj.bridge.MessageUtil;
  34. import org.aspectj.bridge.MessageWriter;
  35. import org.aspectj.bridge.Version;
  36. import org.aspectj.bridge.IMessage.Kind;
  37. import org.aspectj.util.FileUtil;
  38. import org.aspectj.util.LangUtil;
  39. import org.aspectj.weaver.IClassFileProvider;
  40. import org.aspectj.weaver.IWeaveRequestor;
  41. import org.aspectj.weaver.ResolvedType;
  42. import org.aspectj.weaver.bcel.BcelObjectType;
  43. import org.aspectj.weaver.bcel.BcelWeaver;
  44. import org.aspectj.weaver.bcel.BcelWorld;
  45. import org.aspectj.weaver.bcel.UnwovenClassFile;
  46. /**
  47. * This adaptor allows the AspectJ compiler to be embedded in an existing
  48. * system to facilitate load-time weaving. It provides an interface for a
  49. * weaving class loader to provide a classpath to be woven by a set of
  50. * aspects. A callback is supplied to allow a class loader to define classes
  51. * generated by the compiler during the weaving process.
  52. * <p>
  53. * A weaving class loader should create a <code>WeavingAdaptor</code> before
  54. * any classes are defined, typically during construction. The set of aspects
  55. * passed to the adaptor is fixed for the lifetime of the adaptor although the
  56. * classpath can be augmented. A system property can be set to allow verbose
  57. * weaving messages to be written to the console.
  58. *
  59. */
  60. public class WeavingAdaptor {
  61. /**
  62. * System property used to turn on verbose weaving messages
  63. */
  64. public static final String WEAVING_ADAPTOR_VERBOSE = "aj.weaving.verbose";
  65. public static final String SHOW_WEAVE_INFO_PROPERTY = "org.aspectj.weaver.showWeaveInfo";
  66. protected boolean enabled = true;
  67. protected boolean verbose = getVerbose();
  68. protected BcelWorld bcelWorld;
  69. protected BcelWeaver weaver;
  70. private IMessageHandler messageHandler;
  71. private WeavingAdaptorMessageHandler messageHolder;
  72. protected GeneratedClassHandler generatedClassHandler;
  73. protected Map generatedClasses = new HashMap(); /* String -> UnwovenClassFile */
  74. private static Trace trace = TraceFactory.getTraceFactory().getTrace(WeavingAdaptor.class);
  75. protected WeavingAdaptor () {
  76. }
  77. /**
  78. * Construct a WeavingAdaptor with a reference to a weaving class loader. The
  79. * adaptor will automatically search the class loader hierarchy to resolve
  80. * classes. The adaptor will also search the hierarchy for WeavingClassLoader
  81. * instances to determine the set of aspects to be used ofr weaving.
  82. * @param loader instance of <code>ClassLoader</code>
  83. */
  84. public WeavingAdaptor (WeavingClassLoader loader) {
  85. // System.err.println("? WeavingAdaptor.<init>(" + loader +"," + aspectURLs.length + ")");
  86. generatedClassHandler = loader;
  87. init(getFullClassPath((ClassLoader)loader),getFullAspectPath((ClassLoader)loader/*,aspectURLs*/));
  88. }
  89. /**
  90. * Construct a WeavingAdator with a reference to a
  91. * <code>GeneratedClassHandler</code>, a full search path for resolving
  92. * classes and a complete set of aspects. The search path must include
  93. * classes loaded by the class loader constructing the WeavingAdaptor and
  94. * all its parents in the hierarchy.
  95. * @param handler <code>GeneratedClassHandler</code>
  96. * @param classURLs the URLs from which to resolve classes
  97. * @param aspectURLs the aspects used to weave classes defined by this class loader
  98. */
  99. public WeavingAdaptor (GeneratedClassHandler handler, URL[] classURLs, URL[] aspectURLs) {
  100. // System.err.println("? WeavingAdaptor.<init>()");
  101. generatedClassHandler = handler;
  102. init(FileUtil.makeClasspath(classURLs),FileUtil.makeClasspath(aspectURLs));
  103. }
  104. private List getFullClassPath (ClassLoader loader) {
  105. List list = new LinkedList();
  106. for (; loader != null; loader = loader.getParent()) {
  107. if (loader instanceof URLClassLoader) {
  108. URL[] urls = ((URLClassLoader)loader).getURLs();
  109. list.addAll(0,FileUtil.makeClasspath(urls));
  110. }
  111. else {
  112. warn("cannot determine classpath");
  113. }
  114. }
  115. list.addAll(0,makeClasspath(System.getProperty("sun.boot.class.path")));
  116. return list;
  117. }
  118. private List getFullAspectPath (ClassLoader loader) {
  119. List list = new LinkedList();
  120. for (; loader != null; loader = loader.getParent()) {
  121. if (loader instanceof WeavingClassLoader) {
  122. URL[] urls = ((WeavingClassLoader)loader).getAspectURLs();
  123. list.addAll(0,FileUtil.makeClasspath(urls));
  124. }
  125. }
  126. return list;
  127. }
  128. private static boolean getVerbose () {
  129. return Boolean.getBoolean(WEAVING_ADAPTOR_VERBOSE);
  130. }
  131. private void init(List classPath, List aspectPath) {
  132. createMessageHandler();
  133. info("using classpath: " + classPath);
  134. info("using aspectpath: " + aspectPath);
  135. bcelWorld = new BcelWorld(classPath,messageHandler,null);
  136. bcelWorld.setXnoInline(false);
  137. bcelWorld.getLint().loadDefaultProperties();
  138. if (LangUtil.is15VMOrGreater()) {
  139. bcelWorld.setBehaveInJava5Way(true);
  140. }
  141. weaver = new BcelWeaver(bcelWorld);
  142. registerAspectLibraries(aspectPath);
  143. }
  144. protected void createMessageHandler() {
  145. messageHolder = new WeavingAdaptorMessageHandler(new PrintWriter(System.err));
  146. messageHandler = messageHolder;
  147. if (verbose) messageHandler.dontIgnore(IMessage.INFO);
  148. if (Boolean.getBoolean(SHOW_WEAVE_INFO_PROPERTY)) messageHandler.dontIgnore(IMessage.WEAVEINFO);
  149. info("AspectJ Weaver Version " + Version.text + " built on " + Version.time_text); //$NON-NLS-1$
  150. }
  151. protected IMessageHandler getMessageHandler () {
  152. return messageHandler;
  153. }
  154. protected void setMessageHandler (IMessageHandler mh) {
  155. if (messageHolder != null) {
  156. messageHolder.flushMessages();
  157. messageHolder = null;
  158. }
  159. messageHandler = mh;
  160. bcelWorld.setMessageHandler(mh);
  161. }
  162. /**
  163. * Appends URL to path used by the WeavingAdptor to resolve classes
  164. * @param url to be appended to search path
  165. */
  166. public void addURL(URL url) {
  167. File libFile = new File(url.getPath());
  168. try {
  169. weaver.addLibraryJarFile(libFile);
  170. }
  171. catch (IOException ex) {
  172. warn("bad library: '" + libFile + "'");
  173. }
  174. }
  175. /**
  176. * Weave a class using aspects previously supplied to the adaptor.
  177. * @param name the name of the class
  178. * @param bytes the class bytes
  179. * @return the woven bytes
  180. * @exception IOException weave failed
  181. */
  182. public byte[] weaveClass (String name, byte[] bytes) throws IOException {
  183. if (enabled) {
  184. if (trace.isTraceEnabled()) trace.enter("weaveClass",this,new Object[] {name,bytes});
  185. if (shouldWeave(name, bytes)) {
  186. info("weaving '" + name + "'");
  187. bytes = getWovenBytes(name, bytes);
  188. } else if (shouldWeaveAnnotationStyleAspect(name, bytes)) {
  189. // an @AspectJ aspect needs to be at least munged by the aspectOf munger
  190. info("weaving '" + name + "'");
  191. bytes = getAtAspectJAspectBytes(name, bytes);
  192. }
  193. else {
  194. info("not weaving '" + name + "'");
  195. }
  196. if (trace.isTraceEnabled()) trace.exit("weaveClass",bytes);
  197. }
  198. return bytes;
  199. }
  200. /**
  201. * @param name
  202. * @return true if should weave (but maybe we still need to munge it for @AspectJ aspectof support)
  203. */
  204. private boolean shouldWeave (String name, byte[] bytes) {
  205. name = name.replace('/','.');
  206. boolean b = !generatedClasses.containsKey(name) && shouldWeaveName(name);
  207. return b && accept(name, bytes);
  208. // && shouldWeaveAnnotationStyleAspect(name);
  209. // // we recall shouldWeaveAnnotationStyleAspect as we need to add aspectOf methods for @Aspect anyway
  210. // //FIXME AV - this is half ok as the aspect will be weaved by others. In theory if the aspect
  211. // // is excluded from include/exclude config we should only weave late type mungers for aspectof
  212. // return b && (accept(name) || shouldWeaveAnnotationStyleAspect(name));
  213. }
  214. //ATAJ
  215. protected boolean accept(String name, byte[] bytes) {
  216. return true;
  217. }
  218. protected boolean shouldDump(String name, boolean before) {
  219. return false;
  220. }
  221. private boolean shouldWeaveName (String name) {
  222. boolean should =
  223. !((/*(name.startsWith("org.apache.bcel.")//FIXME AV why ? bcel is wrapped in org.aspectj.
  224. ||*/ name.startsWith("org.aspectj.")
  225. || name.startsWith("java.")
  226. || name.startsWith("javax."))
  227. //|| name.startsWith("$Proxy")//JDK proxies//FIXME AV is that 1.3 proxy ? fe. ataspect.$Proxy0 is a java5 proxy...
  228. || name.startsWith("sun.reflect."));//JDK reflect
  229. return should;
  230. }
  231. /**
  232. * We allow @AJ aspect weaving so that we can add aspectOf() as part of the weaving
  233. * (and not part of the source compilation)
  234. *
  235. * @param name
  236. * @param bytes bytecode (from classloader), allow to NOT lookup stuff on disk again during resolve
  237. * @return true if @Aspect
  238. */
  239. private boolean shouldWeaveAnnotationStyleAspect(String name, byte[] bytes) {
  240. // AV: instead of doing resolve that would lookup stuff on disk thru BCEL ClassLoaderRepository
  241. // we reuse bytes[] here to do a fast lookup for @Aspect annotation
  242. return bcelWorld.isAnnotationStyleAspect(name, bytes);
  243. }
  244. /**
  245. * Weave a set of bytes defining a class.
  246. * @param name the name of the class being woven
  247. * @param bytes the bytes that define the class
  248. * @return byte[] the woven bytes for the class
  249. * @throws IOException
  250. */
  251. private byte[] getWovenBytes(String name, byte[] bytes) throws IOException {
  252. WeavingClassFileProvider wcp = new WeavingClassFileProvider(name,bytes);
  253. weaver.weave(wcp);
  254. return wcp.getBytes();
  255. }
  256. /**
  257. * Weave a set of bytes defining a class for only what is needed to turn @AspectJ aspect
  258. * in a usefull form ie with aspectOf method - see #113587
  259. * @param name the name of the class being woven
  260. * @param bytes the bytes that define the class
  261. * @return byte[] the woven bytes for the class
  262. * @throws IOException
  263. */
  264. private byte[] getAtAspectJAspectBytes(String name, byte[] bytes) throws IOException {
  265. WeavingClassFileProvider wcp = new WeavingClassFileProvider(name,bytes);
  266. wcp.setApplyAtAspectJMungersOnly();
  267. weaver.weave(wcp);
  268. return wcp.getBytes();
  269. }
  270. private void registerAspectLibraries(List aspectPath) {
  271. // System.err.println("? WeavingAdaptor.registerAspectLibraries(" + aspectPath + ")");
  272. for (Iterator i = aspectPath.iterator(); i.hasNext();) {
  273. String libName = (String)i.next();
  274. addAspectLibrary(libName);
  275. }
  276. weaver.prepareForWeave();
  277. }
  278. /*
  279. * Register an aspect library with this classloader for use during
  280. * weaving. This class loader will also return (unmodified) any of the
  281. * classes in the library in response to a <code>findClass()</code> request.
  282. * The library is not required to be on the weavingClasspath given when this
  283. * classloader was constructed.
  284. * @param aspectLibraryJarFile a jar file representing an aspect library
  285. * @throws IOException
  286. */
  287. private void addAspectLibrary(String aspectLibraryName) {
  288. File aspectLibrary = new File(aspectLibraryName);
  289. if (aspectLibrary.isDirectory()
  290. || (FileUtil.isZipFile(aspectLibrary))) {
  291. try {
  292. info("adding aspect library: '" + aspectLibrary + "'");
  293. weaver.addLibraryJarFile(aspectLibrary);
  294. } catch (IOException ex) {
  295. error("exception adding aspect library: '" + ex + "'");
  296. }
  297. } else {
  298. error("bad aspect library: '" + aspectLibrary + "'");
  299. }
  300. }
  301. private static List makeClasspath(String cp) {
  302. List ret = new ArrayList();
  303. if (cp != null) {
  304. StringTokenizer tok = new StringTokenizer(cp,File.pathSeparator);
  305. while (tok.hasMoreTokens()) {
  306. ret.add(tok.nextToken());
  307. }
  308. }
  309. return ret;
  310. }
  311. protected boolean info (String message) {
  312. return MessageUtil.info(messageHandler,message);
  313. }
  314. protected boolean warn (String message) {
  315. return MessageUtil.warn(messageHandler,message);
  316. }
  317. protected boolean warn (String message, Throwable th) {
  318. return messageHandler.handleMessage(new Message(message, IMessage.WARNING, th, null));
  319. }
  320. protected boolean error (String message) {
  321. return MessageUtil.error(messageHandler,message);
  322. }
  323. protected String getContextId () {
  324. return "WeavingAdaptor";
  325. }
  326. /**
  327. * Dump the given bytcode in _dump/... (dev mode)
  328. *
  329. * @param name
  330. * @param b
  331. * @param before whether we are dumping before weaving
  332. * @throws Throwable
  333. */
  334. protected void dump(String name, byte[] b, boolean before) {
  335. String dirName = "_ajdump";
  336. if (before) dirName = dirName + File.separator + "_before";
  337. String className = name.replace('.', '/');
  338. final File dir;
  339. if (className.indexOf('/') > 0) {
  340. dir = new File(dirName + File.separator + className.substring(0, className.lastIndexOf('/')));
  341. } else {
  342. dir = new File(dirName);
  343. }
  344. dir.mkdirs();
  345. String fileName = dirName + File.separator + className + ".class";
  346. try {
  347. // System.out.println("WeavingAdaptor.dump() fileName=" + new File(fileName).getAbsolutePath());
  348. FileOutputStream os = new FileOutputStream(fileName);
  349. os.write(b);
  350. os.close();
  351. }
  352. catch (IOException ex) {
  353. warn("unable to dump class " + name + " in directory " + dirName,ex);
  354. }
  355. }
  356. /**
  357. * Processes messages arising from weaver operations.
  358. * Tell weaver to abort on any message more severe than warning.
  359. */
  360. protected class WeavingAdaptorMessageHandler extends MessageWriter {
  361. private Set ignoring = new HashSet();
  362. private IMessage.Kind failKind;
  363. private boolean accumulating = true;
  364. private List messages = new ArrayList();
  365. public WeavingAdaptorMessageHandler (PrintWriter writer) {
  366. super(writer,true);
  367. ignore(IMessage.WEAVEINFO);
  368. ignore(IMessage.INFO);
  369. this.failKind = IMessage.ERROR;
  370. }
  371. public boolean handleMessage(IMessage message) throws AbortException {
  372. addMessage(message);
  373. boolean result = super.handleMessage(message);
  374. if (0 <= message.getKind().compareTo(failKind)) {
  375. throw new AbortException(message);
  376. }
  377. return true;
  378. }
  379. public boolean isIgnoring (Kind kind) {
  380. return ((null != kind) && (ignoring.contains(kind)));
  381. }
  382. /**
  383. * Set a message kind to be ignored from now on
  384. */
  385. public void ignore (IMessage.Kind kind) {
  386. if ((null != kind) && (!ignoring.contains(kind))) {
  387. ignoring.add(kind);
  388. }
  389. }
  390. /**
  391. * Remove a message kind from the list of those ignored from now on.
  392. */
  393. public void dontIgnore (IMessage.Kind kind) {
  394. if (null != kind) {
  395. ignoring.remove(kind);
  396. if (kind.equals(IMessage.INFO)) accumulating = false;
  397. }
  398. }
  399. private void addMessage (IMessage message) {
  400. if (accumulating && isIgnoring(message.getKind())) {
  401. messages.add(message);
  402. }
  403. }
  404. public void flushMessages () {
  405. for (Iterator iter = messages.iterator(); iter.hasNext();) {
  406. IMessage message = (IMessage)iter.next();
  407. super.handleMessage(message);
  408. }
  409. accumulating = false;
  410. messages.clear();
  411. }
  412. protected String render(IMessage message) {
  413. return "[" + getContextId() + "] " + super.render(message);
  414. }
  415. }
  416. private class WeavingClassFileProvider implements IClassFileProvider {
  417. private UnwovenClassFile unwovenClass;
  418. private List unwovenClasses = new ArrayList(); /* List<UnovenClassFile> */
  419. private UnwovenClassFile wovenClass;
  420. private boolean isApplyAtAspectJMungersOnly = false;
  421. private BcelObjectType delegate;
  422. public WeavingClassFileProvider (String name, byte[] bytes) {
  423. this.unwovenClass = new UnwovenClassFile(name,bytes);
  424. this.unwovenClasses.add(unwovenClass);
  425. if (shouldDump(name.replace('/', '.'),true)) {
  426. dump(name, bytes, true);
  427. }
  428. delegate = bcelWorld.addSourceObjectType(unwovenClass.getJavaClass());
  429. }
  430. public void setApplyAtAspectJMungersOnly() {
  431. isApplyAtAspectJMungersOnly = true;
  432. }
  433. public boolean isApplyAtAspectJMungersOnly() {
  434. return isApplyAtAspectJMungersOnly;
  435. }
  436. public byte[] getBytes () {
  437. if (wovenClass != null) return wovenClass.getBytes();
  438. else return unwovenClass.getBytes();
  439. }
  440. public Iterator getClassFileIterator() {
  441. return unwovenClasses.iterator();
  442. }
  443. public IWeaveRequestor getRequestor() {
  444. return new IWeaveRequestor() {
  445. public void acceptResult(UnwovenClassFile result) {
  446. if (wovenClass == null) {
  447. wovenClass = result;
  448. String name = result.getClassName();
  449. if (shouldDump(name.replace('/', '.'), false)) {
  450. dump(name, result.getBytes(), false);
  451. }
  452. }
  453. /* Classes generated by weaver e.g. around closure advice */
  454. else {
  455. String className = result.getClassName();
  456. generatedClasses.put(className,result);
  457. generatedClasses.put(wovenClass.getClassName(),result);
  458. generatedClassHandler.acceptClass(className,result.getBytes());
  459. }
  460. }
  461. public void processingReweavableState() { }
  462. public void addingTypeMungers() {}
  463. public void weavingAspects() {}
  464. public void weavingClasses() {}
  465. public void weaveCompleted() {
  466. if (delegate!=null) delegate.weavingCompleted();
  467. ResolvedType.resetPrimitives();
  468. }
  469. };
  470. }
  471. }
  472. }