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.hook.RuntimeClassProcessor;
16 import org.codehaus.aspectwerkz.transform.delegation.DelegationWeavingStrategy;
17
18 import java.util.Collection;
19 import java.util.HashMap;
20 import java.util.Hashtable;
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, RuntimeClassProcessor {
46
47 private final static String AW_WEAVING_STRATEGY = "aspectwerkz.weaving.strategy";
48
49 private final static String AW_TRANSFORM_FILTER = "aspectwerkz.transform.filter";
50
51 private final static String AW_TRANSFORM_VERBOSE = "aspectwerkz.transform.verbose";
52
53 private final static String AW_TRANSFORM_DUMP = "aspectwerkz.transform.dump";
54
55 private final static String AW_TRANSFORM_DETAILS = "aspectwerkz.transform.details";
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 private final static int WEAVING_STRATEGY;
70
71 static {
72
73 String weavingStrategy = System.getProperty(AW_WEAVING_STRATEGY, "delegation").trim();
74 if (weavingStrategy.equalsIgnoreCase("inlining")) {
75 WEAVING_STRATEGY = WeavingStrategy.INLINING;
76 } else if (weavingStrategy.equalsIgnoreCase("delegation")) {
77 WEAVING_STRATEGY = WeavingStrategy.DELEGATION;
78 } else {
79 throw new RuntimeException("unknown weaving strategy specified: " + weavingStrategy);
80 }
81
82 String verbose = System.getProperty(AW_TRANSFORM_VERBOSE, null);
83 VERBOSE = "yes".equalsIgnoreCase(verbose) || "true".equalsIgnoreCase(verbose);
84 String details = System.getProperty(AW_TRANSFORM_DETAILS, null);
85 DETAILS = "yes".equalsIgnoreCase(details) || "true".equalsIgnoreCase(details);
86 String filter = System.getProperty(AW_TRANSFORM_FILTER, null);
87 NOFILTER = "no".equalsIgnoreCase(filter) || "false".equalsIgnoreCase(filter);
88 String dumpPattern = System.getProperty(AW_TRANSFORM_DUMP, null);
89 if (dumpPattern == null) {
90 DUMP_BEFORE = false;
91 DUMP_AFTER = false;
92 DUMP_PATTERN = null;
93 } else {
94 dumpPattern = dumpPattern.trim();
95 DUMP_AFTER = true;
96 DUMP_BEFORE = dumpPattern.indexOf(",before") > 0;
97 if (DUMP_BEFORE) {
98 DUMP_PATTERN = Pattern.compileTypePattern(
99 dumpPattern.substring(0, dumpPattern.indexOf(',')),
100 SubtypePatternType.NOT_HIERARCHICAL);
101 } else {
102 DUMP_PATTERN = Pattern.compileTypePattern(dumpPattern, SubtypePatternType.NOT_HIERARCHICAL);
103 }
104 }
105 }
106
107 /***
108 * Bytecode cache for prepared class and runtime weaving.
109 *
110 * @TODO: allow for other cache implementations (file, jms, clustered, jcache, JNDI, javagroups etc.)
111 */
112 private static Map s_classByteCache = new HashMap();
113
114 /***
115 * Marks the pre-processor as initialized.
116 */
117 private boolean m_initialized = false;
118
119 /***
120 * Pre processor weaving strategy.
121 */
122 private WeavingStrategy m_weavingStrategy;
123
124 /***
125 * Initializes the transformer stack.
126 *
127 * @param params not used
128 */
129 public void initialize(final Hashtable params) {
130 switch (WEAVING_STRATEGY) {
131 case WeavingStrategy.DELEGATION:
132 m_weavingStrategy = new DelegationWeavingStrategy();
133 break;
134
135 case WeavingStrategy.INLINING:
136 throw new Error("Not supported");
137 }
138 m_weavingStrategy.initialize(params);
139 m_initialized = true;
140 }
141
142 /***
143 * Transform bytecode according to the transformer stack
144 *
145 * @param name class name
146 * @param bytecode bytecode to transform
147 * @param loader classloader loading the class
148 * @return modified (or not) bytecode
149 */
150 public byte[] preProcess(final String name, final byte[] bytecode, final ClassLoader loader) {
151
152
153 if (!NOFILTER) {
154 if ((loader == null) || (loader.getParent() == null)) {
155 return bytecode;
156 }
157 }
158
159 final String className = name.replace('/', '.');
160
161 if (filter(className) || !m_initialized) {
162 return bytecode;
163 }
164 if (VERBOSE) {
165 log(Util.classLoaderToString(loader) + ':' + className + '[' + Thread.currentThread().getName() + ']');
166 }
167 return _preProcess(className, bytecode, loader);
168 }
169
170 public byte[] _preProcess(final String name, final byte[] bytecode, final ClassLoader loader) {
171
172
173 final String className = name.replace('/', '.');
174
175 final Context context;
176 try {
177
178 context = m_weavingStrategy.newContext(name, bytecode, loader);
179 } catch (Exception e) {
180 log("failed " + className);
181 e.printStackTrace();
182 return bytecode;
183 }
184
185
186
187 dumpBefore(className, context);
188
189
190 m_weavingStrategy.transform(className, context);
191
192
193 dumpAfter(className, context);
194
195
196 if (context.isPrepared()) {
197 ClassCacheTuple key = new ClassCacheTuple(loader, className);
198 log("cache prepared " + className);
199 s_classByteCache.put(key, new ByteArray(context.getCurrentBytecode()));
200 }
201
202
203 return context.getCurrentBytecode();
204 }
205
206 /***
207 * Runtime weaving of given Class according to the actual definition
208 *
209 * @param klazz
210 * @return new bytes for Class representation
211 * @throws Throwable
212 */
213 public byte[] preProcessActivate(final Class klazz) throws Throwable {
214 String className = klazz.getName();
215
216
217 ClassCacheTuple key = new ClassCacheTuple(klazz);
218 ByteArray currentBytesArray = (ByteArray) s_classByteCache.get(key);
219 if (currentBytesArray == null) {
220 throw new RuntimeException("can not find cached class in cache for prepared classes: " + className);
221 }
222
223
224
225
226
227
228
229
230 byte[] newBytes = preProcess(klazz.getName(), currentBytesArray.getBytes(), klazz.getClassLoader());
231
232
233 s_classByteCache.put(key, new ByteArray(newBytes));
234 return newBytes;
235 }
236
237 /***
238 * Logs a message.
239 *
240 * @param msg the message to log
241 */
242 public static void log(final String msg) {
243 if (VERBOSE) {
244 System.out.println(msg);
245 }
246 }
247
248 /***
249 * Excludes instrumentation for the class used during the instrumentation
250 *
251 * @param klass the AspectWerkz class
252 */
253 private static boolean filter(final String klass) {
254 return (klass == null)
255 || klass.startsWith("org.codehaus.aspectwerkz.")
256 || klass.startsWith("javassist.")
257 || klass.startsWith("org.objectweb.asm.")
258 || klass.startsWith("com.karneim.")
259 || klass.startsWith("com.bluecast.")
260 || klass.startsWith("gnu.trove.")
261 || klass.startsWith("org.dom4j.")
262 || klass.startsWith("org.xml.sax.")
263 || klass.startsWith("javax.xml.parsers.")
264 || klass.startsWith("sun.reflect.Generated")
265
266
267
268
269 ;
270 }
271
272 /***
273 * Dumps class before weaving.
274 *
275 * @param className
276 * @param context
277 */
278 public static void dumpBefore(final String className, final Context context) {
279 if (DUMP_BEFORE) {
280 if (DUMP_PATTERN.matches(className)) {
281 context.dump("_dump/before/");
282 }
283 }
284 }
285
286 /***
287 * Dumps class after weaving.
288 *
289 * @param className
290 * @param context
291 */
292 public static void dumpAfter(final String className, final Context context) {
293 if (DUMP_AFTER) {
294 if (DUMP_PATTERN.matches(className)) {
295 context.dump("_dump/" + (DUMP_BEFORE ? "after/" : ""));
296 }
297 }
298 }
299
300 /***
301 * Always dumps class.
302 *
303 * @param context
304 */
305 public static void dumpForce(final Context context) {
306 context.dump("_dump/force/");
307 }
308
309 /***
310 * Returns the caching tuples.
311 *
312 * @return
313 */
314 public Collection getClassCacheTuples() {
315 return s_classByteCache.keySet();
316 }
317 }