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 }