Package org.aspectj.weaver.loadtime

Source Code of org.aspectj.weaver.loadtime.ClassLoaderWeavingAdaptor$SimpleGeneratedClassHandler

/*******************************************************************************
* Copyright (c) 2005 Contributors.
* All rights reserved.
* This program and the accompanying materials are made available
* under the terms of the Eclipse Public License v1.0
* which accompanies this distribution and is available at
* http://eclipse.org/legal/epl-v10.html
*
* Contributors:
*   Alexandre Vasseur         initial implementation
*   David Knibb           weaving context enhancments
*******************************************************************************/
package org.aspectj.weaver.loadtime;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;

import org.aspectj.bridge.AbortException;
import org.aspectj.bridge.Constants;
import org.aspectj.util.LangUtil;
import org.aspectj.weaver.Lint;
import org.aspectj.weaver.Lint.Kind;
import org.aspectj.weaver.ResolvedType;
import org.aspectj.weaver.UnresolvedType;
import org.aspectj.weaver.World;
import org.aspectj.weaver.bcel.BcelWeakClassLoaderReference;
import org.aspectj.weaver.bcel.BcelWeaver;
import org.aspectj.weaver.bcel.BcelWorld;
import org.aspectj.weaver.bcel.Utility;
import org.aspectj.weaver.loadtime.definition.Definition;
import org.aspectj.weaver.loadtime.definition.DocumentParser;
import org.aspectj.weaver.ltw.LTWWorld;
import org.aspectj.weaver.patterns.PatternParser;
import org.aspectj.weaver.patterns.TypePattern;
import org.aspectj.weaver.tools.GeneratedClassHandler;
import org.aspectj.weaver.tools.Trace;
import org.aspectj.weaver.tools.TraceFactory;
import org.aspectj.weaver.tools.WeavingAdaptor;

/**
* @author Alexandre Vasseur
* @author Andy Clement
* @author Abraham Nevado
*/
public class ClassLoaderWeavingAdaptor extends WeavingAdaptor {

  private final static String AOP_XML = Constants.AOP_USER_XML + ";" + Constants.AOP_AJC_XML + ";" + Constants.AOP_OSGI_XML;

  private boolean initialized;

  private List m_dumpTypePattern = new ArrayList();
  private boolean m_dumpBefore = false;
  private boolean dumpDirPerClassloader = false;

  private boolean hasExcludes = false;
  private List<TypePattern> excludeTypePattern = new ArrayList<TypePattern>(); // anything
  private List<String> excludeStartsWith = new ArrayList<String>(); // com.foo..*
  private List<String> excludeStarDotDotStar = new ArrayList<String>(); // *..*CGLIB*
  private List<String> excludeExactName = new ArrayList<String>(); // com.foo.Bar
  private List<String> excludeEndsWith = new ArrayList<String>(); // com.foo.Bar
  private List<String[]> excludeSpecial = new ArrayList<String[]>();

  private boolean hasIncludes = false;
  private List<TypePattern> includeTypePattern = new ArrayList<TypePattern>();
  private List<String> m_includeStartsWith = new ArrayList<String>();
  private List<String> includeExactName = new ArrayList<String>();
  private boolean includeStar = false;

  private List<TypePattern> m_aspectExcludeTypePattern = new ArrayList<TypePattern>();
  private List<String> m_aspectExcludeStartsWith = new ArrayList<String>();
  private List<TypePattern> m_aspectIncludeTypePattern = new ArrayList<TypePattern>();
  private List<String> m_aspectIncludeStartsWith = new ArrayList<String>();

  private StringBuffer namespace;
  private IWeavingContext weavingContext;

  private List concreteAspects = new ArrayList();

  private static Trace trace = TraceFactory.getTraceFactory().getTrace(ClassLoaderWeavingAdaptor.class);

  public ClassLoaderWeavingAdaptor() {
    super();
    if (trace.isTraceEnabled()) {
      trace.enter("<init>", this);
    }
    if (trace.isTraceEnabled()) {
      trace.exit("<init>");
    }
  }

  /**
   * We don't need a reference to the class loader and using it during construction can cause problems with recursion. It also
   * makes sense to supply the weaving context during initialization to.
   *
   * @deprecated
   */
  public ClassLoaderWeavingAdaptor(final ClassLoader deprecatedLoader, final IWeavingContext deprecatedContext) {
    super();
    if (trace.isTraceEnabled()) {
      trace.enter("<init>", this, new Object[] { deprecatedLoader, deprecatedContext });
    }
    if (trace.isTraceEnabled()) {
      trace.exit("<init>");
    }
  }

