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.

ClassLoaderWeavingAdaptor.java 34KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051
  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. * David Knibb weaving context enhancments
  12. *******************************************************************************/
  13. package org.aspectj.weaver.loadtime;
  14. import java.io.File;
  15. import java.io.IOException;
  16. import java.io.InputStream;
  17. import java.lang.reflect.InvocationTargetException;
  18. import java.lang.reflect.Method;
  19. import java.net.MalformedURLException;
  20. import java.net.URL;
  21. import java.security.ProtectionDomain;
  22. import java.util.ArrayList;
  23. import java.util.Enumeration;
  24. import java.util.HashMap;
  25. import java.util.HashSet;
  26. import java.util.Iterator;
  27. import java.util.List;
  28. import java.util.Properties;
  29. import java.util.Set;
  30. import java.util.StringTokenizer;
  31. import org.aspectj.bridge.AbortException;
  32. import org.aspectj.bridge.Constants;
  33. import org.aspectj.util.LangUtil;
  34. import org.aspectj.weaver.Lint;
  35. import org.aspectj.weaver.Lint.Kind;
  36. import org.aspectj.weaver.ResolvedType;
  37. import org.aspectj.weaver.UnresolvedType;
  38. import org.aspectj.weaver.World;
  39. import org.aspectj.weaver.bcel.BcelWeakClassLoaderReference;
  40. import org.aspectj.weaver.bcel.BcelWeaver;
  41. import org.aspectj.weaver.bcel.BcelWorld;
  42. import org.aspectj.weaver.bcel.Utility;
  43. import org.aspectj.weaver.loadtime.definition.Definition;
  44. import org.aspectj.weaver.loadtime.definition.DocumentParser;
  45. import org.aspectj.weaver.ltw.LTWWorld;
  46. import org.aspectj.weaver.patterns.PatternParser;
  47. import org.aspectj.weaver.patterns.TypePattern;
  48. import org.aspectj.weaver.tools.GeneratedClassHandler;
  49. import org.aspectj.weaver.tools.Trace;
  50. import org.aspectj.weaver.tools.TraceFactory;
  51. import org.aspectj.weaver.tools.WeavingAdaptor;
  52. /**
  53. * @author Alexandre Vasseur
  54. * @author Andy Clement
  55. * @author Abraham Nevado
  56. */
  57. public class ClassLoaderWeavingAdaptor extends WeavingAdaptor {
  58. private final static String AOP_XML = Constants.AOP_USER_XML + ";" + Constants.AOP_AJC_XML + ";" + Constants.AOP_OSGI_XML;
  59. private boolean initialized;
  60. private List m_dumpTypePattern = new ArrayList();
  61. private boolean m_dumpBefore = false;
  62. private boolean dumpDirPerClassloader = false;
  63. private boolean hasExcludes = false;
  64. private List<TypePattern> excludeTypePattern = new ArrayList<TypePattern>(); // anything
  65. private List<String> excludeStartsWith = new ArrayList<String>(); // com.foo..*
  66. private List<String> excludeStarDotDotStar = new ArrayList<String>(); // *..*CGLIB*
  67. private List<String> excludeExactName = new ArrayList<String>(); // com.foo.Bar
  68. private List<String> excludeEndsWith = new ArrayList<String>(); // com.foo.Bar
  69. private List<String[]> excludeSpecial = new ArrayList<String[]>();
  70. private boolean hasIncludes = false;
  71. private List<TypePattern> includeTypePattern = new ArrayList<TypePattern>();
  72. private List<String> m_includeStartsWith = new ArrayList<String>();
  73. private List<String> includeExactName = new ArrayList<String>();
  74. private boolean includeStar = false;
  75. private List<TypePattern> m_aspectExcludeTypePattern = new ArrayList<TypePattern>();
  76. private List<String> m_aspectExcludeStartsWith = new ArrayList<String>();
  77. private List<TypePattern> m_aspectIncludeTypePattern = new ArrayList<TypePattern>();
  78. private List<String> m_aspectIncludeStartsWith = new ArrayList<String>();
  79. private StringBuffer namespace;
  80. private IWeavingContext weavingContext;
  81. private List concreteAspects = new ArrayList();
  82. private static Trace trace = TraceFactory.getTraceFactory().getTrace(ClassLoaderWeavingAdaptor.class);
  83. public ClassLoaderWeavingAdaptor() {
  84. super();
  85. if (trace.isTraceEnabled()) {
  86. trace.enter("<init>", this);
  87. }
  88. if (trace.isTraceEnabled()) {
  89. trace.exit("<init>");
  90. }
  91. }
  92. /**
  93. * We don't need a reference to the class loader and using it during construction can cause problems with recursion. It also
  94. * makes sense to supply the weaving context during initialization to.
  95. *
  96. * @deprecated
  97. */
  98. public ClassLoaderWeavingAdaptor(final ClassLoader deprecatedLoader, final IWeavingContext deprecatedContext) {
  99. super();
  100. if (trace.isTraceEnabled()) {
  101. trace.enter("<init>", this, new Object[] { deprecatedLoader, deprecatedContext });
  102. }
  103. if (trace.isTraceEnabled()) {
  104. trace.exit("<init>");
  105. }
  106. }
  107. class SimpleGeneratedClassHandler implements GeneratedClassHandler {
  108. private BcelWeakClassLoaderReference loaderRef;
  109. SimpleGeneratedClassHandler(ClassLoader loader) {
  110. loaderRef = new BcelWeakClassLoaderReference(loader);
  111. }
  112. /**
  113. * Callback when we need to define a Closure in the JVM
  114. *
  115. */
  116. public void acceptClass(String name, byte[] bytes) {
  117. try {
  118. if (shouldDump(name.replace('/', '.'), false)) {
  119. dump(name, bytes, false);
  120. }
  121. } catch (Throwable throwable) {
  122. throwable.printStackTrace();
  123. }
  124. if (activeProtectionDomain != null) {
  125. defineClass(loaderRef.getClassLoader(), name, bytes, activeProtectionDomain);
  126. } else {
  127. defineClass(loaderRef.getClassLoader(), name, bytes); // could be done lazily using the hook
  128. }
  129. }
  130. }
  131. public void initialize(final ClassLoader classLoader, IWeavingContext context) {
  132. if (initialized) {
  133. return;
  134. }
  135. boolean success = true;
  136. this.weavingContext = context;
  137. if (weavingContext == null) {
  138. weavingContext = new DefaultWeavingContext(classLoader);
  139. }
  140. createMessageHandler();
  141. this.generatedClassHandler = new SimpleGeneratedClassHandler(classLoader);
  142. List definitions = weavingContext.getDefinitions(classLoader, this);
  143. if (definitions.isEmpty()) {
  144. disable(); // TODO maw Needed to ensure messages are flushed
  145. if (trace.isTraceEnabled()) {
  146. trace.exit("initialize", definitions);
  147. }
  148. return;
  149. }
  150. // TODO when the world works in terms of the context, we can remove the loader
  151. bcelWorld = new LTWWorld(classLoader, weavingContext, getMessageHandler(), null);
  152. weaver = new BcelWeaver(bcelWorld);
  153. // register the definitions
  154. success = registerDefinitions(weaver, classLoader, definitions);
  155. if (success) {
  156. // after adding aspects
  157. weaver.prepareForWeave();
  158. enable(); // TODO maw Needed to ensure messages are flushed
  159. success = weaveAndDefineConceteAspects();
  160. }
  161. if (success) {
  162. enable();
  163. } else {
  164. disable();
  165. bcelWorld = null;
  166. weaver = null;
  167. }
  168. initialized = true;
  169. if (trace.isTraceEnabled()) {
  170. trace.exit("initialize", isEnabled());
  171. }
  172. }
  173. /**
  174. * Load and cache the aop.xml/properties according to the classloader visibility rules
  175. *
  176. * @param weaver
  177. * @param loader
  178. */
  179. List<Definition> parseDefinitions(final ClassLoader loader) {
  180. if (trace.isTraceEnabled()) {
  181. trace.enter("parseDefinitions", this);
  182. }
  183. List<Definition> definitions = new ArrayList<Definition>();
  184. try {
  185. info("register classloader " + getClassLoaderName(loader));
  186. // TODO av underoptimized: we will parse each XML once per CL that see it
  187. // TODO av dev mode needed ? TBD -Daj5.def=...
  188. if (loader.equals(ClassLoader.getSystemClassLoader())) {
  189. String file = System.getProperty("aj5.def", null);
  190. if (file != null) {
  191. info("using (-Daj5.def) " + file);
  192. definitions.add(DocumentParser.parse((new File(file)).toURL()));
  193. }
  194. }
  195. String resourcePath = System.getProperty("org.aspectj.weaver.loadtime.configuration", AOP_XML);
  196. if (trace.isTraceEnabled()) {
  197. trace.event("parseDefinitions", this, resourcePath);
  198. }
  199. StringTokenizer st = new StringTokenizer(resourcePath, ";");
  200. while (st.hasMoreTokens()) {
  201. String nextDefinition = st.nextToken();
  202. if (nextDefinition.startsWith("file:")) {
  203. try {
  204. String fpath = new URL(nextDefinition).getFile();
  205. File configFile = new File(fpath);
  206. if (!configFile.exists()) {
  207. warn("configuration does not exist: " + nextDefinition);
  208. } else {
  209. definitions.add(DocumentParser.parse(configFile.toURL()));
  210. }
  211. } catch (MalformedURLException mue) {
  212. error("malformed definition url: " + nextDefinition);
  213. }
  214. } else {
  215. Enumeration<URL> xmls = weavingContext.getResources(nextDefinition);
  216. // System.out.println("? registerDefinitions: found-aop.xml=" + xmls.hasMoreElements() + ", loader=" + loader);
  217. Set<URL> seenBefore = new HashSet<URL>();
  218. while (xmls.hasMoreElements()) {
  219. URL xml = xmls.nextElement();
  220. if (trace.isTraceEnabled()) {
  221. trace.event("parseDefinitions", this, xml);
  222. }
  223. if (!seenBefore.contains(xml)) {
  224. info("using configuration " + weavingContext.getFile(xml));
  225. definitions.add(DocumentParser.parse(xml));
  226. seenBefore.add(xml);
  227. } else {
  228. warn("ignoring duplicate definition: " + xml);
  229. }
  230. }
  231. }
  232. }
  233. if (definitions.isEmpty()) {
  234. info("no configuration found. Disabling weaver for class loader " + getClassLoaderName(loader));
  235. }
  236. } catch (Exception e) {
  237. definitions.clear();
  238. warn("parse definitions failed", e);
  239. }
  240. if (trace.isTraceEnabled()) {
  241. trace.exit("parseDefinitions", definitions);
  242. }
  243. return definitions;
  244. }
  245. private boolean registerDefinitions(final BcelWeaver weaver, final ClassLoader loader, List<Definition> definitions) {
  246. if (trace.isTraceEnabled()) {
  247. trace.enter("registerDefinitions", this, definitions);
  248. }
  249. boolean success = true;
  250. try {
  251. registerOptions(weaver, loader, definitions);
  252. registerAspectExclude(weaver, loader, definitions);
  253. registerAspectInclude(weaver, loader, definitions);
  254. success = registerAspects(weaver, loader, definitions);
  255. registerIncludeExclude(weaver, loader, definitions);
  256. registerDump(weaver, loader, definitions);
  257. } catch (Exception ex) {
  258. trace.error("register definition failed", ex);
  259. success = false;
  260. warn("register definition failed", (ex instanceof AbortException) ? null : ex);
  261. }
  262. if (trace.isTraceEnabled()) {
  263. trace.exit("registerDefinitions", success);
  264. }
  265. return success;
  266. }
  267. private String getClassLoaderName(ClassLoader loader) {
  268. return weavingContext.getClassLoaderName();
  269. }
  270. /**
  271. * Configure the weaver according to the option directives TODO av - don't know if it is that good to reuse, since we only allow
  272. * a small subset of options in LTW
  273. *
  274. * @param weaver
  275. * @param loader
  276. * @param definitions
  277. */
  278. private void registerOptions(final BcelWeaver weaver, final ClassLoader loader, final List definitions) {
  279. StringBuffer allOptions = new StringBuffer();
  280. for (Iterator iterator = definitions.iterator(); iterator.hasNext();) {
  281. Definition definition = (Definition) iterator.next();
  282. allOptions.append(definition.getWeaverOptions()).append(' ');
  283. }
  284. Options.WeaverOption weaverOption = Options.parse(allOptions.toString(), loader, getMessageHandler());
  285. // configure the weaver and world
  286. // AV - code duplicates AspectJBuilder.initWorldAndWeaver()
  287. World world = weaver.getWorld();
  288. setMessageHandler(weaverOption.messageHandler);
  289. world.setXlazyTjp(weaverOption.lazyTjp);
  290. world.setXHasMemberSupportEnabled(weaverOption.hasMember);
  291. world.setTiming(weaverOption.timers, true);
  292. world.setOptionalJoinpoints(weaverOption.optionalJoinpoints);
  293. world.setPinpointMode(weaverOption.pinpoint);
  294. weaver.setReweavableMode(weaverOption.notReWeavable);
  295. world.performExtraConfiguration(weaverOption.xSet);
  296. world.setXnoInline(weaverOption.noInline);
  297. // AMC - autodetect as per line below, needed for AtAjLTWTests.testLTWUnweavable
  298. world.setBehaveInJava5Way(LangUtil.is15VMOrGreater());
  299. world.setAddSerialVerUID(weaverOption.addSerialVersionUID);
  300. /* First load defaults */
  301. bcelWorld.getLint().loadDefaultProperties();
  302. /* Second overlay LTW defaults */
  303. bcelWorld.getLint().adviceDidNotMatch.setKind(null);
  304. /* Third load user file using -Xlintfile so that -Xlint wins */
  305. if (weaverOption.lintFile != null) {
  306. InputStream resource = null;
  307. try {
  308. resource = loader.getResourceAsStream(weaverOption.lintFile);
  309. Exception failure = null;
  310. if (resource != null) {
  311. try {
  312. Properties properties = new Properties();
  313. properties.load(resource);
  314. world.getLint().setFromProperties(properties);
  315. } catch (IOException e) {
  316. failure = e;
  317. }
  318. }
  319. if (failure != null || resource == null) {
  320. warn("Cannot access resource for -Xlintfile:" + weaverOption.lintFile, failure);
  321. // world.getMessageHandler().handleMessage(new Message(
  322. // "Cannot access resource for -Xlintfile:"+weaverOption.lintFile,
  323. // IMessage.WARNING,
  324. // failure,
  325. // null));
  326. }
  327. } finally {
  328. try {
  329. resource.close();
  330. } catch (Throwable t) {
  331. }
  332. }
  333. }
  334. /* Fourth override with -Xlint */
  335. if (weaverOption.lint != null) {
  336. if (weaverOption.lint.equals("default")) {// FIXME should be AjBuildConfig.AJLINT_DEFAULT but yetanother deps..
  337. bcelWorld.getLint().loadDefaultProperties();
  338. } else {
  339. bcelWorld.getLint().setAll(weaverOption.lint);
  340. if (weaverOption.lint.equals("ignore")) {
  341. bcelWorld.setAllLintIgnored();
  342. }
  343. }
  344. }
  345. // TODO proceedOnError option
  346. }
  347. private void registerAspectExclude(final BcelWeaver weaver, final ClassLoader loader, final List<Definition> definitions) {
  348. String fastMatchInfo = null;
  349. for (Definition definition : definitions) {
  350. for (String exclude : definition.getAspectExcludePatterns()) {
  351. TypePattern excludePattern = new PatternParser(exclude).parseTypePattern();
  352. m_aspectExcludeTypePattern.add(excludePattern);
  353. fastMatchInfo = looksLikeStartsWith(exclude);
  354. if (fastMatchInfo != null) {
  355. m_aspectExcludeStartsWith.add(fastMatchInfo);
  356. }
  357. }
  358. }
  359. }
  360. private void registerAspectInclude(final BcelWeaver weaver, final ClassLoader loader, final List<Definition> definitions) {
  361. String fastMatchInfo = null;
  362. for (Definition definition : definitions) {
  363. for (String include : definition.getAspectIncludePatterns()) {
  364. TypePattern includePattern = new PatternParser(include).parseTypePattern();
  365. m_aspectIncludeTypePattern.add(includePattern);
  366. fastMatchInfo = looksLikeStartsWith(include);
  367. if (fastMatchInfo != null) {
  368. m_aspectIncludeStartsWith.add(fastMatchInfo);
  369. }
  370. }
  371. }
  372. }
  373. protected void lint(String name, String[] infos) {
  374. Lint lint = bcelWorld.getLint();
  375. Kind kind = lint.getLintKind(name);
  376. kind.signal(infos, null, null);
  377. }
  378. @Override
  379. public String getContextId() {
  380. return weavingContext.getId();
  381. }
  382. /**
  383. * Register the aspect, following include / exclude rules
  384. *
  385. * @param weaver
  386. * @param loader
  387. * @param definitions
  388. */
  389. private boolean registerAspects(final BcelWeaver weaver, final ClassLoader loader, final List<Definition> definitions) {
  390. if (trace.isTraceEnabled()) {
  391. trace.enter("registerAspects", this, new Object[] { weaver, loader, definitions });
  392. }
  393. boolean success = true;
  394. // TODO: the exclude aspect allow to exclude aspect defined upper in the CL hierarchy - is it what we want ??
  395. // if not, review the getResource so that we track which resource is defined by which CL
  396. // iterate aspectClassNames
  397. // exclude if in any of the exclude list
  398. for (Definition definition : definitions) {
  399. for (String aspectClassName : definition.getAspectClassNames()) {
  400. if (acceptAspect(aspectClassName)) {
  401. info("register aspect " + aspectClassName);
  402. // System.err.println("? ClassLoaderWeavingAdaptor.registerAspects() aspectName=" + aspectClassName +
  403. // ", loader=" + loader + ", bundle=" + weavingContext.getClassLoaderName());
  404. String requiredType = definition.getAspectRequires(aspectClassName);
  405. if (requiredType != null) {
  406. // This aspect expresses that it requires a type to be around, otherwise it should 'switch off'
  407. ((BcelWorld) weaver.getWorld()).addAspectRequires(aspectClassName, requiredType);
  408. }
  409. String definedScope = definition.getScopeForAspect(aspectClassName);
  410. if (definedScope != null) {
  411. ((BcelWorld) weaver.getWorld()).addScopedAspect(aspectClassName, definedScope);
  412. }
  413. // ResolvedType aspect =
  414. weaver.addLibraryAspect(aspectClassName);
  415. // generate key for SC
  416. if (namespace == null) {
  417. namespace = new StringBuffer(aspectClassName);
  418. } else {
  419. namespace = namespace.append(";").append(aspectClassName);
  420. }
  421. } else {
  422. // warn("aspect excluded: " + aspectClassName);
  423. lint("aspectExcludedByConfiguration", new String[] { aspectClassName, getClassLoaderName(loader) });
  424. }
  425. }
  426. }
  427. // iterate concreteAspects
  428. // exclude if in any of the exclude list - note that the user defined name matters for that to happen
  429. for (Definition definition : definitions) {
  430. for (Definition.ConcreteAspect concreteAspect : definition.getConcreteAspects()) {
  431. if (acceptAspect(concreteAspect.name)) {
  432. info("define aspect " + concreteAspect.name);
  433. ConcreteAspectCodeGen gen = new ConcreteAspectCodeGen(concreteAspect, weaver.getWorld());
  434. if (!gen.validate()) {
  435. error("Concrete-aspect '" + concreteAspect.name + "' could not be registered");
  436. success = false;
  437. break;
  438. }
  439. ((BcelWorld) weaver.getWorld()).addSourceObjectType(Utility.makeJavaClass(concreteAspect.name, gen.getBytes()),
  440. true);
  441. concreteAspects.add(gen);
  442. weaver.addLibraryAspect(concreteAspect.name);
  443. // generate key for SC
  444. if (namespace == null) {
  445. namespace = new StringBuffer(concreteAspect.name);
  446. } else {
  447. namespace = namespace.append(";" + concreteAspect.name);
  448. }
  449. }
  450. }
  451. }
  452. /* We couldn't register one or more aspects so disable the adaptor */
  453. if (!success) {
  454. warn("failure(s) registering aspects. Disabling weaver for class loader " + getClassLoaderName(loader));
  455. }
  456. /* We didn't register any aspects so disable the adaptor */
  457. else if (namespace == null) {
  458. success = false;
  459. info("no aspects registered. Disabling weaver for class loader " + getClassLoaderName(loader));
  460. }
  461. if (trace.isTraceEnabled()) {
  462. trace.exit("registerAspects", success);
  463. }
  464. return success;
  465. }
  466. private boolean weaveAndDefineConceteAspects() {
  467. if (trace.isTraceEnabled()) {
  468. trace.enter("weaveAndDefineConceteAspects", this, concreteAspects);
  469. }
  470. boolean success = true;
  471. for (Iterator iterator = concreteAspects.iterator(); iterator.hasNext();) {
  472. ConcreteAspectCodeGen gen = (ConcreteAspectCodeGen) iterator.next();
  473. String name = gen.getClassName();
  474. byte[] bytes = gen.getBytes();
  475. try {
  476. byte[] newBytes = weaveClass(name, bytes, true);
  477. this.generatedClassHandler.acceptClass(name, newBytes);
  478. } catch (IOException ex) {
  479. trace.error("weaveAndDefineConceteAspects", ex);
  480. error("exception weaving aspect '" + name + "'", ex);
  481. }
  482. }
  483. if (trace.isTraceEnabled()) {
  484. trace.exit("weaveAndDefineConceteAspects", success);
  485. }
  486. return success;
  487. }
  488. /**
  489. * Register the include / exclude filters. We duplicate simple patterns in startWith filters that will allow faster matching
  490. * without ResolvedType
  491. *
  492. * @param weaver
  493. * @param loader
  494. * @param definitions
  495. */
  496. private void registerIncludeExclude(final BcelWeaver weaver, final ClassLoader loader, final List definitions) {
  497. String fastMatchInfo = null;
  498. for (Iterator iterator = definitions.iterator(); iterator.hasNext();) {
  499. Definition definition = (Definition) iterator.next();
  500. for (Iterator iterator1 = definition.getIncludePatterns().iterator(); iterator1.hasNext();) {
  501. hasIncludes = true;
  502. String include = (String) iterator1.next();
  503. fastMatchInfo = looksLikeStartsWith(include);
  504. if (fastMatchInfo != null) {
  505. m_includeStartsWith.add(fastMatchInfo);
  506. } else if (include.equals("*")) {
  507. includeStar = true;
  508. } else if ((fastMatchInfo = looksLikeExactName(include)) != null) {
  509. includeExactName.add(fastMatchInfo);
  510. } else {
  511. TypePattern includePattern = new PatternParser(include).parseTypePattern();
  512. includeTypePattern.add(includePattern);
  513. }
  514. }
  515. for (Iterator iterator1 = definition.getExcludePatterns().iterator(); iterator1.hasNext();) {
  516. hasExcludes = true;
  517. String exclude = (String) iterator1.next();
  518. fastMatchInfo = looksLikeStartsWith(exclude);
  519. if (fastMatchInfo != null) {
  520. excludeStartsWith.add(fastMatchInfo);
  521. } else if ((fastMatchInfo = looksLikeStarDotDotStarExclude(exclude)) != null) {
  522. excludeStarDotDotStar.add(fastMatchInfo);
  523. } else if ((fastMatchInfo = looksLikeExactName(exclude)) != null) {
  524. excludeExactName.add(exclude);
  525. } else if ((fastMatchInfo = looksLikeEndsWith(exclude)) != null) {
  526. excludeEndsWith.add(fastMatchInfo);
  527. } else if (exclude
  528. .equals("org.codehaus.groovy..* && !org.codehaus.groovy.grails.web.servlet.mvc.SimpleGrailsController*")) {
  529. // TODO need a more sophisticated analysis here, to allow for similar situations
  530. excludeSpecial.add(new String[] { "org.codehaus.groovy.",
  531. "org.codehaus.groovy.grails.web.servlet.mvc.SimpleGrailsController" });
  532. // for the related test:
  533. // } else if (exclude.equals("testdata..* && !testdata.sub.Oran*")) {
  534. // excludeSpecial.add(new String[] { "testdata.", "testdata.sub.Oran" });
  535. } else {
  536. TypePattern excludePattern = new PatternParser(exclude).parseTypePattern();
  537. excludeTypePattern.add(excludePattern);
  538. }
  539. }
  540. }
  541. }
  542. /**
  543. * Checks if the pattern looks like "*..*XXXX*" and if so returns XXXX. This will enable fast name matching of CGLIB exclusion
  544. *
  545. */
  546. private String looksLikeStarDotDotStarExclude(String typePattern) {
  547. if (!typePattern.startsWith("*..*")) {
  548. return null;
  549. }
  550. if (!typePattern.endsWith("*")) {
  551. return null;
  552. }
  553. String subPattern = typePattern.substring(4, typePattern.length() - 1);
  554. if (hasStarDot(subPattern, 0)) {
  555. return null;
  556. }
  557. return subPattern.replace('$', '.');
  558. }
  559. /**
  560. * Checks if the pattern looks like "com.foo.Bar" - an exact name
  561. */
  562. private String looksLikeExactName(String typePattern) {
  563. if (hasSpaceAnnotationPlus(typePattern, 0) || typePattern.indexOf("*") != -1) {
  564. return null;
  565. }
  566. return typePattern.replace('$', '.');
  567. }
  568. /**
  569. * Checks if the pattern looks like "*Exception"
  570. */
  571. private String looksLikeEndsWith(String typePattern) {
  572. if (typePattern.charAt(0) != '*') {
  573. return null;
  574. }
  575. if (hasSpaceAnnotationPlus(typePattern, 1) || hasStarDot(typePattern, 1)) {
  576. return null;
  577. }
  578. return typePattern.substring(1).replace('$', '.');
  579. }
  580. /**
  581. * Determine if something in the string is going to affect our ability to optimize. Checks for: ' ' '@' '+'
  582. */
  583. private boolean hasSpaceAnnotationPlus(String string, int pos) {
  584. for (int i = pos, max = string.length(); i < max; i++) {
  585. char ch = string.charAt(i);
  586. if (ch == ' ' || ch == '@' || ch == '+') {
  587. return true;
  588. }
  589. }
  590. return false;
  591. }
  592. /**
  593. * Determine if something in the string is going to affect our ability to optimize. Checks for: '*' '.'
  594. */
  595. private boolean hasStarDot(String string, int pos) {
  596. for (int i = pos, max = string.length(); i < max; i++) {
  597. char ch = string.charAt(i);
  598. if (ch == '*' || ch == '.') {
  599. return true;
  600. }
  601. }
  602. return false;
  603. }
  604. /**
  605. * Checks if the type pattern looks like "com.foo..*"
  606. */
  607. private String looksLikeStartsWith(String typePattern) {
  608. if (hasSpaceAnnotationPlus(typePattern, 0) || typePattern.charAt(typePattern.length() - 1) != '*') {
  609. return null;
  610. }
  611. // now must looks like with "charsss..*" or "cha.rss..*" etc
  612. // note that "*" and "*..*" won't be fast matched
  613. // and that "charsss.*" will not neither
  614. int length = typePattern.length();
  615. if (typePattern.endsWith("..*") && length > 3) {
  616. if (typePattern.indexOf("..") == length - 3 // no ".." before last sequence
  617. && typePattern.indexOf('*') == length - 1) { // no earlier '*'
  618. return typePattern.substring(0, length - 2).replace('$', '.'); // "charsss." or "char.rss." etc
  619. }
  620. }
  621. return null;
  622. }
  623. /**
  624. * Register the dump filter
  625. *
  626. * @param weaver
  627. * @param loader
  628. * @param definitions
  629. */
  630. private void registerDump(final BcelWeaver weaver, final ClassLoader loader, final List definitions) {
  631. for (Iterator iterator = definitions.iterator(); iterator.hasNext();) {
  632. Definition definition = (Definition) iterator.next();
  633. for (Iterator iterator1 = definition.getDumpPatterns().iterator(); iterator1.hasNext();) {
  634. String dump = (String) iterator1.next();
  635. TypePattern pattern = new PatternParser(dump).parseTypePattern();
  636. m_dumpTypePattern.add(pattern);
  637. }
  638. if (definition.shouldDumpBefore()) {
  639. m_dumpBefore = true;
  640. }
  641. if (definition.createDumpDirPerClassloader()) {
  642. dumpDirPerClassloader = true;
  643. }
  644. }
  645. }
  646. /**
  647. * Determine whether a type should be accepted for weaving, by checking it against any includes/excludes.
  648. *
  649. * @param className the name of the type to possibly accept
  650. * @param bytes the bytecode for the type (in case we need to look inside, eg. annotations)
  651. * @return true if it should be accepted for weaving
  652. */
  653. @Override
  654. protected boolean accept(String className, byte[] bytes) {
  655. if (!hasExcludes && !hasIncludes) {
  656. return true;
  657. }
  658. // still try to avoid ResolvedType if we have simple patterns
  659. String fastClassName = className.replace('/', '.');
  660. for (String excludeStartsWithString : excludeStartsWith) {
  661. if (fastClassName.startsWith(excludeStartsWithString)) {
  662. return false;
  663. }
  664. }
  665. // Fast exclusion of patterns like: "*..*CGLIB*"
  666. if (!excludeStarDotDotStar.isEmpty()) {
  667. for (String namePiece : excludeStarDotDotStar) {
  668. int index = fastClassName.lastIndexOf('.');
  669. if (fastClassName.indexOf(namePiece, index + 1) != -1) {
  670. return false;
  671. }
  672. }
  673. }
  674. fastClassName = fastClassName.replace('$', '.');
  675. if (!excludeEndsWith.isEmpty()) {
  676. for (String lastPiece : excludeEndsWith) {
  677. if (fastClassName.endsWith(lastPiece)) {
  678. return false;
  679. }
  680. }
  681. }
  682. // Fast exclusion of exact names
  683. if (!excludeExactName.isEmpty()) {
  684. for (String name : excludeExactName) {
  685. if (fastClassName.equals(name)) {
  686. return false;
  687. }
  688. }
  689. }
  690. if (!excludeSpecial.isEmpty()) {
  691. for (String[] entry : excludeSpecial) {
  692. String excludeThese = entry[0];
  693. String exceptThese = entry[1];
  694. if (fastClassName.startsWith(excludeThese) && !fastClassName.startsWith(exceptThese)) {
  695. return false;
  696. }
  697. }
  698. }
  699. /*
  700. * Bug 120363 If we have an exclude pattern that cannot be matched using "starts with" then we cannot fast accept
  701. */
  702. boolean didSomeIncludeMatching = false;
  703. if (excludeTypePattern.isEmpty()) {
  704. if (includeStar) {
  705. return true;
  706. }
  707. if (!includeExactName.isEmpty()) {
  708. didSomeIncludeMatching = true;
  709. for (String exactname : includeExactName) {
  710. if (fastClassName.equals(exactname)) {
  711. return true;
  712. }
  713. }
  714. }
  715. boolean fastAccept = false;// defaults to false if no fast include
  716. for (int i = 0; i < m_includeStartsWith.size(); i++) {
  717. didSomeIncludeMatching = true;
  718. fastAccept = fastClassName.startsWith(m_includeStartsWith.get(i));
  719. if (fastAccept) {
  720. return true;
  721. }
  722. }
  723. // We may have processed all patterns now... check that and return
  724. if (includeTypePattern.isEmpty()) {
  725. return !didSomeIncludeMatching;
  726. }
  727. }
  728. boolean accept;
  729. try {
  730. ensureDelegateInitialized(className, bytes);
  731. ResolvedType classInfo = delegateForCurrentClass.getResolvedTypeX();
  732. // exclude are "AND"ed
  733. for (TypePattern typePattern : excludeTypePattern) {
  734. if (typePattern.matchesStatically(classInfo)) {
  735. // exclude match - skip
  736. return false;
  737. }
  738. }
  739. // include are "OR"ed
  740. if (includeStar) {
  741. return true;
  742. }
  743. if (!includeExactName.isEmpty()) {
  744. didSomeIncludeMatching = true;
  745. for (String exactname : includeExactName) {
  746. if (fastClassName.equals(exactname)) {
  747. return true;
  748. }
  749. }
  750. }
  751. for (int i = 0; i < m_includeStartsWith.size(); i++) {
  752. didSomeIncludeMatching = true;
  753. boolean fastaccept = fastClassName.startsWith(m_includeStartsWith.get(i));
  754. if (fastaccept) {
  755. return true;
  756. }
  757. }
  758. accept = !didSomeIncludeMatching; // only true if no includes at all
  759. for (TypePattern typePattern : includeTypePattern) {
  760. accept = typePattern.matchesStatically(classInfo);
  761. if (accept) {
  762. break;
  763. }
  764. // goes on if this include did not match ("OR"ed)
  765. }
  766. } finally {
  767. this.bcelWorld.demote();
  768. }
  769. return accept;
  770. }
  771. // FIXME we don't use include/exclude of others aop.xml
  772. // this can be nice but very dangerous as well to change that
  773. private boolean acceptAspect(String aspectClassName) {
  774. // avoid ResolvedType if not needed
  775. if (m_aspectExcludeTypePattern.isEmpty() && m_aspectIncludeTypePattern.isEmpty()) {
  776. return true;
  777. }
  778. // still try to avoid ResolvedType if we have simple patterns
  779. // EXCLUDE: if one match then reject
  780. String fastClassName = aspectClassName.replace('/', '.').replace('.', '$');
  781. for (int i = 0; i < m_aspectExcludeStartsWith.size(); i++) {
  782. if (fastClassName.startsWith(m_aspectExcludeStartsWith.get(i))) {
  783. return false;
  784. }
  785. }
  786. // INCLUDE: if one match then accept
  787. for (int i = 0; i < m_aspectIncludeStartsWith.size(); i++) {
  788. if (fastClassName.startsWith(m_aspectIncludeStartsWith.get(i))) {
  789. return true;
  790. }
  791. }
  792. // needs further analysis
  793. ResolvedType classInfo = weaver.getWorld().resolve(UnresolvedType.forName(aspectClassName), true);
  794. // exclude are "AND"ed
  795. for (Iterator iterator = m_aspectExcludeTypePattern.iterator(); iterator.hasNext();) {
  796. TypePattern typePattern = (TypePattern) iterator.next();
  797. if (typePattern.matchesStatically(classInfo)) {
  798. // exclude match - skip
  799. return false;
  800. }
  801. }
  802. // include are "OR"ed
  803. boolean accept = true;// defaults to true if no include
  804. for (Iterator iterator = m_aspectIncludeTypePattern.iterator(); iterator.hasNext();) {
  805. TypePattern typePattern = (TypePattern) iterator.next();
  806. accept = typePattern.matchesStatically(classInfo);
  807. if (accept) {
  808. break;
  809. }
  810. // goes on if this include did not match ("OR"ed)
  811. }
  812. return accept;
  813. }
  814. @Override
  815. protected boolean shouldDump(String className, boolean before) {
  816. // Don't dump before weaving unless asked to
  817. if (before && !m_dumpBefore) {
  818. return false;
  819. }
  820. // avoid ResolvedType if not needed
  821. if (m_dumpTypePattern.isEmpty()) {
  822. return false;
  823. }
  824. // TODO AV - optimize for className.startWith only
  825. ResolvedType classInfo = weaver.getWorld().resolve(UnresolvedType.forName(className), true);
  826. // dump
  827. for (Iterator iterator = m_dumpTypePattern.iterator(); iterator.hasNext();) {
  828. TypePattern typePattern = (TypePattern) iterator.next();
  829. if (typePattern.matchesStatically(classInfo)) {
  830. // dump match
  831. return true;
  832. }
  833. }
  834. return false;
  835. }
  836. @Override
  837. protected String getDumpDir() {
  838. if (dumpDirPerClassloader) {
  839. StringBuffer dir = new StringBuffer();
  840. dir.append("_ajdump").append(File.separator).append(weavingContext.getId());
  841. return dir.toString();
  842. } else {
  843. return super.getDumpDir();
  844. }
  845. }
  846. /*
  847. * shared classes methods
  848. */
  849. /**
  850. * @return Returns the key.
  851. */
  852. public String getNamespace() {
  853. // System.out.println("ClassLoaderWeavingAdaptor.getNamespace() classloader=" + weavingContext.getClassLoaderName() +
  854. // ", namespace=" + namespace);
  855. if (namespace == null) {
  856. return "";
  857. } else {
  858. return new String(namespace);
  859. }
  860. }
  861. /**
  862. * Check to see if any classes are stored in the generated classes cache. Then flush the cache if it is not empty
  863. *
  864. * @param className TODO
  865. * @return true if a class has been generated and is stored in the cache
  866. */
  867. public boolean generatedClassesExistFor(String className) {
  868. // System.err.println("? ClassLoaderWeavingAdaptor.generatedClassesExist() classname=" + className + ", size=" +
  869. // generatedClasses);
  870. if (className == null) {
  871. return !generatedClasses.isEmpty();
  872. } else {
  873. return generatedClasses.containsKey(className);
  874. }
  875. }
  876. /**
  877. * Flush the generated classes cache
  878. */
  879. public void flushGeneratedClasses() {
  880. // System.err.println("? ClassLoaderWeavingAdaptor.flushGeneratedClasses() generatedClasses=" + generatedClasses);
  881. generatedClasses = new HashMap();
  882. }
  883. private Method defineClassMethod;
  884. private Method defineClassWithProtectionDomainMethod;
  885. private void defineClass(ClassLoader loader, String name, byte[] bytes) {
  886. if (trace.isTraceEnabled()) {
  887. trace.enter("defineClass", this, new Object[] { loader, name, bytes });
  888. }
  889. Object clazz = null;
  890. debug("generating class '" + name + "'");
  891. try {
  892. if (defineClassMethod == null) {
  893. defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass", new Class[] { String.class,
  894. bytes.getClass(), int.class, int.class });
  895. }
  896. defineClassMethod.setAccessible(true);
  897. clazz = defineClassMethod.invoke(loader, new Object[] { name, bytes, new Integer(0), new Integer(bytes.length) });
  898. } catch (InvocationTargetException e) {
  899. if (e.getTargetException() instanceof LinkageError) {
  900. warn("define generated class failed", e.getTargetException());
  901. // is already defined (happens for X$ajcMightHaveAspect interfaces since aspects are reweaved)
  902. // TODO maw I don't think this is OK and
  903. } else {
  904. warn("define generated class failed", e.getTargetException());
  905. }
  906. } catch (Exception e) {
  907. warn("define generated class failed", e);
  908. }
  909. if (trace.isTraceEnabled()) {
  910. trace.exit("defineClass", clazz);
  911. }
  912. }
  913. private void defineClass(ClassLoader loader, String name, byte[] bytes, ProtectionDomain protectionDomain) {
  914. if (trace.isTraceEnabled()) {
  915. trace.enter("defineClass", this, new Object[] { loader, name, bytes, protectionDomain });
  916. }
  917. Object clazz = null;
  918. debug("generating class '" + name + "'");
  919. try {
  920. // System.out.println(">> Defining with protection domain " + name + " pd=" + protectionDomain);
  921. if (defineClassWithProtectionDomainMethod == null) {
  922. defineClassWithProtectionDomainMethod = ClassLoader.class.getDeclaredMethod("defineClass", new Class[] {
  923. String.class, bytes.getClass(), int.class, int.class, ProtectionDomain.class });
  924. }
  925. defineClassWithProtectionDomainMethod.setAccessible(true);
  926. clazz = defineClassWithProtectionDomainMethod.invoke(loader, new Object[] { name, bytes, new Integer(0),
  927. new Integer(bytes.length), protectionDomain });
  928. } catch (InvocationTargetException e) {
  929. if (e.getTargetException() instanceof LinkageError) {
  930. warn("define generated class failed", e.getTargetException());
  931. // is already defined (happens for X$ajcMightHaveAspect interfaces since aspects are reweaved)
  932. // TODO maw I don't think this is OK and
  933. } else {
  934. warn("define generated class failed", e.getTargetException());
  935. }
  936. } catch (Exception e) {
  937. warn("define generated class failed", e);
  938. }
  939. if (trace.isTraceEnabled()) {
  940. trace.exit("defineClass", clazz);
  941. }
  942. }
  943. }