001: /**************************************************************************************
002: * Copyright (c) Jonas Bonr, Alexandre Vasseur. All rights reserved. *
003: * http://aspectwerkz.codehaus.org *
004: * ---------------------------------------------------------------------------------- *
005: * The software in this package is published under the terms of the LGPL license *
006: * a copy of which has been included with this distribution in the license.txt file. *
007: **************************************************************************************/package org.codehaus.aspectwerkz.aspect.management;
008:
009: import org.codehaus.aspectwerkz.aspect.AspectContainer;
010: import org.codehaus.aspectwerkz.aspect.DefaultAspectContainerStrategy;
011: import org.codehaus.aspectwerkz.AspectContext;
012: import org.codehaus.aspectwerkz.cflow.CflowCompiler;
013: import org.codehaus.aspectwerkz.util.ContextClassLoader;
014: import org.codehaus.aspectwerkz.definition.AspectDefinition;
015: import org.codehaus.aspectwerkz.definition.SystemDefinition;
016: import org.codehaus.aspectwerkz.definition.SystemDefinitionContainer;
017: import org.codehaus.aspectwerkz.exception.DefinitionException;
018:
019: import java.util.*;
020: import java.lang.reflect.Constructor;
021: import java.lang.reflect.InvocationTargetException;
022:
023: import gnu.trove.TIntObjectHashMap;
024:
025: /**
026: * Manages the aspects, registry for the aspect containers (one container per aspect type).
027: *
028: * @author <a href="mailto:jboner@codehaus.org">Jonas Bonér </a>
029: * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
030: */
031: public class Aspects {
032:
033: /**
034: * The default aspect container class.
035: */
036: public static final String DEFAULT_ASPECT_CONTAINER = DefaultAspectContainerStrategy.class
037: .getName();
038:
039: /**
040: * Map of TIntHashMap, whose key is containerClass. The TIntHashMap maps container instance, whith
041: * CompositeVisibleFromQNameKey as a key
042: * as a key.
043: * <p/>
044: * TODO:
045: * we end up in having one entry with a list that strong refs the container instances
046: * ie leaks since the DefaultContainer leaves in system CL.
047: */
048: private static Map ASPECT_CONTAINER_LISTS = new WeakHashMap();
049:
050: /**
051: * Returns the aspect container class for the given aspect class qName.
052: * The qName is returned since we may have only the aspect class name upon lookup
053: *
054: * @param visibleFrom class loader to look from
055: * @param qName
056: * @return the container class
057: */
058: public static String[] getAspectQNameContainerClassName(
059: final ClassLoader visibleFrom, final String qName) {
060: AspectDefinition aspectDefinition = lookupAspectDefinition(
061: visibleFrom, qName);
062: return new String[] { aspectDefinition.getQualifiedName(),
063: aspectDefinition.getContainerClassName() };
064: }
065:
066: /**
067: * Returns or create the aspect container for the given container class with the given aspect qualified name
068: * <p/>
069: * We keep a weak key for the containerClass, and we then keep a list of container instance based on a composite key
070: * based on the tuple {visibleFromClassLoader.hashCode, aspectQName}, so that when hot deploying a web app, the
071: * aspects gets tied to the web app class loader even when the container class is higher up (f.e. in aw.jar)
072: *
073: * @param visibleFrom class loader hosting the advised class ie from where all is visible
074: * @param containerClass
075: * @param qName
076: * @return
077: */
078: public static AspectContainer getContainerQNamed(
079: final ClassLoader visibleFrom, final Class containerClass,
080: final String qName) {
081: synchronized (ASPECT_CONTAINER_LISTS) {
082: TIntObjectHashMap containers = (TIntObjectHashMap) ASPECT_CONTAINER_LISTS
083: .get(containerClass);
084: if (containers == null) {
085: containers = new TIntObjectHashMap();
086: ASPECT_CONTAINER_LISTS.put(containerClass, containers);
087: }
088: AspectContainer container = (AspectContainer) containers
089: .get(CompositeVisibleFromQNameKey.hash(visibleFrom,
090: qName));
091: if (container == null) {
092: container = createAspectContainer(visibleFrom,
093: containerClass, qName);
094: containers.put(CompositeVisibleFromQNameKey.hash(
095: visibleFrom, qName), container);
096: }
097: return container;
098: }
099: }
100:
101: /**
102: * Returns the singleton aspect instance for the aspect with the given qualified name.
103: * The aspect is looked up from the thread context classloader.
104: *
105: * @param qName the qualified name of the aspect
106: * @return the singleton aspect instance
107: */
108: public static Object aspectOf(final String qName) {
109: return aspectOf(Thread.currentThread().getContextClassLoader(),
110: qName);
111: }
112:
113: /**
114: * Returns the singleton aspect instance for the given aspect class.
115: * Consider using aspectOf(visibleFrom, qName) if the aspect is used more than once
116: * or if it is used in a class loader which is child of its own classloader.
117: *
118: * @param aspectClass the class of the aspect
119: * @return the singleton aspect instance
120: */
121: public static Object aspectOf(final Class aspectClass) {
122: String aspectClassName = aspectClass.getName()
123: .replace('/', '.');
124: return aspectOf(aspectClass.getClassLoader(), aspectClassName);
125: }
126:
127: /**
128: * Returns the singleton aspect instance for given aspect qName, with visibility from the given class loader
129: *
130: * @param visibleFrom the class loader from where aspect is visible, likely to be the class loader of the
131: * advised classes, or the one where the system hosting the aspect is deployed.
132: * @return the singleton aspect instance
133: */
134: public static Object aspectOf(final ClassLoader visibleFrom,
135: final String qName) {
136: String[] qNameContainerClassName = getAspectQNameContainerClassName(
137: visibleFrom, qName);
138: return aspect$Of(visibleFrom, qNameContainerClassName[0],
139: qNameContainerClassName[1]);
140: }
141:
142: /**
143: * Returns the per class aspect attached to targetClass
144: * Consider using aspectOf(qName, targetClass) if the aspect is used more than once
145: *
146: * @param aspectClass the name of the aspect
147: * @param targetClass the target class
148: * @return the per class aspect instance
149: */
150: public static Object aspectOf(final Class aspectClass,
151: final Class targetClass) {
152: String aspectClassName = aspectClass.getName()
153: .replace('/', '.');
154: return aspectOf(aspectClassName, targetClass);
155: }
156:
157: /**
158: * Returns the per class aspect instance for the aspect with the given qualified name for targetClass
159: *
160: * @param qName the qualified name of the aspect
161: * @param targetClass the target class
162: * @return the per class aspect instance
163: */
164: public static Object aspectOf(final String qName,
165: final Class targetClass) {
166: // look up from the targetClass loader is enough in that case
167: String[] qNameContainerClassName = getAspectQNameContainerClassName(
168: targetClass.getClassLoader(), qName);
169: return aspect$Of(qNameContainerClassName[0],
170: qNameContainerClassName[1], targetClass);
171: }
172:
173: /**
174: * Returns the per instance aspect attached to targetInstance
175: * Consider using aspectOf(qName, targetInstance) if the aspect is used more than once
176: *
177: * @param aspectClass the name of the aspect
178: * @param targetInstance the target instance
179: * @return the per class aspect instance
180: */
181: public static Object aspectOf(final Class aspectClass,
182: final Object targetInstance) {
183: String aspectClassName = aspectClass.getName()
184: .replace('/', '.');
185: return aspectOf(aspectClassName, targetInstance);
186: }
187:
188: /**
189: * Returns the per instance aspect attached to targetInstance
190: *
191: * @param qName the qualified name of the aspect
192: * @param targetInstance the target instance
193: * @return the per class aspect instance
194: */
195: public static Object aspectOf(final String qName,
196: final Object targetInstance) {
197: // look up from the targetInstance loader is enough in that case
198: String[] qNameContainerClassName = getAspectQNameContainerClassName(
199: targetInstance.getClass().getClassLoader(), qName);
200: return aspect$Of(qNameContainerClassName[0],
201: qNameContainerClassName[1], targetInstance);
202: }
203:
204: //---------- weaver exposed
205:
206: public static Object aspect$Of(ClassLoader loader, String qName,
207: String containerClassName) {
208: try {
209: Class containerClass = ContextClassLoader.forName(loader,
210: containerClassName);
211: return getContainerQNamed(loader, containerClass, qName)
212: .aspectOf();
213: } catch (Throwable t) {
214: throw new NoAspectBoundException(t, qName);
215: }
216: }
217:
218: public static Object aspect$Of(String qName,
219: String containerClassName, final Class perClass) {
220: try {
221: ClassLoader loader = perClass.getClassLoader();
222: Class containerClass = ContextClassLoader.forName(loader,
223: containerClassName);
224: return getContainerQNamed(loader, containerClass, qName)
225: .aspectOf(perClass);
226: } catch (Throwable t) {
227: throw new NoAspectBoundException(t, qName);
228: }
229: }
230:
231: public static Object aspect$Of(String qName,
232: String containerClassName, final Object perInstance) {
233: try {
234: ClassLoader loader = perInstance.getClass()
235: .getClassLoader();
236: Class containerClass = ContextClassLoader.forName(loader,
237: containerClassName);
238: return getContainerQNamed(loader, containerClass, qName)
239: .aspectOf(perInstance);
240: } catch (Throwable t) {
241: throw new NoAspectBoundException(t, qName);
242: }
243: }
244:
245: //---------- helpers
246:
247: /**
248: * Creates a new aspect container.
249: *
250: * @param visibleFrom class loader of the advised class from all is visible
251: * @param containerClass the container class
252: * @param qName the aspect qualified name
253: */
254: private static AspectContainer createAspectContainer(
255: final ClassLoader visibleFrom, final Class containerClass,
256: final String qName) {
257: AspectDefinition aspectDefinition = lookupAspectDefinition(
258: visibleFrom, qName);
259:
260: Class aspectClass = null;
261: try {
262: aspectClass = ContextClassLoader.forName(visibleFrom,
263: aspectDefinition.getClassName());
264: } catch (Throwable t) {
265: throw new NoAspectBoundException(t, qName);
266: }
267:
268: try {
269: Constructor constructor = containerClass
270: .getConstructor(new Class[] { AspectContext.class });
271: final AspectContext aspectContext = new AspectContext(
272: aspectDefinition.getSystemDefinition().getUuid(),
273: aspectClass, aspectDefinition.getName(),
274: aspectDefinition.getDeploymentModel(),
275: aspectDefinition, aspectDefinition.getParameters());
276: final AspectContainer container = (AspectContainer) constructor
277: .newInstance(new Object[] { aspectContext });
278: aspectContext.setContainer(container);
279: return container;
280: } catch (InvocationTargetException e) {
281: throw new NoAspectBoundException(e, qName);
282: } catch (NoSuchMethodException e) {
283: throw new NoAspectBoundException(
284: "AspectContainer does not have a valid constructor ["
285: + containerClass.getName()
286: + "] need to take an AspectContext instance as its only parameter: "
287: + e.toString(), qName);
288: } catch (Throwable e) {
289: StringBuffer cause = new StringBuffer();
290: cause
291: .append("Could not create AspectContainer using the implementation specified [");
292: cause.append(containerClass.getName());
293: cause.append("] for ").append(qName);
294: throw new NoAspectBoundException(e, cause.toString());
295: }
296: }
297:
298: /**
299: * Lookup the aspect definition with the given qName, visible from the given loader.
300: * If qName is a class name only, the fallback will ensure only one aspect use is found.
301: *
302: * @param visibleFrom
303: * @param qName
304: * @return
305: */
306: private static AspectDefinition lookupAspectDefinition(
307: final ClassLoader visibleFrom, final String qName) {
308: AspectDefinition aspectDefinition = null;
309:
310: Set definitions = SystemDefinitionContainer
311: .getDefinitionsFor(visibleFrom);
312: if (qName.indexOf('/') > 0) {
313: // has system uuid ie real qName
314: for (Iterator iterator = definitions.iterator(); iterator
315: .hasNext();) {
316: SystemDefinition systemDefinition = (SystemDefinition) iterator
317: .next();
318: for (Iterator iterator1 = systemDefinition
319: .getAspectDefinitions().iterator(); iterator1
320: .hasNext();) {
321: AspectDefinition aspectDef = (AspectDefinition) iterator1
322: .next();
323: if (qName.equals(aspectDef.getQualifiedName())) {
324: aspectDefinition = aspectDef;
325: break;
326: }
327: }
328: }
329: } else {
330: // fallback on class name lookup
331: // must find at most one
332: int found = 0;
333: for (Iterator iterator = definitions.iterator(); iterator
334: .hasNext();) {
335: SystemDefinition systemDefinition = (SystemDefinition) iterator
336: .next();
337: for (Iterator iterator1 = systemDefinition
338: .getAspectDefinitions().iterator(); iterator1
339: .hasNext();) {
340: AspectDefinition aspectDef = (AspectDefinition) iterator1
341: .next();
342: if (qName.equals(aspectDef.getClassName())) {
343: aspectDefinition = aspectDef;
344: found++;
345: }
346: }
347: }
348: if (found > 1) {
349: throw new NoAspectBoundException(
350: "More than one AspectDefinition found, consider using other API methods",
351: qName);
352: }
353:
354: }
355:
356: if (aspectDefinition == null) {
357: throw new NoAspectBoundException(
358: "Could not find AspectDefinition", qName);
359: }
360:
361: return aspectDefinition;
362: }
363:
364: // /**
365: // * Looks up the aspect class name, based on the qualified name of the aspect.
366: // *
367: // * @param loader
368: // * @param qualifiedName
369: // * @return
370: // */
371: // private static String lookupAspectClassName(final ClassLoader loader, final String qualifiedName) {
372: // if (qualifiedName.indexOf('/') <= 0) {
373: // // no uuid
374: // return null;
375: // }
376: //
377: // final Set definitionsBottomUp = SystemDefinitionContainer.getDefinitionsFor(loader);
378: // // TODO: bottom up is broken now
379: // //Collections.reverse(definitionsBottomUp);
380: //
381: // for (Iterator iterator = definitionsBottomUp.iterator(); iterator.hasNext();) {
382: // SystemDefinition definition = (SystemDefinition) iterator.next();
383: // for (Iterator iterator1 = definition.getAspectDefinitions().iterator(); iterator1.hasNext();) {
384: // AspectDefinition aspectDefinition = (AspectDefinition) iterator1.next();
385: // if (qualifiedName.equals(aspectDefinition.getQualifiedName())) {
386: // return aspectDefinition.getClassName();
387: // }
388: // }
389: // }
390: // return null;
391: // }
392:
393: /**
394: * Class is non-instantiable.
395: */
396: private Aspects() {
397: }
398:
399: /**
400: * A composite key to ensure uniqueness of the container key even upon application redeployment
401: * when the classloader gets swapped.
402: *
403: * TODO: we could have a weak ref to the CL, and use it as a weak key in a map then to ensure
404: * release of any container when the visibleFromCL gets dropped (which can be different from
405: * the aspect container CL)?
406: *
407: * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
408: */
409: private static class CompositeVisibleFromQNameKey {
410: // private final int m_hash;
411: // private CompositeVisibleFromQNameKey(ClassLoader loader, String qName) {
412: // m_hash = hash(loader, qName);
413: // }
414: //
415: // public boolean equals(Object o) {
416: // if (this == o) return true;
417: // if (!(o instanceof CompositeVisibleFromQNameKey)) return false;
418: //
419: // final CompositeVisibleFromQNameKey compositeVisibleFromQNameKey = (CompositeVisibleFromQNameKey) o;
420: //
421: // if (m_hash != compositeVisibleFromQNameKey.m_hash) return false;
422: //
423: // return true;
424: // }
425: //
426: // public int hashCode() {
427: // return m_hash;
428: // }
429:
430: /**
431: * Hashing strategy
432: *
433: * @param loader
434: * @param qName
435: * @return
436: */
437: public static int hash(ClassLoader loader, String qName) {
438: int result;
439: result = (loader != null ? loader.hashCode() : 0);
440: result = 29 * result + qName.hashCode();
441: return result;
442: }
443: }
444: }
|