KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > tc > object > bytecode > hook > impl > DefaultWeavingStrategy


1 /*
2  * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright notice. All rights reserved.
3  */

4 package com.tc.object.bytecode.hook.impl;
5
6 import com.tc.asm.ClassReader;
7 import com.tc.asm.ClassVisitor;
8 import com.tc.asm.ClassWriter;
9 import com.tc.asm.commons.SerialVersionUIDAdder;
10 import com.tc.aspectwerkz.definition.SystemDefinition;
11 import com.tc.aspectwerkz.definition.deployer.StandardAspectModuleDeployer;
12 import com.tc.aspectwerkz.exception.WrappedRuntimeException;
13 import com.tc.aspectwerkz.expression.ExpressionContext;
14 import com.tc.aspectwerkz.expression.PointcutType;
15 import com.tc.aspectwerkz.reflect.ClassInfo;
16 import com.tc.aspectwerkz.reflect.ClassInfoHelper;
17 import com.tc.aspectwerkz.reflect.impl.asm.AsmClassInfo;
18 import com.tc.aspectwerkz.transform.InstrumentationContext;
19 import com.tc.aspectwerkz.transform.WeavingStrategy;
20 import com.tc.aspectwerkz.transform.inlining.AsmHelper;
21 import com.tc.aspectwerkz.transform.inlining.weaver.AddInterfaceVisitor;
22 import com.tc.aspectwerkz.transform.inlining.weaver.AddMixinMethodsVisitor;
23 import com.tc.aspectwerkz.transform.inlining.weaver.AddWrapperVisitor;
24 import com.tc.aspectwerkz.transform.inlining.weaver.AlreadyAddedMethodAdapter;
25 import com.tc.aspectwerkz.transform.inlining.weaver.ConstructorBodyVisitor;
26 import com.tc.aspectwerkz.transform.inlining.weaver.ConstructorCallVisitor;
27 import com.tc.aspectwerkz.transform.inlining.weaver.FieldSetFieldGetVisitor;
28 import com.tc.aspectwerkz.transform.inlining.weaver.HandlerVisitor;
29 import com.tc.aspectwerkz.transform.inlining.weaver.InstanceLevelAspectVisitor;
30 import com.tc.aspectwerkz.transform.inlining.weaver.JoinPointInitVisitor;
31 import com.tc.aspectwerkz.transform.inlining.weaver.LabelToLineNumberVisitor;
32 import com.tc.aspectwerkz.transform.inlining.weaver.MethodCallVisitor;
33 import com.tc.aspectwerkz.transform.inlining.weaver.MethodExecutionVisitor;
34 import com.tc.aspectwerkz.transform.inlining.weaver.StaticInitializationVisitor;
35 import com.tc.exception.TCLogicalSubclassNotPortableException;
36 import com.tc.object.config.DSOClientConfigHelper;
37 import com.tc.object.logging.InstrumentationLogger;
38 import com.tc.object.logging.InstrumentationLoggerImpl;
39 import com.tc.util.AdaptedClassDumper;
40
41 import java.util.ArrayList JavaDoc;
42 import java.util.HashMap JavaDoc;
43 import java.util.HashSet JavaDoc;
44 import java.util.Iterator JavaDoc;
45 import java.util.List JavaDoc;
46 import java.util.Map JavaDoc;
47 import java.util.Set JavaDoc;
48
49 /**
50  * A weaving strategy implementing a weaving scheme based on statical compilation, and no reflection.
51  *
52  * @author <a HREF="mailto:jboner@codehaus.org">Jonas Bon&#233;r </a>
53  * @author <a HREF="mailto:alex@gnilux.com">Alexandre Vasseur </a>
54  */

