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;
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
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
131 if (!NOFILTER) {
132 if ((loader == null) || (loader.getParent() == null)) {
133 return bytecode;
134 }
135 }
136
137 final String className = (name!=null)?name.replace('/', '.'):null;
138
139
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
169
170 dumpBefore(className, context);
171
172
173 m_weavingStrategy.transform(className, context);
174
175
176 dumpAfter(className, context);
177
178
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
192 final String className = name.replace('/', '.');
193
194
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
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")
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 }