View Javadoc

    1   /***************************************************************************************
    2    * Copyright (c) Jonas BonŽr, Alexandre Vasseur. All rights reserved.                 *
    3    * http://aspectwerkz.codehaus.org                                                    *
    4    * ---------------------------------------------------------------------------------- *
    5    * The software in this package is published under the terms of the LGPL license      *
    6    * a copy of which has been included with this distribution in the license.txt file.  *
    7    **************************************************************************************/
    8   package org.codehaus.aspectwerkz.transform;
    9
    10  import org.codehaus.aspectwerkz.util.Util;
    11  import org.codehaus.aspectwerkz.expression.SubtypePatternType;
    12  import org.codehaus.aspectwerkz.expression.regexp.Pattern;
    13  import org.codehaus.aspectwerkz.expression.regexp.TypePattern;
    14  import org.codehaus.aspectwerkz.hook.ClassPreProcessor;
    15  import org.codehaus.aspectwerkz.transform.inlining.InliningWeavingStrategy;
    16  import org.codehaus.aspectwerkz.transform.inlining.EmittedJoinPoint;
    17  import org.codehaus.aspectwerkz.transform.inlining.ContextImpl;
    18
    19  import java.util.Collection;
    20  import java.util.HashMap;
    21  import java.util.Map;
    22
    23  /***
    24   * AspectWerkzPreProcessor is the entry point of the AspectWerkz layer 2. <p/>It implements the ClassPreProcessor
    25   * interface defined in layer 1. <p/>Available options are:
    26   * <ul>
    27   * <li><code>-Daspectwerkz.transform.verbose=yes</code> turns on verbose mode: print on stdout all non filtered class
    28   * names and which transformation are applied</li>
    29   * <li><code>-Daspectwerkz.transform.dump=org.myapp.*</code> dumps transformed class matching pattern <i>org.myapp.*
    30   * </i>(even unmodified ones) in <i>./_dump </i> directory (relative to where applications starts). The syntax
    31   * <code>-Daspectwerkz.transform.dump=*</code> matchs all classes. The pattern language is the same as pointcut
    32   * pattern language.</li>
    33   * <li>else <code>-Daspectwerkz.transform.dump=org.myapp.*,before</code> dumps class before and after the
    34   * transformation whose name starts with <i>org.myapp. </i>(even unmodified ones) in <i>./_dump/before </i> and
    35   * <i>./_dump/after </i> directories (relative to where application starts)</li>
    36   * <li><code>-Daspectwerkz.transform.filter=no</code> (or false) disables filtering of
    37   * <code>org.codehaus.aspectwerkz</code> and related classes (trove, dom4j etc.). This should only be used in offline
    38   * mode where weaving of those classes is needed. Setting this option in online mode will lead to
    39   * <code>ClassCircularityError</code>.</li>
    40   * </ul>
    41   *
    42   * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
    43   * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
    44   */
    45  public class AspectWerkzPreProcessor implements ClassPreProcessor {
    46
    47      private final static String AW_TRANSFORM_FILTER = "aspectwerkz.transform.filter";
    48
    49      private final static String AW_TRANSFORM_VERBOSE = "aspectwerkz.transform.verbose";
    50
    51      private final static String AW_TRANSFORM_DETAILS = "aspectwerkz.transform.details";
    52
    53      private final static String AW_TRANSFORM_GENJP = "aspectwerkz.transform.genjp";
    54
    55      private final static String AW_TRANSFORM_DUMP = "aspectwerkz.transform.dump";
    56
    57      private final static TypePattern DUMP_PATTERN;
    58
    59      private final static boolean NOFILTER; // TODO: not used, remove?
    60
    61      private final static boolean DUMP_BEFORE;
    62
    63      private final static boolean DUMP_AFTER;
    64
    65      public final static boolean VERBOSE;
    66
    67      public final static boolean DETAILS;
    68
    69      public final static boolean GENJP;
    70
    71      static {
    72          // define the tracing and dump options
    73          String verbose = System.getProperty(AW_TRANSFORM_VERBOSE, null);
    74          VERBOSE = "yes".equalsIgnoreCase(verbose) || "true".equalsIgnoreCase(verbose);
    75          String details = System.getProperty(AW_TRANSFORM_DETAILS, null);
    76          DETAILS = "yes".equalsIgnoreCase(details) || "true".equalsIgnoreCase(details);
    77          String genjp = System.getProperty(AW_TRANSFORM_GENJP, null);
    78          GENJP = "yes".equalsIgnoreCase(genjp) || "true".equalsIgnoreCase(genjp);
    79          String filter = System.getProperty(AW_TRANSFORM_FILTER, null);
    80          NOFILTER = "no".equalsIgnoreCase(filter) || "false".equalsIgnoreCase(filter);
    81          String dumpPattern = System.getProperty(AW_TRANSFORM_DUMP, null);
    82          if (dumpPattern == null) {
    83              DUMP_BEFORE = false;
    84              DUMP_AFTER = false;
    85              DUMP_PATTERN = null;
    86          } else {
    87              dumpPattern = dumpPattern.trim();
    88              DUMP_AFTER = true;
    89              DUMP_BEFORE = dumpPattern.indexOf(",before") > 0;
    90              if (DUMP_BEFORE) {
    91                  DUMP_PATTERN = Pattern.compileTypePattern(
    92                          dumpPattern.substring(0, dumpPattern.indexOf(',')),
    93                          SubtypePatternType.NOT_HIERARCHICAL
    94                  );
    95              } else {
    96                  DUMP_PATTERN = Pattern.compileTypePattern(dumpPattern, SubtypePatternType.NOT_HIERARCHICAL);
    97              }
    98          }
    99      }
    100
    101     /***
    102      * Marks the pre-processor as initialized.
    103      */
    104     private boolean m_initialized = false;
    105
    106     /***
    107      * Pre processor weaving strategy.
    108      */
    109     private WeavingStrategy m_weavingStrategy;
    110
    111     /***
    112      * Initializes the transformer stack.
    113      */
    114     public void initialize() {
    115         m_weavingStrategy = new InliningWeavingStrategy();
    116         m_initialized = true;
    117     }
    118
    119     /***
    120      * Transform bytecode according to the transformer stack
    121      * Adapted for embedded modes, that will filter out framework classes
    122      * See preProcessWithOutput for a tool entry point.
    123      *
    124      * @param name     class name
    125      * @param bytecode bytecode to transform
    126      * @param loader   classloader loading the class
    127      * @return modified (or not) bytecode
    128      */
    129     public byte[] preProcess(final String name, final byte[] bytecode, final ClassLoader loader) {
    130         // filter out ExtClassLoader and BootClassLoader
    131         if (!NOFILTER) {
    132             if ((loader == null) || (loader.getParent() == null)) {
    133                 return bytecode;
    134             }
    135         }
    136         // needed for JRockit (as well as all in all TFs)
    137         final String className = (name!=null)?name.replace('/', '.'):null;
    138
    139         // will filter null named classes
    140         if (filter(className) || !m_initialized) {
    141             return bytecode;
    142         }
    143         if (VERBOSE) {
    144             log(Util.classLoaderToString(loader) + ':' + className + '[' + Thread.currentThread().getName() + ']');
    145         }
    146
    147         try {
    148             Context context = _preProcess(className, bytecode, loader);
    149             return context.getCurrentBytecode();
    150         } catch (Exception e) {
    151             log("failed " + className);
    152             e.printStackTrace();
    153             return bytecode;
    154         }
    155     }
    156
    157     /***
    158      * Weaving of the class
    159      *
    160      * @param className
    161      * @param bytecode
    162      * @param loader
    163      * @return the weaving context, where getCurrentBytecode is the resulting bytecode
    164      */
    165     public Context _preProcess(final String className, final byte[] bytecode, final ClassLoader loader) {
    166         final Context context = m_weavingStrategy.newContext(className, bytecode, loader);
    167
    168         // dump before (not compliant with multiple CL weaving same class differently, since based
    169         // on class FQN className)
    170         dumpBefore(className, context);
    171
    172         // do the transformation
    173         m_weavingStrategy.transform(className, context);
    174
    175         // dump after as required
    176         dumpAfter(className, context);
    177
    178         // return the transformed bytecode
    179         return context;
    180     }
    181
    182     /***
    183      * Weaving without filtering any class and returning a rich object with emitted joinpoints
    184      *
    185      * @param name
    186      * @param bytecode
    187      * @param loader
    188      * @return
    189      */
    190     public Output preProcessWithOutput(final String name, final byte[] bytecode, final ClassLoader loader) {
    191         // needed for JRockit (as well as all in all TFs)
    192         final String className = name.replace('/', '.');
    193
    194         // we do not filter anything except JP in this mode
    195         if (name.endsWith((TransformationConstants.JOIN_POINT_CLASS_SUFFIX))) {
    196             Output output = new Output();
    197             output.bytecode = bytecode;
    198             output.emittedJoinPoints = null;
    199             return output;
    200         }
    201
    202         Context context = _preProcess(className, bytecode, loader);
    203         Output output = new Output();
    204         output.bytecode = context.getCurrentBytecode();
    205         output.emittedJoinPoints =
    206         (EmittedJoinPoint[]) ((ContextImpl) context).getEmittedJoinPoints().toArray(new EmittedJoinPoint[0]);
    207
    208         // resolve line numbers
    209         for (int i = 0; i < output.emittedJoinPoints.length; i++) {
    210             EmittedJoinPoint emittedJoinPoint = output.emittedJoinPoints[i];
    211             emittedJoinPoint.resolveLineNumber(context);
    212         }
    213         return output;
    214     }
    215
    216     /***
    217      * Logs a message.
    218      *
    219      * @param msg the message to log
    220      */
    221     public static void log(final String msg) {
    222         if (VERBOSE) {
    223             System.out.println(msg);
    224         }
    225     }
    226
    227     /***
    228      * Excludes instrumentation for the class used during the instrumentation
    229      *
    230      * @param klass the AspectWerkz class
    231      */
    232     private static boolean filter(final String klass) {
    233         return (klass == null)
    234                || klass.endsWith(TransformationConstants.JOIN_POINT_CLASS_SUFFIX)
    235                || klass.startsWith("org.codehaus.aspectwerkz.")
    236                || klass.startsWith("org.objectweb.asm.")
    237                || klass.startsWith("com.karneim.")
    238                || klass.startsWith("com.bluecast.")
    239                || klass.startsWith("gnu.trove.")
    240                || klass.startsWith("org.dom4j.")
    241                || klass.startsWith("org.xml.sax.")
    242                || klass.startsWith("javax.xml.parsers.")
    243                || klass.startsWith("sun.reflect.Generated")// issue on J2SE 5 reflection - AW-245
    244                || klass.startsWith("EDU.oswego.cs.dl.util.concurrent")
    245                 ;
    246     }
    247
    248     /***
    249      * Dumps class before weaving.
    250      *
    251      * @param className
    252      * @param context
    253      */
    254     public static void dumpBefore(final String className, final Context context) {
    255         if (DUMP_BEFORE) {
    256             if (DUMP_PATTERN.matches(className)) {
    257                 context.dump("_dump/before/");
    258             }
    259         }
    260     }
    261
    262     /***
    263      * Dumps class after weaving.
    264      *
    265      * @param className
    266      * @param context
    267      */
    268     public static void dumpAfter(final String className, final Context context) {
    269         if (DUMP_AFTER) {
    270             if (DUMP_PATTERN.matches(className)) {
    271                 context.dump("_dump/" + (DUMP_BEFORE ? "after/" : ""));
    272             }
    273         }
    274     }
    275
    276     /***
    277      * Structure build when invoking tool weaving
    278      */
    279     public static class Output {
    280         public byte[] bytecode;
    281         public EmittedJoinPoint[] emittedJoinPoints;
    282     }
    283
    284 }