View Javadoc

    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         // look up from the targetClass loader is enough in that case
    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         // look up from the targetInstance loader is enough in that case
    175         String[] 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 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     //---------- 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 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             // 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 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 }