KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > aop > instrument > Instrumentor


1 /*
2   * JBoss, Home of Professional Open Source
3   * Copyright 2005, JBoss Inc., and individual contributors as indicated
4   * by the @authors tag. See the copyright.txt in the distribution for a
5   * full listing of individual contributors.
6   *
7   * This is free software; you can redistribute it and/or modify it
8   * under the terms of the GNU Lesser General Public License as
9   * published by the Free Software Foundation; either version 2.1 of
10   * the License, or (at your option) any later version.
11   *
12   * This software is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   * Lesser General Public License for more details.
16   *
17   * You should have received a copy of the GNU Lesser General Public
18   * License along with this software; if not, write to the Free
19   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21   */

22 package org.jboss.aop.instrument;
23
24 import java.util.ArrayList JavaDoc;
25 import java.util.Collection JavaDoc;
26 import java.util.Collections JavaDoc;
27 import java.util.HashMap JavaDoc;
28 import java.util.HashSet JavaDoc;
29 import java.util.Iterator JavaDoc;
30 import java.util.List JavaDoc;
31 import java.util.Map JavaDoc;
32 import org.jboss.aop.Advised;
33 import org.jboss.aop.Advisor;
34 import org.jboss.aop.AspectManager;
35 import org.jboss.aop.ClassAdvisor;
36 import org.jboss.aop.annotation.compiler.AnnotationInfoCreator;
37 import org.jboss.aop.classpool.AOPClassPool;
38 import org.jboss.aop.classpool.AOPClassPoolRepository;
39 import org.jboss.aop.introduction.AnnotationIntroduction;
40 import org.jboss.aop.introduction.InterfaceIntroduction;
41 import org.jboss.aop.util.Advisable;
42 import org.jboss.aop.util.CtConstructorComparator;
43 import org.jboss.aop.util.CtFieldComparator;
44 import org.jboss.aop.util.JavassistMethodHashing;
45 import javassist.CannotCompileException;
46 import javassist.ClassPool;
47 import javassist.CodeConverter;
48 import javassist.CtClass;
49 import javassist.CtConstructor;
50 import javassist.CtField;
51 import javassist.CtMethod;
52 import javassist.CtNewMethod;
53 import javassist.Modifier;
54 import javassist.NotFoundException;
55 import javassist.SerialVersionUID;
56 import javassist.bytecode.AnnotationsAttribute;
57 import javassist.bytecode.FieldInfo;
58 import javassist.bytecode.MethodInfo;
59
60 /**
61  * Transforms byte code, making a class advisable. Implements
62  * command line class instrumentor as well. Reads classes from class path and creates
63  * advised versions in specified directory. Usage:
64  * <pre>
65  * Instrumentor [dest. directory] [class[ class...]]
66  * </pre>
67  *
68  * You can control which instrumentor to use by passing in the jboss.aop.instrumentor
69  * system property.
70  *
71  *
72  * @author <a HREF="mailto:bill@jboss.org">Bill Burke</a>
73  * @author <a HREF="mailto:gte863h@prism.gatech.edu">Austin Chau</a>
74  * @author <a HREF="mailto:crazybob@crazybob.org">Bob Lee</a>
75  * @author <a HREF="mailto:kabir.khan@jboss.org">Kabir Khan</a>
76  * @version $Revision: 56807 $
77  */

