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.intercept;
9
10 import java.util.ArrayList;
11 import java.util.List;
12 import java.lang.reflect.Field;
13
14 import gnu.trove.TIntObjectHashMap;
15 import org.codehaus.aspectwerkz.reflect.ClassInfo;
16 import org.codehaus.aspectwerkz.reflect.ReflectionInfo;
17 import org.codehaus.aspectwerkz.reflect.impl.asm.AsmClassInfo;
18 import org.codehaus.aspectwerkz.reflect.impl.java.JavaClassInfo;
19 import org.codehaus.aspectwerkz.expression.PointcutType;
20 import org.codehaus.aspectwerkz.expression.ExpressionContext;
21 import org.codehaus.aspectwerkz.expression.ExpressionInfo;
22 import org.codehaus.aspectwerkz.transform.inlining.AsmHelper;
23 import org.codehaus.aspectwerkz.transform.inlining.EmittedJoinPoint;
24 import org.codehaus.aspectwerkz.transform.TransformationConstants;
25 import org.codehaus.aspectwerkz.joinpoint.management.JoinPointType;
26
27 /***
28 * Implementation of the <code>Advisable</code> mixin.
29 *
30 * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
31 * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
32 */
33 public class AdvisableImpl implements Advisable {
34
35 private static final String EXPRESSION_NAMESPACE = "___AW_ADVISABLE_AW___";
36
37 public static final ClassInfo CLASS_INFO;
38 public static final AroundAdvice[] EMPTY_AROUND_ADVICE_ARRAY = new AroundAdvice[0];
39 public static final BeforeAdvice[] EMPTY_BEFORE_ADVICE_ARRAY = new BeforeAdvice[0];
40 public static final AfterAdvice[] EMPTY_AFTER_ADVICE_ARRAY = new AfterAdvice[0];
41 public static final AfterReturningAdvice[] EMPTY_AFTER_RETURNING_ADVICE_ARRAY = new AfterReturningAdvice[0];
42 public static final AfterThrowingAdvice[] EMPTY_AFTER_THROWING_ADVICE_ARRAY = new AfterThrowingAdvice[0];
43
44 static {
45 final Class clazz = AdvisableImpl.class;
46 try {
47 CLASS_INFO = AsmClassInfo.getClassInfo(clazz.getName(), clazz.getClassLoader());
48 } catch (Exception e) {
49 throw new Error("could not create class info for [" + clazz.getName() + ']');
50 }
51 }
52
53 private final Advisable m_targetInstance;
54 private final TIntObjectHashMap m_emittedJoinPoints;
55
56 private final TIntObjectHashMap m_aroundAdvice = new TIntObjectHashMap();
57 private final TIntObjectHashMap m_beforeAdvice = new TIntObjectHashMap();
58 private final TIntObjectHashMap m_afterAdvice = new TIntObjectHashMap();
59 private final TIntObjectHashMap m_afterReturningAdvice = new TIntObjectHashMap();
60 private final TIntObjectHashMap m_afterThrowingAdvice = new TIntObjectHashMap();
61
62 /***
63 * Creates a new mixin impl.
64 *
65 * @param targetInstance the target for this mixin instance (perInstance deployed)
66 */
67 public AdvisableImpl(final Object targetInstance) {
68 if (!(targetInstance instanceof Advisable)) {
69 throw new RuntimeException(
70 "advisable mixin applied to target class that does not implement the Advisable interface"
71 );
72 }
73 m_targetInstance = (Advisable) targetInstance;
74
75 try {
76 Field f = targetInstance.getClass().getDeclaredField("aw$emittedJoinPoints");
77 f.setAccessible(true);
78 m_emittedJoinPoints = (TIntObjectHashMap) f.get(null);
79 } catch (Exception e) {
80 throw new RuntimeException(
81 "advisable mixin applied to target class cannot access reflective information: " + e.toString()
82 );
83 }
84 }
85
86 /***
87 * @param pointcut
88 * @param advice
89 */
90 public void aw_addAdvice(final String pointcut, final Advice advice) {
91 addAdvice(pointcut, advice);
92 }
93
94 /***
95 * @param pointcut
96 * @param adviceClass
97 */
98 public void aw_removeAdvice(final String pointcut, final Class adviceClass) {
99 removeAdvice(pointcut, adviceClass);
100 }
101
102 /***
103 * @param joinPointIndex
104 * @return
105 */
106 public AroundAdvice[] aw$getAroundAdvice(final int joinPointIndex) {
107 Object advice = m_aroundAdvice.get(joinPointIndex);
108 if (advice == null) {
109 return EMPTY_AROUND_ADVICE_ARRAY;
110 } else {
111 return (AroundAdvice[]) advice;
112 }
113 }
114
115 /***
116 * @param joinPointIndex
117 * @return
118 */
119 public BeforeAdvice[] aw$getBeforeAdvice(final int joinPointIndex) {
120 Object advice = m_beforeAdvice.get(joinPointIndex);
121 if (advice == null) {
122 return EMPTY_BEFORE_ADVICE_ARRAY;
123 } else {
124 return (BeforeAdvice[]) advice;
125 }
126 }
127
128 /***
129 * @param joinPointIndex
130 * @return
131 */
132 public AfterAdvice[] aw$getAfterAdvice(final int joinPointIndex) {
133 Object advice = m_afterAdvice.get(joinPointIndex);
134 if (advice == null) {
135 return EMPTY_AFTER_ADVICE_ARRAY;
136 } else {
137 return (AfterAdvice[]) advice;
138 }
139 }
140
141 /***
142 * @param joinPointIndex
143 * @return
144 */
145 public AfterReturningAdvice[] aw$getAfterReturningAdvice(final int joinPointIndex) {
146 Object advice = m_afterReturningAdvice.get(joinPointIndex);
147 if (advice == null) {
148 return EMPTY_AFTER_RETURNING_ADVICE_ARRAY;
149 } else {
150 return (AfterReturningAdvice[]) advice;
151 }
152 }
153
154 /***
155 * @param joinPointIndex
156 * @return
157 */
158 public AfterThrowingAdvice[] aw$getAfterThrowingAdvice(final int joinPointIndex) {
159 Object advice = m_afterThrowingAdvice.get(joinPointIndex);
160 if (advice == null) {
161 return EMPTY_AFTER_THROWING_ADVICE_ARRAY;
162 } else {
163 return (AfterThrowingAdvice[]) advice;
164 }
165 }
166
167 /***
168 * @param pointcut
169 * @param advice
170 */
171 private void addAdvice(final String pointcut,
172 final Advice advice) {
173 ExpressionInfo expressionInfo = new ExpressionInfo(pointcut, EXPRESSION_NAMESPACE);
174 Object[] emittedJoinPoints = m_emittedJoinPoints.getValues();
175 for (int i = 0; i < emittedJoinPoints.length; i++) {
176 EmittedJoinPoint emittedJoinPoint = (EmittedJoinPoint) emittedJoinPoints[i];
177 if (match(expressionInfo, PointcutType.EXECUTION, emittedJoinPoint)
178 || match(expressionInfo, PointcutType.CALL, emittedJoinPoint)
179 || match(expressionInfo, PointcutType.HANDLER, emittedJoinPoint)
180 || match(expressionInfo, PointcutType.GET, emittedJoinPoint)
181 || match(expressionInfo, PointcutType.SET, emittedJoinPoint)
182
183 ) {
184 int hash = emittedJoinPoint.getJoinPointClassName().hashCode();
185 addAroundAdvice(advice, hash);
186 addBeforeAdvice(advice, hash);
187 addAfterAdvice(advice, hash);
188 addAfterReturningAdvice(advice, hash);
189 addAfterThrowingAdvice(advice, hash);
190 }
191 }
192 }
193
194 /***
195 * @param pointcut
196 * @param adviceClass
197 */
198 private void removeAdvice(final String pointcut,
199 final Class adviceClass) {
200 ExpressionInfo expressionInfo = new ExpressionInfo(pointcut, EXPRESSION_NAMESPACE);
201 Object[] emittedJoinPoints = m_emittedJoinPoints.getValues();
202 for (int i = 0; i < emittedJoinPoints.length; i++) {
203 EmittedJoinPoint emittedJoinPoint = (EmittedJoinPoint) emittedJoinPoints[i];
204 if (match(expressionInfo, PointcutType.EXECUTION, emittedJoinPoint)
205 || match(expressionInfo, PointcutType.CALL, emittedJoinPoint)
206 || match(expressionInfo, PointcutType.HANDLER, emittedJoinPoint)
207 || match(expressionInfo, PointcutType.GET, emittedJoinPoint)
208 || match(expressionInfo, PointcutType.SET, emittedJoinPoint)
209
210 ) {
211 int hash = emittedJoinPoint.getJoinPointClassName().hashCode();
212 removeAroundAdvice(adviceClass, hash);
213 removeBeforeAdvice(adviceClass, hash);
214 removeAfterAdvice(adviceClass, hash);
215 removeAfterReturningAdvice(adviceClass, hash);
216 removeAfterThrowingAdvice(adviceClass, hash);
217 }
218 }
219 }
220
221 /***
222 * @param advice
223 * @param joinPointHash
224 */
225 private void addAroundAdvice(final Advice advice, int joinPointHash) {
226 if (advice instanceof AroundAdvice) {
227 AroundAdvice aroundAdvice = (AroundAdvice) advice;
228 AroundAdvice[] advices;
229 AroundAdvice[] olds = aw$getAroundAdvice(joinPointHash);
230 if (olds != null) {
231 advices = new AroundAdvice[olds.length + 1];
232 System.arraycopy(olds, 0, advices, 0, olds.length);
233 advices[advices.length - 1] = aroundAdvice;
234 } else {
235 advices = new AroundAdvice[]{aroundAdvice};
236 }
237 m_aroundAdvice.put(joinPointHash, advices);
238 }
239 }
240
241 /***
242 * @param advice
243 * @param joinPointHash
244 */
245 private void addBeforeAdvice(final Advice advice, int joinPointHash) {
246 if (advice instanceof BeforeAdvice) {
247 BeforeAdvice beforeAdvice = (BeforeAdvice) advice;
248 BeforeAdvice[] advices;
249 BeforeAdvice[] olds = aw$getBeforeAdvice(joinPointHash);
250 if (olds != null) {
251 advices = new BeforeAdvice[olds.length + 1];
252 System.arraycopy(olds, 0, advices, 0, olds.length);
253 advices[advices.length - 1] = beforeAdvice;
254 } else {
255 advices = new BeforeAdvice[]{beforeAdvice};
256 }
257 m_beforeAdvice.put(joinPointHash, advices);
258 }
259 }
260
261 /***
262 * @param advice
263 * @param joinPointHash
264 */
265 private void addAfterAdvice(final Advice advice, int joinPointHash) {
266 if (advice instanceof AfterAdvice) {
267 AfterAdvice afterFinallyAdvice = (AfterAdvice) advice;
268 AfterAdvice[] advices;
269 AfterAdvice[] olds = aw$getAfterAdvice(joinPointHash);
270 if (olds != null) {
271 advices = new AfterAdvice[olds.length + 1];
272 System.arraycopy(olds, 0, advices, 0, olds.length);
273 advices[advices.length - 1] = afterFinallyAdvice;
274 } else {
275 advices = new AfterAdvice[]{afterFinallyAdvice};
276 }
277 m_afterAdvice.put(joinPointHash, advices);
278 }
279 }
280
281 /***
282 * @param advice
283 * @param joinPointHash
284 */
285 private void addAfterReturningAdvice(final Advice advice, int joinPointHash) {
286 if (advice instanceof AfterReturningAdvice) {
287 AfterReturningAdvice afterReturningAdvice = (AfterReturningAdvice) advice;
288 AfterReturningAdvice[] advices;
289 AfterReturningAdvice[] olds = aw$getAfterReturningAdvice(joinPointHash);
290 if (olds != null) {
291 advices = new AfterReturningAdvice[olds.length + 1];
292 System.arraycopy(olds, 0, advices, 0, olds.length);
293 advices[advices.length - 1] = afterReturningAdvice;
294 } else {
295 advices = new AfterReturningAdvice[]{afterReturningAdvice};
296 }
297 m_afterReturningAdvice.put(joinPointHash, advices);
298 }
299 }
300
301 /***
302 * @param advice
303 * @param joinPointHash
304 */
305 private void addAfterThrowingAdvice(final Advice advice, int joinPointHash) {
306 if (advice instanceof AfterThrowingAdvice) {
307 AfterThrowingAdvice afterThrowingAdvice = (AfterThrowingAdvice) advice;
308 AfterThrowingAdvice[] advices;
309 AfterThrowingAdvice[] olds = aw$getAfterThrowingAdvice(joinPointHash);
310 if (olds != null) {
311 advices = new AfterThrowingAdvice[olds.length + 1];
312 System.arraycopy(olds, 0, advices, 0, olds.length);
313 advices[advices.length - 1] = afterThrowingAdvice;
314 } else {
315 advices = new AfterThrowingAdvice[]{afterThrowingAdvice};
316 }
317 m_afterThrowingAdvice.put(joinPointHash, advices);
318 }
319 }
320
321 /***
322 * @param adviceClass
323 * @param joinPointHash
324 */
325 private void removeAroundAdvice(final Class adviceClass, int joinPointHash) {
326 if (isAroundAdvice(adviceClass)) {
327 AroundAdvice[] oldArray = aw$getAroundAdvice(joinPointHash);
328 if (oldArray.length == 0) {
329 } else if (oldArray.length == 1) {
330 m_aroundAdvice.put(joinPointHash, EMPTY_AROUND_ADVICE_ARRAY);
331 } else {
332 List newArrayList = new ArrayList();
333 for (int i = 0; i < oldArray.length; i++) {
334 AroundAdvice aroundAdvice = oldArray[i];
335 if (!aroundAdvice.getClass().equals(adviceClass)) {
336 newArrayList.add(aroundAdvice);
337 }
338 }
339 m_aroundAdvice.put(
340 joinPointHash,
341 (AroundAdvice[]) newArrayList.toArray(new AroundAdvice[newArrayList.size()])
342 );
343 }
344 }
345 }
346
347 /***
348 * @param adviceClass
349 * @param joinPointHash
350 */
351 private void removeBeforeAdvice(final Class adviceClass, int joinPointHash) {
352 if (isBeforeAdvice(adviceClass)) {
353 BeforeAdvice[] oldArray = aw$getBeforeAdvice(joinPointHash);
354 if (oldArray.length == 0) {
355 } else if (oldArray.length == 1) {
356 m_beforeAdvice.put(joinPointHash, EMPTY_BEFORE_ADVICE_ARRAY);
357 } else {
358 List newArrayList = new ArrayList();
359 for (int i = 0; i < oldArray.length; i++) {
360 BeforeAdvice beforeAdvice = oldArray[i];
361 if (!beforeAdvice.getClass().equals(adviceClass)) {
362 newArrayList.add(beforeAdvice);
363 }
364 }
365 m_beforeAdvice.put(
366 joinPointHash,
367 (BeforeAdvice[]) newArrayList.toArray(new BeforeAdvice[newArrayList.size()])
368 );
369 }
370 }
371 }
372
373 /***
374 * @param adviceClass
375 * @param joinPointHash
376 */
377 private void removeAfterAdvice(final Class adviceClass, int joinPointHash) {
378 if (isAfterAdvice(adviceClass)) {
379 AfterAdvice[] oldArray = aw$getAfterAdvice(joinPointHash);
380 if (oldArray.length == 0) {
381 } else if (oldArray.length == 1) {
382 m_afterAdvice.put(joinPointHash, EMPTY_AFTER_ADVICE_ARRAY);
383 } else {
384 List newArrayList = new ArrayList();
385 for (int i = 0; i < oldArray.length; i++) {
386 AfterAdvice afterAdvice = oldArray[i];
387 if (!afterAdvice.getClass().equals(adviceClass)) {
388 newArrayList.add(afterAdvice);
389 }
390 }
391 m_afterAdvice.put(
392 joinPointHash,
393 (AfterAdvice[]) newArrayList.toArray(new AfterAdvice[newArrayList.size()])
394 );
395 }
396 }
397 }
398
399 /***
400 * @param adviceClass
401 * @param joinPointHash
402 */
403 private void removeAfterReturningAdvice(final Class adviceClass, int joinPointHash) {
404 if (isAfterReturningAdvice(adviceClass)) {
405 AfterReturningAdvice[] oldArray = aw$getAfterReturningAdvice(joinPointHash);
406 if (oldArray.length == 0) {
407 } else if (oldArray.length == 1) {
408 m_afterReturningAdvice.put(joinPointHash, EMPTY_AFTER_RETURNING_ADVICE_ARRAY);
409 } else {
410 List newArrayList = new ArrayList();
411 for (int i = 0; i < oldArray.length; i++) {
412 AfterReturningAdvice afterReturningAdvice = oldArray[i];
413 if (!afterReturningAdvice.getClass().equals(adviceClass)) {
414 newArrayList.add(afterReturningAdvice);
415 }
416 }
417 m_afterReturningAdvice.put(
418 joinPointHash,
419 (AfterReturningAdvice[]) newArrayList.toArray(new AfterReturningAdvice[newArrayList.size()])
420 );
421 }
422 }
423 }
424
425 /***
426 * @param adviceClass
427 * @param joinPointHash
428 */
429 private void removeAfterThrowingAdvice(final Class adviceClass, int joinPointHash) {
430 if (isAfterThrowingAdvice(adviceClass)) {
431 AfterThrowingAdvice[] oldArray = aw$getAfterThrowingAdvice(joinPointHash);
432 if (oldArray.length == 0) {
433 } else if (oldArray.length == 1) {
434 m_afterThrowingAdvice.put(joinPointHash, EMPTY_AFTER_THROWING_ADVICE_ARRAY);
435 } else {
436 List newArrayList = new ArrayList();
437 for (int i = 0; i < oldArray.length; i++) {
438 AfterThrowingAdvice advice = oldArray[i];
439 if (!advice.getClass().equals(adviceClass)) {
440 newArrayList.add(advice);
441 }
442 }
443 m_afterThrowingAdvice.put(
444 joinPointHash,
445 (AfterThrowingAdvice[]) newArrayList.toArray(new AfterThrowingAdvice[newArrayList.size()])
446 );
447 }
448 }
449 }
450
451 private boolean isAroundAdvice(final Class adviceClass) {
452 if (adviceClass == AroundAdvice.class) {
453 return true;
454 }
455 Class[] interfaces = adviceClass.getInterfaces();
456 for (int i = 0; i < interfaces.length; i++) {
457 Class anInterface = interfaces[i];
458 if (anInterface == AroundAdvice.class) {
459 return true;
460 }
461 }
462 return false;
463 }
464
465 private boolean isBeforeAdvice(final Class adviceClass) {
466 if (adviceClass == BeforeAdvice.class) {
467 return true;
468 }
469 Class[] interfaces = adviceClass.getInterfaces();
470 for (int i = 0; i < interfaces.length; i++) {
471 Class anInterface = interfaces[i];
472 if (anInterface == BeforeAdvice.class) {
473 return true;
474 }
475 }
476 return false;
477 }
478
479 private boolean isAfterAdvice(final Class adviceClass) {
480 if (adviceClass == AfterAdvice.class) {
481 return true;
482 }
483 Class[] interfaces = adviceClass.getInterfaces();
484 for (int i = 0; i < interfaces.length; i++) {
485 Class anInterface = interfaces[i];
486 if (anInterface == AfterAdvice.class) {
487 return true;
488 }
489 }
490 return false;
491 }
492
493 private boolean isAfterReturningAdvice(final Class adviceClass) {
494 if (adviceClass == AfterReturningAdvice.class) {
495 return true;
496 }
497 Class[] interfaces = adviceClass.getInterfaces();
498 for (int i = 0; i < interfaces.length; i++) {
499 Class anInterface = interfaces[i];
500 if (anInterface == AfterReturningAdvice.class) {
501 return true;
502 }
503 }
504 return false;
505 }
506
507 private boolean isAfterThrowingAdvice(final Class adviceClass) {
508 if (adviceClass == AfterThrowingAdvice.class) {
509 return true;
510 }
511 Class[] interfaces = adviceClass.getInterfaces();
512 for (int i = 0; i < interfaces.length; i++) {
513 Class anInterface = interfaces[i];
514 if (anInterface == AfterThrowingAdvice.class) {
515 return true;
516 }
517 }
518 return false;
519 }
520
521 /***
522 * Match the given expression for the given pointcut type against the given emittedJoinPoint
523 *
524 * @param expression
525 * @param pointcutType
526 * @param emittedJoinPoint
527 * @return
528 */
529 private boolean match(ExpressionInfo expression, PointcutType pointcutType, EmittedJoinPoint emittedJoinPoint) {
530 ClassInfo callerClassInfo = JavaClassInfo.getClassInfo(m_targetInstance.getClass());
531 ClassInfo calleeClassInfo = AsmClassInfo.getClassInfo(emittedJoinPoint.getCalleeClassName(), m_targetInstance.getClass().getClassLoader());
532
533
534 if (!expression.getAdvisedClassFilterExpression().match(new ExpressionContext(pointcutType, calleeClassInfo, callerClassInfo))) {
535 return false;
536 }
537
538
539 final ReflectionInfo reflectionInfo;
540 final PointcutType joinPointType;
541 switch (emittedJoinPoint.getJoinPointType()) {
542 case JoinPointType.STATIC_INITIALIZATION_INT:
543 reflectionInfo = calleeClassInfo.staticInitializer();
544 joinPointType = PointcutType.STATIC_INITIALIZATION;
545 break;
546 case JoinPointType.METHOD_EXECUTION_INT:
547 reflectionInfo = calleeClassInfo.getMethod(emittedJoinPoint.getJoinPointHash());
548 joinPointType = PointcutType.EXECUTION;
549 break;
550 case JoinPointType.METHOD_CALL_INT:
551 reflectionInfo = calleeClassInfo.getMethod(emittedJoinPoint.getJoinPointHash());
552 joinPointType = PointcutType.CALL;
553 break;
554 case JoinPointType.FIELD_GET_INT:
555 reflectionInfo = calleeClassInfo.getField(emittedJoinPoint.getJoinPointHash());
556 joinPointType = PointcutType.GET;
557 break;
558 case JoinPointType.FIELD_SET_INT:
559 reflectionInfo = calleeClassInfo.getField(emittedJoinPoint.getJoinPointHash());
560 joinPointType = PointcutType.SET;
561 break;
562 case JoinPointType.CONSTRUCTOR_EXECUTION_INT:
563 reflectionInfo = calleeClassInfo.getConstructor(emittedJoinPoint.getJoinPointHash());
564 joinPointType = PointcutType.EXECUTION;
565 break;
566 case JoinPointType.CONSTRUCTOR_CALL_INT:
567 reflectionInfo = calleeClassInfo.getConstructor(emittedJoinPoint.getJoinPointHash());
568 joinPointType = PointcutType.CALL;
569 break;
570 case JoinPointType.HANDLER_INT:
571 reflectionInfo = calleeClassInfo;
572 joinPointType = PointcutType.HANDLER;
573 break;
574 default:
575 throw new RuntimeException("Joinpoint type not supported: " + emittedJoinPoint.getJoinPointType());
576 }
577
578
579 final ReflectionInfo withinInfo;
580 if (TransformationConstants.CLINIT_METHOD_NAME.equals(emittedJoinPoint.getCallerMethodName())) {
581 withinInfo = callerClassInfo.staticInitializer();
582 } else if (TransformationConstants.INIT_METHOD_NAME.equals(emittedJoinPoint.getCallerMethodName())) {
583 withinInfo = callerClassInfo.getConstructor(AsmHelper.calculateConstructorHash(
584 emittedJoinPoint.getCallerMethodDesc()
585 ));
586 } else {
587 withinInfo =
588 callerClassInfo.getMethod(AsmHelper.calculateMethodHash(emittedJoinPoint.getCallerMethodName(),
589 emittedJoinPoint.getCallerMethodDesc())
590 );
591 }
592
593
594 if (pointcutType != PointcutType.WITHIN && pointcutType != joinPointType) {
595 return false;
596 }
597
598 return expression.getExpression().match(new ExpressionContext(pointcutType, reflectionInfo, withinInfo));
599 }
600 }