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
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
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 }