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 org.codehaus.aspectwerkz.expression.ExpressionContext;
11  import org.codehaus.aspectwerkz.expression.ExpressionInfo;
12  import org.codehaus.aspectwerkz.expression.ExpressionVisitor;
13  import org.codehaus.aspectwerkz.util.SequencedHashMap;
14  import org.codehaus.aspectwerkz.transform.AspectWerkzPreProcessor;
15  import org.codehaus.aspectwerkz.cflow.CflowBinding;
16  
17  import java.util.ArrayList;
18  import java.util.Collection;
19  import java.util.HashMap;
20  import java.util.HashSet;
21  import java.util.Iterator;
22  import java.util.List;
23  import java.util.Map;
24  import java.util.Set;
25  
26  /***
27   * TODO clean up - remove methods not used, refactor etc.
28   * <p/>
29   * Abstraction of the system definition, defines the aspect system.
30   *
31   * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
32   * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
33   */
34  public class SystemDefinition {
35  
36      /***
37       * Empty hash map.
38       */
39      public static final Map EMPTY_HASH_MAP = new HashMap();
40  
41      /***
42       * Maps the aspects to it's name.
43       */
44      private final Map m_aspectMap = new SequencedHashMap();
45  
46      /***
47       * Maps the mixins to it's name.
48       */
49      private final Map m_mixinMap = new SequencedHashMap();
50  
51      /***
52       * The UUID for this definition.
53       */
54      private String m_uuid = "default";
55  
56      /***
57       * The include packages.
58       */
59      private final Set m_includePackages = new HashSet();
60  
61      /***
62       * The exclude packages.
63       */
64      private final Set m_excludePackages = new HashSet();
65  
66      /***
67       * The prepare packages.
68       */
69      private final Set m_preparePackages = new HashSet();
70  
71      /***
72       * All prepared pointcuts defined in the system.
73       */
74      private final Map m_deploymentScopes = new HashMap();
75  
76      /***
77       * Creates a new instance, creates and sets the system cflow aspect.
78       */
79      public SystemDefinition(final String uuid) {
80          setUuid(uuid);
81          //TODO REMOVE below
82  //        AspectDefinition systemAspect = new AspectDefinition(
83  //                CFlowSystemAspect.class.getName(),
84  //                JavaClassInfo.getClassInfo(CFlowSystemAspect.class),
85  //                this
86  //        );
87  //        systemAspect.setDeploymentModel(CFlowSystemAspect.DEPLOYMENT_MODEL);
88  //        m_aspectMap.put(CFlowSystemAspect.CLASS_NAME, systemAspect);
89      }
90  
91      /***
92       * Sets the UUID for the definition.
93       *
94       * @param uuid the UUID
95       */
96      private void setUuid(final String uuid) {
97          m_uuid = uuid;
98      }
99  
100     /***
101      * Returns the UUID for the definition.
102      *
103      * @return the UUID
104      */
105     public String getUuid() {
106         return m_uuid;
107     }
108 
109     /***
110      * Returns the include packages.
111      *
112      * @return the include packages
113      */
114     public Set getIncludePackages() {
115         return m_includePackages;
116     }
117 
118     /***
119      * Returns the exclude packages.
120      *
121      * @return the exclude packages
122      */
123     public Set getExcludePackages() {
124         return m_excludePackages;
125     }
126 
127     /***
128      * Returns a collection with the aspect definitions registered.
129      *
130      * @return the aspect definitions
131      */
132     public Collection getAspectDefinitions() {
133         Collection clone = new ArrayList(m_aspectMap.size());
134         for (Iterator it = m_aspectMap.values().iterator(); it.hasNext();) {
135             clone.add(it.next());
136         }
137         return clone;
138     }
139 
140     /***
141      * Returns a collection with the mixin definitions registered.
142      *
143      * @return the mixin definitions
144      */
145     public Collection getMixinDefinitions() {
146         Collection clone = new ArrayList(m_mixinMap.size());
147         for (Iterator it = m_mixinMap.values().iterator(); it.hasNext();) {
148             clone.add(it.next());
149         }
150         return clone;
151     }
152 
153     /***
154      * Returns a collection with the advice definitions registered.
155      *
156      * @return the advice definitions
157      */
158     public Collection getAdviceDefinitions() {
159         final Collection adviceDefs = new ArrayList();
160         for (Iterator it = m_aspectMap.values().iterator(); it.hasNext();) {
161             AspectDefinition aspectDef = (AspectDefinition) it.next();
162             adviceDefs.addAll(aspectDef.getAroundAdviceDefinitions());
163             adviceDefs.addAll(aspectDef.getBeforeAdviceDefinitions());
164             adviceDefs.addAll(aspectDef.getAfterAdviceDefinitions());
165         }
166         return adviceDefs;
167     }
168 
169     /***
170      * Returns a specific aspect definition.
171      *
172      * @param name the name of the aspect definition
173      * @return the aspect definition
174      */
175     public AspectDefinition getAspectDefinition(final String name) {
176         return (AspectDefinition) m_aspectMap.get(name);
177     }
178 
179     /***
180      * Returns the mixin definitions matching a specfic expression.
181      *
182      * @param ctx the expression context
183      * @return a list with the mixin definitions
184      */
185     public List getMixinDefinitions(final ExpressionContext ctx) {
186         final List introDefs = new ArrayList();
187         for (Iterator it = m_mixinMap.values().iterator(); it.hasNext();) {
188             MixinDefinition introDef = (MixinDefinition) it.next();
189             for (int i = 0; i < introDef.getExpressionInfos().length; i++) {
190                 if (introDef.getExpressionInfos()[i].getExpression().match(ctx)) {
191                     introDefs.add(introDef);
192                 }
193             }
194         }
195         return introDefs;
196     }
197 
198     /***
199      * Returns the interface introductions for a certain class merged with the implementation based introductions as
200      * well.
201      *
202      * @param ctx the expression context
203      * @return the names
204      */
205     public List getInterfaceIntroductionDefinitions(final ExpressionContext ctx) {
206         if (ctx == null) {
207             throw new IllegalArgumentException("context can not be null");
208         }
209         List interfaceIntroductionDefs = new ArrayList();
210         for (Iterator iterator = m_aspectMap.values().iterator(); iterator.hasNext();) {
211             AspectDefinition aspectDef = (AspectDefinition) iterator.next();
212             for (Iterator it = aspectDef.getInterfaceIntroductionDefinitions().iterator(); it.hasNext();) {
213                 InterfaceIntroductionDefinition introDef = (InterfaceIntroductionDefinition) it.next();
214                 ExpressionInfo[] expressionInfos = introDef.getExpressionInfos();
215                 for (int i = 0; i < expressionInfos.length; i++) {
216                     ExpressionInfo expressionInfo = expressionInfos[i];
217                     ExpressionVisitor expression = expressionInfo.getExpression();
218                     if (expression.match(ctx)) {
219                         interfaceIntroductionDefs.add(introDef);
220                     }
221                 }
222             }
223         }
224         return interfaceIntroductionDefs;
225     }
226 
227     /***
228      * Adds a new aspect definition
229      * For each of its bounded pointcut, register cflow aspects as necessary.
230      *
231      * @param aspectDef the aspect definition
232      */
233     public void addAspect(final AspectDefinition aspectDef) {
234         if (aspectDef == null) {
235             throw new IllegalArgumentException("aspect definition can not be null");
236         }
237 
238         synchronized (m_aspectMap) {
239             if (m_aspectMap.containsKey(aspectDef.getName())) {
240                 return;
241             }
242 
243             // register the "cflow" aspects for this aspect bindings
244             // note: this one will even support cflow(xx && cflow())
245             // note: the cflow aspect MUST be registered FIRST for precedence purpose
246             // so that pcX && cflow(pcX) match on pcX
247             for (Iterator iterator = aspectDef.getAdviceDefinitions().iterator(); iterator.hasNext();) {
248                 AdviceDefinition adviceDefinition = (AdviceDefinition) iterator.next();
249                 List cflowBindings = CflowBinding.getCflowBindingsForCflowOf(adviceDefinition.getExpressionInfo());
250                 for (Iterator cflows = cflowBindings.iterator(); cflows.hasNext();) {
251                     CflowBinding cflowBinding = (CflowBinding) cflows.next();
252                     if (!cflowBinding.isCflowBelow()) {
253                         addAspect(cflowBinding.getAspectDefinition(this, aspectDef.getClassInfo().getClassLoader()));
254                     }
255                 }
256             }
257 
258             // register the aspect itself
259             m_aspectMap.put(aspectDef.getName(), aspectDef);
260 
261             // register the "cflowbelow" aspects for this aspect bindings
262             // note: this one will even support cflowbelow(xx && cflowbelow())
263             // note: the cflowbelow aspect MUST be registered LAST for precedence purpose
264             // so that pcX && cflowbelow(pcX) does not match on just the pcX joinpoint
265             for (Iterator iterator = aspectDef.getAdviceDefinitions().iterator(); iterator.hasNext();) {
266                 AdviceDefinition adviceDefinition = (AdviceDefinition) iterator.next();
267                 List cflowBindings = CflowBinding.getCflowBindingsForCflowOf(adviceDefinition.getExpressionInfo());
268                 for (Iterator cflows = cflowBindings.iterator(); cflows.hasNext();) {
269                     CflowBinding cflowBinding = (CflowBinding) cflows.next();
270                     if (cflowBinding.isCflowBelow()) {
271                         addAspect(cflowBinding.getAspectDefinition(this, aspectDef.getClassInfo().getClassLoader()));
272                     }
273                 }
274             }
275         }
276     }
277 
278     /***
279      * Adds a new aspect definition, overwrites the previous one with the same name (if there is one).
280      *
281      * @param aspectDef the aspect definition
282      */
283     public void addAspectOverwriteIfExists(final AspectDefinition aspectDef) {
284         if (aspectDef == null) {
285             throw new IllegalArgumentException("aspect definition can not be null");
286         }
287         synchronized (m_aspectMap) {
288             m_aspectMap.put(aspectDef.getName(), aspectDef);
289         }
290     }
291 
292     /***
293      * Adds a new mixin definition.
294      *
295      * @param mixinDef the mixin definition
296      */
297     public void addMixinDefinition(final MixinDefinition mixinDef) {
298         if (mixinDef == null) {
299             throw new IllegalArgumentException("mixin definition can not be null");
300         }
301         synchronized (m_mixinMap) {
302             if (m_mixinMap.containsKey(mixinDef.getMixinImpl().getName())) {
303                 MixinDefinition def = (MixinDefinition) m_mixinMap.get(mixinDef.getMixinImpl().getName());
304                 def.addExpressionInfos(mixinDef.getExpressionInfos());
305                 return;
306             }
307             m_mixinMap.put(mixinDef.getMixinImpl().getName(), mixinDef);
308         }
309     }
310 
311     /***
312      * Adds a new include package.
313      *
314      * @param includePackage the new include package
315      */
316     public void addIncludePackage(final String includePackage) {
317         synchronized (m_includePackages) {
318             m_includePackages.add(includePackage + '.');
319         }
320     }
321 
322     /***
323      * Adds a new exclude package.
324      *
325      * @param excludePackage the new exclude package
326      */
327     public void addExcludePackage(final String excludePackage) {
328         synchronized (m_excludePackages) {
329             m_excludePackages.add(excludePackage + '.');
330         }
331     }
332 
333     /***
334      * Adds a new prepare package.
335      *
336      * @param preparePackage the new prepare package
337      */
338     public void addPreparePackage(final String preparePackage) {
339         synchronized (m_preparePackages) {
340             m_preparePackages.add(preparePackage + '.');
341         }
342     }
343 
344     /***
345      * Returns the prepare packages.
346      *
347      * @return
348      */
349     public Set getPreparePackages() {
350         return m_preparePackages;
351     }
352 
353     /***
354      * Checks if a class should be included.
355      *
356      * @param className the name or the class
357      * @return boolean
358      */
359     public boolean inIncludePackage(final String className) {
360         if (className == null) {
361             throw new IllegalArgumentException("class name can not be null");
362         }
363         if (m_includePackages.isEmpty()) {
364             return true;
365         }
366         for (Iterator it = m_includePackages.iterator(); it.hasNext();) {
367             String packageName = (String) it.next();
368             >if (className.startsWith(packageName)) {
369                 return true;
370             }
371         }
372         return false;
373     }
374 
375     /***
376      * Checks if a class should be excluded.
377      *
378      * @param className the name or the class
379      * @return boolean
380      */
381     public boolean inExcludePackage(final String className) {
382         if (className == null) {
383             throw new IllegalArgumentException("class name can not be null");
384         }
385         for (Iterator it = m_excludePackages.iterator(); it.hasNext();) {
386             String packageName = (String) it.next();
387             >if (className.startsWith(packageName)) {
388                 return true;
389             }
390         }
391         return false;
392     }
393 
394     /***
395      * Checks if a class is in prepare declaration
396      *
397      * @param className the name or the class
398      * @return boolean
399      */
400     public boolean inPreparePackage(String className) {
401         if (className == null) {
402             throw new IllegalArgumentException("class name can not be null");
403         }
404         for (Iterator it = m_preparePackages.iterator(); it.hasNext();) {
405             String packageName = (String) it.next();
406             >if (className.startsWith(packageName)) {
407                 return true;
408             }
409         }
410         return false;
411     }
412 
413     /***
414      * Checks if a context has a pointcut.
415      *
416      * @param ctx the expression context
417      * @return boolean
418      */
419     public boolean hasPointcut(final ExpressionContext ctx) {
420         if (ctx == null) {
421             throw new IllegalArgumentException("context can not be null");
422         }
423         for (Iterator it = m_aspectMap.values().iterator(); it.hasNext();) {
424             AspectDefinition aspectDef = (AspectDefinition) it.next();
425             for (Iterator it2 = aspectDef.getAdviceDefinitions().iterator(); it2.hasNext();) {
426                 AdviceDefinition adviceDef = (AdviceDefinition) it2.next();
427                 final ExpressionInfo expressionInfo = adviceDef.getExpressionInfo();
428                 if (expressionInfo == null) {
429                     continue;
430                 }
431                 ExpressionVisitor expression = expressionInfo.getExpression();
432 
433                 if (expression.match(ctx)) {
434                     if (AspectWerkzPreProcessor.DETAILS) {
435                         System.out.println(
436                                 "[TRACE - match: " + expression + " @ "
437                                 + aspectDef.getQualifiedName() + "/" +
438                                 adviceDef.getName()
439                         );
440                         System.out.println("[       for     " + ctx.getReflectionInfo());
441                         System.out.println("[       within  " + ctx.getWithinReflectionInfo());
442                         System.out.println("[       type    " + ctx.getPointcutType().toString());
443                     }
444                     return true;
445                 }
446             }
447         }
448         return false;
449     }
450 
451     /***
452      * Checks if a class is advised.
453      *
454      * @param ctxs an array with the expression contexts
455      * @return boolean
456      */
457     public boolean isAdvised(final ExpressionContext[] ctxs) {
458         if (ctxs == null) {
459             throw new IllegalArgumentException("context array can not be null");
460         }
461         for (Iterator it = m_aspectMap.values().iterator(); it.hasNext();) {
462             AspectDefinition aspectDef = (AspectDefinition) it.next();
463             List advices = aspectDef.getAdviceDefinitions();
464             for (Iterator it2 = advices.iterator(); it2.hasNext();) {
465                 AdviceDefinition adviceDef = (AdviceDefinition) it2.next();
466                 for (int i = 0; i < ctxs.length; i++) {
467                     ExpressionContext ctx = ctxs[i];
468                     final ExpressionInfo expressionInfo = adviceDef.getExpressionInfo();
469                     if (expressionInfo == null) {
470                         continue;
471                     }
472                     if (expressionInfo.getAdvisedClassFilterExpression().match(ctx)) {
473                         if (AspectWerkzPreProcessor.DETAILS) {
474                             System.out.println(
475                                     "[TRACE - earlymatch: " + expressionInfo + " @ "
476                                     + aspectDef.getQualifiedName() + "/" +
477                                     adviceDef.getName()
478                             );
479                             System.out.println("[       for          " + ctx.getReflectionInfo());
480                             System.out.println("[       within       " + ctx.getWithinReflectionInfo());
481                             System.out.println("[       type         " + ctx.getPointcutType().toString());
482                         }
483                         return true;
484                     }
485                 }
486             }
487         }
488         return false;
489     }
490 
491     /***
492      * Checks if a class is advised.
493      *
494      * @param ctx the expression context
495      * @return boolean
496      */
497     public boolean isAdvised(final ExpressionContext ctx) {
498         if (ctx == null) {
499             throw new IllegalArgumentException("context can not be null");
500         }
501         for (Iterator it = m_aspectMap.values().iterator(); it.hasNext();) {
502             AspectDefinition aspectDef = (AspectDefinition) it.next();
503             List advices = aspectDef.getAdviceDefinitions();
504             for (Iterator it2 = advices.iterator(); it2.hasNext();) {
505                 AdviceDefinition adviceDef = (AdviceDefinition) it2.next();
506                 final ExpressionInfo expressionInfo = adviceDef.getExpressionInfo();
507                 if (expressionInfo == null) {
508                     continue;
509                 }
510                 if (expressionInfo.getAdvisedClassFilterExpression().match(ctx) /*||
511                     expressionInfo.getAdvisedCflowClassFilterExpression().match(ctx) ALEX XXX CFLOW*/ ) {
512                     return true;
513                 }
514             }
515         }
516         return false;
517     }
518 
519     /***
520      * Checks if a class has an mixin.
521      *
522      * @param ctxs an array with the expression contexts
523      * @return boolean
524      */
525     public boolean hasMixin(final ExpressionContext[] ctxs) {
526         if (ctxs == null) {
527             throw new IllegalArgumentException("context array can not be null");
528         }
529         for (Iterator it = m_mixinMap.values().iterator(); it.hasNext();) {
530             MixinDefinition introDef = (MixinDefinition) it.next();
531             ExpressionInfo[] expressionInfos = introDef.getExpressionInfos();
532             for (int i = 0; i < expressionInfos.length; i++) {
533                 ExpressionInfo expressionInfo = expressionInfos[i];
534                 for (int j = 0; j < ctxs.length; j++) {
535                     if (expressionInfo.getExpression().match(ctxs[j])) {
536                         return true;
537                     }
538                 }
539             }
540         }
541         return false;
542     }
543 
544     /***
545      * Checks if a class has an mixin.
546      *
547      * @param ctx the expression context
548      * @return boolean
549      */
550     public boolean hasMixin(final ExpressionContext ctx) {
551         if (ctx == null) {
552             throw new IllegalArgumentException("context can not be null");
553         }
554         for (Iterator it = m_mixinMap.values().iterator(); it.hasNext();) {
555             MixinDefinition introDef = (MixinDefinition) it.next();
556             ExpressionInfo[] expressionInfos = introDef.getExpressionInfos();
557             for (int i = 0; i < expressionInfos.length; i++) {
558                 ExpressionInfo expressionInfo = expressionInfos[i];
559                 if (expressionInfo.getExpression().match(ctx)) {
560                     return true;
561                 }
562             }
563         }
564         return false;
565     }
566 
567     /***
568      * Checks if a class is advised with an interface introduction.
569      *
570      * @param ctxs the expression contexts
571      * @return boolean
572      */
573     public boolean hasIntroducedInterface(final ExpressionContext[] ctxs) {
574         if (ctxs == null) {
575             throw new IllegalArgumentException("context array can not be null");
576         }
577         for (Iterator iterator = m_aspectMap.values().iterator(); iterator.hasNext();) {
578             AspectDefinition aspectDef = (AspectDefinition) iterator.next();
579             for (Iterator it = aspectDef.getInterfaceIntroductionDefinitions().iterator(); it.hasNext();) {
580                 InterfaceIntroductionDefinition introDef = (InterfaceIntroductionDefinition) it.next();
581                 ExpressionInfo[] expressionInfos = introDef.getExpressionInfos();
582                 for (int i = 0; i < expressionInfos.length; i++) {
583                     ExpressionInfo expressionInfo = expressionInfos[i];
584                     for (int j = 0; j < ctxs.length; j++) {
585                         if (expressionInfo.getExpression().match(ctxs[i])) {
586                             return true;
587                         }
588                     }
589                 }
590             }
591         }
592         return false;
593     }
594 
595     /***
596      * Checks if a class is advised with an interface introduction.
597      *
598      * @param ctx the expression context
599      * @return boolean
600      */
601     public boolean hasIntroducedInterface(final ExpressionContext ctx) {
602         if (ctx == null) {
603             throw new IllegalArgumentException("context can not be null");
604         }
605         for (Iterator iterator = m_aspectMap.values().iterator(); iterator.hasNext();) {
606             AspectDefinition aspectDefinition = (AspectDefinition) iterator.next();
607             for (Iterator it = aspectDefinition.getInterfaceIntroductionDefinitions().iterator(); it.hasNext();) {
608                 InterfaceIntroductionDefinition introDef = (InterfaceIntroductionDefinition) it.next();
609                 ExpressionInfo[] expressionInfos = introDef.getExpressionInfos();
610                 for (int i = 0; i < expressionInfos.length; i++) {
611                     ExpressionInfo expressionInfo = expressionInfos[i];
612                     if (expressionInfo.getExpression().match(ctx)) {
613                         return true;
614                     }
615                 }
616             }
617         }
618         return false;
619     }
620 
621     /***
622      * Returns a collection with all deployment scopes in the system.
623      *
624      * @return a collection with all deployment scopes  in the system
625      */
626     public Collection getDeploymentScopes() {
627         return m_deploymentScopes.values();
628     }
629 
630     /***
631      * Returns the deployment scope with the name specified.
632      *
633      * @param name the name of the deployment scope
634      * @return the deployment scope with the name specified
635      */
636     public DeploymentScope getDeploymentScope(final String name) {
637         return (DeploymentScope) m_deploymentScopes.get(name);
638     }
639 
640     /***
641      * Adds a deployment scope to the system.
642      *
643      * @param deploymentScope the deployment scope
644      */
645     public void addDeploymentScope(final DeploymentScope deploymentScope) {
646         m_deploymentScopes.put(deploymentScope.getName(), deploymentScope);
647 
648         //TODO do we need to take care of cflow aspects
649     }
650 
651     public boolean equals(Object o) {
652         return ((SystemDefinition) o).m_uuid.equals(m_uuid);
653     }
654 
655     public int hashCode() {
656         return m_uuid.hashCode();
657     }
658 
659     /***
660      * Create a new virtual system definition for the given loader and add the virtual aspect in it.
661      * 
662      * @param loader
663      * @return
664      */
665     public static SystemDefinition createVirtualDefinitionAt(ClassLoader loader) {
666         SystemDefinition def = new SystemDefinition(SystemDefinitionContainer.getVirtualDefinitionUuid(loader));
667         DocumentParser.addVirtualAspect(def);
668         return def;
669     }
670 }