  class SimpleGeneratedClassHandler implements GeneratedClassHandler {
    private BcelWeakClassLoaderReference loaderRef;

    SimpleGeneratedClassHandler(ClassLoader loader) {
      loaderRef = new BcelWeakClassLoaderReference(loader);
    }

    /**
     * Callback when we need to define a Closure in the JVM
     *
     */
    public void acceptClass(String name, byte[] bytes) {
      try {
        if (shouldDump(name.replace('/', '.'), false)) {
          dump(name, bytes, false);
        }
      } catch (Throwable throwable) {
        throwable.printStackTrace();
      }

      defineClass(loaderRef.getClassLoader(), name, bytes); // could be done lazily using the hook
    }
  }

  public void initialize(final ClassLoader classLoader, IWeavingContext context) {
    if (initialized) {
      return;
    }

    boolean success = true;

    this.weavingContext = context;
    if (weavingContext == null) {
      weavingContext = new DefaultWeavingContext(classLoader);
    }

    createMessageHandler();

    this.generatedClassHandler = new SimpleGeneratedClassHandler(classLoader);

    List definitions = weavingContext.getDefinitions(classLoader, this);
    if (definitions.isEmpty()) {
      disable(); // TODO maw Needed to ensure messages are flushed
      if (trace.isTraceEnabled()) {
        trace.exit("initialize", definitions);
      }
      return;
    }

    // TODO when the world works in terms of the context, we can remove the loader
    bcelWorld = new LTWWorld(classLoader, weavingContext, getMessageHandler(), null);

    weaver = new BcelWeaver(bcelWorld);

    // register the definitions
    success = registerDefinitions(weaver, classLoader, definitions);
    if (success) {

      // after adding aspects
      weaver.prepareForWeave();

      enable(); // TODO maw Needed to ensure messages are flushed
      success = weaveAndDefineConceteAspects();
    }

    if (success) {
      enable();
    } else {
      disable();
      bcelWorld = null;
      weaver = null;
    }

    initialized = true;
    if (trace.isTraceEnabled()) {
      trace.exit("initialize", isEnabled());
    }
  }

  /**
   * Load and cache the aop.xml/properties according to the classloader visibility rules
   *
   * @param weaver
   * @param loader
   */
  List<Definition> parseDefinitions(final ClassLoader loader) {
    if (trace.isTraceEnabled()) {
      trace.enter("parseDefinitions", this);
    }

    List<Definition> definitions = new ArrayList<Definition>();
    try {
      info("register classloader " + getClassLoaderName(loader));
      // TODO av underoptimized: we will parse each XML once per CL that see it

      // TODO av dev mode needed ? TBD -Daj5.def=...
      if (loader.equals(ClassLoader.getSystemClassLoader())) {
        String file = System.getProperty("aj5.def", null);
        if (file != null) {
          info("using (-Daj5.def) " + file);
          definitions.add(DocumentParser.parse((new File(file)).toURL()));
        }
      }

      String resourcePath = System.getProperty("org.aspectj.weaver.loadtime.configuration", AOP_XML);
      if (trace.isTraceEnabled()) {
        trace.event("parseDefinitions", this, resourcePath);
      }

      StringTokenizer st = new StringTokenizer(resourcePath, ";");

      while (st.hasMoreTokens()) {
        String nextDefinition = st.nextToken();
        if (nextDefinition.startsWith("file:")) {
          try {
            String fpath = new URL(nextDefinition).getFile();
            File configFile = new File(fpath);
            if (!configFile.exists()) {
              warn("configuration does not exist: " + nextDefinition);
            } else {
              definitions.add(DocumentParser.parse(configFile.toURL()));
            }
          } catch (MalformedURLException mue) {
            error("malformed definition url: " + nextDefinition);
          }
        } else {
          Enumeration<URL> xmls = weavingContext.getResources(nextDefinition);
          // System.out.println("? registerDefinitions: found-aop.xml=" + xmls.hasMoreElements() + ", loader=" + loader);

          Set<URL> seenBefore = new HashSet<URL>();
          while (xmls.hasMoreElements()) {
            URL xml = xmls.nextElement();
            if (trace.isTraceEnabled()) {
              trace.event("parseDefinitions", this, xml);
            }
            if (!seenBefore.contains(xml)) {
              info("using configuration " + weavingContext.getFile(xml));
              definitions.add(DocumentParser.parse(xml));
              seenBefore.add(xml);
            } else {
              warn("ignoring duplicate definition: " + xml);
            }
          }
        }
      }
      if (definitions.isEmpty()) {
        info("no configuration found. Disabling weaver for class loader " + getClassLoaderName(loader));
      }
    } catch (Exception e) {
      definitions.clear();
      warn("parse definitions failed", e);
    }

    if (trace.isTraceEnabled()) {
      trace.exit("parseDefinitions", definitions);
    }
    return definitions;
  }

