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.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 }