78 public abstract class Instrumentor
79 {
80    /**
81     * Package of AOP classes.
82     */

83    public static final String JavaDoc AOP_PACKAGE =
84            Advised.class.getPackage().getName();
85
86    /**
87     * Name of helper class.
88     */

89    public static final String JavaDoc ASPECT_MANAGER_CLASS_NAME =
90            AOP_PACKAGE + ".AspectManager";
91
92    /**
93     * Helper class's field name.
94     */

95    public static final String JavaDoc HELPER_FIELD_NAME = "aop$classAdvisor" + ClassAdvisor.NOT_TRANSFORMABLE_SUFFIX;
96
97    protected AOPClassPool classPool;
98    protected boolean basicsSet = false;
99
100
101    protected CodeConverter converter;
102    protected AspectManager manager;
103    protected JoinpointClassifier joinpointClassifier;
104    protected static Collection JavaDoc processedClasses = new ArrayList JavaDoc();
105
106    // Transformers, more than meets the eye!
107
MethodExecutionTransformer methodExecutionTransformer;
108    ConstructorExecutionTransformer constructorExecutionTransformer;
109    ConstructionTransformer constructionTransformer;
110    FieldAccessTransformer fieldAccessTransformer;
111    CallerTransformer callerTransformer;
112    DynamicTransformationObserver dynamicTransformationObserver;
113    
114    /**
115     * Constructs new instrumentor.
116     * @joinpointClassifier algorithm of joinpoint classification to be used.
117     * @param observer need be notified of every joinpoint wrapping caused only
118     * by pointcuts dynamicaly added.
119     */

120    protected Instrumentor(AOPClassPool pool,
121          AspectManager manager,
122          JoinpointClassifier joinpointClassifier,
123          DynamicTransformationObserver observer)
124    {
125       this.classPool = pool;
126       this.converter = new CodeConverter();
127       this.manager = manager;
128       this.joinpointClassifier = joinpointClassifier;
129       this.dynamicTransformationObserver = observer;
130       intitialiseTransformers();
131    }
132
133    protected Instrumentor(AspectManager manager,
134          JoinpointClassifier joinpointClassifier)
135    {
136       this(null,
137             manager,
138             joinpointClassifier,
139             null);
140    }
141    
142    protected abstract void intitialiseTransformers();
143    
144    public ClassPool getClassPool()
145    {
146       return classPool;
147    }
148
149    CodeConverter getCodeConverter()
150    {
151       return converter;
152    }
153
154    public boolean isAdvised(CtClass clazz) throws NotFoundException
155    {
156       CtClass[] interfaces = clazz.getInterfaces();
157       CtClass advised = forName(AOP_PACKAGE + ".Advised");
158       for (int i = 0; i < interfaces.length; i++)
159       {
160          if (interfaces[i].equals(advised)) return true;
161          if (interfaces[i].getName().equals(AOP_PACKAGE + ".Advised")) return true;
162       }
163       return false;
164    }
165    
166    public static boolean implementsAdvised(CtClass clazz) throws NotFoundException
167    {
168       CtClass[] interfaces = clazz.getInterfaces();
169       for (int i = 0; i < interfaces.length; i++)
170       {
171          if (interfaces[i].getName().equals(AOP_PACKAGE + ".Advised")) return true;
172       }
173       return false;
174    }
175
176
177    public static boolean isTransformable(CtClass clazz) throws NotFoundException
178    {
179       CtClass[] interfaces = clazz.getInterfaces();
180       //CtClass advised = forName(AOP_PACKAGE + ".instrument.Untransformable");
181
for (int i = 0; i < interfaces.length; i++)
182       {
183          //if (interfaces[i].equals(advised)) return false;
184
if (interfaces[i].getName().equals(AOP_PACKAGE + ".instrument.Untransformable")) return false;
185       }
186       return true;
187    }
188
189    protected boolean isBaseClass(CtClass clazz)
190            throws NotFoundException
191    {
192       if (clazz.getSuperclass() != null)
193       {
194          return !isAdvised(clazz.getSuperclass());
195       }
196       return true;
197    }
198
199
200    protected static String JavaDoc mixinFieldName(CtClass mixinClass)
201    {
202       StringBuffer JavaDoc buf = new StringBuffer JavaDoc("_");
203       buf.append(mixinClass.getName().replace('.', '$'));
204       buf.append("$aop$mixin");
205       return buf.toString();
206    }
207
208    private void addMixinMethod(Advisor advisor, CtMethod method, CtClass clazz, CtMethod delegate, long hash) throws Exception JavaDoc
209    {
210       CtClass[] exceptions = method.getExceptionTypes();
211
212       // create base, delegating method.
213
CtMethod newMethod = CtNewMethod.wrapped(method.getReturnType(),
214                                                method.getName(),
215                                                method.getParameterTypes(),
216                                                exceptions,
217                                                delegate,
218                                                CtMethod.ConstParameter.integer(hash),
219                                                clazz);
220       newMethod.setModifiers(Modifier.PUBLIC);
221       clazz.addMethod(newMethod);
222    }
223
224    private void addMixin(CtClass clazz, InterfaceIntroduction pointcut, InterfaceIntroduction.Mixin mixin, HashMap JavaDoc baseMethods) throws Exception JavaDoc
225    {
226       // REVISIT:
227
// Later on we should follow the same pattern as
228
// C++ public virtual Mixins
229
// But, for now, just throw an exception if the
230
// mixin is adding any interfaces already
231
// defined in base class or another mixin.
232
CtClass mixinClass = classPool.get(mixin.getClassName());
233       String JavaDoc initializer = (mixin.getConstruction() == null) ? ("new " + mixinClass.getName() + "()") : mixin.getConstruction();
234       CtClass type = forName(mixinClass.getName());
235       CtField field = new CtField(type, mixinFieldName(mixinClass), clazz);
236       int modifiers = Modifier.PRIVATE;
237       if (mixin.isTransient()) modifiers = modifiers | Modifier.TRANSIENT;
238       field.setModifiers(modifiers);
239       clazz.addField(field, CtField.Initializer.byExpr(initializer));
240       HashSet JavaDoc addedMethods = new HashSet JavaDoc();
241
242       String JavaDoc[] interfaces = mixin.getInterfaces();
243       for (int i = 0; i < interfaces.length; i++)
244       {
245          CtClass intf = classPool.get(interfaces[i]);
246          if (clazz.subtypeOf(intf)) continue;
247          clazz.addInterface(intf);
248          HashMap JavaDoc intfMap = JavassistMethodHashing.getMethodMap(intf);
249          Iterator JavaDoc entries = intfMap.entrySet().iterator();
250          while (entries.hasNext())
251          {
252             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) entries.next();
253             Long JavaDoc hash = (Long JavaDoc) entry.getKey();
254             CtMethod method = (CtMethod) entry.getValue();
255             CtMethod baseMethod = (CtMethod)baseMethods.get(hash);
256             if (baseMethod != null && !addedMethods.contains(hash))
257             {
258                String JavaDoc msg = "Mixin " + mixinClass.getName() +
259                         " of pointcut " + pointcut.getName() +
260                         " is trying to apply an already existing method" + method.getName() + " for class " + clazz.getName();
261                
262                if (baseMethod.getDeclaringClass().equals(clazz))
263                {
264                   throw new RuntimeException JavaDoc(msg);
265                }
266                else
267                {
268                   if (AspectManager.verbose)System.out.println("[warn] " + msg);
269                }
270             }
271             // If another interface of this mixin has a duplicate method, then its ok, but don't re-add
272
if (addedMethods.contains(hash)) continue;
273             createMixinInvokeMethod(clazz, mixinClass, initializer, method, hash.longValue());
274             baseMethods.put(hash, method);
275             addedMethods.add(hash);
276          }
277       }
278    }
279
280    private void addIntroductionPointcutInterface(CtClass clazz, Advisor advisor, String JavaDoc intf, HashMap JavaDoc baseMethods) throws Exception JavaDoc
281    {
282       CtClass iface = classPool.get(intf);
283       if (clazz.subtypeOf(iface)) return;
284       if (clazz.subclassOf(iface)) return;
285
286       clazz.addInterface(iface);
287
288       CtMethod mixinInvokeMethod = createInvokeMethod(clazz);
289       HashMap JavaDoc intfMap = JavassistMethodHashing.getMethodMap(iface);
290       Iterator JavaDoc entries = intfMap.entrySet().iterator();
291       while (entries.hasNext())
292       {
293          Map.Entry JavaDoc entry = (Map.Entry JavaDoc) entries.next();
294          Long JavaDoc hash = (Long JavaDoc) entry.getKey();
295          if (baseMethods.containsKey(hash)) continue;
296          CtMethod method = (CtMethod) entry.getValue();
297          addMixinMethod(advisor, method, clazz, mixinInvokeMethod, hash.longValue());
298          baseMethods.put(hash, method);
299       }
300    }
301
302    private void instrumentIntroductions(CtClass clazz, Advisor advisor)
303            throws Exception JavaDoc
304    {
305       ArrayList JavaDoc pointcuts = advisor.getInterfaceIntroductions();
306       if (pointcuts.size() == 0) return;
307       HashMap JavaDoc baseMethods = JavassistMethodHashing.getMethodMap(clazz);
308       Iterator JavaDoc it = pointcuts.iterator();
309       if (it.hasNext()) setupBasics(clazz);
310       while (it.hasNext())
311       {
312
313          InterfaceIntroduction pointcut = (InterfaceIntroduction) it.next();
314          ArrayList JavaDoc mixins = pointcut.getMixins();
315          for (int i = 0; i < mixins.size(); i++)
316          {
317             InterfaceIntroduction.Mixin mixin = (InterfaceIntroduction.Mixin) mixins.get(i);
318             addMixin(clazz, pointcut, mixin, baseMethods);
319          }
320       }
321
322       // pointcut interfaces. If a method is already implemented for it then use that method
323
// otherwise delegate to an interceptor
324
it = pointcuts.iterator();
325       while (it.hasNext())
326       {
327          InterfaceIntroduction pointcut = (InterfaceIntroduction) it.next();
328          String JavaDoc[] interfaces = pointcut.getInterfaces();
329          if (interfaces == null) continue;
330          for (int i = 0; i < interfaces.length; i++)
331          {
332             addIntroductionPointcutInterface(clazz, advisor, interfaces[i], baseMethods);
333          }
334       }
335    }
336
337    private boolean instrumentAnnotationIntroductions(CtClass clazz, ClassAdvisor advisor)
338            throws Exception JavaDoc
339    {
340       boolean changed = false;
341       Iterator JavaDoc it = advisor.getManager().getAnnotationIntroductions().iterator();
342       while (it.hasNext())
343       {
344          AnnotationIntroduction introduction = (AnnotationIntroduction) it.next();
345          if (AspectManager.verbose) System.out.println("**** " + introduction.getOriginalAnnotationExpr() + " invisible: " + introduction.isInvisible() + " expr: " + introduction.getOriginalExpression());
346          if (introduction.matches(advisor, clazz))
347          {
348             if (AspectManager.verbose) System.out.println(introduction.getAnnotation() + " binds to " + clazz.getName());
349             javassist.bytecode.annotation.Annotation info = AnnotationInfoCreator.createAnnotationInfo(classPool, clazz.getClassFile2().getConstPool(), introduction.getAnnotation());
350             if (introduction.isInvisible())
351             {
352                AnnotationsAttribute invisible = (AnnotationsAttribute) clazz.getClassFile2().getAttribute(AnnotationsAttribute.invisibleTag);
353                if (invisible == null)
354                {
355                   invisible = new AnnotationsAttribute(clazz.getClassFile2().getConstPool(), AnnotationsAttribute.invisibleTag);
356                   clazz.getClassFile2().addAttribute(invisible);
357                }
358                changed = true;
359                invisible.addAnnotation(info);
360             }
361             else
362             {
363                AnnotationsAttribute visible = (AnnotationsAttribute) clazz.getClassFile2().getAttribute(AnnotationsAttribute.visibleTag);
364                if (visible == null)
365                {
366                   visible = new AnnotationsAttribute(clazz.getClassFile2().getConstPool(), AnnotationsAttribute.visibleTag);
367                   clazz.getClassFile2().addAttribute(visible);
368                }
369                changed = true;
370                visible.addAnnotation(info);
371             }
372          }
373
374          CtMethod[] methods = clazz.getDeclaredMethods();
375          for (int i = 0; i < methods.length; i++)
376          {
377             if (introduction.matches(advisor, methods[i]))
378             {
379                javassist.bytecode.annotation.Annotation info = AnnotationInfoCreator.createAnnotationInfo(classPool, methods[i].getMethodInfo2().getConstPool(), introduction.getAnnotation());
380                MethodInfo mi = methods[i].getMethodInfo2();
381                if (introduction.isInvisible())
382                {
383                   AnnotationsAttribute invisible = (AnnotationsAttribute) mi.getAttribute(AnnotationsAttribute.invisibleTag);
384                   if (invisible == null)
385                   {
386                      invisible = new AnnotationsAttribute(mi.getConstPool(), AnnotationsAttribute.invisibleTag);
387                      mi.addAttribute(invisible);
388                   }
389                   changed = true;
390                   invisible.addAnnotation(info);
391                }
392                else
393                {
394                   AnnotationsAttribute visible = (AnnotationsAttribute) mi.getAttribute(AnnotationsAttribute.visibleTag);
395                   if (visible == null)
396                   {
397                      visible = new AnnotationsAttribute(mi.getConstPool(), AnnotationsAttribute.visibleTag);
398                      mi.addAttribute(visible);
399                   }
400                   changed = true;
401                   visible.addAnnotation(info);
402                }
403             }
404
405          }
406
407          CtConstructor[] cons = clazz.getDeclaredConstructors();
408          for (int i = 0; i < cons.length; i++)
409          {
410             if (introduction.matches(advisor, cons[i]))
411             {
412                javassist.bytecode.annotation.Annotation info = AnnotationInfoCreator.createAnnotationInfo(classPool, cons[i].getMethodInfo2().getConstPool(), introduction.getAnnotation());
413                MethodInfo mi = cons[i].getMethodInfo2();
414                if (introduction.isInvisible())
415                {
416                   AnnotationsAttribute invisible = (AnnotationsAttribute) mi.getAttribute(AnnotationsAttribute.invisibleTag);
417                   if (invisible == null)
418                   {
419                      invisible = new AnnotationsAttribute(mi.getConstPool(), AnnotationsAttribute.invisibleTag);
420                      mi.addAttribute(invisible);
421                   }
422                   changed = true;
423                   invisible.addAnnotation(info);
424                }
425                else
426                {
427                   AnnotationsAttribute visible = (AnnotationsAttribute) mi.getAttribute(AnnotationsAttribute.visibleTag);
428                   if (visible == null)
429                   {
430                      visible = new AnnotationsAttribute(mi.getConstPool(), AnnotationsAttribute.visibleTag);
431                      mi.addAttribute(visible);
432                   }
433                   changed = true;
434                   visible.addAnnotation(info);
435                }
436             }
437          }
438
439          CtField[] fields = clazz.getDeclaredFields();
440          for (int i = 0; i < fields.length; i++)
441          {
442             if (introduction.matches(advisor, fields[i]))
443             {
444                javassist.bytecode.annotation.Annotation info = AnnotationInfoCreator.createAnnotationInfo(classPool, fields[i].getFieldInfo2().getConstPool(), introduction.getAnnotation());
445                FieldInfo mi = fields[i].getFieldInfo2();
446                if (introduction.isInvisible())
447                {
448                   AnnotationsAttribute invisible = (AnnotationsAttribute) mi.getAttribute(AnnotationsAttribute.invisibleTag);
449                   if (invisible == null)
450                   {
451                      invisible = new AnnotationsAttribute(mi.getConstPool(), AnnotationsAttribute.invisibleTag);
452                      mi.addAttribute(invisible);
453                   }
454                   changed = true;
455                   invisible.addAnnotation(info);
456                }
457                else
458                {
459                   AnnotationsAttribute visible = (AnnotationsAttribute) mi.getAttribute(AnnotationsAttribute.visibleTag);
460                   if (visible == null)
461                   {
462                      visible = new AnnotationsAttribute(mi.getConstPool(), AnnotationsAttribute.visibleTag);
463                      mi.addAttribute(visible);
464                   }
465                   changed = true;
466                   visible.addAnnotation(info);
467                }
468             }
469          }
470       }
471       return changed;
472    }
473
474    private boolean instrumentAnnotationOverrides(CtClass clazz, ClassAdvisor advisor)
475            throws Exception JavaDoc
476    {
477       boolean changed = false;
478       Iterator JavaDoc it = advisor.getManager().getAnnotationOverrides().iterator();
479       while (it.hasNext())
480       {
481          AnnotationIntroduction introduction = (AnnotationIntroduction) it.next();
482          if (introduction.matches(advisor, clazz))
483          {
484             advisor.getAnnotations().addClassAnnotation(introduction.getAnnotation().getIdentifier(), introduction.getOriginalAnnotationExpr());
485          }
486
487          CtMethod[] methods = clazz.getDeclaredMethods();
488          for (int i = 0; i < methods.length; i++)
489          {
490             if (introduction.matches(advisor, methods[i]))
491             {
492                advisor.getAnnotations().addAnnotation(methods[i], introduction.getAnnotation().getIdentifier());
493             }
494          }
495
496          CtConstructor[] cons = clazz.getDeclaredConstructors();
497          for (int i = 0; i < cons.length; i++)
498          {
499             if (introduction.matches(advisor, cons[i]))
500             {
501                advisor.getAnnotations().addAnnotation(cons[i], introduction.getAnnotation().getIdentifier());
502             }
503          }
504
505          CtField[] fields = clazz.getDeclaredFields();
506          for (int i = 0; i < fields.length; i++)
507          {
508             if (introduction.matches(advisor, fields[i]))
509             {
510                advisor.getAnnotations().addAnnotation(fields[i], introduction.getAnnotation().getIdentifier());
511             }
512          }
513       }
514       return changed;
515    }
516
517    public boolean applyCallerPointcuts(CtClass clazz, ClassAdvisor advisor) throws CannotCompileException
518    {
519       return callerTransformer.applyCallerPointcuts(clazz, advisor);
520    }
521
522    /**
523     * Find all classes that this class references. If any of those classes are advised and have field and/or constructor
524     * interception, do instrumentation on this class so that those fields and constructors are instrumented
525     */