  private boolean registerDefinitions(final BcelWeaver weaver, final ClassLoader loader, List<Definition> definitions) {
    if (trace.isTraceEnabled()) {
      trace.enter("registerDefinitions", this, definitions);
    }
    boolean success = true;

    try {
      registerOptions(weaver, loader, definitions);
      registerAspectExclude(weaver, loader, definitions);
      registerAspectInclude(weaver, loader, definitions);
      success = registerAspects(weaver, loader, definitions);
      registerIncludeExclude(weaver, loader, definitions);
      registerDump(weaver, loader, definitions);
    } catch (Exception ex) {
      trace.error("register definition failed", ex);
      success = false;
      warn("register definition failed", (ex instanceof AbortException) ? null : ex);
    }

    if (trace.isTraceEnabled()) {
      trace.exit("registerDefinitions", success);
    }
    return success;
  }

  private String getClassLoaderName(ClassLoader loader) {
    return weavingContext.getClassLoaderName();
  }

  /**
   * Configure the weaver according to the option directives TODO av - don't know if it is that good to reuse, since we only allow
   * a small subset of options in LTW
   *
   * @param weaver
   * @param loader
   * @param definitions
   */
  private void registerOptions(final BcelWeaver weaver, final ClassLoader loader, final List definitions) {
    StringBuffer allOptions = new StringBuffer();
    for (Iterator iterator = definitions.iterator(); iterator.hasNext();) {
      Definition definition = (Definition) iterator.next();
      allOptions.append(definition.getWeaverOptions()).append(' ');
    }

    Options.WeaverOption weaverOption = Options.parse(allOptions.toString(), loader, getMessageHandler());

    // configure the weaver and world
    // AV - code duplicates AspectJBuilder.initWorldAndWeaver()
    World world = weaver.getWorld();
    setMessageHandler(weaverOption.messageHandler);
    world.setXlazyTjp(weaverOption.lazyTjp);
    world.setXHasMemberSupportEnabled(weaverOption.hasMember);
    world.setTiming(weaverOption.timers, true);
    world.setOptionalJoinpoints(weaverOption.optionalJoinpoints);
    world.setPinpointMode(weaverOption.pinpoint);
    weaver.setReweavableMode(weaverOption.notReWeavable);
    world.performExtraConfiguration(weaverOption.xSet);
    world.setXnoInline(weaverOption.noInline);
    // AMC - autodetect as per line below, needed for AtAjLTWTests.testLTWUnweavable
    world.setBehaveInJava5Way(LangUtil.is15VMOrGreater());
    world.setAddSerialVerUID(weaverOption.addSerialVersionUID);

    /* First load defaults */
    bcelWorld.getLint().loadDefaultProperties();

    /* Second overlay LTW defaults */
    bcelWorld.getLint().adviceDidNotMatch.setKind(null);

    /* Third load user file using -Xlintfile so that -Xlint wins */
    if (weaverOption.lintFile != null) {
      InputStream resource = null;
      try {
        resource = loader.getResourceAsStream(weaverOption.lintFile);
        Exception failure = null;
        if (resource != null) {
          try {
            Properties properties = new Properties();
            properties.load(resource);
            world.getLint().setFromProperties(properties);
          } catch (IOException e) {
            failure = e;
          }
        }
        if (failure != null || resource == null) {
          warn("Cannot access resource for -Xlintfile:" + weaverOption.lintFile, failure);
          // world.getMessageHandler().handleMessage(new Message(
          // "Cannot access resource for -Xlintfile:"+weaverOption.lintFile,
          // IMessage.WARNING,
          // failure,
          // null));
        }
      } finally {
        try {
          resource.close();
        } catch (Throwable t) {
        }
      }
    }

    /* Fourth override with -Xlint */
    if (weaverOption.lint != null) {
      if (weaverOption.lint.equals("default")) {// FIXME should be AjBuildConfig.AJLINT_DEFAULT but yetanother deps..
        bcelWorld.getLint().loadDefaultProperties();
      } else {
        bcelWorld.getLint().setAll(weaverOption.lint);
        if (weaverOption.lint.equals("ignore")) {
          bcelWorld.setAllLintIgnored();
        }
      }
    }
    // TODO proceedOnError option
  }

