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.

Aj.java 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. /*******************************************************************************
  2. * Copyright (c) 2005 Contributors.
  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://eclipse.org/legal/epl-v10.html
  8. *
  9. * Contributors:
  10. * Alexandre Vasseur initial implementation
  11. *******************************************************************************/
  12. package org.aspectj.weaver.loadtime;
  13. import java.lang.ref.ReferenceQueue;
  14. import java.lang.ref.WeakReference;
  15. import java.security.ProtectionDomain;
  16. import java.util.Collections;
  17. import java.util.HashMap;
  18. import java.util.Iterator;
  19. import java.util.Map;
  20. import java.util.Set;
  21. import org.aspectj.bridge.context.CompilationAndWeavingContext;
  22. import org.aspectj.weaver.Dump;
  23. import org.aspectj.weaver.tools.Trace;
  24. import org.aspectj.weaver.tools.TraceFactory;
  25. import org.aspectj.weaver.tools.WeavingAdaptor;
  26. /**
  27. * Adapter between the generic class pre processor interface and the AspectJ weaver Load time weaving consistency relies on
  28. * Bcel.setRepository
  29. *
  30. * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
  31. */
  32. public class Aj implements ClassPreProcessor {
  33. private IWeavingContext weavingContext;
  34. /**
  35. * References are added to this queue when their associated classloader is removed, and once on here that indicates that we
  36. * should tidy up the adaptor map and remove the adaptor (weaver) from the map we are maintaining from adaptorkey > adaptor
  37. * (weaver)
  38. */
  39. private static ReferenceQueue adaptorQueue = new ReferenceQueue();
  40. private static Trace trace = TraceFactory.getTraceFactory().getTrace(Aj.class);
  41. public Aj() {
  42. this(null);
  43. }
  44. public Aj(IWeavingContext context) {
  45. if (trace.isTraceEnabled())
  46. trace.enter("<init>", this, new Object[] { context, getClass().getClassLoader() });
  47. this.weavingContext = context;
  48. if (trace.isTraceEnabled())
  49. trace.exit("<init>");
  50. }
  51. /**
  52. * Initialization
  53. */
  54. public void initialize() {
  55. }
  56. private final static String deleLoader = "sun.reflect.DelegatingClassLoader";
  57. /**
  58. * Weave
  59. *
  60. * @param className
  61. * @param bytes
  62. * @param loader
  63. * @return weaved bytes
  64. */
  65. public byte[] preProcess(String className, byte[] bytes, ClassLoader loader, ProtectionDomain protectionDomain) {
  66. // TODO AV needs to doc that
  67. if (loader == null || className == null || loader.getClass().getName().equals(deleLoader)) {
  68. // skip boot loader, null classes (hibernate), or those from a reflection loader
  69. return bytes;
  70. }
  71. if (trace.isTraceEnabled())
  72. trace.enter("preProcess", this, new Object[] { className, bytes, loader });
  73. if (trace.isTraceEnabled())
  74. trace.event("preProcess", this, new Object[] { loader.getParent(), Thread.currentThread().getContextClassLoader() });
  75. try {
  76. synchronized (loader) {
  77. WeavingAdaptor weavingAdaptor = WeaverContainer.getWeaver(loader, weavingContext);
  78. if (weavingAdaptor == null) {
  79. if (trace.isTraceEnabled())
  80. trace.exit("preProcess");
  81. return bytes;
  82. }
  83. try {
  84. weavingAdaptor.setActiveProtectionDomain(protectionDomain);
  85. byte[] newBytes = weavingAdaptor.weaveClass(className, bytes, false);
  86. Dump.dumpOnExit(weavingAdaptor.getMessageHolder(), true);
  87. if (trace.isTraceEnabled())
  88. trace.exit("preProcess", newBytes);
  89. return newBytes;
  90. } finally {
  91. weavingAdaptor.setActiveProtectionDomain(null);
  92. }
  93. }
  94. /* Don't like to do this but JVMTI swallows all exceptions */
  95. } catch (Throwable th) {
  96. trace.error(className, th);
  97. Dump.dumpWithException(th);
  98. // FIXME AV wondering if we should have the option to fail (throw runtime exception) here
  99. // would make sense at least in test f.e. see TestHelper.handleMessage()
  100. if (trace.isTraceEnabled())
  101. trace.exit("preProcess", th);
  102. return bytes;
  103. } finally {
  104. CompilationAndWeavingContext.resetForThread();
  105. }
  106. }
  107. /**
  108. * An AdaptorKey is a WeakReference wrapping a classloader reference that will enqueue to a specified queue when the classloader
  109. * is GC'd. Since the AdaptorKey is used as a key into a hashmap we need to give it a non-varying hashcode/equals
  110. * implementation, and we need that hashcode not to vary even when the internal referent has been GC'd. The hashcode is
  111. * calculated on creation of the AdaptorKey based on the loader instance that it is wrapping. This means even when the referent
  112. * is gone we can still use the AdaptorKey and it will 'point' to the same place as it always did.
  113. */
  114. private static class AdaptorKey extends WeakReference {
  115. private int hashcode = -1;
  116. public AdaptorKey(ClassLoader loader) {
  117. super(loader, adaptorQueue);
  118. hashcode = loader.hashCode() * 37;
  119. }
  120. public ClassLoader getClassLoader() {
  121. ClassLoader instance = (ClassLoader) get();
  122. // Assert instance!=null - shouldn't be asked for after a GC of the referent has occurred !
  123. return instance;
  124. }
  125. public boolean equals(Object obj) {
  126. if (!(obj instanceof AdaptorKey))
  127. return false;
  128. AdaptorKey other = (AdaptorKey) obj;
  129. return other.hashcode == hashcode;
  130. }
  131. public int hashCode() {
  132. return hashcode;
  133. }
  134. }
  135. /**
  136. * The reference queue is only processed when a request is made for a weaver adaptor. This means there can be one or two stale
  137. * weavers left around. If the user knows they have finished all their weaving, they might wish to call removeStaleAdaptors
  138. * which will process anything left on the reference queue containing adaptorKeys for garbage collected classloaders.
  139. *
  140. * @param displayProgress produce System.err info on the tidying up process
  141. * @return number of stale weavers removed
  142. */
  143. public static int removeStaleAdaptors(boolean displayProgress) {
  144. int removed = 0;
  145. synchronized (WeaverContainer.weavingAdaptors) {
  146. if (displayProgress) {
  147. System.err.println("Weaver adaptors before queue processing:");
  148. Map m = WeaverContainer.weavingAdaptors;
  149. Set keys = m.keySet();
  150. for (Iterator iterator = keys.iterator(); iterator.hasNext();) {
  151. Object object = iterator.next();
  152. System.err.println(object + " = " + WeaverContainer.weavingAdaptors.get(object));
  153. }
  154. }
  155. Object o = adaptorQueue.poll();
  156. while (o != null) {
  157. if (displayProgress)
  158. System.err.println("Processing referencequeue entry " + o);
  159. AdaptorKey wo = (AdaptorKey) o;
  160. boolean didit = WeaverContainer.weavingAdaptors.remove(wo) != null;
  161. if (didit) {
  162. removed++;
  163. } else {
  164. throw new RuntimeException("Eh?? key=" + wo);
  165. }
  166. if (displayProgress)
  167. System.err.println("Removed? " + didit);
  168. o = adaptorQueue.poll();
  169. }
  170. if (displayProgress) {
  171. System.err.println("Weaver adaptors after queue processing:");
  172. Map m = WeaverContainer.weavingAdaptors;
  173. Set keys = m.keySet();
  174. for (Iterator iterator = keys.iterator(); iterator.hasNext();) {
  175. Object object = iterator.next();
  176. System.err.println(object + " = " + WeaverContainer.weavingAdaptors.get(object));
  177. }
  178. }
  179. }
  180. return removed;
  181. }
  182. /**
  183. * @return the number of entries still in the weavingAdaptors map
  184. */
  185. public static int getActiveAdaptorCount() {
  186. return WeaverContainer.weavingAdaptors.size();
  187. }
  188. /**
  189. * Process the reference queue that contains stale AdaptorKeys - the keys are put on the queue when their classloader referent
  190. * is garbage collected and so the associated adaptor (weaver) should be removed from the map
  191. */
  192. public static void checkQ() {
  193. synchronized (adaptorQueue) {
  194. Object o = adaptorQueue.poll();
  195. while (o != null) {
  196. AdaptorKey wo = (AdaptorKey) o;
  197. // boolean removed =
  198. WeaverContainer.weavingAdaptors.remove(wo);
  199. // DBG System.err.println("Evicting key " + wo + " = " + didit);
  200. o = adaptorQueue.poll();
  201. }
  202. }
  203. }
  204. static {
  205. // pr271840 - touch the types early and outside the locks
  206. new ExplicitlyInitializedClassLoaderWeavingAdaptor(new ClassLoaderWeavingAdaptor());
  207. }
  208. /**
  209. * Cache of weaver There is one weaver per classloader
  210. */
  211. static class WeaverContainer {
  212. final static Map weavingAdaptors = Collections.synchronizedMap(new HashMap());
  213. static WeavingAdaptor getWeaver(ClassLoader loader, IWeavingContext weavingContext) {
  214. ExplicitlyInitializedClassLoaderWeavingAdaptor adaptor = null;
  215. AdaptorKey adaptorKey = new AdaptorKey(loader);
  216. String loaderClassName = loader.getClass().getName();
  217. synchronized (weavingAdaptors) {
  218. checkQ();
  219. adaptor = (ExplicitlyInitializedClassLoaderWeavingAdaptor) weavingAdaptors.get(adaptorKey);
  220. if (adaptor == null) {
  221. // create it and put it back in the weavingAdaptors map but avoid any kind of instantiation
  222. // within the synchronized block
  223. ClassLoaderWeavingAdaptor weavingAdaptor = new ClassLoaderWeavingAdaptor();
  224. adaptor = new ExplicitlyInitializedClassLoaderWeavingAdaptor(weavingAdaptor);
  225. weavingAdaptors.put(adaptorKey, adaptor);
  226. }
  227. }
  228. // perform the initialization
  229. return adaptor.getWeavingAdaptor(loader, weavingContext);
  230. }
  231. }
  232. static class ExplicitlyInitializedClassLoaderWeavingAdaptor {
  233. private final ClassLoaderWeavingAdaptor weavingAdaptor;
  234. private boolean isInitialized;
  235. public ExplicitlyInitializedClassLoaderWeavingAdaptor(ClassLoaderWeavingAdaptor weavingAdaptor) {
  236. this.weavingAdaptor = weavingAdaptor;
  237. this.isInitialized = false;
  238. }
  239. private void initialize(ClassLoader loader, IWeavingContext weavingContext) {
  240. if (!isInitialized) {
  241. isInitialized = true;
  242. weavingAdaptor.initialize(loader, weavingContext);
  243. }
  244. }
  245. public ClassLoaderWeavingAdaptor getWeavingAdaptor(ClassLoader loader, IWeavingContext weavingContext) {
  246. initialize(loader, weavingContext);
  247. return weavingAdaptor;
  248. }
  249. }
  250. /**
  251. * Returns a namespace based on the contest of the aspects available
  252. */
  253. public String getNamespace(ClassLoader loader) {
  254. ClassLoaderWeavingAdaptor weavingAdaptor = (ClassLoaderWeavingAdaptor) WeaverContainer.getWeaver(loader, weavingContext);
  255. return weavingAdaptor.getNamespace();
  256. }
  257. /**
  258. * Check to see if any classes have been generated for a particular classes loader. Calls
  259. * ClassLoaderWeavingAdaptor.generatedClassesExist()
  260. *
  261. * @param loader the class cloder
  262. * @return true if classes have been generated.
  263. */
  264. public boolean generatedClassesExist(ClassLoader loader) {
  265. return ((ClassLoaderWeavingAdaptor) WeaverContainer.getWeaver(loader, weavingContext)).generatedClassesExistFor(null);
  266. }
  267. public void flushGeneratedClasses(ClassLoader loader) {
  268. ((ClassLoaderWeavingAdaptor) WeaverContainer.getWeaver(loader, weavingContext)).flushGeneratedClasses();
  269. }
  270. }