526    protected boolean convertReferences(CtClass clazz) throws Exception JavaDoc
527    {
528       boolean converted = false;
529       String JavaDoc ref = null;
530       try
531       {
532          AOPClassPool pool = AOPClassPool.createAOPClassPool(clazz.getClassPool(), AOPClassPoolRepository.getInstance());
533          
534 // if (clazz.getName().startsWith("org.jboss.test.aop.scopedextender."))
535
// {
536
// //Debug
537
// System.out.println("========> Converting references for " + clazz.getName() + " in " + clazz.getClassPool());
538
// System.out.println("---> Created temp pool " + pool);
539
// }
540
Iterator JavaDoc it = clazz.getRefClasses().iterator();
541          while (it.hasNext())
542          {
543             ref = (String JavaDoc) it.next();
544             if (!manager.convertReference(ref)
545                 || manager.isNonAdvisableClassName(ref)
546                 || ref.startsWith("java.")
547                 || ref.startsWith("javax.")
548                 || ref.startsWith("[")) continue;
549             // Only need a temporary advisor for resolving metadata
550
CtClass ctRef = null;
551             try
552             {
553                ctRef = pool.get(ref);
554             }
555             catch (NotFoundException e)
556             {
557                if (AspectManager.suppressReferenceErrors)
558                {
559                   System.err.println("[warn] Could not find class " + ref + " that " + clazz.getName() + " references. It may not be in your classpath and you may not be getting field and constructor weaving for this class.");
560                   if (AspectManager.verbose) e.printStackTrace();
561                   continue;
562                }
563                else
564                {
565                   throw e;
566                }
567             }
568             if (!isTransformable(ctRef)) continue;
569
570             ClassAdvisor advisor = manager.getTempClassAdvisor(ctRef);
571             
572 // if (clazz.getName().startsWith("org.jboss.test.aop.scopedextender."))
573
// {
574
// //Debug
575
// System.out.println("---> Found class " + ctRef.getName() + " in " + ctRef.getClassPool());
576
// System.out.println("---> Using manager " + manager + " should convert fields " + !manager.shouldSkipFieldAccess(ref));
577
// }
578

579             if (!manager.shouldSkipFieldAccess(ref) && !ref.equals(clazz.getName()))
580             {
581                List JavaDoc fields = getAdvisableFields(ctRef);
582                if (fieldAccessTransformer.replaceFieldAccess(fields, ctRef, advisor))
583                {
584 // System.out.println("---> !!!Replaced field access!!!");
585
manager.addFieldInterceptionMarker(ref);
586                   converted = true;
587                }
588                else
589                {
590                   manager.skipFieldAccess(ref);
591                }
592             }
593             if (!manager.shouldSkipConstruction(ref))
594             {
595                if (constructorExecutionTransformer.replaceConstructorAccess(advisor, ctRef))
596                {
597                   manager.addConstructionInterceptionMarker(ref);
598                   converted = true;
599                }
600                else
601                {
602                   manager.skipConstruction(ref);
603                }
604             }
605
606             if (!converted)
607             {
608                manager.skipReference(ref);
609             }
610             ref = null;
611          }
612       }
613       catch (Exception JavaDoc ex)
614       {
615          if (ref != null)
616          {
617             throw new TransformationException("Failed to aspectize class " + clazz.getName() + ". Could not find class it references " + ref + " It may not be in your classpath and you may not be getting field and constructor weaving for this class.");
618          }
619          throw ex;
620       }
621       return converted;
622    }
623
624    protected boolean shouldNotTransform(CtClass clazz)throws NotFoundException
625    {
626       return (clazz.isInterface() ||
627             clazz.isFrozen() ||
628             clazz.isArray() ||
629             clazz.getName().startsWith("org.jboss.aop") ||
630             isAdvised(clazz) ||
631             !isTransformable(clazz));
632    }
633    
634    /**
635     * Makes class advisable.
636     */

