KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > codehaus > aspectwerkz > transform > aspectj > AspectJAspectModel


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.aspectj;
9
10 import org.apache.bcel.classfile.JavaClass;
11 import org.apache.bcel.classfile.Method;
12 import org.apache.bcel.classfile.Attribute;
13 import org.apache.bcel.classfile.ClassParser;
14 import org.apache.bcel.classfile.Unknown;
15 import org.apache.bcel.generic.Type;
16
17 import java.util.*;
18 import java.io.InputStream JavaDoc;
19 import java.io.IOException JavaDoc;
20 import java.lang.reflect.Field JavaDoc;
21
22 import org.aspectj.weaver.AjAttribute;
23 import org.aspectj.weaver.ISourceContext;
24 import org.aspectj.weaver.patterns.Pointcut;
25 import org.aspectj.weaver.patterns.KindedPointcut;
26 import org.aspectj.weaver.patterns.SignaturePattern;
27
28 import org.codehaus.aspectwerkz.exception.WrappedRuntimeException;
29 import org.codehaus.aspectwerkz.exception.DefinitionException;
30 import org.codehaus.aspectwerkz.aspect.AdviceType;
31 import org.codehaus.aspectwerkz.definition.AspectDefinition;
32 import org.codehaus.aspectwerkz.definition.DefinitionParserHelper;
33 import org.codehaus.aspectwerkz.definition.AdviceDefinition;
34 import org.codehaus.aspectwerkz.reflect.ClassInfo;
35 import org.codehaus.aspectwerkz.reflect.MethodInfo;
36 import org.codehaus.aspectwerkz.transform.inlining.spi.AspectModel;
37 import org.codehaus.aspectwerkz.transform.inlining.AdviceMethodInfo;
38 import org.codehaus.aspectwerkz.transform.inlining.AspectInfo;
39 import org.codehaus.aspectwerkz.transform.inlining.compiler.AbstractJoinPointCompiler;
40 import org.codehaus.aspectwerkz.transform.TransformationConstants;
41
42 import org.codehaus.aspectwerkz.org.objectweb.asm.ClassWriter;
43 import org.codehaus.aspectwerkz.org.objectweb.asm.CodeVisitor;
44
45 /**
46  * Implementation of the AspectModel interface for the AspectJ framework.
47  * <p/>
48  * Provides methods for definition of aspects and framework specific bytecode generation
49  * used by the join point compiler.
50  *
51  * @author <a HREF="mailto:jboner@codehaus.org">Jonas BonŽr </a>
52  */