  private void registerAspectExclude(final BcelWeaver weaver, final ClassLoader loader, final List<Definition> definitions) {
    String fastMatchInfo = null;
    for (Definition definition : definitions) {
      for (String exclude : definition.getAspectExcludePatterns()) {
        TypePattern excludePattern = new PatternParser(exclude).parseTypePattern();
        m_aspectExcludeTypePattern.add(excludePattern);
        fastMatchInfo = looksLikeStartsWith(exclude);
        if (fastMatchInfo != null) {
          m_aspectExcludeStartsWith.add(fastMatchInfo);
        }
      }
    }
  }

  private void registerAspectInclude(final BcelWeaver weaver, final ClassLoader loader, final List<Definition> definitions) {
    String fastMatchInfo = null;
    for (Definition definition : definitions) {
      for (String include : definition.getAspectIncludePatterns()) {
        TypePattern includePattern = new PatternParser(include).parseTypePattern();
        m_aspectIncludeTypePattern.add(includePattern);
        fastMatchInfo = looksLikeStartsWith(include);
        if (fastMatchInfo != null) {
          m_aspectIncludeStartsWith.add(fastMatchInfo);
        }
      }
    }
  }

  protected void lint(String name, String[] infos) {
    Lint lint = bcelWorld.getLint();
    Kind kind = lint.getLintKind(name);
    kind.signal(infos, null, null);
  }

  @Override
  public String getContextId() {
    return weavingContext.getId();
  }

  /**
   * Register the aspect, following include / exclude rules
   *
   * @param weaver
   * @param loader
   * @param definitions
   */
  private boolean registerAspects(final BcelWeaver weaver, final ClassLoader loader, final List<Definition> definitions) {
    if (trace.isTraceEnabled()) {
      trace.enter("registerAspects", this, new Object[] { weaver, loader, definitions });
    }
    boolean success = true;

    // TODO: the exclude aspect allow to exclude aspect defined upper in the CL hierarchy - is it what we want ??
    // if not, review the getResource so that we track which resource is defined by which CL

    // iterate aspectClassNames
    // exclude if in any of the exclude list
    for (Definition definition : definitions) {
      for (String aspectClassName : definition.getAspectClassNames()) {
        if (acceptAspect(aspectClassName)) {
          info("register aspect " + aspectClassName);
          // System.err.println("? ClassLoaderWeavingAdaptor.registerAspects() aspectName=" + aspectClassName +
          // ", loader=" + loader + ", bundle=" + weavingContext.getClassLoaderName());
          String requiredType = definition.getAspectRequires(aspectClassName);
          if (requiredType != null) {
            // This aspect expresses that it requires a type to be around, otherwise it should 'switch off'
            ((BcelWorld) weaver.getWorld()).addAspectRequires(aspectClassName, requiredType);
          }
          String definedScope = definition.getScopeForAspect(aspectClassName);
          if (definedScope != null) {
            ((BcelWorld) weaver.getWorld()).addScopedAspect(aspectClassName, definedScope);
          }
          // ResolvedType aspect =
          weaver.addLibraryAspect(aspectClassName);

          // generate key for SC
          if (namespace == null) {
            namespace = new StringBuffer(aspectClassName);
          } else {
            namespace = namespace.append(";").append(aspectClassName);
          }

        } else {
          // warn("aspect excluded: " + aspectClassName);
          lint("aspectExcludedByConfiguration", new String[] { aspectClassName, getClassLoaderName(loader) });
        }
      }
    }

    // iterate concreteAspects
    // exclude if in any of the exclude list - note that the user defined name matters for that to happen
    for (Definition definition : definitions) {
      for (Definition.ConcreteAspect concreteAspect : definition.getConcreteAspects()) {
        if (acceptAspect(concreteAspect.name)) {
          info("define aspect " + concreteAspect.name);
          ConcreteAspectCodeGen gen = new ConcreteAspectCodeGen(concreteAspect, weaver.getWorld());
          if (!gen.validate()) {
            error("Concrete-aspect '" + concreteAspect.name + "' could not be registered");
            success = false;
            break;
          }

          ((BcelWorld) weaver.getWorld()).addSourceObjectType(Utility.makeJavaClass(concreteAspect.name, gen.getBytes()),
              true);

          concreteAspects.add(gen);

          weaver.addLibraryAspect(concreteAspect.name);

          // generate key for SC
          if (namespace == null) {
            namespace = new StringBuffer(concreteAspect.name);
          } else {
            namespace = namespace.append(";" + concreteAspect.name);
          }
        }
      }
    }

    /* We couldn't register one or more aspects so disable the adaptor */
    if (!success) {
      warn("failure(s) registering aspects. Disabling weaver for class loader " + getClassLoaderName(loader));
    }

    /* We didn't register any aspects so disable the adaptor */
    else if (namespace == null) {
      success = false;
      info("no aspects registered. Disabling weaver for class loader " + getClassLoaderName(loader));
    }

    if (trace.isTraceEnabled()) {
      trace.exit("registerAspects", success);
    }
    return success;
  }