637    public boolean transform(CtClass clazz,
638                             ClassAdvisor advisor)
639    {
640       synchronized(this.processedClasses)
641       {
642          processedClasses.add(clazz);
643       }
644       try
645       {
646          if (shouldNotTransform(clazz)) return false;
647          if (AspectManager.verbose) System.out.println("[trying to transform] " + clazz.getName());
648
649          DeclareChecker.checkDeclares(manager, clazz, advisor);
650
651          boolean converted = instrumentAnnotationIntroductions(clazz, advisor);
652          converted = instrumentAnnotationOverrides(clazz, advisor) || converted;
653          boolean constructorAccessConverted = false;
654          converted = applyCallerPointcuts(clazz, advisor) || converted;
655          methodExecutionTransformer.instrument(clazz, advisor);
656          boolean constructionTransformation = constructionTransformer.insertConstructionInterception(clazz, advisor);
657          constructorAccessConverted = constructorExecutionTransformer.transform(clazz, advisor);
658          String JavaDoc classname = clazz.getName();
659          if (constructorAccessConverted)
660          {
661             manager.addConstructionInterceptionMarker(classname);
662          }
663          else
664          {
665             manager.skipConstruction(classname);
666          }
667          converted = converted || constructorAccessConverted;
668
669          instrumentIntroductions(clazz, advisor);
670
671          converted = convertReferences(clazz) || converted;
672          // need to instrument no matter what so that
673
// previously declared field and constructor interceptions
674
// get instrumented within this class.
675
if (converted || basicsSet)
676          {
677             clazz.instrument(converter);
678          }
679
680          // create static wrapper methods after
681
// clazz.instrument because the wrappers may call cons or fields
682
fieldAccessTransformer.buildFieldWrappers(clazz, advisor);
683          if (constructorAccessConverted)
684          {
685             constructorExecutionTransformer.codeConverted();
686          }
687          else
688          {
689             if (manager.shouldSkipFieldAccess(classname))
690             {
691                manager.skipReference(classname);
692             }
693          }
694
695
696          // notifies dynamic transformation observer
697
dynamicTransformationObserver.transformationFinished(clazz, converter);
698
699          if (AspectManager.verbose) System.out.println("[debug] was " + clazz.getName() + " converted: " + (basicsSet || converted));
700
701          if (basicsSet || converted)
702          {
703             return true;
704          }
705          else
706          {
707             //classPool.flushClass(clazz.getName());
708
return false;
709          }
710
711       }
712       catch (Throwable JavaDoc e)
713       {
714          if (AspectManager.suppressTransformationErrors)
715          {
716             System.err.println("[warn] AOP Instrumentor failed to transform " + clazz.getName());
717             e.printStackTrace();
718             return false;
719          }
720          else
721          {
722             if (e instanceof TransformationException)
723             {
724                throw ((TransformationException) e);
725             }
726             else
727             {
728                e.printStackTrace();
729                throw new RuntimeException JavaDoc("failed to transform: " + clazz.getName(), e);
730             }
731          }
732
733       }
734    }
735
736
737    public List JavaDoc getConstructors(CtClass clazz)
738    {
739       List JavaDoc list = new ArrayList JavaDoc();
740
741       CtConstructor[] constructors = clazz.getDeclaredConstructors();
742
743       for (int i = 0; i < constructors.length; i++)
744       {
745          list.add(constructors[i]);
746       }
747       Collections.sort(list, CtConstructorComparator.INSTANCE);
748
749       return list;
750    }
751
752    /**
753     * Gets sorted collection of advisable methods.
754     */

