KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > codehaus > aspectwerkz > aspect > management > Aspects


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.aspect.AspectContainer;
11 import org.codehaus.aspectwerkz.aspect.DefaultAspectContainerStrategy;
12 import org.codehaus.aspectwerkz.AspectContext;
13 import org.codehaus.aspectwerkz.cflow.CflowCompiler;
14 import org.codehaus.aspectwerkz.util.ContextClassLoader;
15 import org.codehaus.aspectwerkz.definition.AspectDefinition;
16 import org.codehaus.aspectwerkz.definition.SystemDefinition;
17 import org.codehaus.aspectwerkz.definition.SystemDefinitionContainer;
18 import org.codehaus.aspectwerkz.exception.DefinitionException;
19
20 import java.util.*;
21 import java.lang.reflect.Constructor JavaDoc;
22 import java.lang.reflect.InvocationTargetException JavaDoc;
23
24 import gnu.trove.TIntObjectHashMap;
25
26 /**
27  * Manages the aspects, registry for the aspect containers (one container per aspect type).
28  *
29  * @author <a HREF="mailto:jboner@codehaus.org">Jonas Bonér </a>
30  * @author <a HREF="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
31  */

32 public class Aspects {
33
34     /**
35      * The default aspect container class.
36      */

37     public static final String JavaDoc DEFAULT_ASPECT_CONTAINER = DefaultAspectContainerStrategy.class.getName();
38
39     /**
40      * Map of TIntHashMap, whose key is containerClass. The TIntHashMap maps container instance, whith
41      * CompositeVisibleFromQNameKey as a key
42      * as a key.
43      * <p/>
44      * TODO:
45      * we end up in having one entry with a list that strong refs the container instances
46      * ie leaks since the DefaultContainer leaves in system CL.
47      */

48     private static Map ASPECT_CONTAINER_LISTS = new WeakHashMap();
49
50     /**
51      * Returns the aspect container class for the given aspect class qName.
52      * The qName is returned since we may have only the aspect class name upon lookup
53      *
54      * @param visibleFrom class loader to look from
55      * @param qName
56      * @return the container class
57      */

58     public static String JavaDoc[] getAspectQNameContainerClassName(final ClassLoader JavaDoc visibleFrom, final String JavaDoc qName) {
59         AspectDefinition aspectDefinition = lookupAspectDefinition(visibleFrom, qName);
60         return new String JavaDoc[]{aspectDefinition.getQualifiedName(), aspectDefinition.getContainerClassName()};
61     }
62
63     /**
64      * Returns or create the aspect container for the given container class with the given aspect qualified name
65      * <p/>
66      * We keep a weak key for the containerClass, and we then keep a list of container instance based on a composite key
67      * based on the tuple {visibleFromClassLoader.hashCode, aspectQName}, so that when hot deploying a web app, the
68      * aspects gets tied to the web app class loader even when the container class is higher up (f.e. in aw.jar)
69      *
70      * @param visibleFrom class loader hosting the advised class ie from where all is visible
71      * @param containerClass
72      * @param qName
73      * @return
74      */

75     public static AspectContainer getContainerQNamed(final ClassLoader JavaDoc visibleFrom, final Class JavaDoc containerClass, final String JavaDoc qName) {
76         synchronized (ASPECT_CONTAINER_LISTS) {
77             TIntObjectHashMap containers = (TIntObjectHashMap) ASPECT_CONTAINER_LISTS.get(containerClass);
78             if (containers == null) {
79                 containers = new TIntObjectHashMap();
80                 ASPECT_CONTAINER_LISTS.put(containerClass, containers);
81             }
82             AspectContainer container = (AspectContainer) containers.get(CompositeVisibleFromQNameKey.hash(visibleFrom, qName));
83             if (container == null) {
84                 container = createAspectContainer(visibleFrom, containerClass, qName);
85                 containers.put(CompositeVisibleFromQNameKey.hash(visibleFrom, qName), container);
86             }
87             return container;
88         }
89     }
90
91     /**
92      * Returns the singleton aspect instance for the aspect with the given qualified name.
93      * The aspect is looked up from the thread context classloader.
94      *
95      * @param qName the qualified name of the aspect
96      * @return the singleton aspect instance
97      */

98     public static Object JavaDoc aspectOf(final String JavaDoc qName) {
99         return aspectOf(Thread.currentThread().getContextClassLoader(), qName);
100     }
101
102     /**
103      * Returns the singleton aspect instance for the given aspect class.
104      * Consider using aspectOf(visibleFrom, qName) if the aspect is used more than once
105      * or if it is used in a class loader which is child of its own classloader.
106      *
107      * @param aspectClass the class of the aspect
108      * @return the singleton aspect instance
109      */

110     public static Object JavaDoc aspectOf(final Class JavaDoc aspectClass) {
111         String JavaDoc aspectClassName = aspectClass.getName().replace('/', '.');
112         return aspectOf(aspectClass.getClassLoader(), aspectClassName);
113     }
114
115     /**
116      * Returns the singleton aspect instance for given aspect qName, with visibility from the given class loader
117      *
118      * @param visibleFrom the class loader from where aspect is visible, likely to be the class loader of the
119      * advised classes, or the one where the system hosting the aspect is deployed.
120      * @return the singleton aspect instance
121      */

122     public static Object JavaDoc aspectOf(final ClassLoader JavaDoc visibleFrom, final String JavaDoc qName) {
123         String JavaDoc[] qNameContainerClassName = getAspectQNameContainerClassName(visibleFrom, qName);
124         return aspect$Of(visibleFrom, qNameContainerClassName[0], qNameContainerClassName[1]);
125     }
126
127     /**
128      * Returns the per class aspect attached to targetClass
129      * Consider using aspectOf(qName, targetClass) if the aspect is used more than once
130      *
131      * @param aspectClass the name of the aspect
132      * @param targetClass the target class
133      * @return the per class aspect instance
134      */

135     public static Object JavaDoc aspectOf(final Class JavaDoc aspectClass, final Class JavaDoc targetClass) {
136         String JavaDoc aspectClassName = aspectClass.getName().replace('/', '.');
137         return aspectOf(aspectClassName, targetClass);
138     }
139
140     /**
141      * Returns the per class aspect instance for the aspect with the given qualified name for targetClass
142      *
143      * @param qName the qualified name of the aspect
144      * @param targetClass the target class
145      * @return the per class aspect instance
146      */

147     public static Object JavaDoc aspectOf(final String JavaDoc qName, final Class JavaDoc targetClass) {
148         // look up from the targetClass loader is enough in that case
149
String JavaDoc[] qNameContainerClassName = getAspectQNameContainerClassName(targetClass.getClassLoader(), qName);
150         return aspect$Of(qNameContainerClassName[0], qNameContainerClassName[1], targetClass);
151     }
152
153     /**
154      * Returns the per instance aspect attached to targetInstance
155      * Consider using aspectOf(qName, targetInstance) if the aspect is used more than once
156      *
157      * @param aspectClass the name of the aspect
158      * @param targetInstance the target instance
159      * @return the per class aspect instance
160      */

161     public static Object JavaDoc aspectOf(final Class JavaDoc aspectClass, final Object JavaDoc targetInstance) {
162         String JavaDoc aspectClassName = aspectClass.getName().replace('/', '.');
163         return aspectOf(aspectClassName, targetInstance);
164     }
165
166     /**
167      * Returns the per instance aspect attached to targetInstance
168      *
169      * @param qName the qualified name of the aspect
170      * @param targetInstance the target instance
171      * @return the per class aspect instance
172      */

173     public static Object JavaDoc aspectOf(final String JavaDoc qName, final Object JavaDoc targetInstance) {
174         // look up from the targetInstance loader is enough in that case
175
String JavaDoc[] qNameContainerClassName = getAspectQNameContainerClassName(targetInstance.getClass().getClassLoader(), qName);
176         return aspect$Of(qNameContainerClassName[0], qNameContainerClassName[1], targetInstance);
177     }
178
179     //---------- weaver exposed
180

181     public static Object JavaDoc aspect$Of(ClassLoader JavaDoc loader, String JavaDoc qName, String JavaDoc containerClassName) {
182         try {
183             Class JavaDoc containerClass = ContextClassLoader.forName(loader, containerClassName);
184             return getContainerQNamed(loader, containerClass, qName).aspectOf();
185         } catch (Throwable JavaDoc t) {
186             throw new NoAspectBoundException(t, qName);
187         }
188     }
189
190     public static Object JavaDoc aspect$Of(String JavaDoc qName, String JavaDoc containerClassName, final Class JavaDoc perClass) {
191         try {
192             ClassLoader JavaDoc loader = perClass.getClassLoader();
193             Class JavaDoc containerClass = ContextClassLoader.forName(loader, containerClassName);
194             return getContainerQNamed(loader, containerClass, qName).aspectOf(perClass);
195         } catch (Throwable JavaDoc t) {
196             throw new NoAspectBoundException(t, qName);
197         }
198     }
199
200     public static Object JavaDoc aspect$Of(String JavaDoc qName, String JavaDoc containerClassName, final Object JavaDoc perInstance) {
201         try {
202             ClassLoader JavaDoc loader = perInstance.getClass().getClassLoader();
203             Class JavaDoc containerClass = ContextClassLoader.forName(loader, containerClassName);
204             return getContainerQNamed(loader, containerClass, qName).aspectOf(perInstance);
205         } catch (Throwable JavaDoc t) {
206             throw new NoAspectBoundException(t, qName);
207         }
208     }
209
210
211
212
213     //---------- helpers
214

215     /**
216      * Creates a new aspect container.
217      *
218      * @param visibleFrom class loader of the advised class from all is visible
219      * @param containerClass the container class
220      * @param qName the aspect qualified name
221      */

222     private static AspectContainer createAspectContainer(final ClassLoader JavaDoc visibleFrom, final Class JavaDoc containerClass, final String JavaDoc qName) {
223         AspectDefinition aspectDefinition = lookupAspectDefinition(visibleFrom, qName);
224
225         Class JavaDoc aspectClass = null;
226         try {
227             aspectClass = ContextClassLoader.forName(visibleFrom, aspectDefinition.getClassName());
228         } catch (Throwable JavaDoc t) {
229             throw new NoAspectBoundException(t, qName);
230         }
231
232         try {
233             Constructor JavaDoc constructor = containerClass.getConstructor(new Class JavaDoc[]{AspectContext.class});
234             final AspectContext aspectContext = new AspectContext(
235                     aspectDefinition.getSystemDefinition().getUuid(),
236                     aspectClass,
237                     aspectDefinition.getName(),
238                     aspectDefinition.getDeploymentModel(),
239                     aspectDefinition,
240                     aspectDefinition.getParameters()
241             );
242             final AspectContainer container = (AspectContainer) constructor.newInstance(new Object JavaDoc[]{aspectContext});
243             aspectContext.setContainer(container);
244             return container;
245         } catch (InvocationTargetException JavaDoc e) {
246             throw new NoAspectBoundException(e, qName);
247         } catch (NoSuchMethodException JavaDoc e) {
248             throw new NoAspectBoundException(
249                     "AspectContainer does not have a valid constructor ["
250                     + containerClass.getName()
251                     + "] need to take an AspectContext instance as its only parameter: "
252                     + e.toString(),
253                     qName
254             );
255         } catch (Throwable JavaDoc e) {
256             StringBuffer JavaDoc cause = new StringBuffer JavaDoc();
257             cause.append("Could not create AspectContainer using the implementation specified [");
258             cause.append(containerClass.getName());
259             cause.append("] for ").append(qName);
260             throw new NoAspectBoundException(e, cause.toString());
261         }
262     }
263
264     /**
265      * Lookup the aspect definition with the given qName, visible from the given loader.
266      * If qName is a class name only, the fallback will ensure only one aspect use is found.
267      *
268      * @param visibleFrom
269      * @param qName
270      * @return
271      */

272     private static AspectDefinition lookupAspectDefinition(final ClassLoader JavaDoc visibleFrom, final String JavaDoc qName) {
273         AspectDefinition aspectDefinition = null;
274
275         Set definitions = SystemDefinitionContainer.getDefinitionsFor(visibleFrom);
276         if (qName.indexOf('/')>0) {
277             // has system uuid ie real qName
278
for (Iterator iterator = definitions.iterator(); iterator.hasNext();) {
279                 SystemDefinition systemDefinition = (SystemDefinition) iterator.next();
280                 for (Iterator iterator1 = systemDefinition.getAspectDefinitions().iterator(); iterator1.hasNext();) {
281                     AspectDefinition aspectDef = (AspectDefinition) iterator1.next();
282                     if (qName.equals(aspectDef.getQualifiedName())) {
283                         aspectDefinition = aspectDef;
284                         break;
285                     }
286                 }
287             }
288         } else {
289             // fallback on class name lookup
290
// must find at most one
291
int found = 0;
292             for (Iterator iterator = definitions.iterator(); iterator.hasNext();) {
293                 SystemDefinition systemDefinition = (SystemDefinition) iterator.next();
294                 for (Iterator iterator1 = systemDefinition.getAspectDefinitions().iterator(); iterator1.hasNext();) {
295                     AspectDefinition aspectDef = (AspectDefinition) iterator1.next();
296                     if (qName.equals(aspectDef.getClassName())) {
297                         aspectDefinition = aspectDef;
298                         found++;
299                     }
300                 }
301             }
302             if (found > 1) {
303                 throw new NoAspectBoundException("More than one AspectDefinition found, consider using other API methods", qName);
304             }
305
306         }
307
308         if (aspectDefinition == null) {
309             throw new NoAspectBoundException("Could not find AspectDefinition", qName);
310         }
311
312         return aspectDefinition;
313     }
314
315 // /**
316
// * Looks up the aspect class name, based on the qualified name of the aspect.
317
// *
318
// * @param loader
319
// * @param qualifiedName
320
// * @return
321
// */
322
// private static String lookupAspectClassName(final ClassLoader loader, final String qualifiedName) {
323
// if (qualifiedName.indexOf('/') <= 0) {
324
// // no uuid
325
// return null;
326
// }
327
//
328
// final Set definitionsBottomUp = SystemDefinitionContainer.getDefinitionsFor(loader);
329
// // TODO: bottom up is broken now
330
// //Collections.reverse(definitionsBottomUp);
331
//
332
// for (Iterator iterator = definitionsBottomUp.iterator(); iterator.hasNext();) {
333
// SystemDefinition definition = (SystemDefinition) iterator.next();
334
// for (Iterator iterator1 = definition.getAspectDefinitions().iterator(); iterator1.hasNext();) {
335
// AspectDefinition aspectDefinition = (AspectDefinition) iterator1.next();
336
// if (qualifiedName.equals(aspectDefinition.getQualifiedName())) {
337
// return aspectDefinition.getClassName();
338
// }
339
// }
340
// }
341
// return null;
342
// }
343

344     /**
345      * Class is non-instantiable.
346      */

347     private Aspects() {
348     }
349
350     /**
351      * A composite key to ensure uniqueness of the container key even upon application redeployment
352      * when the classloader gets swapped.
353      *
354      * TODO: we could have a weak ref to the CL, and use it as a weak key in a map then to ensure
355      * release of any container when the visibleFromCL gets dropped (which can be different from
356      * the aspect container CL)?
357      *
358      * @author <a HREF="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
359      */

360     private static class CompositeVisibleFromQNameKey {
361 // private final int m_hash;
362
// private CompositeVisibleFromQNameKey(ClassLoader loader, String qName) {
363
// m_hash = hash(loader, qName);
364
// }
365
//
366
// public boolean equals(Object o) {
367
// if (this == o) return true;
368
// if (!(o instanceof CompositeVisibleFromQNameKey)) return false;
369
//
370
// final CompositeVisibleFromQNameKey compositeVisibleFromQNameKey = (CompositeVisibleFromQNameKey) o;
371
//
372
// if (m_hash != compositeVisibleFromQNameKey.m_hash) return false;
373
//
374
// return true;
375
// }
376
//
377
// public int hashCode() {
378
// return m_hash;
379
// }
380

381         /**
382          * Hashing strategy
383          *
384          * @param loader
385          * @param qName
386          * @return
387          */

388         public static int hash(ClassLoader JavaDoc loader, String JavaDoc qName) {
389             int result;
390             result = (loader != null ? loader.hashCode() : 0);
391             result = 29 * result + qName.hashCode();
392             return result;
393         }
394     }
395 }
396
Popular Tags