55 public class DefaultWeavingStrategy implements WeavingStrategy {
56
57   private final DSOClientConfigHelper m_configHelper;
58   private final InstrumentationLogger m_logger;
59   private final InstrumentationLogger m_instrumentationLogger;
60
61   public DefaultWeavingStrategy(final DSOClientConfigHelper configHelper, InstrumentationLogger instrumentationLogger) {
62     m_configHelper = configHelper;
63     m_instrumentationLogger = instrumentationLogger;
64     m_logger = new InstrumentationLoggerImpl(m_configHelper.getInstrumentationLoggingOptions());
65
66     // deploy all system aspect modules
67
StandardAspectModuleDeployer.deploy(getClass().getClassLoader(), StandardAspectModuleDeployer.ASPECT_MODULES);
68   }
69
70   /**
71    * Performs the weaving of the target class.
72    *
73    * @param className
74    * @param context
75    */

76   public void transform(String JavaDoc className, final InstrumentationContext context) {
77     try {
78       final byte[] bytecode = context.getInitialBytecode();
79       final ClassLoader JavaDoc loader = context.getLoader();
80
81       Map JavaDoc aspectModules = m_configHelper.getAspectModules();
82       for (Iterator JavaDoc it = aspectModules.entrySet().iterator(); it.hasNext();) {
83         Map.Entry JavaDoc e = (Map.Entry JavaDoc) it.next();
84         if (className.startsWith((String JavaDoc) e.getKey())) {
85           List JavaDoc modules = (List JavaDoc) e.getValue();
86           for (Iterator JavaDoc it2 = modules.iterator(); it2.hasNext();) {
87             StandardAspectModuleDeployer.deploy(loader, (String JavaDoc) it2.next());
88           }
89         }
90       }
91
92       ClassInfo classInfo = AsmClassInfo.getClassInfo(className, bytecode, loader);
93
94       // skip Java reflect proxies for which we cannot get the resource as a stream
95
// which leads to warnings when using annotation matching
96
// Note: we use an heuristic assuming JDK proxy are classes named "$..."
97
// to avoid to call getSuperClass everytime
98
if (classInfo.getName().startsWith("$") && classInfo.getSuperclass().getName().equals("java.lang.reflect.Proxy")) {
99         context.setCurrentBytecode(context.getInitialBytecode());
100         return;
101       }
102
103       // filtering out all proxy classes that have been transformed by DSO already
104
if (context.isProxy() && isInstrumentedByDSO(classInfo)) {
105         context.setCurrentBytecode(context.getInitialBytecode());
106         return;
107       }
108
109       // TODO VM option? rich AW subtype match or old DSO match?
110
// is DSO adaptable?
111
// final boolean subTypeMatch = false;
112
// final boolean isDsoAdaptable;
113
// if (subTypeMatch) {
114
// isDsoAdaptable = m_configHelper.isAdaptable(classInfo.getName());
115
// } else {
116
// isDsoAdaptable = m_configHelper.isAdaptable(className);
117
// }
118
final boolean isDsoAdaptable = m_configHelper.shouldBeAdapted(classInfo);
119
120       // TODO match on (within, null, classInfo) should be equivalent to those ones.
121
final Set JavaDoc definitions = context.getDefinitions();
122       final ExpressionContext[] ctxs = new ExpressionContext[] {
123           new ExpressionContext(PointcutType.EXECUTION, classInfo, classInfo),
124           new ExpressionContext(PointcutType.CALL, null, classInfo),
125           new ExpressionContext(PointcutType.GET, null, classInfo),
126           new ExpressionContext(PointcutType.SET, null, classInfo),
127           new ExpressionContext(PointcutType.HANDLER, null, classInfo),
128           new ExpressionContext(PointcutType.STATIC_INITIALIZATION, classInfo, classInfo),
129           new ExpressionContext(PointcutType.WITHIN, classInfo, classInfo) };
130
131       // has AW aspects?
132
final boolean isAdvisable = !classFilter(definitions, ctxs, classInfo);
133
134       if (!isAdvisable && !isDsoAdaptable) {
135         context.setCurrentBytecode(context.getInitialBytecode());
136         return;
137       }
138
139       if (m_instrumentationLogger.classInclusion()) {
140         m_instrumentationLogger.classIncluded(className);
141       }
142
143       // ------------------------------------------------
144
// -- Phase AW -- weave in aspects
145
if (isAdvisable) {
146         // compute CALL + GET/SET early matching results to avoid registering useless visitors
147
final boolean filterForCall = classFilterFor(definitions, new ExpressionContext[] {
148             new ExpressionContext(PointcutType.CALL, null, classInfo),
149             new ExpressionContext(PointcutType.WITHIN, classInfo, classInfo) });// FIXME - within make match
150
// all
151
final boolean filterForGetSet = classFilterFor(definitions, new ExpressionContext[] {
152             new ExpressionContext(PointcutType.GET, null, classInfo),
153             new ExpressionContext(PointcutType.SET, null, classInfo),
154             new ExpressionContext(PointcutType.WITHIN, classInfo, classInfo) });// FIXME - within make match
155
// all
156
final boolean filterForHandler = classFilterFor(definitions, new ExpressionContext[] {
157             new ExpressionContext(PointcutType.HANDLER, null, classInfo),
158             new ExpressionContext(PointcutType.WITHIN, classInfo, classInfo) });// FIXME - within make match
159
// all
160

161         // note: for staticinitialization we do an exact match right there
162
boolean filterForStaticinitialization = !classInfo.hasStaticInitializer()
163                                                 || classFilterFor(
164                                                                   definitions,
165                                                                   new ExpressionContext[] { new ExpressionContext(
166                                                                                                                   PointcutType.STATIC_INITIALIZATION,
167                                                                                                                   classInfo
168                                                                                                                       .staticInitializer(),
169                                                                                                                   classInfo) });
170         if (!filterForStaticinitialization) {
171           filterForStaticinitialization = !hasPointcut(definitions,
172                                                        new ExpressionContext(PointcutType.STATIC_INITIALIZATION,
173                                                                              classInfo.staticInitializer(), classInfo));
174         }
175
176         // prepare ctor call jp
177
final ClassReader crLookahead = new ClassReader(bytecode);
178         HashMap JavaDoc newInvocationsByCallerMemberHash = null;
179         if (!filterForCall) {
180           newInvocationsByCallerMemberHash = new HashMap JavaDoc();
181           crLookahead.accept(
182               new ConstructorCallVisitor.LookaheadNewDupInvokeSpecialInstructionClassAdapter(newInvocationsByCallerMemberHash),
183               true);
184         }
185
186         // prepare handler jp, by gathering ALL catch blocks and their exception type
187
List JavaDoc catchLabels = new ArrayList JavaDoc();
188         if (!filterForHandler) {
189           final ClassWriter cw2 = AsmHelper.newClassWriter(true);
190           HandlerVisitor.LookaheadCatchLabelsClassAdapter lookForCatches = //
191
new HandlerVisitor.LookaheadCatchLabelsClassAdapter(cw2, loader, classInfo, context, catchLabels);
192           // we must visit exactly as we will do further on with debug info (that produces extra labels)
193
final ClassReader crLookahead2 = new ClassReader(bytecode);
194           crLookahead2.accept(lookForCatches, null, false);
195         }
196
197         // gather wrapper methods to support multi-weaving
198
// skip annotations visit and debug info by using the lookahead read-only classreader
199
Set JavaDoc addedMethods = new HashSet JavaDoc();
200         crLookahead.accept(new AlreadyAddedMethodAdapter(addedMethods), true);
201
202         // ------------------------------------------------
203
// -- Phase 1 -- type change (ITDs)
204
final ClassWriter writerPhase1 = AsmHelper.newClassWriter(true);
205         ClassVisitor reversedChainPhase1 = new AddMixinMethodsVisitor(writerPhase1, classInfo, context, addedMethods);
206         reversedChainPhase1 = new AddInterfaceVisitor(reversedChainPhase1, classInfo, context);
207         final ClassReader readerPhase1 = new ClassReader(bytecode);
208         readerPhase1.accept(reversedChainPhase1, null, false);
209         context.setCurrentBytecode(writerPhase1.toByteArray());
210
211         // ------------------------------------------------
212
// update the class info with new ITDs
213
classInfo = AsmClassInfo.newClassInfo(context.getCurrentBytecode(), loader);
214
215         // ------------------------------------------------
216
// -- Phase 2 -- advice
217
final ClassWriter writerPhase2 = AsmHelper.newClassWriter(true);
218         ClassVisitor reversedChainPhase2 = new InstanceLevelAspectVisitor(writerPhase2, classInfo, context);
219         reversedChainPhase2 = new MethodExecutionVisitor(reversedChainPhase2, classInfo, context, addedMethods);
220         reversedChainPhase2 = new ConstructorBodyVisitor(reversedChainPhase2, classInfo, context, addedMethods);
221         if (!filterForStaticinitialization) {
222           reversedChainPhase2 = new StaticInitializationVisitor(reversedChainPhase2, context, addedMethods);
223         }
224         reversedChainPhase2 = new HandlerVisitor(reversedChainPhase2, context, catchLabels);
225         if (!filterForCall) {
226           reversedChainPhase2 = new MethodCallVisitor(reversedChainPhase2, loader, classInfo, context);
227           reversedChainPhase2 = new ConstructorCallVisitor(reversedChainPhase2, loader, classInfo, context,
228                                                            newInvocationsByCallerMemberHash);
229         }
230         if (!filterForGetSet) {
231           reversedChainPhase2 = new FieldSetFieldGetVisitor(reversedChainPhase2, loader, classInfo, context);
232         }
233         reversedChainPhase2 = new LabelToLineNumberVisitor(reversedChainPhase2, context);
234
235         final ClassReader readerPhase2 = new ClassReader(context.getCurrentBytecode());
236         readerPhase2.accept(reversedChainPhase2, null, false);
237         context.setCurrentBytecode(writerPhase2.toByteArray());
238
239         // ------------------------------------------------
240
// -- AW Finalization -- JP init code and wrapper methods
241
if (context.isAdvised()) {
242           final ClassWriter writerPhase3 = AsmHelper.newClassWriter(true);
243           ClassVisitor reversedChainPhase3 = new AddWrapperVisitor(writerPhase3, context, addedMethods);
244           reversedChainPhase3 = new JoinPointInitVisitor(reversedChainPhase3, context);
245           ClassReader readerPhase3 = new ClassReader(context.getCurrentBytecode());
246           readerPhase3.accept(reversedChainPhase3, null, false);
247           context.setCurrentBytecode(writerPhase3.toByteArray());
248         }
249       }
250
251       // ------------------------------------------------
252
// -- Phase DSO -- DSO clustering
253
if (isDsoAdaptable) {
254         final ClassWriter dsoWriter = AsmHelper.newClassWriter(true);
255         ClassVisitor dsoVisitor = m_configHelper.createClassAdapterFor(dsoWriter, classInfo, m_logger, loader);
256
257         final ClassReader dsoReader = new ClassReader(context.getCurrentBytecode());
258         try {
259           dsoReader.accept(dsoVisitor, false);
260           context.setCurrentBytecode(dsoWriter.toByteArray());
261         } catch (TCLogicalSubclassNotPortableException e) {
262           List JavaDoc l = new ArrayList JavaDoc(1);
263           l.add(e.getSuperClassName());
264           m_logger.subclassOfLogicallyManagedClasses(e.getClassName(), l);
265         }
266       }
267
268       // ------------------------------------------------
269
// -- Generic finalization -- serialVersionUID
270
if (context.isAdvised() || isDsoAdaptable) {
271         if (ClassInfoHelper.implementsInterface(classInfo, "java.io.Serializable")) {
272           final ClassWriter writerPhase3 = AsmHelper.newClassWriter(true);
273           ClassReader readerPhase3 = new ClassReader(context.getCurrentBytecode());
274           readerPhase3.accept(new SerialVersionUIDAdder(writerPhase3), false);
275           context.setCurrentBytecode(writerPhase3.toByteArray());
276         }
277       }
278
279       AdaptedClassDumper.write(className, context.getCurrentBytecode());
280     } catch (Throwable JavaDoc t) {
281       t.printStackTrace();
282       throw new WrappedRuntimeException(t);
283     }
284   }
285
286   private boolean isInstrumentedByDSO(ClassInfo classInfo) {
287     ClassInfo[] interfaces = classInfo.getInterfaces();
288     for (int i = 0; i < interfaces.length; i++) {
289       if (interfaces[i].getName().equals("com.tc.object.bytecode.TransparentAccess")) { return true; }
290     }
291     return false;
292   }
293
294   /**
295    * Filters out the classes that are not eligible for transformation.
296    *
297    * @param definitions the definitions
298    * @param ctxs an array with the contexts
299    * @param classInfo the class to filter
300    * @return boolean true if the class should be filtered out
301    */

302   private static boolean classFilter(final Set JavaDoc definitions, final ExpressionContext[] ctxs, final ClassInfo classInfo) {
303     if (classInfo.isInterface()) { return true; }
304     for (Iterator JavaDoc defs = definitions.iterator(); defs.hasNext();) {
305       if (!classFilter((SystemDefinition) defs.next(), ctxs, classInfo)) { return false; }
306     }
307     return true;
308   }
309
310   /**
311    * Filters out the classes that are not eligible for transformation.
312    *
313    * @param definition the definition
314    * @param ctxs an array with the contexts
315    * @param classInfo the class to filter
316    * @return boolean true if the class should be filtered out
317    * @TODO: when a class had execution pointcut that were removed it must be unweaved, thus not filtered out How to
318    * handle that? cache lookup? or custom class level attribute ?
319    */

320   private static boolean classFilter(final SystemDefinition definition, final ExpressionContext[] ctxs,
321                                      final ClassInfo classInfo) {
322     if (classInfo.isInterface()) { return true; }
323     String JavaDoc className = classInfo.getName();
324     if (definition.inExcludePackage(className)) { return true; }
325     if (!definition.inIncludePackage(className)) { return true; }
326     if (definition.isAdvised(ctxs)) { return false; }
327     if (definition.hasMixin(ctxs)) { return false; }
328     if (definition.hasIntroducedInterface(ctxs)) { return false; }
329     return !definition.inPreparePackage(className);
330   }
331
332   private static boolean classFilterFor(final Set JavaDoc definitions, final ExpressionContext[] ctxs) {
333     for (Iterator JavaDoc defs = definitions.iterator(); defs.hasNext();) {
334       if (!classFilterFor((SystemDefinition) defs.next(), ctxs)) { return false; }
335     }
336     return true;
337   }
338
339   private static boolean classFilterFor(final SystemDefinition definition, final ExpressionContext[] ctxs) {
340     return !definition.isAdvised(ctxs);
341   }
342
343   private static boolean hasPointcut(final Set JavaDoc definitions, final ExpressionContext ctx) {
344     for (Iterator JavaDoc defs = definitions.iterator(); defs.hasNext();) {
345       if (hasPointcut((SystemDefinition) defs.next(), ctx)) { return true; }
346     }
347     return false;
348   }
349
350   private static boolean hasPointcut(final SystemDefinition definition, final ExpressionContext ctx) {
351     return definition.hasPointcut(ctx);
352   }
353 }
354
Popular Tags