755    public static List JavaDoc getAdvisableFields(CtClass clazz) throws NotFoundException
756    {
757       List JavaDoc list = new ArrayList JavaDoc();
758       CtField[] fields = clazz.getDeclaredFields();
759       for (int i = 0; i < fields.length; i++)
760       {
761          if (Advisable.isAdvisable(fields[i]))
762          {
763             list.add(fields[i]);
764          }
765       }
766       Collections.sort(list, CtFieldComparator.INSTANCE);
767
768       return list;
769    }
770
771    /**
772     * Creates generic invoke method to be wrapped by real signatures.
773     */

774    private CtMethod createInvokeMethod(CtClass clazz)
775            throws CannotCompileException
776    {
777       return CtNewMethod.make("public java.lang.Object invoke(java.lang.Object[] args, long i)" +
778                               " throws java.lang.Throwable {" +
779                               " return ((org.jboss.aop.ClassAdvisor)this._getAdvisor()).invokeMethod(this, i, args);" +
780                               "}",
781                               clazz);
782    }
783
784    /**
785     * Gets a class by its name.
786     */

787    public CtClass forName(String JavaDoc name) throws NotFoundException
788    {
789       return this.classPool.get(name);
790    }
791
792    /**
793     * Gets a class by its name.
794     */

795    public CtClass forName(ClassPool pool, String JavaDoc name) throws NotFoundException
796    {
797       return pool.get(name);
798    }
799
800
801    /**
802     * Adds a static field to a class.
803     */

