001: /**************************************************************************************
002: * Copyright (c) Jonas BonŽr, Alexandre Vasseur. All rights reserved. *
003: * http://aspectwerkz.codehaus.org *
004: * ---------------------------------------------------------------------------------- *
005: * The software in this package is published under the terms of the LGPL license *
006: * a copy of which has been included with this distribution in the license.txt file. *
007: **************************************************************************************/package org.codehaus.aspectwerkz.transform;
008:
009: import org.codehaus.aspectwerkz.util.Util;
010: import org.codehaus.aspectwerkz.expression.SubtypePatternType;
011: import org.codehaus.aspectwerkz.expression.regexp.Pattern;
012: import org.codehaus.aspectwerkz.expression.regexp.TypePattern;
013: import org.codehaus.aspectwerkz.hook.ClassPreProcessor;
014: import org.codehaus.aspectwerkz.transform.inlining.InliningWeavingStrategy;
015: import org.codehaus.aspectwerkz.transform.inlining.EmittedJoinPoint;
016: import org.codehaus.aspectwerkz.transform.inlining.ContextImpl;
017:
018: import java.util.Collection;
019: import java.util.HashMap;
020: import java.util.Map;
021:
022: /**
023: * AspectWerkzPreProcessor is the entry point of the AspectWerkz layer 2. <p/>It implements the ClassPreProcessor
024: * interface defined in layer 1. <p/>Available options are:
025: * <ul>
026: * <li><code>-Daspectwerkz.transform.verbose=yes</code> turns on verbose mode: print on stdout all non filtered class
027: * names and which transformation are applied</li>
028: * <li><code>-Daspectwerkz.transform.dump=org.myapp.*</code> dumps transformed class matching pattern <i>org.myapp.*
029: * </i>(even unmodified ones) in <i>./_dump </i> directory (relative to where applications starts). The syntax
030: * <code>-Daspectwerkz.transform.dump=*</code> matchs all classes. The pattern language is the same as pointcut
031: * pattern language.</li>
032: * <li>else <code>-Daspectwerkz.transform.dump=org.myapp.*,before</code> dumps class before and after the
033: * transformation whose name starts with <i>org.myapp. </i>(even unmodified ones) in <i>./_dump/before </i> and
034: * <i>./_dump/after </i> directories (relative to where application starts)</li>
035: * <li><code>-Daspectwerkz.transform.filter=no</code> (or false) disables filtering of
036: * <code>org.codehaus.aspectwerkz</code> and related classes (trove, dom4j etc.). This should only be used in offline
037: * mode where weaving of those classes is needed. Setting this option in online mode will lead to
038: * <code>ClassCircularityError</code>.</li>
039: * </ul>
040: *
041: * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
042: * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
043: */
044: public class AspectWerkzPreProcessor implements ClassPreProcessor {
045:
046: private final static String AW_TRANSFORM_FILTER = "aspectwerkz.transform.filter";
047:
048: private final static String AW_TRANSFORM_VERBOSE = "aspectwerkz.transform.verbose";
049:
050: private final static String AW_TRANSFORM_DETAILS = "aspectwerkz.transform.details";
051:
052: private final static String AW_TRANSFORM_GENJP = "aspectwerkz.transform.genjp";
053:
054: private final static String AW_TRANSFORM_DUMP = "aspectwerkz.transform.dump";
055:
056: private final static TypePattern DUMP_PATTERN;
057:
058: private final static boolean NOFILTER; // TODO: not used, remove?
059:
060: private final static boolean DUMP_BEFORE;
061:
062: private final static boolean DUMP_AFTER;
063:
064: public final static boolean VERBOSE;
065:
066: public final static boolean DETAILS;
067:
068: public final static boolean GENJP;
069:
070: static {
071: // define the tracing and dump options
072: String verbose = System.getProperty(AW_TRANSFORM_VERBOSE, null);
073: VERBOSE = "yes".equalsIgnoreCase(verbose)
074: || "true".equalsIgnoreCase(verbose);
075: String details = System.getProperty(AW_TRANSFORM_DETAILS, null);
076: DETAILS = "yes".equalsIgnoreCase(details)
077: || "true".equalsIgnoreCase(details);
078: String genjp = System.getProperty(AW_TRANSFORM_GENJP, null);
079: GENJP = "yes".equalsIgnoreCase(genjp)
080: || "true".equalsIgnoreCase(genjp);
081: String filter = System.getProperty(AW_TRANSFORM_FILTER, null);
082: NOFILTER = "no".equalsIgnoreCase(filter)
083: || "false".equalsIgnoreCase(filter);
084: String dumpPattern = System
085: .getProperty(AW_TRANSFORM_DUMP, null);
086: if (dumpPattern == null) {
087: DUMP_BEFORE = false;
088: DUMP_AFTER = false;
089: DUMP_PATTERN = null;
090: } else {
091: dumpPattern = dumpPattern.trim();
092: DUMP_AFTER = true;
093: DUMP_BEFORE = dumpPattern.indexOf(",before") > 0;
094: if (DUMP_BEFORE) {
095: DUMP_PATTERN = Pattern.compileTypePattern(dumpPattern
096: .substring(0, dumpPattern.indexOf(',')),
097: SubtypePatternType.NOT_HIERARCHICAL);
098: } else {
099: DUMP_PATTERN = Pattern.compileTypePattern(dumpPattern,
100: SubtypePatternType.NOT_HIERARCHICAL);
101: }
102: }
103: }
104:
105: /**
106: * Marks the pre-processor as initialized.
107: */
108: private boolean m_initialized = false;
109:
110: /**
111: * Pre processor weaving strategy.
112: */
113: private WeavingStrategy m_weavingStrategy;
114:
115: /**
116: * Initializes the transformer stack.
117: */
118: public void initialize() {
119: m_weavingStrategy = new InliningWeavingStrategy();
120: m_initialized = true;
121: }
122:
123: /**
124: * Transform bytecode according to the transformer stack
125: * Adapted for embedded modes, that will filter out framework classes
126: * See preProcessWithOutput for a tool entry point.
127: *
128: * @param name class name
129: * @param bytecode bytecode to transform
130: * @param loader classloader loading the class
131: * @return modified (or not) bytecode
132: */
133: public byte[] preProcess(final String name, final byte[] bytecode,
134: final ClassLoader loader) {
135: // filter out ExtClassLoader and BootClassLoader
136: if (!NOFILTER) {
137: if ((loader == null) || (loader.getParent() == null)) {
138: return bytecode;
139: }
140: }
141: // needed for JRockit (as well as all in all TFs)
142: final String className = (name != null) ? name
143: .replace('/', '.') : null;
144:
145: // will filter null named classes
146: if (filter(className) || !m_initialized) {
147: return bytecode;
148: }
149: if (VERBOSE) {
150: log(Util.classLoaderToString(loader) + ':' + className
151: + '[' + Thread.currentThread().getName() + ']');
152: }
153:
154: try {
155: Context context = _preProcess(className, bytecode, loader);
156: return context.getCurrentBytecode();
157: } catch (Exception e) {
158: log("failed " + className);
159: e.printStackTrace();
160: return bytecode;
161: }
162: }
163:
164: /**
165: * Weaving of the class
166: *
167: * @param className
168: * @param bytecode
169: * @param loader
170: * @return the weaving context, where getCurrentBytecode is the resulting bytecode
171: */
172: public Context _preProcess(final String className,
173: final byte[] bytecode, final ClassLoader loader) {
174: final Context context = m_weavingStrategy.newContext(className,
175: bytecode, loader);
176:
177: // dump before (not compliant with multiple CL weaving same class differently, since based
178: // on class FQN className)
179: dumpBefore(className, context);
180:
181: // do the transformation
182: m_weavingStrategy.transform(className, context);
183:
184: // dump after as required
185: dumpAfter(className, context);
186:
187: // return the transformed bytecode
188: return context;
189: }
190:
191: /**
192: * Weaving without filtering any class and returning a rich object with emitted joinpoints
193: *
194: * @param name
195: * @param bytecode
196: * @param loader
197: * @return
198: */
199: public Output preProcessWithOutput(final String name,
200: final byte[] bytecode, final ClassLoader loader) {
201: // needed for JRockit (as well as all in all TFs)
202: final String className = name.replace('/', '.');
203:
204: // we do not filter anything except JP in this mode
205: if (name
206: .endsWith((TransformationConstants.JOIN_POINT_CLASS_SUFFIX))) {
207: Output output = new Output();
208: output.bytecode = bytecode;
209: output.emittedJoinPoints = null;
210: return output;
211: }
212:
213: Context context = _preProcess(className, bytecode, loader);
214: Output output = new Output();
215: output.bytecode = context.getCurrentBytecode();
216: output.emittedJoinPoints = (EmittedJoinPoint[]) ((ContextImpl) context)
217: .getEmittedJoinPoints()
218: .toArray(new EmittedJoinPoint[0]);
219:
220: // resolve line numbers
221: for (int i = 0; i < output.emittedJoinPoints.length; i++) {
222: EmittedJoinPoint emittedJoinPoint = output.emittedJoinPoints[i];
223: emittedJoinPoint.resolveLineNumber(context);
224: }
225: return output;
226: }
227:
228: /**
229: * Logs a message.
230: *
231: * @param msg the message to log
232: */
233: public static void log(final String msg) {
234: if (VERBOSE) {
235: System.out.println(msg);
236: }
237: }
238:
239: /**
240: * Excludes instrumentation for the class used during the instrumentation
241: *
242: * @param klass the AspectWerkz class
243: */
244: private static boolean filter(final String klass) {
245: return (klass == null)
246: || klass
247: .endsWith(TransformationConstants.JOIN_POINT_CLASS_SUFFIX)
248: || klass.startsWith("org.codehaus.aspectwerkz.")
249: || klass.startsWith("org.objectweb.asm.")
250: || klass.startsWith("com.karneim.")
251: || klass.startsWith("com.bluecast.")
252: || klass.startsWith("gnu.trove.")
253: || klass.startsWith("org.dom4j.")
254: || klass.startsWith("org.xml.sax.")
255: || klass.startsWith("javax.xml.parsers.")
256: || klass.startsWith("sun.reflect.Generated")// issue on J2SE 5 reflection - AW-245
257: || klass.startsWith("EDU.oswego.cs.dl.util.concurrent");
258: }
259:
260: /**
261: * Dumps class before weaving.
262: *
263: * @param className
264: * @param context
265: */
266: public static void dumpBefore(final String className,
267: final Context context) {
268: if (DUMP_BEFORE) {
269: if (DUMP_PATTERN.matches(className)) {
270: context.dump("_dump/before/");
271: }
272: }
273: }
274:
275: /**
276: * Dumps class after weaving.
277: *
278: * @param className
279: * @param context
280: */
281: public static void dumpAfter(final String className,
282: final Context context) {
283: if (DUMP_AFTER) {
284: if (DUMP_PATTERN.matches(className)) {
285: context.dump("_dump/" + (DUMP_BEFORE ? "after/" : ""));
286: }
287: }
288: }
289:
290: /**
291: * Structure build when invoking tool weaving
292: */
293: public static class Output {
294: public byte[] bytecode;
295: public EmittedJoinPoint[] emittedJoinPoints;
296: }
297:
298: }
|