001: /**************************************************************************************
002: * Copyright (c) Jonas BonŽr, Alexandre Vasseur. All rights reserved. *
003: * http://aspectwerkz.codehaus.org *
004: * ---------------------------------------------------------------------------------- *
005: * The software in this package is published under the terms of the LGPL license *
006: * a copy of which has been included with this distribution in the license.txt file. *
007: **************************************************************************************/package org.codehaus.aspectwerkz.proxy;
008:
009: import java.util.WeakHashMap;
010: import java.util.Map;
011: import java.util.Set;
012: import java.util.Iterator;
013:
014: import org.codehaus.aspectwerkz.hook.impl.ClassPreProcessorHelper;
015: import org.codehaus.aspectwerkz.transform.inlining.AsmHelper;
016: import org.codehaus.aspectwerkz.definition.DefinitionParserHelper;
017: import org.codehaus.aspectwerkz.definition.SystemDefinition;
018: import org.codehaus.aspectwerkz.definition.SystemDefinitionContainer;
019: import org.codehaus.aspectwerkz.intercept.AdvisableImpl;
020: import org.codehaus.aspectwerkz.DeploymentModel;
021:
022: /**
023: * Compiles proxy classes from target classes and weaves in all matching aspects deployed in the class loader
024: * and defined by the <code>META-INF/aop.xml</code> file.
025: *
026: * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
027: */
028: public class Proxy {
029:
030: /**
031: * The suffix for the compiled proxy classes.
032: */
033: public static final String PROXY_SUFFIX_START = "$$ProxiedByAW$$";
034:
035: /**
036: * Cache for the compiled proxy classes. Target class is key.
037: */
038: private static final Map PROXY_CLASS_CACHE = new WeakHashMap();
039:
040: /**
041: * Creates a new proxy instance based for the class specified and instantiates it using its default no-argument
042: * constructor.
043: * <p/>
044: * The proxy will be cached and non-advisable.
045: *
046: * @param clazz the target class to make a proxy for
047: * @return the proxy instance
048: */
049: public static Object newInstance(final Class clazz) {
050: try {
051: Class proxyClass = getProxyClassFor(clazz, true, false);
052: return proxyClass.newInstance();
053: } catch (Throwable e) {
054: e.printStackTrace();
055: throw new Error(e.toString());
056: }
057: }
058:
059: /**
060: * Creates a new proxy instance for the class specified and instantiates it using the constructor matching
061: * the argument type array specified.
062: * <p/>
063: * The proxy will be cached and non-advisable.
064: *
065: * @param clazz the target class to make a proxy for
066: * @param argumentTypes the argument types matching the signature of the constructor to use when instantiating the proxy
067: * @param argumentValues the argument values to use when instantiating the proxy
068: * @return the proxy instance
069: */
070: public static Object newInstance(final Class clazz,
071: final Class[] argumentTypes, final Object[] argumentValues) {
072: try {
073: Class proxyClass = getProxyClassFor(clazz, true, false);
074: return proxyClass.getDeclaredConstructor(argumentTypes)
075: .newInstance(argumentValues);
076: } catch (Throwable e) {
077: e.printStackTrace();
078: throw new Error(e.toString());
079: }
080: }
081:
082: /**
083: * Creates a new proxy instance based for the class specified and instantiates it using its default no-argument
084: * constructor.
085: *
086: * @param clazz the target class to make a proxy for
087: * @param useCache true if a cached instance of the proxy classed should be used
088: * @param makeAdvisable true if the proxy class should implement the <code>Advisable</code> interface,
089: * e.g. be prepared for programmatic, runtime, per instance hot deployement of advice
090: * @return the proxy instance
091: */
092: public static Object newInstance(final Class clazz,
093: final boolean useCache, final boolean makeAdvisable) {
094: try {
095: Class proxyClass = getProxyClassFor(clazz, useCache,
096: makeAdvisable);
097: return proxyClass.newInstance();
098: } catch (Throwable e) {
099: e.printStackTrace();
100: throw new Error(e.toString());
101: }
102: }
103:
104: /**
105: * Creates a new proxy instance for the class specified and instantiates it using the constructor matching
106: * the argument type array specified.
107: *
108: * @param clazz the target class to make a proxy for
109: * @param argumentTypes the argument types matching the signature of the constructor to use when instantiating the proxy
110: * @param argumentValues the argument values to use when instantiating the proxy
111: * @param useCache true if a cached instance of the proxy classed should be used
112: * @param makeAdvisable true if the proxy class should implement the <code>Advisable</code> interface,
113: * e.g. be prepared for programmatic, runtime, per instance hot deployement of advice
114: * @return the proxy instance
115: */
116: public static Object newInstance(final Class clazz,
117: final Class[] argumentTypes, final Object[] argumentValues,
118: final boolean useCache, final boolean makeAdvisable) {
119: try {
120: Class proxyClass = getProxyClassFor(clazz, useCache,
121: makeAdvisable);
122: return proxyClass.getDeclaredConstructor(argumentTypes)
123: .newInstance(argumentValues);
124: } catch (Throwable e) {
125: e.printStackTrace();
126: throw new Error(e.toString());
127: }
128: }
129:
130: /**
131: * Compiles and returns a proxy class for the class specified.
132: *
133: * @param clazz the target class to make a proxy for
134: * @param useCache true if a cached instance of the proxy classed should be used
135: * @param makeAdvisable true if the proxy class should implement the <code>Advisable</code> interface,
136: * e.g. be prepared for programmatic, runtime, per instance hot deployement of advice
137: * @return the proxy class
138: */
139: public static Class getProxyClassFor(final Class clazz,
140: final boolean useCache, final boolean makeAdvisable) {
141:
142: // FIXME - add support for proxying java.* classes
143: if (clazz.getName().startsWith("java.")) {
144: throw new RuntimeException(
145: "can not create proxies from system classes (java.*)");
146: }
147: if (!useCache) {
148: return getNewProxyClassFor(clazz, makeAdvisable);
149: } else {
150: synchronized (PROXY_CLASS_CACHE) {
151: Object cachedProxyClass = PROXY_CLASS_CACHE.get(clazz);
152: if (cachedProxyClass != null) {
153: return (Class) cachedProxyClass;
154: }
155: Class proxyClass = getNewProxyClassFor(clazz,
156: makeAdvisable);
157: PROXY_CLASS_CACHE.put(clazz, proxyClass);
158: return proxyClass;
159: }
160: }
161: }
162:
163: /**
164: * Compiles and returns a proxy class for the class specified.
165: * No cache is used, but compiles a new one each invocation.
166: *
167: * @param clazz
168: * @param makeAdvisable true if the proxy class should implement the <code>Advisable</code> interface,
169: * e.g. be prepared for programmatic, runtime, per instance hot deployement of advice
170: * @return the proxy class
171: */
172: private static Class getNewProxyClassFor(final Class clazz,
173: final boolean makeAdvisable) {
174: ClassLoader loader = clazz.getClassLoader();
175: String proxyClassName = getUniqueClassNameForProxy(clazz);
176:
177: if (makeAdvisable) {
178: makeProxyAdvisable(clazz);
179: }
180:
181: byte[] bytes = ProxyCompiler.compileProxyFor(clazz,
182: proxyClassName);
183: byte[] transformedBytes = ClassPreProcessorHelper
184: .defineClass0Pre(loader, proxyClassName, bytes, 0,
185: bytes.length, null);
186:
187: return AsmHelper.defineClass(loader, transformedBytes,
188: proxyClassName);
189: }
190:
191: /**
192: * Returns a unique name for the proxy class.
193: *
194: * @param clazz target class
195: * @return the proxy class name
196: */
197: private static String getUniqueClassNameForProxy(final Class clazz) {
198: return clazz.getName().replace('.', '/') + PROXY_SUFFIX_START
199: + new Long(Uuid.newUuid()).toString();
200: }
201:
202: /**
203: * Returns a unique name for the proxy class.
204: *
205: * @param proxyClassName
206: * @return the class name beeing proxied
207: */
208: public static String getUniqueClassNameFromProxy(
209: final String proxyClassName) {
210: int index = proxyClassName.lastIndexOf(PROXY_SUFFIX_START);
211: if (index > 0) {
212: return proxyClassName.substring(0, index);
213: } else {
214: return null;
215: }
216: }
217:
218: /**
219: * Enhances the proxy class with the Advisable mixin, to allow runtime per instance additions of
220: * interceptors.
221: *
222: * @param clazz
223: */
224: private static void makeProxyAdvisable(final Class clazz) {
225: // changes occurs in the virtual definition only
226: SystemDefinition definition = SystemDefinitionContainer
227: .getVirtualDefinitionAt(clazz.getClassLoader());
228: addAdvisableDefToSystemDef(clazz, definition);
229: }
230:
231: private static void addAdvisableDefToSystemDef(final Class clazz,
232: final SystemDefinition definition) {
233: String withinPointcut = "within("
234: + clazz.getName().replace('/', '.') + ')';
235: definition
236: .addMixinDefinition(DefinitionParserHelper
237: .createAndAddMixinDefToSystemDef(
238: AdvisableImpl.CLASS_INFO,
239: withinPointcut,
240: DeploymentModel.PER_INSTANCE, false,
241: definition));
242: DefinitionParserHelper.createAndAddAdvisableDef(
243: "(execution(!static * *.*(..)) && " + withinPointcut
244: + ')', definition);
245: }
246: }
|