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.definition;
9
10 import gnu.trove.TObjectIntHashMap;
11 import org.codehaus.aspectwerkz.aspect.CFlowSystemAspect;
12 import org.codehaus.aspectwerkz.expression.ExpressionContext;
13 import org.codehaus.aspectwerkz.expression.ExpressionInfo;
14 import org.codehaus.aspectwerkz.expression.ExpressionVisitor;
15 import org.codehaus.aspectwerkz.util.SequencedHashMap;
16 import org.codehaus.aspectwerkz.transform.AspectWerkzPreProcessor;
17
18 import java.util.ArrayList;
19 import java.util.Collection;
20 import java.util.HashMap;
21 import java.util.HashSet;
22 import java.util.Iterator;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Set;
26
27 /***
28 * Abstraction of the system definition, defines the aspect system.
29 *
30 * @author <a href="mailto:jboner@codehaus.org">Jonas Bonér </a>
31 * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
32 */
33 public class SystemDefinition {
34 public static final String PER_JVM = "perJVM";
35
36 public static final String PER_CLASS = "perClass";
37
38 public static final String PER_INSTANCE = "perInstance";
39
40 public static final String PER_THREAD = "perThread";
41
42 /***
43 * Empty hash map.
44 */
45 public static final Map EMPTY_HASH_MAP = new HashMap();
46
47 /***
48 * Holds the indexes for the aspects. The aspect indexes are needed here (instead of in the AspectWerkz class like
49 * the advice indexes) since they need to be available to the transformers before the AspectWerkz system has been
50 * initialized.
51 */
52 private final TObjectIntHashMap m_aspectIndexes = new TObjectIntHashMap();
53
54 /***
55 * Holds the indexes for the mixins. The mixin indexes are needed here (instead of in the AspectWerkz class like the
56 * advice indexes) since they need to be available to the transformers before the AspectWerkz system has been
57 * initialized.
58 */
59 private final TObjectIntHashMap m_introductionIndexes = new TObjectIntHashMap();
60
61 /***
62 * Maps the aspects to it's name.
63 */
64 private final Map m_aspectMap = new SequencedHashMap();
65
66 /***
67 * Maps the mixins to it's name.
68 */
69 private final Map m_introductionMap = new HashMap();
70
71 /***
72 * Maps the interface mixins to it's name.
73 */
74 private final Map m_interfaceIntroductionMap = new HashMap();
75
76 /***
77 * The UUID for this definition.
78 */
79 private String m_uuid = "default";
80
81 /***
82 * The include packages.
83 */
84 private final Set m_includePackages = new HashSet();
85
86 /***
87 * The exclude packages.
88 */
89 private final Set m_excludePackages = new HashSet();
90
91 /***
92 * The prepare packages.
93 */
94 private final Set m_preparePackages = new HashSet();
95
96 /***
97 * The parameters passed to the aspects.
98 */
99 private final Map m_parametersToAspects = new HashMap();
100
101 /***
102 * Creates a new instance, creates and sets the system cflow aspect.
103 */
104 public SystemDefinition(final String uuid) {
105 setUuid(uuid);
106 AspectDefinition systemAspect = new AspectDefinition(
107 CFlowSystemAspect.CLASS_NAME,
108 CFlowSystemAspect.CLASS_NAME,
109 m_uuid
110 );
111 systemAspect.setDeploymentModel(CFlowSystemAspect.DEPLOYMENT_MODEL);
112 m_aspectMap.put(CFlowSystemAspect.CLASS_NAME, systemAspect);
113 }
114
115 /***
116 * Sets the UUID for the definition.
117 *
118 * @param uuid the UUID
119 */
120 private void setUuid(final String uuid) {
121 m_uuid = uuid;
122 }
123
124 /***
125 * Returns the UUID for the definition.
126 *
127 * @return the UUID
128 */
129 public String getUuid() {
130 return m_uuid;
131 }
132
133 /***
134 * Returns the include packages.
135 *
136 * @return the include packages
137 */
138 public Set getIncludePackages() {
139 return m_includePackages;
140 }
141
142 /***
143 * Returns the exclude packages.
144 *
145 * @return the exclude packages
146 */
147 public Set getExcludePackages() {
148 return m_excludePackages;
149 }
150
151 /***
152 * Returns a collection with the aspect definitions registered.
153 *
154 * @return the aspect definitions
155 */
156 public Collection getAspectDefinitions() {
157 Collection clone = new ArrayList(m_aspectMap.size());
158 for (Iterator it = m_aspectMap.values().iterator(); it.hasNext();) {
159 clone.add(it.next());
160 }
161 return clone;
162 }
163
164 /***
165 * Returns a collection with the introduction definitions registered.
166 *
167 * @return the introduction definitions
168 */
169 public Collection getIntroductionDefinitions() {
170 Collection clone = new ArrayList(m_introductionMap.size());
171 for (Iterator it = m_introductionMap.values().iterator(); it.hasNext();) {
172 clone.add(it.next());
173 }
174 return clone;
175 }
176
177 /***
178 * Returns a collection with the advice definitions registered.
179 *
180 * @return the advice definitions
181 */
182 public Collection getAdviceDefinitions() {
183 final Collection adviceDefs = new ArrayList();
184 for (Iterator it = m_aspectMap.values().iterator(); it.hasNext();) {
185 AspectDefinition aspectDef = (AspectDefinition) it.next();
186 adviceDefs.addAll(aspectDef.getAroundAdvices());
187 adviceDefs.addAll(aspectDef.getBeforeAdvices());
188 adviceDefs.addAll(aspectDef.getAfterAdvices());
189 }
190 return adviceDefs;
191 }
192
193 /***
194 * Returns a specific aspect definition.
195 *
196 * @param name the name of the aspect definition
197 * @return the aspect definition
198 */
199 public AspectDefinition getAspectDefinition(final String name) {
200 return (AspectDefinition) m_aspectMap.get(name);
201 }
202
203 /***
204 * Returns a specific advice definition.
205 *
206 * @param name the name of the advice definition
207 * @return the advice definition
208 */
209 public AdviceDefinition getAdviceDefinition(final String name) {
210 Collection adviceDefs = getAdviceDefinitions();
211 for (Iterator it = adviceDefs.iterator(); it.hasNext();) {
212 AdviceDefinition adviceDef = (AdviceDefinition) it.next();
213 if (adviceDef.getName().equals(name)) {
214 return adviceDef;
215 }
216 }
217 return null;
218 }
219
220 /***
221 * Returns the introduction definitions for a specific class.
222 *
223 * @param ctx the expression context
224 * @return a list with the introduction definitions
225 */
226 public List getIntroductionDefinitions(final ExpressionContext ctx) {
227 final List introDefs = new ArrayList();
228 for (Iterator it = m_introductionMap.values().iterator(); it.hasNext();) {
229 IntroductionDefinition introDef = (IntroductionDefinition) it.next();
230 for (int i = 0; i < introDef.getExpressionInfos().length; i++) {
231 if (introDef.getExpressionInfos()[i].getExpression().match(ctx)) {
232 introDefs.add(introDef);
233 }
234 }
235 }
236 return introDefs;
237 }
238
239 /***
240 * Returns the interface introductions for a certain class merged with the implementation based introductions as
241 * well.
242 *
243 * @param ctx the expression context
244 * @return the names
245 */
246 public List getInterfaceIntroductionDefinitions(final ExpressionContext ctx) {
247 if (ctx == null) {
248 throw new IllegalArgumentException("context can not be null");
249 }
250 List interfaceIntroductionDefs = new ArrayList();
251 for (Iterator it = m_interfaceIntroductionMap.values().iterator(); it.hasNext();) {
252 InterfaceIntroductionDefinition introDef = (InterfaceIntroductionDefinition) it.next();
253 ExpressionInfo[] expressionInfos = introDef.getExpressionInfos();
254 for (int i = 0; i < expressionInfos.length; i++) {
255 ExpressionInfo expressionInfo = expressionInfos[i];
256 ExpressionVisitor expression = expressionInfo.getExpression();
257 if (expression.match(ctx)) {
258 interfaceIntroductionDefs.add(introDef);
259 }
260 }
261 }
262 return interfaceIntroductionDefs;
263 }
264
265 /***
266 * Returns the index for a specific introduction.
267 *
268 * @param aspectName the name of the aspect
269 * @return the index
270 */
271 public int getAspectIndexByName(final String aspectName) {
272 if (aspectName == null) {
273 throw new IllegalArgumentException("aspect name can not be null");
274 }
275 int index = m_aspectIndexes.get(aspectName);
276 if (index < 1) {
277 throw new RuntimeException(
278 "aspect [" + aspectName + "] does not exist, failed in retrieving aspect index"
279 );
280 }
281 return index;
282 }
283
284 /***
285 * Returns the index for a specific introduction.
286 *
287 * @param mixinName the name of the mixin
288 * @return the index
289 */
290 public int getMixinIndexByName(final String mixinName) {
291 if (mixinName == null) {
292 throw new IllegalArgumentException("mixin name can not be null");
293 }
294 int index = m_introductionIndexes.get(mixinName);
295 if (index < 1) {
296 throw new RuntimeException("mixin [" + mixinName + "] does not exist, failed in retrieving mixin index");
297 }
298 return index;
299 }
300
301 /***
302 * Adds a new aspect definition.
303 *
304 * @param aspectDef the aspect definition
305 */
306 public void addAspect(final AspectDefinition aspectDef) {
307 if (aspectDef == null) {
308 throw new IllegalArgumentException("aspect definition can not be null");
309 }
310 if (m_aspectIndexes.containsKey(aspectDef.getName())) {
311 return;
312 }
313 synchronized (m_aspectMap) {
314 synchronized (m_aspectIndexes) {
315 final int index = m_aspectMap.values().size() + 1;
316 m_aspectIndexes.put(aspectDef.getName(), index);
317 m_aspectMap.put(aspectDef.getName(), aspectDef);
318 }
319 }
320 }
321
322 /***
323 * Adds a new mixin definition.
324 *
325 * @param introDef the mixin definition
326 */
327 public void addIntroductionDefinition(final IntroductionDefinition introDef) {
328 if (introDef == null) {
329 throw new IllegalArgumentException("introduction definition can not be null");
330 }
331 if (m_introductionIndexes.containsKey(introDef.getName())) {
332 IntroductionDefinition def = (IntroductionDefinition) m_introductionMap.get(introDef.getName());
333 def.addExpressionInfos(introDef.getExpressionInfos());
334 return;
335 }
336 synchronized (m_introductionMap) {
337 synchronized (m_introductionIndexes) {
338 final int index = m_introductionMap.values().size() + 1;
339 m_introductionIndexes.put(introDef.getName(), index);
340 m_introductionMap.put(introDef.getName(), introDef);
341 }
342 }
343 }
344
345 /***
346 * Adds a new pure interface mixin definition.
347 *
348 * @param introDef the mixin definition
349 */
350 public void addInterfaceIntroductionDefinition(final InterfaceIntroductionDefinition introDef) {
351 if (introDef == null) {
352 throw new IllegalArgumentException("introduction definition can not be null");
353 }
354 synchronized (m_interfaceIntroductionMap) {
355 m_interfaceIntroductionMap.put(introDef.getName(), introDef);
356 }
357 }
358
359 /***
360 * Adds a new include package.
361 *
362 * @param includePackage the new include package
363 */
364 public void addIncludePackage(final String includePackage) {
365 synchronized (m_includePackages) {
366 m_includePackages.add(includePackage + '.');
367 }
368 }
369
370 /***
371 * Adds a new exclude package.
372 *
373 * @param excludePackage the new exclude package
374 */
375 public void addExcludePackage(final String excludePackage) {
376 synchronized (m_excludePackages) {
377 m_excludePackages.add(excludePackage + '.');
378 }
379 }
380
381 /***
382 * Adds a new prepare package.
383 *
384 * @param preparePackage the new prepare package
385 */
386 public void addPreparePackage(final String preparePackage) {
387 synchronized (m_preparePackages) {
388 m_preparePackages.add(preparePackage + '.');
389 }
390 }
391
392 /***
393 * Returns the prepare packages.
394 *
395 * @return
396 */
397 public Set getPreparePackages() {
398 return m_preparePackages;
399 }
400
401 /***
402 * Checks if there exists an advice with the name specified.
403 *
404 * @param name the name of the advice
405 * @return boolean
406 */
407 public boolean hasAdvice(final String name) {
408 Collection adviceDefs = getAdviceDefinitions();
409 for (Iterator it = adviceDefs.iterator(); it.hasNext();) {
410 AdviceDefinition adviceDef = (AdviceDefinition) it.next();
411 if (adviceDef.getName().equals(name)) {
412 return true;
413 }
414 }
415 return false;
416 }
417
418 /***
419 * Checks if there exists an introduction with the name specified.
420 *
421 * @param name the name of the introduction
422 * @return boolean
423 */
424 public boolean hasIntroduction(final String name) {
425 return m_introductionMap.containsKey(name);
426 }
427
428 /***
429 * Checks if a class should be included.
430 *
431 * @param className the name or the class
432 * @return boolean
433 */
434 public boolean inIncludePackage(final String className) {
435 if (className == null) {
436 throw new IllegalArgumentException("class name can not be null");
437 }
438 if (m_includePackages.isEmpty()) {
439 return true;
440 }
441 for (Iterator it = m_includePackages.iterator(); it.hasNext();) {
442 String packageName = (String) it.next();
443 >if (className.startsWith(packageName)) {
444 return true;
445 }
446 }
447 return false;
448 }
449
450 /***
451 * Checks if a class should be excluded.
452 *
453 * @param className the name or the class
454 * @return boolean
455 */
456 public boolean inExcludePackage(final String className) {
457 if (className == null) {
458 throw new IllegalArgumentException("class name can not be null");
459 }
460 for (Iterator it = m_excludePackages.iterator(); it.hasNext();) {
461 String packageName = (String) it.next();
462 >if (className.startsWith(packageName)) {
463 return true;
464 }
465 }
466 return false;
467 }
468
469 /***
470 * Checks if a class is in prepare declaration
471 *
472 * @param className the name or the class
473 * @return boolean
474 */
475 public boolean inPreparePackage(String className) {
476 if (className == null) {
477 throw new IllegalArgumentException("class name can not be null");
478 }
479 for (Iterator it = m_preparePackages.iterator(); it.hasNext();) {
480 String packageName = (String) it.next();
481 >if (className.startsWith(packageName)) {
482 return true;
483 }
484 }
485 return false;
486 }
487
488 /***
489 * Checks if a class has an introduction.
490 *
491 * @param ctx the expression context
492 * @return boolean
493 */
494 public boolean hasIntroduction(final ExpressionContext ctx) {
495 if (ctx == null) {
496 throw new IllegalArgumentException("context can not be null");
497 }
498 for (Iterator it = m_introductionMap.values().iterator(); it.hasNext();) {
499 IntroductionDefinition introDef = (IntroductionDefinition) it.next();
500 ExpressionInfo[] expressionInfos = introDef.getExpressionInfos();
501 for (int i = 0; i < expressionInfos.length; i++) {
502 ExpressionInfo expressionInfo = expressionInfos[i];
503 ExpressionVisitor expression = expressionInfo.getExpression();
504 if (expression.match(ctx)) {
505 return true;
506 }
507 }
508 }
509 return false;
510 }
511
512 /***
513 * Checks if a method has an pointcut.
514 *
515 * @param ctx the expression context
516 * @return boolean
517 */
518 public boolean hasPointcut(final ExpressionContext ctx) {
519 if (ctx == null) {
520 throw new IllegalArgumentException("context can not be null");
521 }
522 for (Iterator it = m_aspectMap.values().iterator(); it.hasNext();) {
523 AspectDefinition aspectDef = (AspectDefinition) it.next();
524 for (Iterator it2 = aspectDef.getAllAdvices().iterator(); it2.hasNext();) {
525 AdviceDefinition adviceDef = (AdviceDefinition) it2.next();
526 ExpressionVisitor expression = adviceDef.getExpressionInfo().getExpression();
527
528 if (expression.match(ctx)) {
529 if (AspectWerkzPreProcessor.DETAILS) {
530 System.out.println(
531 "match: " + expression.toString() + " @ " + aspectDef.getName() + "/" +
532 adviceDef.getName()
533 );
534 System.out.println("\tfor " + ctx.getReflectionInfo());
535 System.out.println("\twithin " + ctx.getWithinReflectionInfo());
536 System.out.println("\ttype " + ctx.getPointcutType().toString());
537 }
538 return true;
539 }
540 }
541 }
542 return false;
543 }
544
545 /***
546 * Checks if a method has an cflow pointcut.
547 *
548 * @param ctx the expression context
549 * @return boolean
550 */
551 public boolean hasCflowPointcut(final ExpressionContext ctx) {
552 if (ctx == null) {
553 throw new IllegalArgumentException("context can not be null");
554 }
555 for (Iterator it = m_aspectMap.values().iterator(); it.hasNext();) {
556 AspectDefinition aspectDef = (AspectDefinition) it.next();
557 for (Iterator it2 = aspectDef.getAllAdvices().iterator(); it2.hasNext();) {
558 AdviceDefinition adviceDef = (AdviceDefinition) it2.next();
559 ExpressionInfo expressionInfo = adviceDef.getExpressionInfo();
560 if (expressionInfo.hasCflowPointcut() && expressionInfo.getCflowExpression().match(ctx)) {
561 return true;
562 }
563 }
564 }
565 return false;
566 }
567
568 /***
569 * Checks if a class is advised.
570 *
571 * @param ctxs an array with the expression contexts
572 * @return boolean
573 */
574 public boolean isAdvised(final ExpressionContext[] ctxs) {
575 if (ctxs == null) {
576 throw new IllegalArgumentException("context array can not be null");
577 }
578 for (Iterator it = m_aspectMap.values().iterator(); it.hasNext();) {
579 AspectDefinition aspectDef = (AspectDefinition) it.next();
580 List advices = aspectDef.getAllAdvices();
581 for (Iterator it2 = advices.iterator(); it2.hasNext();) {
582 AdviceDefinition adviceDef = (AdviceDefinition) it2.next();
583 final ExpressionInfo expressionInfo = adviceDef.getExpressionInfo();
584 for (int i = 0; i < ctxs.length; i++) {
585 ExpressionContext ctx = ctxs[i];
586 if (expressionInfo.getAdvisedClassFilterExpression().match(ctx)
587 || expressionInfo.getAdvisedCflowClassFilterExpression().match(ctx)) {
588 if (AspectWerkzPreProcessor.DETAILS) {
589 System.out.println(
590 "early match: " + expressionInfo.toString() + " @ " +
591 aspectDef.getName() +
592 "/" +
593 adviceDef.getName()
594 );
595 System.out.println("\tfor " + ctx.getReflectionInfo());
596 System.out.println("\twithin " + ctx.getWithinReflectionInfo());
597 System.out.println("\ttype " + ctx.getPointcutType().toString());
598 }
599 return true;
600 }
601 }
602 }
603 }
604 return false;
605 }
606
607 /***
608 * Checks if a class is advised.
609 *
610 * @param ctx the expression context
611 * @return boolean
612 */
613 public boolean isAdvised(final ExpressionContext ctx) {
614 if (ctx == null) {
615 throw new IllegalArgumentException("context can not be null");
616 }
617 for (Iterator it = m_aspectMap.values().iterator(); it.hasNext();) {
618 AspectDefinition aspectDef = (AspectDefinition) it.next();
619 List advices = aspectDef.getAllAdvices();
620 for (Iterator it2 = advices.iterator(); it2.hasNext();) {
621 AdviceDefinition adviceDef = (AdviceDefinition) it2.next();
622 if (adviceDef.getExpressionInfo().getAdvisedClassFilterExpression().match(ctx)
623 || adviceDef.getExpressionInfo().getAdvisedCflowClassFilterExpression().match(ctx)) {
624 return true;
625 }
626 }
627 }
628 return false;
629 }
630
631 /***
632 * Checks if a class has an introduction.
633 *
634 * @param ctxs an array with the expression contexts
635 * @return boolean
636 */
637 public boolean isIntroduced(final ExpressionContext[] ctxs) {
638 if (ctxs == null) {
639 throw new IllegalArgumentException("context array can not be null");
640 }
641 for (Iterator it = m_introductionMap.values().iterator(); it.hasNext();) {
642 IntroductionDefinition introDef = (IntroductionDefinition) it.next();
643 ExpressionInfo[] expressionInfos = introDef.getExpressionInfos();
644 for (int i = 0; i < expressionInfos.length; i++) {
645 ExpressionInfo expressionInfo = expressionInfos[i];
646 for (int j = 0; j < ctxs.length; j++) {
647 if (expressionInfo.getAdvisedClassFilterExpression().match(ctxs[j])) {
648 return true;
649 }
650 }
651 }
652 }
653 return false;
654 }
655
656 /***
657 * Checks if a class has an introduction.
658 *
659 * @param ctx the expression context
660 * @return boolean
661 */
662 public boolean isIntroduced(final ExpressionContext ctx) {
663 if (ctx == null) {
664 throw new IllegalArgumentException("context can not be null");
665 }
666 for (Iterator it = m_introductionMap.values().iterator(); it.hasNext();) {
667 IntroductionDefinition introDef = (IntroductionDefinition) it.next();
668 ExpressionInfo[] expressionInfos = introDef.getExpressionInfos();
669 for (int i = 0; i < expressionInfos.length; i++) {
670 ExpressionInfo expressionInfo = expressionInfos[i];
671 if (expressionInfo.getAdvisedClassFilterExpression().match(ctx)) {
672 return true;
673 }
674 }
675 }
676 return false;
677 }
678
679 /***
680 * Checks if a class is advised with an interface introduction.
681 *
682 * @param ctx the expression context
683 * @return boolean
684 */
685 public boolean isInterfaceIntroduced(final ExpressionContext ctx) {
686 if (ctx == null) {
687 throw new IllegalArgumentException("context can not be null");
688 }
689 for (Iterator it = m_interfaceIntroductionMap.values().iterator(); it.hasNext();) {
690 InterfaceIntroductionDefinition introDef = (InterfaceIntroductionDefinition) it.next();
691 ExpressionInfo[] expressionInfos = introDef.getExpressionInfos();
692 for (int i = 0; i < expressionInfos.length; i++) {
693 ExpressionInfo expressionInfo = expressionInfos[i];
694 if (expressionInfo.getAdvisedClassFilterExpression().match(ctx)) {
695 return true;
696 }
697 }
698 }
699 return false;
700 }
701
702 /***
703 * Adds a new parameter for the aspect.
704 *
705 * @param aspectName the name of the aspect
706 * @param key the key
707 * @param value the value
708 * @TODO: should perhaps move to the aspect def instead of being separated from the aspect def concept?
709 */
710 public void addParameter(final String aspectName, final String key, final String value) {
711 Map parameters;
712 if (m_parametersToAspects.containsKey(aspectName)) {
713 parameters = (Map) m_parametersToAspects.get(aspectName);
714 parameters.put(key, value);
715 } else {
716 parameters = new HashMap();
717 parameters.put(key, value);
718 m_parametersToAspects.put(aspectName, parameters);
719 }
720 }
721
722 /***
723 * Returns parameters for the aspect.
724 *
725 * @param aspectName the name of the aspect
726 * @return parameters
727 */
728 public Map getParameters(final String aspectName) {
729 if (m_parametersToAspects.containsKey(aspectName)) {
730 return (Map) m_parametersToAspects.get(aspectName);
731 } else {
732 return EMPTY_HASH_MAP;
733 }
734 }
735 }