53 public class AspectJAspectModel implements AspectModel, TransformationConstants {
54
55     private static final String JavaDoc ASPECT_MODEL_TYPE = "aspectj";
56     private static final String JavaDoc ASPECTJ_AROUND_CLOSURE_CLASS_NAME = "org/aspectj/runtime/internal/AroundClosure";
57     private static final String JavaDoc ASPECTJ_AROUND_CLOSURE_RUN_METHOD_NAME = "run";
58     private static final String JavaDoc ASPECTJ_AROUND_CLOSURE_RUN_METHOD_SIGNATURE = "([Ljava/lang/Object;)Ljava/lang/Object;";
59     private static final String JavaDoc PREFIX_AROUND_ADVICE = "ajc$around$";
60     private static final String JavaDoc PREFIX_BEFORE_ADVICE = "ajc$before$";
61     private static final String JavaDoc PREFIX_AFTER_FINALLY_ADVICE = "ajc$after$";
62     private static final String JavaDoc PREFIX_AFTER_THROWING_ADVICE = "ajc$afterThrowing$";
63     private static final String JavaDoc PREFIX_AFTER_RETURNING_ADVICE = "ajc$afterReturning$";
64     private static final String JavaDoc PROCEED_SUFFIX = "proceed";
65
66     /**
67      * Returns the aspect model type, which is an id for the the special aspect model, can be anything as long
68      * as it is unique.
69      *
70      * @return the aspect model type id
71      */

72     public String JavaDoc getAspectModelType() {
73         return ASPECT_MODEL_TYPE;
74     }
75
76     /**
77      * Defines the aspect.
78      *
79      * @param classInfo
80      * @param aspectDef
81      * @param loader
82      */

83     public void defineAspect(final ClassInfo classInfo,
84                              final AspectDefinition aspectDef,
85                              final ClassLoader JavaDoc loader) {
86
87         JavaClass javaClass = null;
88         String JavaDoc classFileName = classInfo.getName().replace('.', '/') + ".class";
89         try {
90             InputStream JavaDoc classStream = loader.getResourceAsStream(classFileName);
91             ClassParser classParser = new ClassParser(classStream, classFileName);
92             javaClass = classParser.parse();
93         } catch (IOException JavaDoc e) {
94             throw new WrappedRuntimeException(e);
95         }
96
97         List attributes = getAspectAttributes(javaClass);
98         if (attributes.size() == 0) {
99             return; // not an AspectJ aspect
100
}
101
102         aspectDef.setAspectModel(getAspectModelType());
103
104         for (Iterator it = attributes.iterator(); it.hasNext();) {
105             AjAttribute attr = (AjAttribute) it.next();
106             if (attr instanceof AjAttribute.PointcutDeclarationAttribute) {
107                 AjAttribute.PointcutDeclarationAttribute pcAttr = (AjAttribute.PointcutDeclarationAttribute) attr;
108                 Pointcut pointcut = pcAttr.reify().getPointcut();
109                 if (pointcut instanceof KindedPointcut) {
110                     try {
111                         Field JavaDoc sigField = KindedPointcut.class.getDeclaredField("signature");
112                         sigField.setAccessible(true);
113                         SignaturePattern signature = (SignaturePattern) sigField.get(pointcut);
114                         DefinitionParserHelper.createAndAddPointcutDefToAspectDef(
115                                 signature.getName().toString(), pointcut.toString(), aspectDef
116                         );
117                     } catch (Exception JavaDoc e) {
118                         throw new WrappedRuntimeException(e);
119                     }
120                 }
121             }
122         }
123         for (Iterator iterator = getAroundAdviceInfo(javaClass).iterator(); iterator.hasNext();) {
124             AdviceInfo ajAdviceInfo = (AdviceInfo) iterator.next();
125             AdviceDefinition adviceDef = handleAdviceInfo(classInfo, aspectDef, ajAdviceInfo);
126             aspectDef.addAroundAdviceDefinition(adviceDef);
127         }
128         for (Iterator iterator = getBeforeAdviceInfo(javaClass).iterator(); iterator.hasNext();) {
129             AdviceInfo ajAdviceInfo = (AdviceInfo) iterator.next();
130             AdviceDefinition adviceDef = handleAdviceInfo(classInfo, aspectDef, ajAdviceInfo);
131             aspectDef.addBeforeAdviceDefinition(adviceDef);
132         }
133         for (Iterator iterator = getAfterFinallyAdviceInfo(javaClass).iterator(); iterator.hasNext();) {
134             AdviceInfo ajAdviceInfo = (AdviceInfo) iterator.next();
135             AdviceDefinition adviceDef = handleAdviceInfo(classInfo, aspectDef, ajAdviceInfo);
136             aspectDef.addAfterAdviceDefinition(adviceDef);
137         }
138         for (Iterator iterator = getAfterReturningAdviceInfo(javaClass).iterator(); iterator.hasNext();) {
139             AdviceInfo ajAdviceInfo = (AdviceInfo) iterator.next();
140             AdviceDefinition adviceDef = handleAdviceInfo(classInfo, aspectDef, ajAdviceInfo);
141             aspectDef.addAfterAdviceDefinition(adviceDef);
142         }
143         for (Iterator iterator = getAfterThrowingAdviceInfo(javaClass).iterator(); iterator.hasNext();) {
144             AdviceInfo ajAdviceInfo = (AdviceInfo) iterator.next();
145             AdviceDefinition adviceDef = handleAdviceInfo(classInfo, aspectDef, ajAdviceInfo);
146             aspectDef.addAfterAdviceDefinition(adviceDef);
147         }
148     }
149
150     /**
151      * AspectJ is not in need for reflective information, passes contextual info through args() binding etc.
152      * or handles it itself using 'thisJoinPoint'.
153      *
154      * @return true
155      */

156     public boolean requiresReflectiveInfo() {
157         return false;
158     }
159
160     /**
161      * Returns info about the closure class, name and type (interface or class).
162      *
163      * @return the closure class info
164      */

165     public AroundClosureClassInfo getAroundClosureClassInfo() {
166         return new AspectModel.AroundClosureClassInfo( ASPECTJ_AROUND_CLOSURE_CLASS_NAME, new String JavaDoc[]{});
167     }
168
169
170     /**
171      * Creates the methods required to implement or extend to implement the closure for the specific aspect model type.
172      *
173      * @param cw
174      * @param className
175      */

176     public void createMandatoryMethods(final ClassWriter cw, final String JavaDoc className) {
177         CodeVisitor cv = cw.visitMethod(
178                 ACC_PUBLIC, ASPECTJ_AROUND_CLOSURE_RUN_METHOD_NAME,
179                 ASPECTJ_AROUND_CLOSURE_RUN_METHOD_SIGNATURE,
180                 new String JavaDoc[]{THROWABLE_CLASS_NAME},
181                 null
182         );
183         cv.visitVarInsn(ALOAD, 0);
184         cv.visitMethodInsn(INVOKEVIRTUAL, className, PROCEED_METHOD_NAME, PROCEED_METHOD_SIGNATURE);
185         cv.visitInsn(ARETURN);
186         cv.visitMaxs(0, 0);
187     }
188
189     /**
190      * Creates an invocation of the around closure class' constructor.
191      *
192      * @param cv
193      */

194     public void createInvocationOfAroundClosureSuperClass(final CodeVisitor cv) {
195         cv.visitInsn(ACONST_NULL);
196         cv.visitMethodInsn(
197                 INVOKESPECIAL,
198                 ASPECTJ_AROUND_CLOSURE_CLASS_NAME,
199                 INIT_METHOD_NAME,
200                 "([Ljava/lang/Object;)V"
201         );
202     }
203
204     /**
205      * Creates a field to host the aspectj aspect instance
206      * <p/>
207      * TODO support other aspect deployment model
208      *
209      * @param cw
210      * @param aspectInfo
211      * @param joinPointClassName
212      */

213     public void createAspectReferenceField(final ClassWriter cw,
214                                            final AspectInfo aspectInfo,
215                                            final String JavaDoc joinPointClassName) {
216         AbstractJoinPointCompiler.createAspectReferenceField(cw, aspectInfo);
217     }
218
219     /**
220      * Creates instantiation of the aspectj aspect instance by invoking aspectOf().
221      * <p/>
222      * TODO support other aspectOf() types of aspect retrieval
223      *
224      * @param cv
225      * @param aspectInfo
226      * @param joinPointClassName
227      */

228     public void createAspectInstantiation(final CodeVisitor cv,
229                                           final AspectInfo aspectInfo,
230                                           final String JavaDoc joinPointClassName) {
231         cv.visitMethodInsn(
232                 INVOKESTATIC,
233                 aspectInfo.getAspectClassName(),
234                 "aspectOf()",
235                 "()" + aspectInfo.getAspectClassSignature()
236         );
237         // no cast needed
238
cv.visitFieldInsn(
239                 PUTSTATIC, joinPointClassName, aspectInfo.getAspectFieldName(), aspectInfo.getAspectClassSignature()
240         );
241
242     }
243
244     /**
245      * Handles the arguments to the before around.
246      *
247      * @param cv
248      * @param adviceMethodInfo
249      */

250     public void createAroundAdviceArgumentHandling(final CodeVisitor cv, final AdviceMethodInfo adviceMethodInfo) {
251     }
252
253     /**
254      * Handles the arguments to the before advice.
255      *
256      * @param cv
257      * @param adviceMethodInfo
258      */

259     public void createBeforeAdviceArgumentHandling(final CodeVisitor cv, final AdviceMethodInfo adviceMethodInfo) {
260     }
261
262     /**
263      * Handles the arguments to the after advice.
264      *
265      * @param cv
266      * @param adviceMethodInfo
267      */

268     public void createAfterAdviceArgumentHandling(final CodeVisitor cv, final AdviceMethodInfo adviceMethodInfo) {
269         final AdviceType adviceType = adviceMethodInfo.getAdviceInfo().getType();
270         final int specialArgumentIndex = adviceMethodInfo.getSpecialArgumentIndex();
271         final String JavaDoc specialArgumentTypeName = adviceMethodInfo.getSpecialArgumentTypeName();
272         if (adviceType.equals(AdviceType.AFTER_RETURNING) ||
273             adviceType.equals(AdviceType.AFTER_THROWING)) {
274             cv.visitVarInsn(ALOAD, specialArgumentIndex);
275             cv.visitTypeInsn(CHECKCAST, specialArgumentTypeName);
276         }
277     }
278
279     /**
280      * Handles the AspectJ advice info.
281      *
282      * @param classInfo
283      * @param aspectDef
284      * @param ajAdviceInfo
285      * @return the new advice definition
286      */

287     private AdviceDefinition handleAdviceInfo(final ClassInfo classInfo,
288                                               final AspectDefinition aspectDef,
289                                               final AdviceInfo ajAdviceInfo) {
290
291         MethodInfo adviceMethod = null;
292         MethodInfo[] methods = classInfo.getMethods();
293         for (int i = 0; i < methods.length; i++) {
294             MethodInfo method = methods[i];
295             if (method.getName().equals(ajAdviceInfo.adviceMethodName)) {
296                 adviceMethod = method;
297                 break;
298             }
299         }
300         if (adviceMethod == null) {
301             throw new Error JavaDoc(
302                     "advice method [" + ajAdviceInfo.adviceMethodName +
303                     "] could not be found in class [" + classInfo.getName() + "]"
304             );
305         }
306         String JavaDoc specialArgType = null;
307         if (ajAdviceInfo.extraParameterFlags != 0) {
308             specialArgType = ajAdviceInfo.parameterTypes[0];
309         }
310         return AdviceDefinition.newInstance(
311                 createFullAspectJAdviceMethodName(ajAdviceInfo),
312                 ajAdviceInfo.type,
313                 ajAdviceInfo.pointcut,
314                 specialArgType,
315                 classInfo.getName(),
316                 classInfo.getName(),
317                 adviceMethod,
318                 aspectDef
319         );
320     }
321
322     /**
323      * Creates a full AspectJ advice method name.
324      *
325      * @param ajAdviceInfo
326      * @return the method name
327      */

328     private String JavaDoc createFullAspectJAdviceMethodName(final AdviceInfo ajAdviceInfo) {
329         StringBuffer JavaDoc fullAdviceMethodName = new StringBuffer JavaDoc();
330         fullAdviceMethodName.append(ajAdviceInfo.adviceMethodName).append('(');
331
332         // FIXME support args() target() and this()
333
// for (int i = 0; i < ajAdviceInfo.parameterTypes.length; i++) {
334
// String type = ajAdviceInfo.parameterTypes[i];
335
// fullAdviceMethodName.append(type).append(" arg").append(i);
336
// if (i < ajAdviceInfo.parameterTypes.length - 1) {
337
// fullAdviceMethodName.append(',');
338
// }
339
// }
340
fullAdviceMethodName.append(')');
341         return fullAdviceMethodName.toString();
342     }
343
344     /**
345      * Returns the around advice infos.
346      *
347      * @param javaClass
348      * @return
349      */

350     private List getAroundAdviceInfo(final JavaClass javaClass) {
351         List advice = new ArrayList();
352         Method[] methods = javaClass.getMethods();
353         for (int i = 0; i < methods.length; i++) {
354             Method method = methods[i];
355             String JavaDoc adviceName = method.getName();
356             if (adviceName.startsWith(PREFIX_AROUND_ADVICE) && !adviceName.endsWith(PROCEED_SUFFIX)) {
357                 advice.add(createAdviceInfo(javaClass, AdviceType.AROUND, method));
358             }
359         }
360         return advice;
361     }
362
363     /**
364      * Returns the before advice infos.
365      *
366      * @param javaClass
367      * @return
368      */

369     private List getBeforeAdviceInfo(final JavaClass javaClass) {
370         List advice = new ArrayList();
371         Method[] methods = javaClass.getMethods();
372         for (int i = 0; i < methods.length; i++) {
373             Method method = methods[i];
374             String JavaDoc adviceName = method.getName();
375             if (adviceName.startsWith(PREFIX_BEFORE_ADVICE)) {
376                 advice.add(createAdviceInfo(javaClass, AdviceType.BEFORE, method));
377             }
378         }
379         return advice;
380     }
381
382     /**
383      * Returns the around advice infos.
384      *
385      * @param javaClass
386      * @return
387      */

388     private List getAfterFinallyAdviceInfo(final JavaClass javaClass) {
389         List advice = new ArrayList();
390         Method[] methods = javaClass.getMethods();
391         for (int i = 0; i < methods.length; i++) {
392             Method method = methods[i];
393             String JavaDoc adviceName = method.getName();
394             if (adviceName.startsWith(PREFIX_AFTER_FINALLY_ADVICE)) {
395                 advice.add(createAdviceInfo(javaClass, AdviceType.AFTER_FINALLY, method));
396             }
397         }
398         return advice;
399     }
400
401     /**
402      * Returns the after throwing advice infos.
403      *
404      * @param javaClass
405      * @return
406      */

407     private List getAfterThrowingAdviceInfo(final JavaClass javaClass) {
408         List advice = new ArrayList();
409         Method[] methods = javaClass.getMethods();
410         for (int i = 0; i < methods.length; i++) {
411             Method method = methods[i];
412             String JavaDoc adviceName = method.getName();
413             if (adviceName.startsWith(PREFIX_AFTER_THROWING_ADVICE)) {
414                 advice.add(createAdviceInfo(javaClass, AdviceType.AFTER_THROWING, method));
415             }
416         }
417         return advice;
418     }
419
420     /**
421      * Returns the after returning advice infos.
422      *
423      * @param javaClass
424      * @return
425      */

426     private List getAfterReturningAdviceInfo(final JavaClass javaClass) {
427         List advice = new ArrayList();
428         Method[] methods = javaClass.getMethods();
429         for (int i = 0; i < methods.length; i++) {
430             Method method = methods[i];
431             String JavaDoc adviceName = method.getName();
432             if (adviceName.startsWith(PREFIX_AFTER_RETURNING_ADVICE)) {
433                 advice.add(createAdviceInfo(javaClass, AdviceType.AFTER_RETURNING, method));
434             }
435         }
436         return advice;
437     }
438
439     /**
440      * Creates advice info for the advice method.
441      *
442      * @param adviceType
443      * @param method
444      * @return
445      */

446     private AdviceInfo createAdviceInfo(final JavaClass javaClassfinal,
447                                         final AdviceType adviceType,
448                                         final Method method) {
449         Attribute[] attributes = method.getAttributes();
450         List ajAttributes = readAjAttributes(attributes, null);
451         AdviceInfo adviceInfo = new AdviceInfo();
452         for (Iterator it = ajAttributes.iterator(); it.hasNext();) {
453             AjAttribute attr = (AjAttribute) it.next();
454             if (attr instanceof AjAttribute.AdviceAttribute) {
455                 AjAttribute.AdviceAttribute adviceAttr = (AjAttribute.AdviceAttribute) attr;
456                 adviceInfo.type = adviceType;
457                 adviceInfo.aspectClassName = javaClassfinal.getClassName().replace('.', '/');
458                 adviceInfo.adviceMethodName = method.getName();
459                 String JavaDoc pointcut = adviceAttr.getPointcut().toString();
460                 if (pointcut.startsWith("execution(") ||
461                         pointcut.startsWith("call(") ||
462                         pointcut.startsWith("set(") ||
463                         pointcut.startsWith("get(") ||
464                         pointcut.startsWith("handler(") ||
465                         pointcut.startsWith("adviceexecution(") ||
466                         pointcut.startsWith("within(") ||
467                         pointcut.startsWith("withincode(") ||
468                         pointcut.startsWith("cflow(") ||
469                         pointcut.startsWith("cflowbelow(") ||
470                         pointcut.startsWith("if(") ||
471                         pointcut.startsWith("this(") ||
472                         pointcut.startsWith("target(") ||
473                         pointcut.startsWith("args(") ||
474                         pointcut.startsWith("initialization(") ||
475                         pointcut.startsWith("staticinitialization(") ||
476                         pointcut.startsWith("preinitialization(")) {
477                     adviceInfo.pointcut = pointcut;
478                 } else if (pointcut.endsWith("()")) {
479                     adviceInfo.pointcut = pointcut.substring(0, pointcut.length() - 2);
480                 } else {
481                     throw new DefinitionException("pointcuts of type [" + pointcut + " are not yet supported");
482                 }
483                 adviceInfo.extraParameterFlags = adviceAttr.getExtraParameterFlags();
484                 int nrArgs = method.getArgumentTypes().length;
485                 String JavaDoc[] parameterTypes = new String JavaDoc[nrArgs];
486                 for (int i = 0; i < nrArgs; i++) {
487                     Type type = method.getArgumentTypes()[i];
488                     parameterTypes[i] = type.toString().replace('.', '/');
489                 }
490                 adviceInfo.parameterTypes = parameterTypes;
491             }
492         }
493         return adviceInfo;
494     }
495
496     /**
497      * Returns the aspect attributes.
498      *
499      * @param javaClass
500      * @return
501      */

502     private List getAspectAttributes(final JavaClass javaClass) {
503         return readAjAttributes(javaClass.getAttributes(), null);
504     }
505
506     /**
507      * Reads in the AjAttributes from the bytecode.
508      *
509      * @param attrs
510      * @param context
511      * @return
512      */

513     private static List readAjAttributes(final Attribute[] attrs, final ISourceContext context) {
514         List ajAttrs = new ArrayList();
515         for (int i = attrs.length - 1; i >= 0; i--) {
516             Attribute a = attrs[i];
517             if (a instanceof Unknown) {
518                 Unknown u = (Unknown) a;
519                 String JavaDoc name = u.getName();
520                 if (name.startsWith(AjAttribute.AttributePrefix)) {
521                     ajAttrs.add(AjAttribute.read(name, u.getBytes(), context));
522                 }
523             }
524         }
525         return ajAttrs;
526     }
527 }
528
Popular Tags