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
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
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
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();
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
478
479
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 }