804    protected CtField addStaticField(CtClass clazz, String JavaDoc name, String JavaDoc typeName,
805                                   CtField.Initializer initializer)
806            throws CannotCompileException, NotFoundException
807    {
808       CtClass type = forName(typeName);
809       CtField field = new CtField(type, name, clazz);
810       field.setModifiers(Modifier.PRIVATE | Modifier.STATIC);
811       clazz.addField(field, initializer);
812
813       return field;
814    }
815
816    /**
817     * Adds a protected field to a class.
818     */

819    protected CtField addProtectedField(CtClass clazz, String JavaDoc name, String JavaDoc typeName,
820                                      CtField.Initializer initializer)
821            throws CannotCompileException, NotFoundException
822    {
823       CtClass type = forName(typeName);
824       CtField field = new CtField(type, name, clazz);
825       field.setModifiers(Modifier.PROTECTED | Modifier.TRANSIENT);
826       if (initializer != null)
827       {
828          clazz.addField(field, initializer);
829       }
830       else
831       {
832          clazz.addField(field);
833       }
834       return field;
835    }
836
837    public void setupBasics(CtClass clazz) throws CannotCompileException, NotFoundException
838    {
839       if (basicsSet) return;
840       basicsSet = true;
841       // add serialVersionUID.
842
SerialVersionUID.setSerialVersionUID(clazz);
843
844       // add marker interface.
845
clazz.addInterface(forName(AOP_PACKAGE + ".Advised"));
846       
847       doSetupBasics(clazz);
848    }
849    
850    /**
851     * Notifies the <code>Instrumentor</code> that some joinpoint status were updated.
852     * This method hot swaps the code of afected classes.
853     * @param joinpointUpdates a collection of <code>org.jboss.aop.instrument.JoinpointStatusUpdate</code>.
854     * @param hotSwapper object capable of hot swapping classes.
855     */

