/**************************************************************************************
* Copyright (c) Jonas Bon�r, Alexandre Vasseur. All rights reserved. *
* http://aspectwerkz.codehaus.org *
* ---------------------------------------------------------------------------------- *
* The software in this package is published under the terms of the LGPL license *
* a copy of which has been included with this distribution in the license.txt file. *
**************************************************************************************/
package org.codehaus.aspectwerkz.proxy;
import org.codehaus.aspectwerkz.exception.WrappedRuntimeException;
import org.codehaus.aspectwerkz.hook.impl.ClassPreProcessorHelper;
import org.codehaus.aspectwerkz.transform.inlining.AsmHelper;
import org.codehaus.backport175.reader.bytecode.spi.BytecodeProvider;
import org.codehaus.backport175.reader.bytecode.AnnotationReader;
import java.util.Map;
import java.util.WeakHashMap;
/**
* Get proxy classes from target classes and weaves in all matching aspects deployed in the class loader
* and defined by the <code>META-INF/aop.xml</code> file.
*
* @author <a href="mailto:jboner@codehaus.org">Jonas Bon�r </a>
*/
public class ProxySubclassingStrategy {
/**
* The suffix for the compiled proxy classes.
*/
public static final String PROXY_SUFFIX_START = "$$ProxiedByAWSubclassing$$";
/**
* Cache for the compiled proxy classes. Target class is key.
*/
private static final Map PROXY_CLASS_CACHE = new WeakHashMap();
/**
* Creates a new proxy instance based for the class specified and instantiates it using its default no-argument
* constructor.
* <p/>
* The proxy will be cached and non-advisable.
*
* @param clazz the target class to make a proxy for
* @return the proxy instance
*/
static Object newInstance(final Class clazz) {
try {
Class proxyClass = getProxyClassFor(clazz, true, false);
return proxyClass.newInstance();
} catch (Throwable t) {
throw new WrappedRuntimeException(t);
}
}
/**
* Creates a new proxy instance for the class specified and instantiates it using the constructor matching
* the argument type array specified.
* <p/>
* The proxy will be cached and non-advisable.
*
* @param clazz the target class to make a proxy for
* @param argumentTypes the argument types matching the signature of the constructor to use when instantiating the proxy
* @param argumentValues the argument values to use when instantiating the proxy
* @return the proxy instance
*/
static Object newInstance(final Class clazz, final Class[] argumentTypes, final Object[] argumentValues) {
try {
Class proxyClass = getProxyClassFor(clazz, true, false);
return proxyClass.getDeclaredConstructor(argumentTypes).newInstance(argumentValues);
} catch (Throwable t) {
throw new WrappedRuntimeException(t);
}
}
/**
* Creates a new proxy instance based for the class specified and instantiates it using its default no-argument
* constructor.
*
* @param clazz the target class to make a proxy for
* @param useCache true if a cached instance of the proxy classed should be used
* @param makeAdvisable true if the proxy class should implement the <code>Advisable</code> interface,
* e.g. be prepared for programmatic, runtime, per instance hot deployement of advice
* @return the proxy instance
*/
static Object newInstance(final Class clazz, final boolean useCache, final boolean makeAdvisable) {
try {
Class proxyClass = getProxyClassFor(clazz, useCache, makeAdvisable);
return proxyClass.newInstance();
} catch (Throwable t) {
throw new WrappedRuntimeException(t);
}
}
/**
* Creates a new proxy instance for the class specified and instantiates it using the constructor matching
* the argument type array specified.
*
* @param clazz the target class to make a proxy for
* @param argumentTypes the argument types matching the signature of the constructor to use when instantiating the proxy
* @param argumentValues the argument values to use when instantiating the proxy
* @param useCache true if a cached instance of the proxy classed should be used
* @param makeAdvisable true if the proxy class should implement the <code>Advisable</code> interface,
* e.g. be prepared for programmatic, runtime, per instance hot deployement of advice
* @return the proxy instance
*/
static Object newInstance(final Class clazz,
final Class[] argumentTypes,
final Object[] argumentValues,
final boolean useCache,
final boolean makeAdvisable) {
try {
Class proxyClass = getProxyClassFor(clazz, useCache, makeAdvisable);
return proxyClass.getDeclaredConstructor(argumentTypes).newInstance(argumentValues);
} catch (Throwable t) {
throw new WrappedRuntimeException(t);
}
}
/**
* Compiles and returns a proxy class for the class specified.
*
* @param clazz the target class to make a proxy for
* @param useCache true if a cached instance of the proxy classed should be used
* @param makeAdvisable true if the proxy class should implement the <code>Advisable</code> interface,
* e.g. be prepared for programmatic, runtime, per instance hot deployement of advice
* @return the proxy class
*/
static Class getProxyClassFor(final Class clazz, final boolean useCache, final boolean makeAdvisable) {
// FIXME - add support for proxying java.* classes
if (clazz.getName().startsWith("java.")) {
throw new RuntimeException("can not create proxies from system classes (java.*)");
}
if (!useCache) {
return getNewProxyClassFor(clazz, makeAdvisable);
} else {
synchronized (PROXY_CLASS_CACHE) {
Object cachedProxyClass = PROXY_CLASS_CACHE.get(clazz);
if (cachedProxyClass != null) {
return (Class) cachedProxyClass;
}
Class proxyClass = getNewProxyClassFor(clazz, makeAdvisable);
PROXY_CLASS_CACHE.put(clazz, proxyClass);
return proxyClass;
}
}
}
/**
* Compiles and returns a proxy class for the class specified.
* No cache is used, but compiles a new one each invocation.
*
* @param clazz
* @param makeAdvisable true if the proxy class should implement the <code>Advisable</code> interface,
* e.g. be prepared for programmatic, runtime, per instance hot deployement of advice
* @return the proxy class
*/
private static Class getNewProxyClassFor(final Class clazz, final boolean makeAdvisable) {
ClassLoader loader = clazz.getClassLoader();
String proxyClassName = getUniqueClassNameForProxy(clazz);
if (makeAdvisable) {
Proxy.makeProxyAdvisable(proxyClassName, loader);
}
final byte[] bytes = ProxySubclassingCompiler.compileProxyFor(clazz, proxyClassName);
// register the bytecode provider
// TODO AV - could be optimized ?(f.e. recompile everytime instead of creating many provider)
AnnotationReader.setBytecodeProviderFor(
proxyClassName, loader, new BytecodeProvider() {
public byte[] getBytecode(String className, ClassLoader loader) throws Exception {
return bytes;
}
}
);
byte[] transformedBytes = ClassPreProcessorHelper.defineClass0Pre(
loader, proxyClassName, bytes, 0, bytes.length, null
);
return AsmHelper.defineClass(loader, transformedBytes, proxyClassName);
}
/**
* Returns a unique name for the proxy class.
*
* @param clazz target class
* @return the proxy class name
*/
private static String getUniqueClassNameForProxy(final Class clazz) {
return clazz.getName().replace('.', '/') + PROXY_SUFFIX_START + new Long(Uuid.newUuid()).toString();
}
/**
* Returns a unique name for the proxy class.
*
* @param proxyClassName
* @return the class name beeing proxied
*/
static String getUniqueClassNameFromProxy(final String proxyClassName) {
int index = proxyClassName.lastIndexOf(PROXY_SUFFIX_START);
if (index > 0) {
return proxyClassName.substring(0, index);
} else {
return null;
}
}
}