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.proxy;
    9
    10  import java.util.WeakHashMap;
    11  import java.util.Map;
    12  import java.util.Set;
    13  import java.util.Iterator;
    14
    15  import org.codehaus.aspectwerkz.hook.impl.ClassPreProcessorHelper;
    16  import org.codehaus.aspectwerkz.transform.inlining.AsmHelper;
    17  import org.codehaus.aspectwerkz.definition.DefinitionParserHelper;
    18  import org.codehaus.aspectwerkz.definition.SystemDefinition;
    19  import org.codehaus.aspectwerkz.definition.SystemDefinitionContainer;
    20  import org.codehaus.aspectwerkz.intercept.AdvisableImpl;
    21  import org.codehaus.aspectwerkz.DeploymentModel;
    22
    23  /***
    24   * Compiles proxy classes from target classes and weaves in all matching aspects deployed in the class loader
    25   * and defined by the <code>META-INF/aop.xml</code> file.
    26   *
    27   * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
    28   */
    29  public class Proxy {
    30
    31      /***
    32       * The suffix for the compiled proxy classes.
    33       */
    34      public static final String PROXY_SUFFIX_START = "$$ProxiedByAW$$";
    35
    36      /***
    37       * Cache for the compiled proxy classes. Target class is key.
    38       */
    39      private static final Map PROXY_CLASS_CACHE = new WeakHashMap();
    40
    41      /***
    42       * Creates a new proxy instance based for the class specified and instantiates it using its default no-argument
    43       * constructor.
    44       * <p/>
    45       * The proxy will be cached and non-advisable.
    46       *
    47       * @param clazz the target class to make a proxy for
    48       * @return the proxy instance
    49       */
    50      public static Object newInstance(final Class clazz) {
    51          try {
    52              Class proxyClass = getProxyClassFor(clazz, true, false);
    53              return proxyClass.newInstance();
    54          } catch (Throwable e) {
    55              e.printStackTrace();
    56              throw new Error(e.toString());
    57          }
    58      }
    59
    60      /***
    61       * Creates a new proxy instance for the class specified and instantiates it using the constructor matching
    62       * the argument type array specified.
    63       * <p/>
    64       * The proxy will be cached and non-advisable.
    65       *
    66       * @param clazz          the target class to make a proxy for
    67       * @param argumentTypes  the argument types matching the signature of the constructor to use when instantiating the proxy
    68       * @param argumentValues the argument values to use when instantiating the proxy
    69       * @return the proxy instance
    70       */
    71      public static Object newInstance(final Class clazz, final Class[] argumentTypes, final Object[] argumentValues) {
    72          try {
    73              Class proxyClass = getProxyClassFor(clazz, true, false);
    74              return proxyClass.getDeclaredConstructor(argumentTypes).newInstance(argumentValues);
    75          } catch (Throwable e) {
    76              e.printStackTrace();
    77              throw new Error(e.toString());
    78          }
    79      }
    80
    81      /***
    82       * Creates a new proxy instance based for the class specified and instantiates it using its default no-argument
    83       * constructor.
    84       *
    85       * @param clazz         the target class to make a proxy for
    86       * @param useCache      true if a cached instance of the proxy classed should be used
    87       * @param makeAdvisable true if the proxy class should implement the <code>Advisable</code> interface,
    88       *                      e.g. be prepared for programmatic, runtime, per instance hot deployement of advice
    89       * @return the proxy instance
    90       */
    91      public static Object newInstance(final Class clazz, final boolean useCache, final boolean makeAdvisable) {
    92          try {
    93              Class proxyClass = getProxyClassFor(clazz, useCache, makeAdvisable);
    94              return proxyClass.newInstance();
    95          } catch (Throwable e) {
    96              e.printStackTrace();
    97              throw new Error(e.toString());
    98          }
    99      }
    100
    101     /***
    102      * Creates a new proxy instance for the class specified and instantiates it using the constructor matching
    103      * the argument type array specified.
    104      *
    105      * @param clazz          the target class to make a proxy for
    106      * @param argumentTypes  the argument types matching the signature of the constructor to use when instantiating the proxy
    107      * @param argumentValues the argument values to use when instantiating the proxy
    108      * @param useCache       true if a cached instance of the proxy classed should be used
    109      * @param makeAdvisable  true if the proxy class should implement the <code>Advisable</code> interface,
    110      *                       e.g. be prepared for programmatic, runtime, per instance hot deployement of advice
    111      * @return the proxy instance
    112      */
    113     public static Object newInstance(final Class clazz,
    114                                      final Class[] argumentTypes,
    115                                      final Object[] argumentValues,
    116                                      final boolean useCache,
    117                                      final boolean makeAdvisable) {
    118         try {
    119             Class proxyClass = getProxyClassFor(clazz, useCache, makeAdvisable);
    120             return proxyClass.getDeclaredConstructor(argumentTypes).newInstance(argumentValues);
    121         } catch (Throwable e) {
    122             e.printStackTrace();
    123             throw new Error(e.toString());
    124         }
    125     }
    126
    127     /***
    128      * Compiles and returns a proxy class for the class specified.
    129      *
    130      * @param clazz         the target class to make a proxy for
    131      * @param useCache      true if a cached instance of the proxy classed should be used
    132      * @param makeAdvisable true if the proxy class should implement the <code>Advisable</code> interface,
    133      *                      e.g. be prepared for programmatic, runtime, per instance hot deployement of advice
    134      * @return the proxy class
    135      */
    136     public static Class getProxyClassFor(final Class clazz, final boolean useCache, final boolean makeAdvisable) {
    137
    138         // FIXME - add support for proxying java.* classes
    139         if (clazz.getName().startsWith("java.")) {
    140             throw new RuntimeException("can not create proxies from system classes (java.*)");
    141         }
    142         if (!useCache) {
    143             return getNewProxyClassFor(clazz, makeAdvisable);
    144         } else {
    145             synchronized (PROXY_CLASS_CACHE) {
    146                 Object cachedProxyClass = PROXY_CLASS_CACHE.get(clazz);
    147                 if (cachedProxyClass != null) {
    148                     return (Class) cachedProxyClass;
    149                 }
    150                 Class proxyClass = getNewProxyClassFor(clazz, makeAdvisable);
    151                 PROXY_CLASS_CACHE.put(clazz, proxyClass);
    152                 return proxyClass;
    153             }
    154         }
    155     }
    156
    157     /***
    158      * Compiles and returns a proxy class for the class specified.
    159      * No cache is used, but compiles a new one each invocation.
    160      *
    161      * @param clazz
    162      * @param makeAdvisable true if the proxy class should implement the <code>Advisable</code> interface,
    163      *                      e.g. be prepared for programmatic, runtime, per instance hot deployement of advice
    164      * @return the proxy class
    165      */
    166     private static Class getNewProxyClassFor(final Class clazz, final boolean makeAdvisable) {
    167         ClassLoader loader = clazz.getClassLoader();
    168         String proxyClassName = getUniqueClassNameForProxy(clazz);
    169
    170         if (makeAdvisable) {
    171             makeProxyAdvisable(clazz);
    172         }
    173
    174         byte[] bytes = ProxyCompiler.compileProxyFor(clazz, proxyClassName);
    175         byte[] transformedBytes = ClassPreProcessorHelper.defineClass0Pre(
    176                 loader, proxyClassName, bytes, 0, bytes.length, null
    177         );
    178
    179         return AsmHelper.defineClass(loader, transformedBytes, proxyClassName);
    180     }
    181
    182     /***
    183      * Returns a unique name for the proxy class.
    184      *
    185      * @param clazz target class
    186      * @return the proxy class name
    187      */
    188     private static String getUniqueClassNameForProxy(final Class clazz) {
    189         return clazz.getName().replace('.', '/') + PROXY_SUFFIX_START + new Long(Uuid.newUuid()).toString();
    190     }
    191
    192     /***
    193      * Returns a unique name for the proxy class.
    194      *
    195      * @param proxyClassName
    196      * @return the class name beeing proxied
    197      */
    198     public static String getUniqueClassNameFromProxy(final String proxyClassName) {
    199         int index = proxyClassName.lastIndexOf(PROXY_SUFFIX_START);
    200         if (index > 0) {
    201             return proxyClassName.substring(0, index);
    202         } else {
    203             return null;
    204         }
    205     }
    206
    207     /***
    208      * Enhances the proxy class with the Advisable mixin, to allow runtime per instance additions of
    209      * interceptors.
    210      *
    211      * @param clazz
    212      */
    213     private static void makeProxyAdvisable(final Class clazz) {
    214         // changes occurs in the virtual definition only
    215         SystemDefinition definition = SystemDefinitionContainer.getVirtualDefinitionAt(clazz.getClassLoader());
    216         addAdvisableDefToSystemDef(clazz, definition);
    217     }
    218
    219     private static void addAdvisableDefToSystemDef(final Class clazz, final SystemDefinition definition) {
    220         String withinPointcut = "within(" + clazz.getName().replace('/', '.') + ')';
    221         definition.addMixinDefinition(
    222                 DefinitionParserHelper.createAndAddMixinDefToSystemDef(
    223                         AdvisableImpl.CLASS_INFO,
    224                         withinPointcut,
    225                         DeploymentModel.PER_INSTANCE,
    226                         false,
    227                         definition
    228                 )
    229         );
    230         DefinitionParserHelper.createAndAddAdvisableDef(
    231                 "(execution(!static * *.*(..)) && " + withinPointcut + ')',
    232                 definition
    233         );
    234     }
    235 }