856    public synchronized void interceptorChainsUpdated(Collection JavaDoc joinpointUpdates, HotSwapper hotSwapper) {
857       //creates a converter
858
this.converter = new CodeConverter();
859       // list of instrumented classes
860
Collection JavaDoc classes = new HashSet JavaDoc();
861       try {
862          // transform classes whose joinpont status have changed
863
for (Iterator JavaDoc iterator = joinpointUpdates.iterator(); iterator.hasNext(); )
864          {
865             JoinpointStatusUpdate update = (JoinpointStatusUpdate) iterator.next();
866             CtClass clazz = update.clazz;
867             JoinpointStatusUpdate.ClassJoinpoints wrapTargets = update.newlyAdvisedJoinpoints;
868             JoinpointStatusUpdate.ClassJoinpoints unwrapTargets = update.newlyUnadvisedJoinpoints;
869             
870             clazz.defrost();
871             fieldAccessTransformer.wrap(clazz, wrapTargets.fieldReads, wrapTargets.fieldWrites);
872             fieldAccessTransformer.unwrap(clazz, unwrapTargets.fieldReads, unwrapTargets.fieldWrites);
873             constructorExecutionTransformer.wrap(clazz, wrapTargets.constructorExecutions);
874             constructorExecutionTransformer.unwrap(clazz, unwrapTargets.constructorExecutions);
875             methodExecutionTransformer.wrap(clazz, wrapTargets.methodExecutions);
876             methodExecutionTransformer.unwrap(clazz, unwrapTargets.methodExecutions);
877             if (!update.isEmpty())
878             {
879                clazz.instrument(converter);
880                classes.add(clazz);
881             }
882          }
883          // instrument classes that access the joinpoints whose status have changed, in
884
// order to make this classes access the joinpoint wrapper instead
885
Collection JavaDoc classPools = manager.getRegisteredCLs().values();
886          Collection JavaDoc conversionsRegistered = new HashSet JavaDoc();
887          synchronized(this.processedClasses)
888          {
889             for (Iterator JavaDoc iterator2 = processedClasses.iterator(); iterator2.hasNext(); ) {
890
891                CtClass clazz = (CtClass) iterator2.next();
892                if (manager.isNonAdvisableClassName(clazz.getName()) || ! isTransformable(clazz))
893                {
894                   continue;
895                }
896                // class already instrumented
897
if (classes.contains(clazz))
898                {
899                   continue;
900                }
901                // check if clazz should be added to classes
902
clazz.defrost();
903                byte[] previousByteCode = clazz.toBytecode();
904                clazz.defrost();
905                clazz.instrument(converter);
906                if (!java.util.Arrays.equals(clazz.toBytecode(), previousByteCode))
907                {
908                   classes.add(clazz);
909                }
910                clazz.defrost();
911             }
912          }
913          // notifies code conversion observers
914
fieldAccessTransformer.codeConverted();
915          constructorExecutionTransformer.codeConverted();
916          
917          // registers the classes bytecodes to be hot swapped
918
for (Iterator JavaDoc iterator = classes.iterator(); iterator.hasNext(); )
919          {
920             CtClass clazz = (CtClass) iterator.next();
921             AOPClassPool classPool = (AOPClassPool) clazz.getClassPool();
922             clazz.defrost();
923             hotSwapper.registerChange(classPool.getClassLoader().loadClass(clazz.getName()),
924                   clazz.toBytecode());
925          }
926          // performs the hot swap of registered classes
927
hotSwapper.hotSwap();
928       }
929       catch (Exception JavaDoc e) {
930          e.printStackTrace();
931          if (AspectManager.suppressTransformationErrors)
932          {
933             System.err.println("[warn] AOP Instrumentor failed to updated wrapping status.");
934             e.printStackTrace();
935          }
936          else
937          {
938             if (e instanceof TransformationException)
939             {
940                throw ((TransformationException) e);
941             }
942             else
943             {
944                throw new RuntimeException JavaDoc("failed to update wrapping status", e);
945             }
946          }
947       }
948    }
949
950    
951    /**
952     * Converts all processed classes to make wrapping of the appropriate joinpoints.
953     * This method must be called if some dynamic transformation ocurred (i. e. a
954     * class has just been loaded and one or more of its joinpoints were wrapped due
955     * only to bindings added dynamicaly; in this case, the previously loaded classes
956     * may not call the wrappers of this joinpoints, and need to be instrumented).
957     * @param hostSwapper
958     * @param clazz the clazz whose transformation involved dynamic wrapping.
959     * @param fieldReads collection of fields whose read joinpoit was dynamicaly wrapped.
960     * @param fieldWrites collection of fields whose read joinpoit was dynamicaly wrapped.
961     * @param constructor <code>true</code> if the <code>clazz</code> constructors were
962     * dynamicaly wrapped.
963     */

