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

13 years ago
11 years ago
11 years ago
11 years ago
11 years ago
13 years ago
13 years ago
11 years ago
13 years ago
9 years ago
9 years ago
11 years ago
11 years ago
11 years ago
11 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  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 v 2.0
  6. * which accompanies this distribution and is available at
  7. * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt
  8. *
  9. * Contributors:
  10. * Alexandre Vasseur initial implementation
  11. *******************************************************************************/
  12. package org.aspectj.weaver.loadtime;
  13. import java.lang.ref.Reference;
  14. import java.lang.ref.ReferenceQueue;
  15. import java.lang.ref.WeakReference;
  16. import java.security.ProtectionDomain;
  17. import java.util.ArrayList;
  18. import java.util.Collections;
  19. import java.util.HashMap;
  20. import java.util.List;
  21. import java.util.Map;
  22. import java.util.Optional;
  23. import java.util.Set;
  24. import java.util.StringTokenizer;
  25. import org.aspectj.bridge.context.CompilationAndWeavingContext;
  26. import org.aspectj.weaver.Dump;
  27. import org.aspectj.weaver.tools.Trace;
  28. import org.aspectj.weaver.tools.TraceFactory;
  29. import org.aspectj.weaver.tools.WeavingAdaptor;
  30. import org.aspectj.weaver.tools.cache.SimpleCache;
  31. import org.aspectj.weaver.tools.cache.SimpleCacheFactory;
  32. /**
  33. * Adapter between the generic class pre processor interface and the AspectJ weaver Load time weaving consistency relies on
  34. * Bcel.setRepository
  35. *
  36. * @author Alexandre Vasseur (alex AT gnilux DOT com)
  37. */
  38. public class Aj implements ClassPreProcessor {
  39. private IWeavingContext weavingContext;
  40. public static SimpleCache laCache=SimpleCacheFactory.createSimpleCache();
  41. /**
  42. * References are added to this queue when their associated classloader is removed, and once on here that indicates that we
  43. * should tidy up the adaptor map and remove the adaptor (weaver) from the map we are maintaining from adaptorkey > adaptor
  44. * (weaver)
  45. */
  46. private static ReferenceQueue<ClassLoader> adaptorQueue = new ReferenceQueue<>();
  47. private static Trace trace = TraceFactory.getTraceFactory().getTrace(Aj.class);
  48. public Aj() {
  49. this(null);
  50. }
  51. public Aj(IWeavingContext context) {
  52. if (trace.isTraceEnabled())
  53. trace.enter("<init>", this, new Object[] { context, getClass().getClassLoader() });
  54. this.weavingContext = context;
  55. if (trace.isTraceEnabled())
  56. trace.exit("<init>");
  57. }
  58. /**
  59. * Initialization
  60. */
  61. @Override
  62. public void initialize() {
  63. }
  64. private final static String deleLoader = "sun.reflect.DelegatingClassLoader";
  65. private final static String deleLoader2 = "jdk.internal.reflect.DelegatingClassLoader"; // On JDK11+
  66. @Override
  67. public byte[] preProcess(String className, final byte[] bytes, ClassLoader classLoader, ProtectionDomain protectionDomain) {
  68. // Implementation note: Try to return null, whenever it is clear that the original byte code was not changed by the
  69. // weaver. While not strictly necessary, the changes from commit 3e3c83712e are defensive programming, which helps
  70. // to avoid problems like https://bugs.openjdk.org/browse/JDK-8325536, even if unfixed in the JDK.
  71. if (classLoader == null || className == null ||
  72. classLoader.getClass().getName().equals(deleLoader) || classLoader.getClass().getName().equals(deleLoader2)) {
  73. // skip boot loader, null classes (hibernate), or those from a reflection loader
  74. return null;
  75. }
  76. if (loadersToSkip != null) {
  77. // Check whether to reject it
  78. if (loadersToSkip.contains(classLoader.getClass().getName())) {
  79. // System.out.println("debug: no weaver created for loader '"+loader.getClass().getName()+"'");
  80. return null;
  81. }
  82. }
  83. if (trace.isTraceEnabled())
  84. trace.enter("preProcess", this, new Object[] { className, bytes, classLoader });
  85. if (trace.isTraceEnabled())
  86. trace.event("preProcess", this, new Object[] { classLoader.getParent(), Thread.currentThread().getContextClassLoader() });
  87. try {
  88. synchronized (classLoader) {
  89. if (SimpleCacheFactory.isEnabled()) {
  90. Optional<byte[]> cacheBytes = laCache.getAndInitialize(className, bytes, classLoader, protectionDomain);
  91. if (cacheBytes != null){
  92. return cacheBytes.orElse(null);
  93. }
  94. }
  95. WeavingAdaptor weavingAdaptor = WeaverContainer.getWeaver(classLoader, weavingContext);
  96. if (weavingAdaptor == null) {
  97. if (trace.isTraceEnabled())
  98. trace.exit("preProcess");
  99. return null;
  100. }
  101. try {
  102. weavingAdaptor.setActiveProtectionDomain(protectionDomain);
  103. byte[] newBytes = weavingAdaptor.weaveClass(className, bytes, false);
  104. Dump.dumpOnExit(weavingAdaptor.getMessageHolder(), true);
  105. if (trace.isTraceEnabled())
  106. trace.exit("preProcess", newBytes);
  107. if (SimpleCacheFactory.isEnabled()) {
  108. laCache.put(className, bytes, newBytes);
  109. }
  110. return newBytes;
  111. } finally {
  112. weavingAdaptor.setActiveProtectionDomain(null);
  113. }
  114. }
  115. /* Don't like to do this but JVMTI swallows all exceptions */
  116. } catch (Throwable th) {
  117. trace.error(className, th);
  118. Dump.dumpWithException(th);
  119. // FIXME AV wondering if we should have the option to fail (throw runtime exception) here
  120. // would make sense at least in test f.e. see TestHelper.handleMessage()
  121. if (trace.isTraceEnabled())
  122. trace.exit("preProcess", th);
  123. return null;
  124. } finally {
  125. CompilationAndWeavingContext.resetForThread();
  126. }
  127. }
  128. /**
  129. * An AdaptorKey is a WeakReference wrapping a classloader reference that will enqueue to a specified queue when the classloader
  130. * is GC'd. Since the AdaptorKey is used as a key into a hashmap we need to give it a non-varying hashcode/equals
  131. * implementation, and we need that hashcode not to vary even when the internal referent has been GC'd. The hashcode is
  132. * calculated on creation of the AdaptorKey based on the loader instance that it is wrapping. This means even when the referent
  133. * is gone we can still use the AdaptorKey and it will 'point' to the same place as it always did.
  134. */
  135. private static class AdaptorKey extends WeakReference {
  136. private final int loaderHashCode, sysHashCode, hashValue;
  137. private final String loaderClass;
  138. public AdaptorKey(ClassLoader loader) {
  139. super(loader, adaptorQueue);
  140. loaderHashCode = loader.hashCode();
  141. sysHashCode = System.identityHashCode(loader);
  142. loaderClass = loader.getClass().getName();
  143. hashValue = loaderHashCode + sysHashCode + loaderClass.hashCode();
  144. }
  145. public ClassLoader getClassLoader() {
  146. ClassLoader instance = (ClassLoader) get();
  147. // Assert instance!=null - shouldn't be asked for after a GC of the referent has occurred !
  148. return instance;
  149. }
  150. @Override
  151. public boolean equals(Object obj) {
  152. if (!(obj instanceof AdaptorKey)) {
  153. return false;
  154. }
  155. AdaptorKey other = (AdaptorKey) obj;
  156. return (other.loaderHashCode == loaderHashCode)
  157. && (other.sysHashCode == sysHashCode)
  158. && loaderClass.equals(other.loaderClass);
  159. }
  160. @Override
  161. public int hashCode() {
  162. return hashValue;
  163. }
  164. }
  165. /**
  166. * The reference queue is only processed when a request is made for a weaver adaptor. This means there can be one or two stale
  167. * weavers left around. If the user knows they have finished all their weaving, they might wish to call removeStaleAdaptors
  168. * which will process anything left on the reference queue containing adaptorKeys for garbage collected classloaders.
  169. *
  170. * @param displayProgress produce System.err info on the tidying up process
  171. * @return number of stale weavers removed
  172. */
  173. public static int removeStaleAdaptors(boolean displayProgress) {
  174. int removed = 0;
  175. synchronized (WeaverContainer.weavingAdaptors) {
  176. if (displayProgress) {
  177. System.err.println("Weaver adaptors before queue processing:");
  178. Map<AdaptorKey,ExplicitlyInitializedClassLoaderWeavingAdaptor> m = WeaverContainer.weavingAdaptors;
  179. Set<AdaptorKey> keys = m.keySet();
  180. for (AdaptorKey object : keys) {
  181. System.err.println(object + " = " + WeaverContainer.weavingAdaptors.get(object));
  182. }
  183. }
  184. Reference<?> o = adaptorQueue.poll();
  185. while (o != null) {
  186. if (displayProgress)
  187. System.err.println("Processing referencequeue entry " + o);
  188. AdaptorKey wo = (AdaptorKey) o;
  189. boolean didit = WeaverContainer.weavingAdaptors.remove(wo) != null;
  190. if (didit) {
  191. removed++;
  192. } else {
  193. throw new RuntimeException("Eh?? key=" + wo);
  194. }
  195. if (displayProgress)
  196. System.err.println("Removed? " + didit);
  197. o = adaptorQueue.poll();
  198. }
  199. if (displayProgress) {
  200. System.err.println("Weaver adaptors after queue processing:");
  201. Map<AdaptorKey,ExplicitlyInitializedClassLoaderWeavingAdaptor> m = WeaverContainer.weavingAdaptors;
  202. Set<AdaptorKey> keys = m.keySet();
  203. for (AdaptorKey object : keys) {
  204. System.err.println(object + " = " + WeaverContainer.weavingAdaptors.get(object));
  205. }
  206. }
  207. }
  208. return removed;
  209. }
  210. /**
  211. * @return the number of entries still in the weavingAdaptors map
  212. */
  213. public static int getActiveAdaptorCount() {
  214. return WeaverContainer.weavingAdaptors.size();
  215. }
  216. /**
  217. * Process the reference queue that contains stale AdaptorKeys - the keys are put on the queue when their classloader referent
  218. * is garbage collected and so the associated adaptor (weaver) should be removed from the map
  219. */
  220. public static void checkQ() {
  221. synchronized (adaptorQueue) {
  222. Reference<?> o = adaptorQueue.poll();
  223. while (o != null) {
  224. AdaptorKey wo = (AdaptorKey) o;
  225. // boolean removed =
  226. WeaverContainer.weavingAdaptors.remove(wo);
  227. // DBG System.err.println("Evicting key " + wo + " = " + didit);
  228. o = adaptorQueue.poll();
  229. }
  230. }
  231. }
  232. public static List<String> loadersToSkip = null;
  233. static {
  234. // pr271840 - touch the types early and outside the locks
  235. new ExplicitlyInitializedClassLoaderWeavingAdaptor(new ClassLoaderWeavingAdaptor());
  236. try {
  237. String loadersToSkipProperty = System.getProperty("aj.weaving.loadersToSkip","");
  238. StringTokenizer st = new StringTokenizer(loadersToSkipProperty, ",");
  239. if (loadersToSkipProperty != null && loadersToSkip == null) {
  240. if (st.hasMoreTokens()) {
  241. // System.out.println("aj.weaving.loadersToSkip is set. Skipping loaders: '"+loadersToSkipProperty+"'");
  242. loadersToSkip = new ArrayList<>();
  243. }
  244. while (st.hasMoreTokens()) {
  245. String nextLoader = st.nextToken();
  246. loadersToSkip.add(nextLoader);
  247. }
  248. }
  249. } catch (Exception e) {
  250. // Likely security issue related to property access...
  251. }
  252. }
  253. /**
  254. * Cache of weaver There is one weaver per classloader
  255. */
  256. static class WeaverContainer {
  257. final static Map<AdaptorKey,ExplicitlyInitializedClassLoaderWeavingAdaptor> weavingAdaptors =
  258. Collections.synchronizedMap(new HashMap<>());
  259. static WeavingAdaptor getWeaver(ClassLoader loader, IWeavingContext weavingContext) {
  260. ExplicitlyInitializedClassLoaderWeavingAdaptor adaptor = null;
  261. AdaptorKey adaptorKey = new AdaptorKey(loader);
  262. String loaderClassName = loader.getClass().getName();
  263. synchronized (weavingAdaptors) {
  264. checkQ();
  265. if (loader.equals(myClassLoader)){
  266. adaptor = myClassLoaderAdaptor;
  267. } else {
  268. adaptor = weavingAdaptors.get(adaptorKey);
  269. }
  270. if (adaptor == null) {
  271. // create it and put it back in the weavingAdaptors map but avoid any kind of instantiation
  272. // within the synchronized block
  273. ClassLoaderWeavingAdaptor weavingAdaptor = new ClassLoaderWeavingAdaptor();
  274. adaptor = new ExplicitlyInitializedClassLoaderWeavingAdaptor(weavingAdaptor);
  275. if(myClassLoaderAdaptor == null && loader.equals(myClassLoader)){
  276. myClassLoaderAdaptor = adaptor;
  277. } else {
  278. weavingAdaptors.put(adaptorKey, adaptor);
  279. }
  280. }
  281. }
  282. // perform the initialization
  283. return adaptor.getWeavingAdaptor(loader, weavingContext);
  284. }
  285. private static final ClassLoader myClassLoader = WeavingAdaptor.class.getClassLoader();
  286. private static ExplicitlyInitializedClassLoaderWeavingAdaptor myClassLoaderAdaptor;
  287. }
  288. static class ExplicitlyInitializedClassLoaderWeavingAdaptor {
  289. private final ClassLoaderWeavingAdaptor weavingAdaptor;
  290. private boolean isInitialized;
  291. public ExplicitlyInitializedClassLoaderWeavingAdaptor(ClassLoaderWeavingAdaptor weavingAdaptor) {
  292. this.weavingAdaptor = weavingAdaptor;
  293. this.isInitialized = false;
  294. }
  295. private void initialize(ClassLoader loader, IWeavingContext weavingContext) {
  296. if (!isInitialized) {
  297. isInitialized = true;
  298. weavingAdaptor.initialize(loader, weavingContext);
  299. }
  300. }
  301. public ClassLoaderWeavingAdaptor getWeavingAdaptor(ClassLoader loader, IWeavingContext weavingContext) {
  302. initialize(loader, weavingContext);
  303. return weavingAdaptor;
  304. }
  305. }
  306. /**
  307. * Returns a namespace based on the contest of the aspects available
  308. */
  309. public String getNamespace(ClassLoader loader) {
  310. ClassLoaderWeavingAdaptor weavingAdaptor = (ClassLoaderWeavingAdaptor) WeaverContainer.getWeaver(loader, weavingContext);
  311. return weavingAdaptor.getNamespace();
  312. }
  313. /**
  314. * Check to see if any classes have been generated for a particular classes loader. Calls
  315. * ClassLoaderWeavingAdaptor.generatedClassesExist()
  316. *
  317. * @param loader the class cloder
  318. * @return true if classes have been generated.
  319. */
  320. public boolean generatedClassesExist(ClassLoader loader) {
  321. return ((ClassLoaderWeavingAdaptor) WeaverContainer.getWeaver(loader, weavingContext)).generatedClassesExistFor(null);
  322. }
  323. public void flushGeneratedClasses(ClassLoader loader) {
  324. ((ClassLoaderWeavingAdaptor) WeaverContainer.getWeaver(loader, weavingContext)).flushGeneratedClasses();
  325. }
  326. @Override
  327. public void prepareForRedefinition(ClassLoader loader, String className) {
  328. ((ClassLoaderWeavingAdaptor) WeaverContainer.getWeaver(loader, weavingContext)).flushGeneratedClassesFor(className);
  329. }
  330. }