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.aspect.management;
9   
10  import org.codehaus.aspectwerkz.AspectSystem;
11  import org.codehaus.aspectwerkz.ConstructorTuple;
12  import org.codehaus.aspectwerkz.ContextClassLoader;
13  import org.codehaus.aspectwerkz.CrossCuttingInfo;
14  import org.codehaus.aspectwerkz.DeploymentModel;
15  import org.codehaus.aspectwerkz.AdviceInfo;
16  import org.codehaus.aspectwerkz.MethodTuple;
17  import org.codehaus.aspectwerkz.Mixin;
18  import org.codehaus.aspectwerkz.annotation.AspectAnnotationParser;
19  import org.codehaus.aspectwerkz.aspect.AspectContainer;
20  import org.codehaus.aspectwerkz.definition.AspectDefinition;
21  import org.codehaus.aspectwerkz.definition.StartupManager;
22  import org.codehaus.aspectwerkz.definition.SystemDefinition;
23  import org.codehaus.aspectwerkz.expression.ExpressionContext;
24  import org.codehaus.aspectwerkz.util.Util;
25  
26  import java.lang.reflect.Field;
27  import java.util.ArrayList;
28  import java.util.Collection;
29  import java.util.HashMap;
30  import java.util.List;
31  import java.util.Map;
32  import java.util.WeakHashMap;
33  
34  /***
35   * Manages the aspects. <p/>Handles deployment, redeployment, management, configuration or redefinition of the aspects.
36   * 
37   * @author <a href="mailto:jboner@codehaus.org">Jonas Bonér </a>
38   * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
39   * @TODO: Must handle : - undeployment of the aspects - notification of all the pointcuts that it should remove a
40   *        certain advice from the pointcut - notification of the JoinPoinManager.
41   */
42  public final class AspectManager {
43      /***
44       * The system this AspectManager is defined in.
45       */
46      public final AspectSystem m_system;
47  
48      /***
49       * The definition.
50       */
51      public SystemDefinition m_definition;
52  
53      /***
54       * The aspect registry.
55       */
56      private final AspectRegistry m_aspectRegistry;
57  
58      /***
59       * Cache for the pointcuts.
60       * 
61       * @TODO: when unweaving (and reordering) of aspects is supported then this cache must have a way of being
62       *        invalidated.
63       */
64      private final Map m_pointcutCache = new WeakHashMap();
65  
66      /***
67       * Cache for the cflow pointcuts.
68       * 
69       * @TODO: when unweaving (and reordering) of aspects is supported then this cache must have a way of being
70       *        invalidated.
71       */
72      private final Map m_cflowPointcutCache = new WeakHashMap();
73  
74      /***
75       * Creates a new aspect manager.
76       * 
77       * @param system the system
78       * @param definition the system definition
79       */
80      public AspectManager(final AspectSystem system, final SystemDefinition definition) {
81          m_system = system;
82          m_definition = definition;
83          m_aspectRegistry = new AspectRegistry(this, m_definition);
84      }
85  
86      /***
87       * Initializes the manager. The initialization needs to be separated fromt he construction of the manager, and is
88       * triggered by the runtime system.
89       */
90      public void initialize() {
91          m_aspectRegistry.initialize();
92      }
93  
94      /***
95       * Registers a new aspect.
96       * 
97       * @param container the containern for the aspect to register
98       * @param aspectMetaData the aspect meta-data
99       */
100     public void register(final AspectContainer container, final PointcutManager aspectMetaData) {
101         m_aspectRegistry.register(container, aspectMetaData);
102     }
103 
104     /***
105      * Creates and registers new aspect at runtime.
106      * 
107      * @param name the name of the aspect
108      * @param aspectClassName the class name of the aspect
109      * @param deploymentModel the deployment model for the aspect (constants in the DeploymemtModel class, e.g. f.e.
110      *            DeploymentModel.PER_JVM)
111      * @param loader an optional class loader (if null it uses the context classloader)
112      */
113     public void createAspect(
114         final String name,
115         final String aspectClassName,
116         final int deploymentModel,
117         final ClassLoader loader) {
118         if (name == null) {
119             throw new IllegalArgumentException("aspect name can not be null");
120         }
121         if (aspectClassName == null) {
122             throw new IllegalArgumentException("class name can not be null");
123         }
124         if ((deploymentModel < 0) || (deploymentModel > 3)) {
125             throw new IllegalArgumentException(deploymentModel + " is not a valid deployment model type");
126         }
127         Class aspectClass = null;
128         try {
129             if (loader == null) {
130                 aspectClass = ContextClassLoader.loadClass(aspectClassName);
131             } else {
132                 aspectClass = loader.loadClass(aspectClassName);
133             }
134         } catch (Exception e) {
135             StringBuffer msg = new StringBuffer();
136             msg.append("could not load aspect class [");
137             msg.append(aspectClassName);
138             msg.append("] with name ");
139             msg.append(name);
140             msg.append(": ");
141             msg.append(e.toString());
142             throw new RuntimeException(msg.toString());
143         }
144 
145         // create the aspect definition
146         AspectDefinition aspectDef = new AspectDefinition(aspectClassName, aspectClassName, m_definition.getUuid());
147         aspectDef.setDeploymentModel(DeploymentModel.getDeploymentModelAsString(deploymentModel));
148 
149         // parse the class attributes and create a definition
150         AspectAnnotationParser.parse(aspectClass, aspectDef, m_definition);
151         m_definition.addAspect(aspectDef);
152         CrossCuttingInfo crossCuttingInfo = new CrossCuttingInfo(
153             null,
154             aspectClass,
155             aspectDef.getName(),
156             deploymentModel,
157             aspectDef,
158             new HashMap());
159         AspectContainer container = StartupManager.createAspectContainer(crossCuttingInfo);
160         crossCuttingInfo.setContainer(container);
161         m_aspectRegistry.register(container, new PointcutManager(name, deploymentModel));
162     }
163 
164     /***
165      * Returns the UUID for the system.
166      * 
167      * @return the UUID
168      */
169     public String getUuid() {
170         return m_definition.getUuid();
171     }
172 
173     /***
174      * Returns the aspect container by its index.
175      * 
176      * @param index the index of the aspect
177      * @return the aspect
178      */
179     public AspectContainer getAspectContainer(final int index) {
180         return m_aspectRegistry.getAspectContainer(index);
181     }
182 
183     /***
184      * Returns the aspect container for a specific name.
185      * 
186      * @param name the name of the aspect
187      * @return the the aspect prototype
188      */
189     public AspectContainer getAspectContainer(final String name) {
190         return m_aspectRegistry.getAspectContainer(name);
191     }
192 
193     /***
194      * Returns an array with all the aspect containers.
195      * 
196      * @return the aspect containers
197      */
198     public AspectContainer[] getAspectContainers() {
199         return m_aspectRegistry.getAspectContainers();
200     }
201 
202     /***
203      * Returns the aspect for a specific name, deployed as perJVM.
204      * 
205      * @param name the name of the aspect
206      * @return the the aspect
207      */
208     public Object getCrossCuttingInfo(final String name) {
209         return m_aspectRegistry.getCrossCuttingInfo(name);
210     }
211 
212     /***
213      * Returns an array with all the cross-cutting infos.
214      * 
215      * @return the cross-cutting infos
216      */
217     public CrossCuttingInfo[] getCrossCuttingInfos() {
218         AspectContainer[] aspectContainers = m_aspectRegistry.getAspectContainers();
219         CrossCuttingInfo[] infos = new CrossCuttingInfo[aspectContainers.length];
220         for (int i = 0; i < aspectContainers.length; i++) {
221             AspectContainer aspectContainer = aspectContainers[i];
222             infos[i] = aspectContainer.getCrossCuttingInfo();
223         }
224         return infos;
225     }
226 
227     /***
228      * Retrieves a specific mixin based on its index.
229      * 
230      * @param index the index of the introduction (aspect in this case)
231      * @return the the mixin (aspect in this case)
232      */
233     public Mixin getMixin(final int index) {
234         return m_aspectRegistry.getMixin(index);
235     }
236 
237     /***
238      * Returns the mixin implementation for a specific name.
239      * 
240      * @param name the name of the introduction (aspect in this case)
241      * @return the the mixin (aspect in this case)
242      */
243     public Mixin getMixin(final String name) {
244         return m_aspectRegistry.getMixin(name);
245     }
246 
247     /***
248      * Returns the index for a specific name to aspect mapping.
249      * 
250      * @param name the name of the aspect
251      * @return the index of the aspect
252      */
253     public int getAspectIndexFor(final String name) {
254         return m_aspectRegistry.getAspectIndexFor(name);
255     }
256 
257     /***
258      * Returns the index for a specific name to advice mapping.
259      * 
260      * @param name the name of the advice
261      * @return the index of the advice
262      */
263     public AdviceInfo getAdviceIndexFor(final String name) {
264         return m_aspectRegistry.getAdviceIndexFor(name);
265     }
266 
267     /***
268      * Returns the pointcut manager for the name specified.
269      * 
270      * @param name the name of the aspect
271      * @return thepointcut manager
272      */
273     public PointcutManager getPointcutManager(final String name) {
274         return m_aspectRegistry.getPointcutManager(name);
275     }
276 
277     /***
278      * Returns a list with all the pointcut managers.
279      * 
280      * @return thepointcut managers
281      */
282     public Collection getPointcutManagers() {
283         return m_aspectRegistry.getPointcutManagers();
284     }
285 
286     /***
287      * Returns the pointcut list for the context specified. <p/>Caches the list, needed since the actual method call is
288      * expensive and is made each time a new instance of an advised class is created.
289      * 
290      * @param ctx the expression context
291      * @return the pointcuts for this join point
292      */
293     public List getPointcuts(final ExpressionContext ctx) {
294         if (ctx == null) {
295             throw new IllegalArgumentException("expression context can not be null");
296         }
297         initialize();
298         List pointcuts;
299         if (m_pointcutCache.containsKey(ctx)) {
300             pointcuts = (List) m_pointcutCache.get(ctx);
301             if (pointcuts == null) { // strange enough, but can be null
302                 System.out.println("AspectManager.getPointcuts " + "**IS NULL**");
303                 pointcuts = new ArrayList();
304             }
305         } else {
306             pointcuts = m_aspectRegistry.getPointcuts(ctx);
307             synchronized (m_pointcutCache) {
308                 m_pointcutCache.put(ctx, pointcuts);
309             }
310         }
311         return pointcuts;
312     }
313 
314     /***
315      * Returns the cflow pointcut list for the context specified. <p/>Caches the list, needed since the actual method
316      * call is expensive and is made each time a new instance of an advised class is created.
317      * 
318      * @param ctx the expression context
319      * @return the pointcuts for this join point
320      */
321     public List getCflowPointcuts(final ExpressionContext ctx) {
322         if (ctx == null) {
323             throw new IllegalArgumentException("expression context can not be null");
324         }
325         initialize();
326         List pointcuts;
327         if (m_cflowPointcutCache.containsKey(ctx)) {
328             pointcuts = (List) m_cflowPointcutCache.get(ctx);
329             if (pointcuts == null) { // strange enough, but can be null
330                 pointcuts = new ArrayList();
331             }
332         } else {
333             pointcuts = m_aspectRegistry.getCflowPointcuts(ctx);
334             synchronized (m_cflowPointcutCache) {
335                 m_cflowPointcutCache.put(ctx, pointcuts);
336             }
337         }
338         return pointcuts;
339     }
340 
341     /***
342      * Checks if a specific class has an aspect defined.
343      * 
344      * @param name the name of the aspect
345      * @return boolean true if the class has an aspect defined
346      */
347     public boolean hasAspect(final String name) {
348         return m_aspectRegistry.hasAspect(name);
349     }
350 
351     /***
352      * Returns a specific method by the class and the method index.
353      * 
354      * @param klass the class housing the method
355      * @param methodHash the method hash
356      * @return the method
357      */
358     public MethodTuple getMethodTuple(final Class klass, final int methodHash) {
359         return AspectRegistry.getMethodTuple(klass, methodHash);
360     }
361 
362     /***
363      * Returns a specific constructor by the class and the constructor index.
364      * 
365      * @param klass the class housing the method
366      * @param constructorHash the method hash
367      * @return the constructor
368      */
369     public ConstructorTuple getConstructorTuple(final Class klass, final int constructorHash) {
370         return AspectRegistry.getConstructorTuple(klass, constructorHash);
371     }
372 
373     /***
374      * Returns a specific field by the class and the field index.
375      * 
376      * @param klass the class housing the method
377      * @param fieldHash the method hash
378      * @return the field
379      */
380     public Field getField(final Class klass, final int fieldHash) {
381         return AspectRegistry.getField(klass, fieldHash);
382     }
383 
384     /***
385      * Returns the string representation of the manager.
386      * 
387      * @return
388      */
389     public String toString() {
390         StringBuffer sb = new StringBuffer("AspectManager::");
391         sb.append(this.hashCode());
392         sb.append("[").append(m_definition.getUuid());
393         sb.append(" @ ").append(Util.classLoaderToString(m_system.getDefiningClassLoader()));
394         sb.append("]");
395         return sb.toString();
396     }
397 }