964    public void convertProcessedClasses(HotSwapper hotSwapper, CtClass clazz,
965          Collection JavaDoc fieldReads, Collection JavaDoc fieldWrites, boolean constructor)
966    {
967       AOPClassPool classPool = (AOPClassPool) clazz.getClassPool();
968       CodeConverter codeConverter = new CodeConverter();
969       for (Iterator JavaDoc iterator = fieldReads.iterator(); iterator.hasNext(); )
970       {
971          CtField field = (CtField) iterator.next();
972          codeConverter.replaceFieldRead(field, clazz, fieldAccessTransformer.fieldRead(field.getName()));
973       }
974       for (Iterator JavaDoc iterator = fieldReads.iterator(); iterator.hasNext(); )
975       {
976          CtField field = (CtField) iterator.next();
977          codeConverter.replaceFieldWrite(field, clazz, fieldAccessTransformer.fieldWrite(field.getName()));
978       }
979       if (constructor)
980       {
981          codeConverter.replaceNew(clazz, clazz, ConstructorExecutionTransformer.constructorFactory(clazz.getSimpleName()));
982       }
983          
984       synchronized(processedClasses)
985       {
986       for (Iterator JavaDoc iterator = processedClasses.iterator(); iterator.hasNext();)
987       {
988          CtClass processedClass = (CtClass) iterator.next();
989          if (processedClass == clazz)
990             continue;
991          if (processedClass.getRefClasses() == null ||
992                 ! clazz.getRefClasses().contains(clazz.getName()))
993           {
994              continue;
995           }
996           try
997           {
998              processedClass.defrost();
999              byte[] previousByteCode = processedClass.toBytecode();
1000             processedClass.defrost();
1001             processedClass.instrument(codeConverter);
1002             byte[] updatedByteCode = processedClass.toBytecode();
1003             if (!java.util.Arrays.equals(updatedByteCode, previousByteCode))
1004             {
1005               hotSwapper.registerChange(classPool.getClassLoader().loadClass(processedClass.getName()), updatedByteCode);
1006             }
1007             processedClass.defrost();
1008          }
1009          catch (Exception JavaDoc e)
1010          {
1011             e.printStackTrace();
1012             if (AspectManager.suppressTransformationErrors)
1013             {
1014                System.err.println("[warn] AOP Instrumentor failed to updated wrapping status.");
1015                e.printStackTrace();
1016             }
1017             else if (e instanceof TransformationException)
1018             {
1019                throw ((TransformationException) e);
1020             }
1021             else
1022             {
1023                throw new RuntimeException JavaDoc("failed to update wrapping status", e);
1024             }
1025          }
1026
1027         }
1028      }
1029      hotSwapper.hotSwap();
1030   }
1031   
1032   protected abstract void doSetupBasics(CtClass clazz) throws CannotCompileException, NotFoundException;
1033   
1034   /**
1035    * Creates generic invoke method to be wrapped by real signatures.
1036    */

1037   protected abstract CtMethod createMixinInvokeMethod(CtClass clazz, CtClass mixinClass, String JavaDoc initializer, CtMethod method, long hash)
1038           throws CannotCompileException, NotFoundException, Exception JavaDoc;
1039
1040}
Popular Tags