  private boolean weaveAndDefineConceteAspects() {
    if (trace.isTraceEnabled()) {
      trace.enter("weaveAndDefineConceteAspects", this, concreteAspects);
    }
    boolean success = true;

    for (Iterator iterator = concreteAspects.iterator(); iterator.hasNext();) {
      ConcreteAspectCodeGen gen = (ConcreteAspectCodeGen) iterator.next();
      String name = gen.getClassName();
      byte[] bytes = gen.getBytes();

      try {
        byte[] newBytes = weaveClass(name, bytes, true);
        this.generatedClassHandler.acceptClass(name, newBytes);
      } catch (IOException ex) {
        trace.error("weaveAndDefineConceteAspects", ex);
        error("exception weaving aspect '" + name + "'", ex);
      }
    }

    if (trace.isTraceEnabled()) {
      trace.exit("weaveAndDefineConceteAspects", success);
    }
    return success;
  }

  /**
   * Register the include / exclude filters. We duplicate simple patterns in startWith filters that will allow faster matching
   * without ResolvedType
   *
   * @param weaver
   * @param loader
   * @param definitions
   */
  private void registerIncludeExclude(final BcelWeaver weaver, final ClassLoader loader, final List definitions) {
    String fastMatchInfo = null;
    for (Iterator iterator = definitions.iterator(); iterator.hasNext();) {
      Definition definition = (Definition) iterator.next();
      for (Iterator iterator1 = definition.getIncludePatterns().iterator(); iterator1.hasNext();) {
        hasIncludes = true;
        String include = (String) iterator1.next();
        fastMatchInfo = looksLikeStartsWith(include);
        if (fastMatchInfo != null) {
          m_includeStartsWith.add(fastMatchInfo);
        } else if (include.equals("*")) {
          includeStar = true;
        } else if ((fastMatchInfo = looksLikeExactName(include)) != null) {
          includeExactName.add(fastMatchInfo);
        } else {
          TypePattern includePattern = new PatternParser(include).parseTypePattern();
          includeTypePattern.add(includePattern);
        }
      }
      for (Iterator iterator1 = definition.getExcludePatterns().iterator(); iterator1.hasNext();) {
        hasExcludes = true;
        String exclude = (String) iterator1.next();
        fastMatchInfo = looksLikeStartsWith(exclude);
        if (fastMatchInfo != null) {
          excludeStartsWith.add(fastMatchInfo);
        } else if ((fastMatchInfo = looksLikeStarDotDotStarExclude(exclude)) != null) {
          excludeStarDotDotStar.add(fastMatchInfo);
        } else if ((fastMatchInfo = looksLikeExactName(exclude)) != null) {
          excludeExactName.add(exclude);
        } else if ((fastMatchInfo = looksLikeEndsWith(exclude)) != null) {
          excludeEndsWith.add(fastMatchInfo);
        } else if (exclude
            .equals("org.codehaus.groovy..* && !org.codehaus.groovy.grails.web.servlet.mvc.SimpleGrailsController*")) {
          // TODO need a more sophisticated analysis here, to allow for similar situations
          excludeSpecial.add(new String[] { "org.codehaus.groovy.",
              "org.codehaus.groovy.grails.web.servlet.mvc.SimpleGrailsController" });
          // for the related test:
          // } else if (exclude.equals("testdata..* && !testdata.sub.Oran*")) {
          // excludeSpecial.add(new String[] { "testdata.", "testdata.sub.Oran" });
        } else {
          TypePattern excludePattern = new PatternParser(exclude).parseTypePattern();
          excludeTypePattern.add(excludePattern);
        }
      }
    }
  }

