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