View Javadoc

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.joinpoint.management;
9   
10  import org.codehaus.aspectwerkz.AspectSystem;
11  import org.codehaus.aspectwerkz.SystemLoader;
12  import org.codehaus.aspectwerkz.expression.CflowExpressionVisitorRuntime;
13  import org.codehaus.aspectwerkz.expression.ExpressionContext;
14  import org.codehaus.aspectwerkz.expression.PointcutType;
15  import org.codehaus.aspectwerkz.joinpoint.FieldSignature;
16  import org.codehaus.aspectwerkz.joinpoint.JoinPoint;
17  import org.codehaus.aspectwerkz.joinpoint.Rtti;
18  import org.codehaus.aspectwerkz.joinpoint.StaticJoinPoint;
19  import org.codehaus.aspectwerkz.joinpoint.impl.ConstructorRttiImpl;
20  import org.codehaus.aspectwerkz.joinpoint.impl.ConstructorSignatureImpl;
21  import org.codehaus.aspectwerkz.joinpoint.impl.FieldRttiImpl;
22  import org.codehaus.aspectwerkz.joinpoint.impl.MethodRttiImpl;
23  import org.codehaus.aspectwerkz.joinpoint.impl.MethodSignatureImpl;
24  
25  import java.io.ObjectInputStream;
26  import java.io.Serializable;
27  import java.lang.reflect.Constructor;
28  import java.lang.reflect.Field;
29  import java.lang.reflect.InvocationTargetException;
30  import java.lang.reflect.Method;
31  import java.util.HashMap;
32  import java.util.Iterator;
33  import java.util.List;
34  import java.util.Map;
35  
36  /***
37   * Base class for the join point implementations.
38   *
39   * @author <a href="mailto:jboner@codehaus.org">Jonas Bonér </a>
40   * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
41   */
42  public abstract class JoinPointBase implements JoinPoint, Serializable {
43      protected Class m_targetClass;
44  
45      protected int m_type;
46  
47      protected String m_typeAsString;
48  
49      protected transient AspectSystem m_system;
50  
51      protected boolean m_checkCflow;
52  
53      protected AroundAdviceExecutor m_aroundAdviceExecutor;
54  
55      protected BeforeAdviceExecutor m_beforeAdviceExecutor;
56  
57      protected AfterAdviceExecutor m_afterAdviceExecutor;
58  
59      //protected transient WeakReference m_targetInstanceRef;//AW-265
60  
61      protected Map m_metaData = new HashMap();
62  
63      protected PointcutType m_pointcutType;
64  
65      protected transient JoinPointMetaData m_joinPointMetaData;
66  
67      /***
68       * Creates a new join point base instance.
69       *
70       * @param type
71       * @param targetClass
72       * @param joinPointMetaData
73       * @param aroundAdviceExecutor
74       * @param beforeAdviceExecutor
75       * @param afterAdviceExecutor
76       */
77      public JoinPointBase(
78              final int type,
79              final Class targetClass,
80              final JoinPointMetaData joinPointMetaData,
81              final AroundAdviceExecutor aroundAdviceExecutor,
82              final BeforeAdviceExecutor beforeAdviceExecutor,
83              final AfterAdviceExecutor afterAdviceExecutor) {
84          m_type = type;
85          m_typeAsString = getJoinPointTypeAsString(type);
86          m_pointcutType = getPointcutType(type);
87          m_targetClass = targetClass;
88          m_checkCflow = joinPointMetaData.cflowExpressions.size() > 0;
89          m_joinPointMetaData = joinPointMetaData;
90          m_aroundAdviceExecutor = aroundAdviceExecutor;
91          m_beforeAdviceExecutor = beforeAdviceExecutor;
92          m_afterAdviceExecutor = afterAdviceExecutor;
93          m_system = SystemLoader.getSystem(targetClass.getClassLoader());
94      }
95  
96      /***
97       * Creates a new join point base instance.
98       *
99       * @param uuid
100      * @param type
101      * @param targetClass
102      * @param joinPointMetaData
103      * @param aroundAdviceExecutor
104      * @param beforeAdviceExecutor
105      * @param afterAdviceExecutor
106      */
107     public JoinPointBase(
108             final String uuid,
109             final int type,
110             final Class targetClass,
111             final JoinPointMetaData joinPointMetaData,
112             final AroundAdviceExecutor aroundAdviceExecutor,
113             final BeforeAdviceExecutor beforeAdviceExecutor,
114             final AfterAdviceExecutor afterAdviceExecutor) {
115         this(type, targetClass, joinPointMetaData, aroundAdviceExecutor, beforeAdviceExecutor, afterAdviceExecutor);
116     }
117 
118     /***
119      * Creates a new join point base instance.
120      *
121      * @param uuid
122      * @param type
123      * @param targetClass
124      * @param cflow
125      * @param ctx
126      * @param aroundAdviceExecutor
127      * @param beforeAdviceExecutor
128      * @param afterAdviceExecutor
129      */
130     public JoinPointBase(
131             final String uuid,
132             final int type,
133             final Class targetClass,
134             final List cflow,
135             final ExpressionContext ctx,
136             final AroundAdviceExecutor aroundAdviceExecutor,
137             final BeforeAdviceExecutor beforeAdviceExecutor,
138             final AfterAdviceExecutor afterAdviceExecutor) {
139         //TODO clean me
140         m_type = type;
141         m_typeAsString = getJoinPointTypeAsString(type);
142         m_pointcutType = getPointcutType(type);
143         m_targetClass = targetClass;
144         m_joinPointMetaData = new JoinPointMetaData();
145         m_joinPointMetaData.expressionContext = ctx;
146         m_joinPointMetaData.cflowExpressions = cflow;
147         m_checkCflow = m_joinPointMetaData.cflowExpressions.size() > 0;
148         m_aroundAdviceExecutor = aroundAdviceExecutor;
149         m_beforeAdviceExecutor = beforeAdviceExecutor;
150         m_afterAdviceExecutor = afterAdviceExecutor;
151         m_system = SystemLoader.getSystem(targetClass.getClassLoader());
152     }
153 
154     /***
155      * Clones the join point instance.
156      *
157      * @return
158      */
159     public StaticJoinPoint deepCopy() {
160         throw new UnsupportedOperationException("deepCopy() not supported in 1.0");
161     }
162 
163     /***
164      * Resets the join point. <p/>Will restart the execution chain of advice.
165      */
166     public void reset() {
167         m_aroundAdviceExecutor.reset();
168     }
169 
170     /***
171      * Returns metadata matching a specfic key.
172      *
173      * @param key the key to the metadata
174      * @return the value
175      */
176     public Object getMetaData(final Object key) {
177         return m_metaData.get(key);
178     }
179 
180     /***
181      * Adds metadata.
182      *
183      * @param key   the key to the metadata
184      * @param value the value
185      */
186     public void addMetaData(final Object key, final Object value) {
187         m_metaData.put(key, value);
188     }
189 
190     /***
191      * Returns the callee instance.
192      *
193      * @return the callee instance
194      */
195     public Object getCallee() {
196         throw new UnsupportedOperationException("getCallee() not supported in 1.0");
197     }
198 
199     /***
200      * Returns the caller instance.
201      *
202      * @return the caller instance
203      */
204     public Object getCaller() {
205         throw new UnsupportedOperationException("getCaller() not supported in 1.0");
206     }
207 
208     /***
209      * Returns the 'this' instance (the one currently executing).
210      *
211      * @return 'this'
212      */
213     public Object getThis() {
214         throw new UnsupportedOperationException("getThis() not supported in 1.0");
215     }
216 
217     /***
218      * Returns the caller class.
219      *
220      * @return the caller class
221      */
222     public Class getCallerClass() {
223         throw new UnsupportedOperationException("getCallerClass() not supported in 1.0");
224     }
225 
226     /***
227      * Invokes the original method - execution context.
228      *
229      * @param joinPoint the join point instance
230      * @return the result from the method invocation
231      * @throws Throwable the exception from the original method
232      */
233     public static Object invokeTargetMethodExecution(final JoinPoint joinPoint) throws Throwable {
234         MethodSignatureImpl signature = (MethodSignatureImpl)joinPoint.getSignature();
235         MethodRttiImpl rtti = (MethodRttiImpl)joinPoint.getRtti();
236         Method targetMethod = signature.getMethodTuple().getOriginalMethod();
237         Object[] parameterValues = rtti.getParameterValues();
238         Object targetInstance = joinPoint.getTarget();
239         try {
240             return targetMethod.invoke(targetInstance, parameterValues);
241         } catch (InvocationTargetException e) {
242             throw e.getTargetException();
243         }
244     }
245 
246     /***
247      * Invokes the original method - call context.
248      *
249      * @param joinPoint the join point instance
250      * @return the result from the method invocation
251      * @throws Throwable the exception from the original method
252      */
253     public static Object invokeTargetMethodCall(final JoinPoint joinPoint) throws Throwable {
254         MethodSignatureImpl signature = (MethodSignatureImpl)joinPoint.getSignature();
255         MethodRttiImpl rtti = (MethodRttiImpl)joinPoint.getRtti();
256         Method targetMethod = signature.getMethodTuple().getWrapperMethod();
257         Object[] parameterValues = rtti.getParameterValues();
258         Object targetInstance = joinPoint.getTarget();
259         try {
260             return targetMethod.invoke(targetInstance, parameterValues);
261         } catch (InvocationTargetException e) {
262             throw e.getTargetException();
263         }
264     }
265 
266     /***
267      * Invokes the prefixed constructor.
268      *
269      * @param joinPoint the join point instance
270      * @return the newly created instance
271      * @throws Throwable the exception from the original constructor
272      */
273     public static Object invokeTargetConstructorExecution(final JoinPoint joinPoint) throws Throwable {
274         ConstructorSignatureImpl signature = (ConstructorSignatureImpl)joinPoint.getSignature();
275         ConstructorRttiImpl rtti = (ConstructorRttiImpl)joinPoint.getRtti();
276         Constructor targetConstructor = signature.getConstructorTuple().getOriginalConstructor();
277         Object[] parameterValues = rtti.getParameterValues();
278         int length = parameterValues.length;
279         Object[] fakeParameterValues = new Object[length + 1];
280         java.lang.System.arraycopy(parameterValues, 0, fakeParameterValues, 0, length);
281         fakeParameterValues[length] = null;
282         try {
283             return targetConstructor.newInstance(fakeParameterValues);
284         } catch (InvocationTargetException e) {
285             throw e.getTargetException();
286         }
287     }
288 
289     /***
290      * Invokes the original constructor.
291      *
292      * @param joinPoint the join point instance
293      * @return the newly created instance
294      * @throws Throwable the exception from the original constructor TODO: FIX BUG - When a constructor has both a CALL
295      *                   and EXECUTION join point, only the CALL will be executed, redirecting to the wrapper
296      *                   constructor
297      */
298     public static Object invokeTargetConstructorCall(final JoinPoint joinPoint) throws Throwable {
299         ConstructorSignatureImpl signature = (ConstructorSignatureImpl)joinPoint.getSignature();
300         ConstructorRttiImpl rtti = (ConstructorRttiImpl)joinPoint.getRtti();
301         Object[] parameterValues = rtti.getParameterValues();
302         Constructor wrapperConstructor = signature.getConstructorTuple().getWrapperConstructor();
303         Constructor originalConstructor = signature.getConstructorTuple().getOriginalConstructor();
304         if (originalConstructor.equals(wrapperConstructor)) {
305             try {
306                 return wrapperConstructor.newInstance(parameterValues);
307             } catch (InvocationTargetException e) {
308                 throw e.getTargetException();
309             }
310         } else {
311             java.lang.System.err
312                     .println(
313                             "WARNING: When a constructor has both a CALL and EXECUTION join point, only the CALL will be executed. This limitation is due to a bug that has currently not been fixed yet."
314                     );
315             Object[] parameters = new Object[parameterValues.length + 1];
316             for (int i = 0; i < parameterValues.length; i++) {
317                 parameters[i] = parameterValues[i];
318             }
319             try {
320                 return originalConstructor.newInstance(parameters);
321             } catch (InvocationTargetException e) {
322                 throw e.getTargetException();
323             }
324         }
325     }
326 
327     /***
328      * Sets the target field.
329      *
330      * @param joinPoint the join point instance
331      * @throws Throwable the exception from the original method
332      */
333     public static void setTargetField(final JoinPoint joinPoint) throws Throwable {
334         FieldSignature signature = (FieldSignature)joinPoint.getSignature();
335         FieldRttiImpl rtti = (FieldRttiImpl)joinPoint.getRtti();
336         Field targetField = signature.getField();
337         Object fieldValue = rtti.getFieldValue();
338         Object targetInstance = joinPoint.getTarget();
339         targetField.set(targetInstance, fieldValue);
340     }
341 
342     /***
343      * Gets the target field.
344      *
345      * @param joinPoint the join point instance
346      * @return the target field
347      * @throws Throwable the exception from the original method
348      */
349     public static Object getTargetField(final JoinPoint joinPoint) throws Throwable {
350         FieldSignature signature = (FieldSignature)joinPoint.getSignature();
351         Field targetField = signature.getField();
352         Object targetInstance = joinPoint.getTarget();
353         return targetField.get(targetInstance);
354     }
355 
356     /***
357      * Sets the join point type to a string representation.
358      *
359      * @param type the type
360      */
361     public static String getJoinPointTypeAsString(final int type) {
362         switch (type) {
363             case JoinPointType.METHOD_EXECUTION:
364                 return JoinPoint.METHOD_EXECUTION;
365             case JoinPointType.METHOD_CALL:
366                 return JoinPoint.METHOD_CALL;
367             case JoinPointType.CONSTRUCTOR_EXECUTION:
368                 return JoinPoint.CONSTRUCTOR_EXECUTION;
369             case JoinPointType.CONSTRUCTOR_CALL:
370                 return JoinPoint.CONSTRUCTOR_CALL;
371             case JoinPointType.FIELD_SET:
372                 return JoinPoint.FIELD_SET;
373             case JoinPointType.FIELD_GET:
374                 return JoinPoint.FIELD_GET;
375             case JoinPointType.HANDLER:
376                 return JoinPoint.HANDLER;
377             case JoinPointType.STATIC_INITALIZATION:
378                 return JoinPoint.STATIC_INITIALIZATION;
379             default:
380                 throw new RuntimeException("join point type [" + type + "] is not a valid type");
381         }
382     }
383 
384     /***
385      * Sets the join point type to a string representation.
386      *
387      * @param type the type
388      */
389     public static PointcutType getPointcutType(final int type) {
390         switch (type) {
391             case JoinPointType.METHOD_EXECUTION:
392                 return PointcutType.EXECUTION;
393             case JoinPointType.METHOD_CALL:
394                 return PointcutType.CALL;
395             case JoinPointType.CONSTRUCTOR_EXECUTION:
396                 return PointcutType.EXECUTION;
397             case JoinPointType.CONSTRUCTOR_CALL:
398                 return PointcutType.CALL;
399             case JoinPointType.FIELD_SET:
400                 return PointcutType.SET;
401             case JoinPointType.FIELD_GET:
402                 return PointcutType.GET;
403             case JoinPointType.HANDLER:
404                 return PointcutType.HANDLER;
405             case JoinPointType.STATIC_INITALIZATION:
406                 return PointcutType.STATIC_INITIALIZATION;
407             default:
408                 throw new RuntimeException("join point type [" + type + "] is not a valid type");
409         }
410     }
411 
412     /***
413      * Invoke the join point.
414      *
415      * @param joinPoint the join point instance
416      * @return the result from the invocation
417      * @throws Throwable
418      */
419     public static Object invokeJoinPoint(final JoinPoint joinPoint, final int joinPointType) throws Throwable {
420         Object result = null;
421         switch (joinPointType) {
422             case JoinPointType.METHOD_EXECUTION:
423                 result = invokeTargetMethodExecution(joinPoint);
424                 break;
425             case JoinPointType.METHOD_CALL:
426                 result = invokeTargetMethodCall(joinPoint);
427                 break;
428             case JoinPointType.CONSTRUCTOR_EXECUTION:
429                 result = invokeTargetConstructorExecution(joinPoint);
430                 break;
431             case JoinPointType.CONSTRUCTOR_CALL:
432                 result = invokeTargetConstructorCall(joinPoint);
433                 break;
434             case JoinPointType.FIELD_SET:
435                 // for field set join point, the returned value from around advice is ignored
436                 setTargetField(joinPoint);
437                 break;
438             case JoinPointType.FIELD_GET:
439                 result = getTargetField(joinPoint);
440                 break;
441         }
442         return result;
443     }
444 
445     /***
446      * Returns the target instance ('this'). If the join point is executing in a static context it returns null.
447      *
448      * @return the target instance
449      */
450     public Object getTarget() {
451         return getRtti().getTarget();//AW-265
452     }
453 
454     /***
455      * Returns the target class.
456      *
457      * @return the target class
458      */
459     public Class getTargetClass() {
460         return m_targetClass;
461     }
462 
463     /***
464      * Returns the join point type.
465      *
466      * @return the type
467      */
468     public String getType() {
469         return m_typeAsString;
470     }
471 
472     /***
473      * Sets the target instance.
474      * 
475      * @param targetInstance the target instance
476      */
477     //AW-265
478 //    public void setTarget(final Object targetInstance) {
479 //        m_targetInstanceRef = new WeakReference(targetInstance);
480 //    }
481 
482     /***
483      * Checks if the join point is in the correct control flow.
484      *
485      * @return true if we have a parse
486      */
487     public boolean isInCflow() {
488         if (m_checkCflow) {
489             boolean isInCFlow = false;
490             for (Iterator iterator = m_joinPointMetaData.cflowExpressions.iterator(); iterator.hasNext();) {
491                 CflowExpressionVisitorRuntime cflowExpressionRuntime = (CflowExpressionVisitorRuntime)iterator.next();
492                 if (m_system.isInControlFlowOf(cflowExpressionRuntime, m_joinPointMetaData.expressionContext)) {
493                     isInCFlow = true;
494                     break;
495                 }
496             }
497             if (!isInCFlow) {
498                 return false;
499             }
500         }
501         return true;
502     }
503 
504     public String toString() {
505         StringBuffer sb = new StringBuffer(30);
506         sb.append(getJoinPointTypeAsString(m_type));
507         sb.append('.').append(m_targetClass.getName());
508         sb.append('.').append(getSignature().getName());
509         return sb.toString();
510     }
511 
512     /***
513      * Provides custom deserialization.
514      *
515      * @param stream the object input stream containing the serialized object
516      * @throws Exception in case of failure
517      * @TODO: for this to work it requires that the instance is read from the same CL that it was written in
518      * @TODO: target instance is not read in
519      */
520     private void readObject(final ObjectInputStream stream) throws Exception {
521         ObjectInputStream.GetField fields = stream.readFields();
522         m_type = fields.get("m_type", 0);
523         m_typeAsString = getJoinPointTypeAsString(m_type);
524         m_targetClass = (Class)fields.get("m_targetClass", null);
525         m_checkCflow = m_joinPointMetaData.cflowExpressions.size() > 0;
526         m_aroundAdviceExecutor = (AroundAdviceExecutor)fields.get("m_aroundAdviceExecutor", null);
527         m_beforeAdviceExecutor = (BeforeAdviceExecutor)fields.get("m_beforeAdviceExecutor", null);
528         m_afterAdviceExecutor = (AfterAdviceExecutor)fields.get("m_afterAdviceExecutor", null);
529         m_metaData = (Map)fields.get("m_metaData", new HashMap());
530         m_system = SystemLoader.getSystem(m_targetClass.getClassLoader());
531         m_system.initialize();
532     }
533 
534     /***
535      * Extracts a subset of the joinPoint instance RTTI arguments. This is used to support args() syntax. This method is
536      * not exposed in the JoinPoint interface since user does not need it.
537      *
538      * @param methodToArgIndexes
539      * @return
540      */
541     public abstract Object[] extractArguments(int[] methodToArgIndexes);
542 
543     /***
544      * Allows to pass the RTTI to the JP. The JPBase implementation delegates getTarget to the RTTI. Since in 1.0
545      * engine, JP are cached and shared, while the RTTI is not, we need to set the RTTI (AW-265). This method MUST not
546      * be called by the user.
547      *
548      * @param rtti
549      */
550     protected abstract void setRtti(Rtti rtti);
551 }