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.aspect.DefaultMixinFactory;
    11  import org.codehaus.aspectwerkz.aspect.MixinFactory;
    12  import org.codehaus.aspectwerkz.util.ContextClassLoader;
    13  import org.codehaus.aspectwerkz.DeploymentModel;
    14  import org.codehaus.aspectwerkz.util.ContextClassLoader;
    15  import org.codehaus.aspectwerkz.definition.SystemDefinition;
    16  import org.codehaus.aspectwerkz.definition.SystemDefinitionContainer;
    17  import org.codehaus.aspectwerkz.definition.MixinDefinition;
    18  import org.codehaus.aspectwerkz.exception.DefinitionException;
    19  import org.codehaus.aspectwerkz.DeploymentModel;
    20
    21  import java.util.*;
    22  import java.lang.reflect.Constructor;
    23  import java.lang.reflect.InvocationTargetException;
    24
    25  /***
    26   * Manages the mixins, registry for the mixin factories (one factory per mixin type).
    27   *
    28   * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
    29   * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
    30   */
    31  public class Mixins {
    32
    33      /***
    34       * The default mixin factory class.
    35       */
    36      public static final String DEFAULT_MIXIN_FACTORY = DefaultMixinFactory.class.getName();
    37
    38      /***
    39       * Map with all the mixin factories mapped to the mixin class
    40       */
    41      private static final Map MIXIN_FACTORIES = new WeakHashMap();
    42
    43      /***
    44       * Returns the mixin factory for the mixin with the given name.
    45       *
    46       * @param mixinClass the class of the mixin
    47       * @param mixinCalledFromLoader
    48       * @return the factory, put in cache based on mixin class as a key
    49       */
    50      public static MixinFactory getFactory(final Class mixinClass, final ClassLoader mixinCalledFromLoader) {
    51          synchronized (MIXIN_FACTORIES) {
    52              MixinFactory factory = (MixinFactory) MIXIN_FACTORIES.get(mixinClass);
    53              if (factory == null) {
    54                  factory = createMixinFactory(mixinClass, mixinCalledFromLoader);
    55                  //FIXME by using a lookup by uuid/aspectNickName
    56                  // right now broken since we have 1 container per mixin CLASS while the definition
    57                  // does allow for some mix (several mixin, several container, same mixin class)
    58                  MIXIN_FACTORIES.put(mixinClass, factory);
    59              }
    60              return factory;
    61          }
    62      }
    63
    64      /***
    65       * Returns the per JVM mixin instance for the mixin with the given name
    66       *
    67       * @param name        the name of the mixin
    68       * @param loader    target class classloader
    69       * @return the per jvm mixin instance
    70       */
    71      public static Object mixinOf(final String name, ClassLoader loader) {
    72          try {
    73              Class mixinClass = Class.forName(name, false, loader);
    74              return mixinOf(mixinClass);
    75          } catch (ClassNotFoundException e) {
    76              throw new RuntimeException("could not load mixin " + name + " from " + loader);
    77          }
    78      }
    79
    80      /***
    81       * Returns the per jvm mixin instance for the mixin with the given implementation class
    82       * deployed using the perJVM model.
    83       *
    84       * @param mixinClass  the name of the mixin
    85       * @return the per jvm mixin instance
    86       */
    87      public static Object mixinOf(final Class mixinClass) {
    88          return getFactory(mixinClass, mixinClass.getClassLoader()).mixinOf();
    89      }
    90
    91      /***
    92       * Returns the per class mixin instance for the mixin with the given name for the perClass model
    93       *
    94       * @param name        the name of the mixin
    95       * @param targetClass the targetClass class
    96       * @return the per class mixin instance
    97       */
    98      public static Object mixinOf(final String name, final Class targetClass) {
    99          try {
    100             Class mixinClass = Class.forName(name, false, targetClass.getClassLoader());
    101             return mixinOf(mixinClass, targetClass);
    102         } catch (ClassNotFoundException e) {
    103             throw new RuntimeException("could not load mixin " + name + " from " + targetClass.getClassLoader());
    104         }
    105     }
    106
    107     /***
    108      * Returns the per class mixin instance for the mixin with the given implemnentation class
    109      * deployed using the perClass model.
    110      *
    111      * @param mixinClass  the name of the mixin
    112      * @param targetClass the targetClass class
    113      * @return the per class mixin instance
    114      */
    115     public static Object mixinOf(final Class mixinClass, final Class targetClass) {
    116         return getFactory(mixinClass, targetClass.getClassLoader()).mixinOf(targetClass);
    117     }
    118
    119     /***
    120      * Returns the per targetClass instance mixin instance for the mixin with the given name for the perInstance model.
    121      *
    122      * @param name           the name of the mixin
    123      * @param targetInstance the targetClass instance, can be null (static method, ctor call)
    124      * @return the per instance mixin instance, fallback on perClass if targetInstance is null
    125      */
    126     public static Object mixinOf(final String name, final Object targetInstance) {
    127         try {
    128             Class mixinClass = Class.forName(name, false, targetInstance.getClass().getClassLoader());
    129             return mixinOf(mixinClass, targetInstance);
    130         } catch (ClassNotFoundException e) {
    131             throw new RuntimeException(
    132                     "could not load mixin " + name + " from " + targetInstance.getClass().getClassLoader()
    133             );
    134         }
    135     }
    136
    137     /***
    138      * Returns the per class mixin instance for the mixin with the given implemnentation class
    139      * deployed using the perClass model.
    140      *
    141      * @param mixinClass     the name of the mixin
    142      * @param targetInstance the targetClass instance, can be null
    143      * @return the per targetClass instance mixin instance, fallback to perClass if targetInstance is null
    144      */
    145     public static Object mixinOf(final Class mixinClass, final Object targetInstance) {
    146         //TODO WHAT IF targetInstance is null ? f.e. ITD static methods
    147         return getFactory(mixinClass, targetInstance.getClass().getClassLoader()).mixinOf(targetInstance);
    148     }
    149
    150     /***
    151      * Creates a new mixin factory.
    152      *
    153      * @param mixinClass the mixin class
    154      * @param mixinCalledFromLoader classloader of the target class advised by the mixin (app server packaging)
    155      */
    156     private static MixinFactory createMixinFactory(final Class mixinClass, final ClassLoader mixinCalledFromLoader) {
    157         final MixinDefinition mixinDefinition = getMixinDefinition(mixinClass, mixinCalledFromLoader);
    158
    159         String factoryClassName = mixinDefinition.getFactoryClassName();
    160         try {
    161             Class containerClass;
    162             if (factoryClassName == null) {
    163                 containerClass = ContextClassLoader.forName(mixinClass.getClassLoader(), DEFAULT_MIXIN_FACTORY);
    164             } else {
    165                 containerClass = ContextClassLoader.forName(mixinClass.getClassLoader(), factoryClassName);
    166             }
    167             Constructor constructor = containerClass.getConstructor(new Class[]{Class.class, DeploymentModel.class});
    168             final MixinFactory factory = (MixinFactory) constructor.newInstance(
    169                     new Object[]{mixinClass, mixinDefinition.getDeploymentModel()}
    170             );
    171             return factory;
    172         } catch (InvocationTargetException e) {
    173             throw new DefinitionException(e.getTargetException().toString());
    174         } catch (NoSuchMethodException e) {
    175             throw new DefinitionException(
    176                     "mixin factory does not have a valid constructor ["
    177                     + factoryClassName
    178                     + "] need to have a signature like this [MyMixinFactory(Class mixin, DeploymentModel scope)]: "
    179                     + e.toString()
    180             );
    181         } catch (Throwable e) {
    182             StringBuffer cause = new StringBuffer();
    183             cause.append("could not create mixin container using the implementation specified [");
    184             cause.append(factoryClassName);
    185             cause.append("] due to: ");
    186             cause.append(e.toString());
    187             throw new DefinitionException(cause.toString());
    188         }
    189     }
    190
    191     /***
    192      * Returns the parameter for a mixin based on the mixin implementation class and a classloader from
    193      * where the mixin is visible (the classloader that owns the aop.xml with the "mixin" element, or a child of it).
    194      * <p/>
    195      * Note: the mixinClass classloader can be different, if you place the mixin in the system classpath, and reference
    196      * it only from a deployed application.
    197      * <p/>
    198      * Note: you should not use a mixin more than once. Consider subclassing the mixin in this case. The returned parameters
    199      * are the one from the first mixin found.
    200      *
    201      * @param mixinClass
    202      * @return
    203      */
    204     public static Map getParameters(Class mixinClass, ClassLoader loader) {
    205         MixinDefinition mixinDefinition = getMixinDefinition(mixinClass,  loader);
    206         return mixinDefinition.getParameters();
    207     }
    208
    209     /***
    210      * Lookups a mixin definition based on the mixin impl class and a classloader from where the mixin is
    211      * visible. The given classloader can be different from the mixin class classloader.
    212      *
    213      * @param mixinClass
    214      * @param visibleFrom
    215      * @return
    216      */
    217     public static MixinDefinition getMixinDefinition(Class mixinClass, ClassLoader visibleFrom) {
    218         MixinDefinition mixinDefinition = null;
    219
    220         Set definitions = SystemDefinitionContainer.getDefinitionsFor(visibleFrom);
    221         for (Iterator iterator = definitions.iterator(); iterator.hasNext() && mixinDefinition == null;) {
    222             SystemDefinition systemDefinition = (SystemDefinition) iterator.next();
    223             for (Iterator iterator1 = systemDefinition.getMixinDefinitions().iterator(); iterator1.hasNext();) {
    224                 MixinDefinition mixinDef = (MixinDefinition) iterator1.next();
    225                 if (mixinClass.getName().replace('/', '.').equals(mixinDef.getMixinImpl().getName())) {
    226                     mixinDefinition = mixinDef;
    227                     break;
    228                 }
    229             }
    230         }
    231         if (mixinDefinition == null) {
    232             throw new DefinitionException("could not find definition for mixin: " + mixinClass.getName()
    233                         + " (loader " + mixinClass.getClassLoader() + ")"
    234                         + " from loader " + visibleFrom);
    235         }
    236         return mixinDefinition;
    237     }
    238
    239     /***
    240      * Class is non-instantiable.
    241      */
    242     private Mixins() {
    243     }
    244 }