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.aspect.management;
9
10 import gnu.trove.TIntObjectHashMap;
11 import gnu.trove.TObjectIntHashMap;
12 import org.codehaus.aspectwerkz.ConstructorTuple;
13 import org.codehaus.aspectwerkz.CrossCuttingInfo;
14 import org.codehaus.aspectwerkz.AdviceInfo;
15 import org.codehaus.aspectwerkz.MethodTuple;
16 import org.codehaus.aspectwerkz.Mixin;
17 import org.codehaus.aspectwerkz.joinpoint.management.JoinPointManager;
18 import org.codehaus.aspectwerkz.aspect.AspectContainer;
19 import org.codehaus.aspectwerkz.aspect.Introduction;
20 import org.codehaus.aspectwerkz.aspect.IntroductionContainer;
21 import org.codehaus.aspectwerkz.definition.AdviceDefinition;
22 import org.codehaus.aspectwerkz.definition.IntroductionDefinition;
23 import org.codehaus.aspectwerkz.definition.StartupManager;
24 import org.codehaus.aspectwerkz.definition.SystemDefinition;
25 import org.codehaus.aspectwerkz.exception.DefinitionException;
26 import org.codehaus.aspectwerkz.exception.WrappedRuntimeException;
27 import org.codehaus.aspectwerkz.expression.ExpressionContext;
28 import org.codehaus.aspectwerkz.transform.ReflectHelper;
29 import org.codehaus.aspectwerkz.transform.TransformationUtil;
30 import org.codehaus.aspectwerkz.transform.TransformationConstants;
31 import org.codehaus.aspectwerkz.util.SequencedHashMap;
32 import org.codehaus.aspectwerkz.util.Strings;
33
34 import java.lang.reflect.Constructor;
35 import java.lang.reflect.Field;
36 import java.lang.reflect.Method;
37 import java.util.ArrayList;
38 import java.util.Collection;
39 import java.util.HashMap;
40 import java.util.Iterator;
41 import java.util.List;
42 import java.util.Map;
43
44 /***
45 * Stores the aspects, advices, pointcuts etc. Manages the method, advice and aspect indexing.
46 *
47 * @author <a href="mailto:jboner@codehaus.org">Jonas Bonér </a>
48 * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
49 * @TODO Use hashes, aspect=>hashcode for class advice=>hashcode for method signature
50 * @TODO Store references to all join points that uses advices from a certain aspect [aspectKey=>joinPoints]
51 * @TODO Map all aspects to a key, meaning have a key that maps to a data structure that contains full info about the
52 * aspect and all its advice methods. [aspectKey=>aspectDataStructure].
53 */
54 public class AspectRegistry {
55 /***
56 * Holds references to the methods to the advised classes in the system.
57 */
58 private final static Map s_methods = new HashMap();
59
60 /***
61 * Holds references to the fields to the advised classes in the system.
62 */
63 private final static Map s_fields = new HashMap();
64
65 /***
66 * Holds references to all the the advised constructors in the system, maps the target Class to a sorted list of all
67 * the advised constructors in the class.
68 */
69 private final static Map s_constructors = new HashMap();
70
71 /***
72 * The AspectManager for the system.
73 */
74 private final AspectManager m_aspectManager;
75
76 /***
77 * The definition.
78 */
79 private final SystemDefinition m_definition;
80
81 /***
82 * Marks the system as initialized.
83 */
84 private boolean m_initialized = false;
85
86 /***
87 * Sorted map with PointcutManager instance containing the pointcut instance the aspect, mapped to its name (the
88 * name of the class implementing the aspect).
89 */
90 private final Map m_pointcutManagerMap = new SequencedHashMap();
91
92 /***
93 * Holds the indexes for the aspects, maps the aspect name to the index for the aspect.
94 */
95 private final TObjectIntHashMap m_aspectIndexes = new TObjectIntHashMap();
96
97 /***
98 * Holds the index (a tuple of the aspect index and the advice index) for the advices, mapped to its name
99 * ([fullyQualifiedClassName].[methodName]).
100 */
101 private final Map m_adviceIndexes = new HashMap();
102
103 /***
104 * An array with all the the aspect containers in the system.
105 */
106 private AspectContainer[] m_aspectContainers = new AspectContainer[0];
107
108 /***
109 * An array of all the mixins in the system, each nested class in aspect has its own index.
110 */
111 private Mixin[] m_mixins = new Mixin[0];
112
113 /***
114 * Creates a new aspect registry.
115 *
116 * @param aspectManager the system aspectManager
117 * @param definition the system definition
118 */
119 public AspectRegistry(final AspectManager aspectManager, final SystemDefinition definition) {
120 m_aspectManager = aspectManager;
121 m_definition = definition;
122 }
123
124 /***
125 * Initializes the aspect registry. The initialization needs to be separated fromt he construction of the registry,
126 * and is triggered by the runtime system.
127 */
128 public void initialize() {
129 synchronized (this) {
130 if (m_initialized) {
131 return;
132 }
133 m_initialized = true;
134 StartupManager.initializeSystem(m_aspectManager, m_definition);
135 }
136 }
137
138 /***
139 * Registers a new aspect.
140 *
141 * @param aspectContainer the aspectContainer for the aspect to register
142 * @param pointcutManager the pointcut manager
143 */
144 public void register(final AspectContainer aspectContainer, final PointcutManager pointcutManager) {
145 if (aspectContainer == null) {
146 throw new IllegalArgumentException("aspect aspectContainer can not be null");
147 }
148 if (pointcutManager == null) {
149 throw new IllegalArgumentException("pointcut manager can not be null");
150 }
151 synchronized (m_aspectContainers) {
152 synchronized (m_aspectIndexes) {
153 synchronized (m_adviceIndexes) {
154 synchronized (m_mixins) {
155 synchronized (m_pointcutManagerMap) {
156 try {
157 CrossCuttingInfo crossCuttingInfo = aspectContainer.getCrossCuttingInfo();
158 m_pointcutManagerMap.put(crossCuttingInfo.getName(), pointcutManager);
159 final int indexAspect = m_aspectContainers.length + 1;
160 m_aspectIndexes.put(crossCuttingInfo.getName(), indexAspect);
161 final Object[] tmpAspects = new Object[m_aspectContainers.length + 1];
162 java.lang.System.arraycopy(
163 m_aspectContainers,
164 0,
165 tmpAspects,
166 0,
167 m_aspectContainers.length);
168 tmpAspects[m_aspectContainers.length] = aspectContainer;
169 m_aspectContainers = new AspectContainer[m_aspectContainers.length + 1];
170 java.lang.System.arraycopy(tmpAspects, 0, m_aspectContainers, 0, tmpAspects.length);
171
172
173
174
175 List advices = crossCuttingInfo.getAspectDefinition().getAllAdvices();
176 for (Iterator it = advices.iterator(); it.hasNext();) {
177 final AdviceDefinition adviceDef = (AdviceDefinition) it.next();
178 AdviceInfo tuple = new AdviceInfo(
179 indexAspect,
180 adviceDef.getMethodIndex(),
181 m_aspectManager,
182 adviceDef.getType(),
183 adviceDef.getSpecialArgumentType());
184
185
186 m_adviceIndexes.put(crossCuttingInfo.getName() + "/" + adviceDef.getName(), tuple);
187 }
188
189
190 List introductions = crossCuttingInfo.getAspectDefinition().getIntroductions();
191
192 for (Iterator it = introductions.iterator(); it.hasNext();) {
193 IntroductionDefinition introDef = (IntroductionDefinition) it.next();
194
195
196
197 Class defaultImplClass = crossCuttingInfo.getAspectClass().getClassLoader()
198 .loadClass(introDef.getName());
199
200 Introduction mixinPrototype = new Introduction(
201 introDef.getName(),
202 defaultImplClass,
203 crossCuttingInfo,
204 introDef);
205 IntroductionContainer introductionContainer = new IntroductionContainer(
206 mixinPrototype,
207 aspectContainer);
208 aspectContainer.addIntroductionContainer(introDef.getName(), introductionContainer);
209
210
211 mixinPrototype.setContainer(introductionContainer);
212 final Mixin[] tmpMixins = new Mixin[m_mixins.length + 1];
213 java.lang.System.arraycopy(m_mixins, 0, tmpMixins, 0, m_mixins.length);
214 tmpMixins[m_mixins.length] = mixinPrototype;
215 m_mixins = new Mixin[m_mixins.length + 1];
216 java.lang.System.arraycopy(tmpMixins, 0, m_mixins, 0, tmpMixins.length);
217 }
218 } catch (Exception e) {
219 e.printStackTrace();
220 throw new DefinitionException("could not register aspect ["
221 + aspectContainer.getCrossCuttingInfo().getName()
222 + "] due to: "
223 + e.toString());
224 }
225 if (m_aspectContainers.length != m_aspectIndexes.size()) {
226 throw new IllegalStateException("aspect indexing out of synch");
227 }
228 }
229 }
230 }
231 }
232 }
233 }
234
235 /***
236 * Retrieves a specific aspect container based on index.
237 *
238 * @param index the index of the aspect
239 * @return the aspect container
240 */
241 public AspectContainer getAspectContainer(final int index) {
242 AspectContainer aspect;
243 try {
244 aspect = m_aspectContainers[index - 1];
245 } catch (Throwable e) {
246 initialize();
247 try {
248 aspect = m_aspectContainers[index - 1];
249 } catch (ArrayIndexOutOfBoundsException e1) {
250 throw new DefinitionException("no aspect with index " + index);
251 }
252 }
253 return aspect;
254 }
255
256 /***
257 * Returns the aspect container for a specific name.
258 *
259 * @param name the name of the aspect
260 * @return the the aspect container
261 */
262 public AspectContainer getAspectContainer(final String name) {
263 AspectContainer container;
264 try {
265 container = m_aspectContainers[m_aspectIndexes.get(name) - 1];
266 } catch (Throwable e1) {
267 initialize();
268 try {
269 container = m_aspectContainers[m_aspectIndexes.get(name) - 1];
270 } catch (ArrayIndexOutOfBoundsException e2) {
271 throw new DefinitionException("container for cross-cutting class ["
272 + name
273 + "] is not properly defined");
274 }
275 }
276 return container;
277 }
278
279 /***
280 * Returns the aspect for a specific name, deployed as perJVM.
281 *
282 * @param name the name of the aspect
283 * @return the the aspect
284 */
285 public CrossCuttingInfo getCrossCuttingInfo(final String name) {
286 return getAspectContainer(name).getCrossCuttingInfo();
287 }
288
289 /***
290 * Retrieves a specific mixin based on its index.
291 *
292 * @param index the index of the introduction (aspect in this case)
293 * @return the the mixin (aspect in this case)
294 */
295 public Mixin getMixin(final int index) {
296 Mixin mixin;
297 try {
298 mixin = m_mixins[index - 1];
299 } catch (Throwable e1) {
300 initialize();
301 try {
302 mixin = m_mixins[index - 1];
303 } catch (ArrayIndexOutOfBoundsException e2) {
304 throw new DefinitionException("no mixin with index " + index);
305 }
306 }
307 return mixin;
308 }
309
310 /***
311 * Returns the mixin implementation for a specific name.
312 *
313 * @param name the name of the introduction (aspect in this case)
314 * @return the the mixin (aspect in this case)
315 */
316 public Mixin getMixin(final String name) {
317 if (name == null) {
318 throw new IllegalArgumentException("introduction name can not be null");
319 }
320 Mixin introduction;
321 try {
322 introduction = m_mixins[m_definition.getMixinIndexByName(name) - 1];
323 } catch (Throwable e1) {
324 initialize();
325 try {
326 introduction = m_mixins[m_definition.getMixinIndexByName(name) - 1];
327 } catch (ArrayIndexOutOfBoundsException e2) {
328 throw new DefinitionException("no introduction with name " + name);
329 }
330 }
331 return introduction;
332 }
333
334 /***
335 * Returns the index for a specific name to aspect mapping.
336 *
337 * @param name the name of the aspect
338 * @return the index of the aspect
339 */
340 public int getAspectIndexFor(final String name) {
341 if (name == null) {
342 throw new IllegalArgumentException("aspect name can not be null");
343 }
344 final int index = m_aspectIndexes.get(name);
345 if (index == 0) {
346 throw new DefinitionException("aspect " + name + " is not properly defined");
347 }
348 return index;
349 }
350
351 /***
352 * Returns the index for a specific name to advice mapping.
353 *
354 * @param name the name of the advice
355 * @return the index of the advice
356 */
357 public AdviceInfo getAdviceIndexFor(final String name) {
358 if (name == null) {
359 throw new IllegalArgumentException("advice name can not be null");
360 }
361 final AdviceInfo index = (AdviceInfo) m_adviceIndexes.get(name);
362 if (index == null) {
363 throw new DefinitionException("advice " + name + " is not properly defined");
364 }
365 return index;
366 }
367
368 /***
369 * Returns the pointcut managers for the name specified.
370 *
371 * @param name the name of the aspect
372 * @return the pointcut manager
373 */
374 public PointcutManager getPointcutManager(final String name) {
375 if (name == null) {
376 throw new IllegalArgumentException("aspect name can not be null");
377 }
378 if (m_pointcutManagerMap.containsKey(name)) {
379 return (PointcutManager) m_pointcutManagerMap.get(name);
380 } else {
381 initialize();
382 if (m_pointcutManagerMap.containsKey(name)) {
383 return (PointcutManager) m_pointcutManagerMap.get(name);
384 } else {
385 throw new DefinitionException("aspect " + name + " is not properly defined");
386 }
387 }
388 }
389
390 /***
391 * Returns a list with all the pointcut managers.
392 *
393 * @return the pointcut managers
394 */
395 public Collection getPointcutManagers() {
396 initialize();
397 return m_pointcutManagerMap.values();
398 }
399
400 /***
401 * Returns an array with all the aspect containers.
402 *
403 * @return the aspect containers
404 */
405 public AspectContainer[] getAspectContainers() {
406 initialize();
407 return m_aspectContainers;
408 }
409
410 /***
411 * Returns the pointcut list for the context specified.
412 *
413 * @param ctx the expression context
414 * @return the pointcuts for this join point
415 */
416 public List getPointcuts(final ExpressionContext ctx) {
417 List pointcuts = new ArrayList();
418 for (Iterator it = m_pointcutManagerMap.values().iterator(); it.hasNext();) {
419 PointcutManager pointcutManager = (PointcutManager) it.next();
420 pointcuts.addAll(pointcutManager.getPointcuts(ctx));
421 }
422 return pointcuts;
423 }
424
425 /***
426 * Returns the cflow pointcut list for the context specified.
427 *
428 * @param ctx the expression context
429 * @return the pointcuts for this join point
430 */
431 public List getCflowPointcuts(final ExpressionContext ctx) {
432 List pointcuts = new ArrayList();
433 for (Iterator it = m_pointcutManagerMap.values().iterator(); it.hasNext();) {
434 PointcutManager pointcutManager = (PointcutManager) it.next();
435 pointcuts.addAll(pointcutManager.getCflowPointcuts(ctx));
436 }
437 return pointcuts;
438 }
439
440 /***
441 * Checks if a specific class has an aspect defined.
442 *
443 * @param name the name of the aspect
444 * @return boolean true if the class has an aspect defined
445 */
446 public boolean hasAspect(final String name) {
447 if (name == null) {
448 throw new IllegalArgumentException("aspect name can not be null");
449 }
450 initialize();
451 if (m_pointcutManagerMap.containsKey(name)) {
452 return true;
453 } else {
454 return false;
455 }
456 }
457
458 /***
459 * Returns a specific method by the class and the method hash.
460 *
461 * @param klass the class housing the method
462 * @param methodHash the method hash
463 * @return the method tuple
464 */
465 public static MethodTuple getMethodTuple(final Class klass, final int methodHash) {
466 if (klass == null) {
467 throw new IllegalArgumentException("class can not be null");
468 }
469 try {
470
471 if (!s_methods.containsKey(klass)) {
472 createMethodRepository(klass);
473 }
474 } catch (Exception e) {
475 throw new WrappedRuntimeException(e);
476 }
477 MethodTuple methodTuple;
478 try {
479 methodTuple = (MethodTuple) ((TIntObjectHashMap) s_methods.get(klass)).get(methodHash);
480 } catch (Throwable e1) {
481 throw new WrappedRuntimeException(e1);
482 }
483 return methodTuple;
484 }
485
486 /***
487 * Returns a specific constructor by the class and the method hash.
488 *
489 * @param klass the class housing the method
490 * @param constructorHash the constructor hash
491 * @return the constructor
492 */
493 public static ConstructorTuple getConstructorTuple(final Class klass, final int constructorHash) {
494 if (klass == null) {
495 throw new IllegalArgumentException("class can not be null");
496 }
497 try {
498
499 if (!s_constructors.containsKey(klass)) {
500 createConstructorRepository(klass);
501 }
502 } catch (Exception e) {
503 throw new WrappedRuntimeException(e);
504 }
505 ConstructorTuple constructorTuple;
506 try {
507 constructorTuple = (ConstructorTuple) ((TIntObjectHashMap) s_constructors.get(klass)).get(constructorHash);
508 } catch (Throwable e1) {
509 throw new WrappedRuntimeException(e1);
510 }
511
512 if (constructorTuple == null) {
513 Constructor constructor = getConstructor(klass, constructorHash);
514 return new ConstructorTuple(constructor, constructor);
515 }
516 return constructorTuple;
517 }
518
519 /***
520 * Returns a specific field by the class and the field hash.
521 *
522 * @param klass the class housing the method
523 * @param fieldHash the field hash
524 * @return the field
525 */
526 public static Field getField(final Class klass, final int fieldHash) {
527 if (klass == null) {
528 throw new IllegalArgumentException("class can not be null");
529 }
530 try {
531
532 if (!s_fields.containsKey(klass)) {
533 createFieldRepository(klass);
534 }
535 } catch (Exception e) {
536 throw new WrappedRuntimeException(e);
537 }
538 Field field;
539 try {
540 field = (Field) ((TIntObjectHashMap) s_fields.get(klass)).get(fieldHash);
541 } catch (Throwable e1) {
542 throw new WrappedRuntimeException(e1);
543 }
544 return field;
545 }
546
547 /***
548 * Returns a specific constructor by the class and the constructor hash.
549 *
550 * @param klass the class housing the method
551 * @param constructorHash the constructor hash
552 * @return the constructor
553 */
554 public static Constructor getConstructor(final Class klass, final int constructorHash) {
555 if (klass == null) {
556 throw new IllegalArgumentException("class can not be null");
557 }
558
559 for (int i = 0; i < klass.getDeclaredConstructors().length; i++) {
560 Constructor constructor = klass.getDeclaredConstructors()[i];
561 if (ReflectHelper.calculateHash(constructor) == constructorHash) {
562 return constructor;
563 }
564 }
565 return null;
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582 }
583
584 /***
585 * Creates a new method repository for the class specified.
586 *
587 * @param klass the class
588 */
589 protected static void createMethodRepository(final Class klass) {
590 if (klass == null) {
591 throw new IllegalArgumentException("class can not be null");
592 }
593 Method[] methods = klass.getDeclaredMethods();
594 TIntObjectHashMap methodMap = new TIntObjectHashMap(methods.length);
595 for (int i = 0; i < methods.length; i++) {
596 Method wrapperMethod = methods[i];
597 if (!wrapperMethod.getName().startsWith(TransformationConstants.ASPECTWERKZ_PREFIX)) {
598 Method prefixedMethod = null;
599 for (int j = 0; j < methods.length; j++) {
600 Method method2 = methods[j];
601 if (method2.getName().startsWith(TransformationConstants.ASPECTWERKZ_PREFIX)) {
602 String[] tokens = Strings.splitString(method2.getName(), TransformationConstants.DELIMITER);
603 String methodName = tokens[1];
604 if (!methodName.equals(wrapperMethod.getName())) {
605 continue;
606 }
607 Class[] parameterTypes1 = wrapperMethod.getParameterTypes();
608 Class[] parameterTypes2 = method2.getParameterTypes();
609 if (parameterTypes2.length != parameterTypes1.length) {
610 continue;
611 }
612 boolean match = true;
613 for (int k = 0; k < parameterTypes1.length; k++) {
614 if (parameterTypes1[k] != parameterTypes2[k]) {
615 match = false;
616 break;
617 }
618 }
619 if (!match) {
620 continue;
621 }
622 prefixedMethod = method2;
623 break;
624 }
625 }
626
627
628 MethodTuple methodTuple = new MethodTuple(wrapperMethod, prefixedMethod);
629
630
631 int methodHash = ReflectHelper.calculateHash(wrapperMethod);
632 methodMap.put(methodHash, methodTuple);
633 }
634 }
635 synchronized (s_methods) {
636 s_methods.put(klass, methodMap);
637 }
638 }
639
640 /***
641 * Creates a new constructor repository for the class specified.
642 *
643 * @param klass the class
644 */
645 protected static void createConstructorRepository(final Class klass) {
646 if (klass == null) {
647 throw new IllegalArgumentException("class can not be null");
648 }
649 Constructor[] constructors = klass.getDeclaredConstructors();
650 TIntObjectHashMap constructorMap = new TIntObjectHashMap(constructors.length);
651 for (int i = 0; i < constructors.length; i++) {
652 Constructor constructor1 = constructors[i];
653 Constructor prefixedConstructor = null;
654
655 Class[] parameterTypes2 = new Class[constructor1.getParameterTypes().length + 1];
656 System.arraycopy(constructor1.getParameterTypes(), 0, parameterTypes2, 0, constructor1.getParameterTypes().length);
657 parameterTypes2[parameterTypes2.length-1] = JoinPointManager.class;
658 try {
659 prefixedConstructor = klass.getDeclaredConstructor(parameterTypes2);
660 } catch (NoSuchMethodException e) {
661
662 continue;
663 }
664
665
666 ConstructorTuple constructorTuple = new ConstructorTuple(constructor1, prefixedConstructor);
667
668
669 int constructorHash = ReflectHelper.calculateHash(constructor1);
670 constructorMap.put(constructorHash, constructorTuple);
671 }
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737 synchronized (s_constructors) {
738 s_constructors.put(klass, constructorMap);
739 }
740 }
741
742 /***
743 * Creates a new field repository for the class specified.
744 *
745 * @param klass the class
746 */
747 protected static void createFieldRepository(final Class klass) {
748 if (klass == null) {
749 throw new IllegalArgumentException("class can not be null");
750 }
751 Field[] fields = klass.getDeclaredFields();
752 TIntObjectHashMap fieldMap = new TIntObjectHashMap(fields.length);
753 for (int i = 0; i < fields.length; i++) {
754 Field field = fields[i];
755 field.setAccessible(true);
756 int fieldHash = ReflectHelper.calculateHash(field);
757 fieldMap.put(fieldHash, field);
758 }
759 synchronized (s_fields) {
760 s_fields.put(klass, fieldMap);
761 }
762 }
763 }