1 /***************************************************************************************
2 * Copyright (c) Jonas Bonr, 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;
22 import java.lang.reflect.InvocationTargetException;
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 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[] getAspectQNameContainerClassName(final ClassLoader visibleFrom, final String qName) {
59 AspectDefinition aspectDefinition = lookupAspectDefinition(visibleFrom, qName);
60 return new String[]{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 visibleFrom, final Class containerClass, final String 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 aspectOf(final String 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 aspectOf(final Class aspectClass) {
111 String 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 aspectOf(final ClassLoader visibleFrom, final String qName) {
123 String[] 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 aspectOf(final Class aspectClass, final Class targetClass) {
136 String 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 aspectOf(final String qName, final Class targetClass) {
148
149 String[] 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 aspectOf(final Class aspectClass, final Object targetInstance) {
162 String 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 aspectOf(final String qName, final Object targetInstance) {
174
175 String[] qNameContainerClassName = getAspectQNameContainerClassName(targetInstance.getClass().getClassLoader(), qName);
176 return aspect$Of(qNameContainerClassName[0], qNameContainerClassName[1], targetInstance);
177 }
178
179
180
181 public static Object aspect$Of(ClassLoader loader, String qName, String containerClassName) {
182 try {
183 Class containerClass = ContextClassLoader.forName(loader, containerClassName);
184 return getContainerQNamed(loader, containerClass, qName).aspectOf();
185 } catch (Throwable t) {
186 throw new NoAspectBoundException(t, qName);
187 }
188 }
189
190 public static Object aspect$Of(String qName, String containerClassName, final Class perClass) {
191 try {
192 ClassLoader loader = perClass.getClassLoader();
193 Class containerClass = ContextClassLoader.forName(loader, containerClassName);
194 return getContainerQNamed(loader, containerClass, qName).aspectOf(perClass);
195 } catch (Throwable t) {
196 throw new NoAspectBoundException(t, qName);
197 }
198 }
199
200 public static Object aspect$Of(String qName, String containerClassName, final Object perInstance) {
201 try {
202 ClassLoader loader = perInstance.getClass().getClassLoader();
203 Class containerClass = ContextClassLoader.forName(loader, containerClassName);
204 return getContainerQNamed(loader, containerClass, qName).aspectOf(perInstance);
205 } catch (Throwable t) {
206 throw new NoAspectBoundException(t, qName);
207 }
208 }
209
210
211
212
213
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 visibleFrom, final Class containerClass, final String qName) {
223 AspectDefinition aspectDefinition = lookupAspectDefinition(visibleFrom, qName);
224
225 Class aspectClass = null;
226 try {
227 aspectClass = ContextClassLoader.forName(visibleFrom, aspectDefinition.getClassName());
228 } catch (Throwable t) {
229 throw new NoAspectBoundException(t, qName);
230 }
231
232 try {
233 Constructor constructor = containerClass.getConstructor(new Class[]{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[]{aspectContext});
243 aspectContext.setContainer(container);
244 return container;
245 } catch (InvocationTargetException e) {
246 throw new NoAspectBoundException(e, qName);
247 } catch (NoSuchMethodException 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 e) {
256 StringBuffer cause = new StringBuffer();
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 visibleFrom, final String qName) {
273 AspectDefinition aspectDefinition = null;
274
275 Set definitions = SystemDefinitionContainer.getDefinitionsFor(visibleFrom);
276 if (qName.indexOf('/')>0) {
277
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
290
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
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
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
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381 /***
382 * Hashing strategy
383 *
384 * @param loader
385 * @param qName
386 * @return
387 */
388 public static int hash(ClassLoader loader, String qName) {
389 int result;
390 result = (loader != null ? loader.hashCode() : 0);
391 result = 29 * result + qName.hashCode();
392 return result;
393 }
394 }
395 }