  /**
   * Checks if the pattern looks like "*..*XXXX*" and if so returns XXXX. This will enable fast name matching of CGLIB exclusion
   *
   */
  private String looksLikeStarDotDotStarExclude(String typePattern) {
    if (!typePattern.startsWith("*..*")) {
      return null;
    }
    if (!typePattern.endsWith("*")) {
      return null;
    }
    String subPattern = typePattern.substring(4, typePattern.length() - 1);
    if (hasStarDot(subPattern, 0)) {
      return null;
    }
    return subPattern.replace('$', '.');
  }

  /**
   * Checks if the pattern looks like "com.foo.Bar" - an exact name
   */
  private String looksLikeExactName(String typePattern) {
    if (hasSpaceAnnotationPlus(typePattern, 0) || typePattern.indexOf("*") != -1) {
      return null;
    }
    return typePattern.replace('$', '.');
  }

  /**
   * Checks if the pattern looks like "*Exception"
   */
  private String looksLikeEndsWith(String typePattern) {
    if (typePattern.charAt(0) != '*') {
      return null;
    }
    if (hasSpaceAnnotationPlus(typePattern, 1) || hasStarDot(typePattern, 1)) {
      return null;
    }
    return typePattern.substring(1).replace('$', '.');
  }

  /**
   * Determine if something in the string is going to affect our ability to optimize. Checks for: ' ' '@' '+'
   */
  private boolean hasSpaceAnnotationPlus(String string, int pos) {
    for (int i = pos, max = string.length(); i < max; i++) {
      char ch = string.charAt(i);
      if (ch == ' ' || ch == '@' || ch == '+') {
        return true;
      }
    }
    return false;
  }

  /**
   * Determine if something in the string is going to affect our ability to optimize. Checks for: '*' '.'
   */
  private boolean hasStarDot(String string, int pos) {
    for (int i = pos, max = string.length(); i < max; i++) {
      char ch = string.charAt(i);
      if (ch == '*' || ch == '.') {
        return true;
      }
    }
    return false;
  }

  /**
   * Checks if the type pattern looks like "com.foo..*"
   */
  private String looksLikeStartsWith(String typePattern) {
    if (hasSpaceAnnotationPlus(typePattern, 0) || typePattern.charAt(typePattern.length() - 1) != '*') {
      return null;
    }
    // now must looks like with "charsss..*" or "cha.rss..*" etc
    // note that "*" and "*..*" won't be fast matched
    // and that "charsss.*" will not neither
    int length = typePattern.length();
    if (typePattern.endsWith("..*") && length > 3) {
      if (typePattern.indexOf("..") == length - 3 // no ".." before last sequence
          && typePattern.indexOf('*') == length - 1) { // no earlier '*'
        return typePattern.substring(0, length - 2).replace('$', '.'); // "charsss." or "char.rss." etc
      }
    }
    return null;
  }

  /**
   * Register the dump filter
   *
   * @param weaver
   * @param loader
   * @param definitions
   */
  private void registerDump(final BcelWeaver weaver, final ClassLoader loader, final List definitions) {
    for (Iterator iterator = definitions.iterator(); iterator.hasNext();) {
      Definition definition = (Definition) iterator.next();
      for (Iterator iterator1 = definition.getDumpPatterns().iterator(); iterator1.hasNext();) {
        String dump = (String) iterator1.next();
        TypePattern pattern = new PatternParser(dump).parseTypePattern();
        m_dumpTypePattern.add(pattern);
      }
      if (definition.shouldDumpBefore()) {
        m_dumpBefore = true;
      }
      if (definition.createDumpDirPerClassloader()) {
        dumpDirPerClassloader = true;
      }
    }
  }

  /**
   * Determine whether a type should be accepted for weaving, by checking it against any includes/excludes.
   *
   * @param className the name of the type to possibly accept
   * @param bytes the bytecode for the type (in case we need to look inside, eg. annotations)
   * @return true if it should be accepted for weaving
   */
  @Override
  protected boolean accept(String className, byte[] bytes) {

    if (!hasExcludes && !hasIncludes) {
      return true;
    }

    // still try to avoid ResolvedType if we have simple patterns
    String fastClassName = className.replace('/', '.');
    for (String excludeStartsWithString : excludeStartsWith) {
      if (fastClassName.startsWith(excludeStartsWithString)) {
        return false;
      }
    }

    // Fast exclusion of patterns like: "*..*CGLIB*"
    if (!excludeStarDotDotStar.isEmpty()) {
      for (String namePiece : excludeStarDotDotStar) {
        int index = fastClassName.lastIndexOf('.');
        if (fastClassName.indexOf(namePiece, index + 1) != -1) {
          return false;
        }
      }
    }
    fastClassName = fastClassName.replace('$', '.');

    if (!excludeEndsWith.isEmpty()) {
      for (String lastPiece : excludeEndsWith) {
        if (fastClassName.endsWith(lastPiece)) {
          return false;
        }
      }
    }

    // Fast exclusion of exact names
    if (!excludeExactName.isEmpty()) {
      for (String name : excludeExactName) {
        if (fastClassName.equals(name)) {
          return false;
        }
      }
    }

    if (!excludeSpecial.isEmpty()) {
      for (String[] entry : excludeSpecial) {
        String excludeThese = entry[0];
        String exceptThese = entry[1];
        if (fastClassName.startsWith(excludeThese) && !fastClassName.startsWith(exceptThese)) {
          return false;
        }
      }
    }

    /*
     * Bug 120363 If we have an exclude pattern that cannot be matched using "starts with" then we cannot fast accept
     */
    boolean didSomeIncludeMatching = false;
    if (excludeTypePattern.isEmpty()) {
      if (includeStar) {
        return true;
      }
      if (!includeExactName.isEmpty()) {
        didSomeIncludeMatching = true;
        for (String exactname : includeExactName) {
          if (fastClassName.equals(exactname)) {
            return true;
          }
        }
      }
      boolean fastAccept = false;// defaults to false if no fast include
      for (int i = 0; i < m_includeStartsWith.size(); i++) {
        didSomeIncludeMatching = true;
        fastAccept = fastClassName.startsWith(m_includeStartsWith.get(i));
        if (fastAccept) {
          return true;
        }
      }
      // We may have processed all patterns now... check that and return
      if (includeTypePattern.isEmpty()) {
        return !didSomeIncludeMatching;
      }
    }

    boolean accept;
    try {
      ensureDelegateInitialized(className, bytes);

      ResolvedType classInfo = delegateForCurrentClass.getResolvedTypeX();

      // exclude are "AND"ed
      for (TypePattern typePattern : excludeTypePattern) {
        if (typePattern.matchesStatically(classInfo)) {
          // exclude match - skip
          return false;
        }
      }
      // include are "OR"ed
      if (includeStar) {
        return true;
      }
      if (!includeExactName.isEmpty()) {
        didSomeIncludeMatching = true;
        for (String exactname : includeExactName) {
          if (fastClassName.equals(exactname)) {
            return true;
          }
        }
      }
      for (int i = 0; i < m_includeStartsWith.size(); i++) {
        didSomeIncludeMatching = true;
        boolean fastaccept = fastClassName.startsWith(m_includeStartsWith.get(i));
        if (fastaccept) {
          return true;
        }
      }
      accept = !didSomeIncludeMatching; // only true if no includes at all
      for (TypePattern typePattern : includeTypePattern) {
        accept = typePattern.matchesStatically(classInfo);
        if (accept) {
          break;
        }
        // goes on if this include did not match ("OR"ed)
      }
    } finally {
      this.bcelWorld.demote();
    }
    return accept;
  }

  // FIXME we don't use include/exclude of others aop.xml
  // this can be nice but very dangerous as well to change that
  private boolean acceptAspect(String aspectClassName) {
    // avoid ResolvedType if not needed
    if (m_aspectExcludeTypePattern.isEmpty() && m_aspectIncludeTypePattern.isEmpty()) {
      return true;
    }

    // still try to avoid ResolvedType if we have simple patterns
    // EXCLUDE: if one match then reject
    String fastClassName = aspectClassName.replace('/', '.').replace('.', '$');
    for (int i = 0; i < m_aspectExcludeStartsWith.size(); i++) {
      if (fastClassName.startsWith(m_aspectExcludeStartsWith.get(i))) {
        return false;
      }
    }
    // INCLUDE: if one match then accept
    for (int i = 0; i < m_aspectIncludeStartsWith.size(); i++) {
      if (fastClassName.startsWith(m_aspectIncludeStartsWith.get(i))) {
        return true;
      }
    }

    // needs further analysis
    ResolvedType classInfo = weaver.getWorld().resolve(UnresolvedType.forName(aspectClassName), true);
    // exclude are "AND"ed
    for (Iterator iterator = m_aspectExcludeTypePattern.iterator(); iterator.hasNext();) {
      TypePattern typePattern = (TypePattern) iterator.next();
      if (typePattern.matchesStatically(classInfo)) {
        // exclude match - skip
        return false;
      }
    }
    // include are "OR"ed
    boolean accept = true;// defaults to true if no include
    for (Iterator iterator = m_aspectIncludeTypePattern.iterator(); iterator.hasNext();) {
      TypePattern typePattern = (TypePattern) iterator.next();
      accept = typePattern.matchesStatically(classInfo);
      if (accept) {
        break;
      }
      // goes on if this include did not match ("OR"ed)
    }
    return accept;
  }

  @Override
  protected boolean shouldDump(String className, boolean before) {
    // Don't dump before weaving unless asked to
    if (before && !m_dumpBefore) {
      return false;
    }

    // avoid ResolvedType if not needed
    if (m_dumpTypePattern.isEmpty()) {
      return false;
    }

    // TODO AV - optimize for className.startWith only
    ResolvedType classInfo = weaver.getWorld().resolve(UnresolvedType.forName(className), true);
    // dump
    for (Iterator iterator = m_dumpTypePattern.iterator(); iterator.hasNext();) {
      TypePattern typePattern = (TypePattern) iterator.next();
      if (typePattern.matchesStatically(classInfo)) {
        // dump match
        return true;
      }
    }
    return false;
  }

  @Override
  protected String getDumpDir() {
    if (dumpDirPerClassloader) {
      StringBuffer dir = new StringBuffer();
      dir.append("_ajdump").append(File.separator).append(weavingContext.getId());
      return dir.toString();
    } else {
      return super.getDumpDir();
    }
  }

  /*
   * shared classes methods
   */

  /**
   * @return Returns the key.
   */
  public String getNamespace() {
    // System.out.println("ClassLoaderWeavingAdaptor.getNamespace() classloader=" + weavingContext.getClassLoaderName() +
    // ", namespace=" + namespace);
    if (namespace == null) {
      return "";
    } else {
      return new String(namespace);
    }
  }

  /**
   * Check to see if any classes are stored in the generated classes cache. Then flush the cache if it is not empty
   *
   * @param className TODO
   * @return true if a class has been generated and is stored in the cache
   */
  public boolean generatedClassesExistFor(String className) {
    // System.err.println("? ClassLoaderWeavingAdaptor.generatedClassesExist() classname=" + className + ", size=" +
    // generatedClasses);
    if (className == null) {
      return !generatedClasses.isEmpty();
    } else {
      return generatedClasses.containsKey(className);
    }
  }

  /**
   * Flush the generated classes cache
   */
  public void flushGeneratedClasses() {
    // System.err.println("? ClassLoaderWeavingAdaptor.flushGeneratedClasses() generatedClasses=" + generatedClasses);
    generatedClasses = new HashMap();
  }

  private void defineClass(ClassLoader loader, String name, byte[] bytes) {
    if (trace.isTraceEnabled()) {
      trace.enter("defineClass", this, new Object[] { loader, name, bytes });
    }
    Object clazz = null;
    debug("generating class '" + name + "'");

    try {
      // TODO av protection domain, and optimize
      Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", new Class[] { String.class, bytes.getClass(),
          int.class, int.class });
      defineClass.setAccessible(true);
      clazz = defineClass.invoke(loader, new Object[] { name, bytes, new Integer(0), new Integer(bytes.length) });
    } catch (InvocationTargetException e) {
      if (e.getTargetException() instanceof LinkageError) {
        warn("define generated class failed", e.getTargetException());
        // is already defined (happens for X$ajcMightHaveAspect interfaces since aspects are reweaved)
        // TODO maw I don't think this is OK and
      } else {
        warn("define generated class failed", e.getTargetException());
      }
    } catch (Exception e) {
      warn("define generated class failed", e);
    }

    if (trace.isTraceEnabled()) {
      trace.exit("defineClass", clazz);
    }
  }
}
TOP

Related Classes of org.aspectj.weaver.loadtime.ClassLoaderWeavingAdaptor$SimpleGeneratedClassHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.