/**************************************************************************************
* 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.definition.SystemDefinitionContainer;
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.AnnotationReader;
import org.codehaus.backport175.reader.bytecode.spi.BytecodeProvider;
import java.util.Arrays;
import java.util.Map;
import java.util.WeakHashMap;
/**
* Get proxy classes from target classes that implement target interfaces
* 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:alex AT gnilux DOT com">Alexandre Vasseur</a>
*/
public class ProxyDelegationStrategy {
/**
* Suffix for proxy class name. A UUID is further suffixed.
*/
private static final String PROXY_SUFFIX_START = "$$ProxiedByAWDelegation$$";
/**
* Cache for the compiled proxy classes. Implemented interfaces classes are composite key.
*/
private final static Map PROXY_CLASS_CACHE = new WeakHashMap();
/**
* Compile or retrieve from cache a delegation proxy for the given interfaces.
*
* @param interfaces
* @param useCache
* @param makeAdvisable
* @return
*/
static Class getProxyClassFor(Class[] interfaces, boolean useCache, boolean makeAdvisable) {
if (!useCache) {
return getNewProxyClassFor(interfaces, makeAdvisable);
} else {
CompositeClassKey key = new CompositeClassKey(interfaces);
synchronized (PROXY_CLASS_CACHE) {
Object cachedProxyClass = PROXY_CLASS_CACHE.get(key);
if (cachedProxyClass != null) {
return (Class) cachedProxyClass;
}
Class proxyClass = getNewProxyClassFor(interfaces, makeAdvisable);
PROXY_CLASS_CACHE.put(key, proxyClass);
return proxyClass;
}
}
}
/**
* Create a delegation proxy or retrieve it from cache and instantiate it, using the given implementations.
* <p/>
* Each implementation must implement the respective given interface.
*
* @param interfaces
* @param implementations
* @param useCache
* @param makeAdvisable
* @return
*/
static Object newInstance(final Class[] interfaces, final Object[] implementations, final boolean useCache, final boolean makeAdvisable) {
if (!implementsRespectively(interfaces, implementations)) {
throw new RuntimeException("Given implementations not consistents with given interfaces");
}
Class proxy = getProxyClassFor(interfaces, useCache, makeAdvisable);
try {
return proxy.getConstructor(interfaces).newInstance(implementations);
} catch (Throwable t) {
throw new WrappedRuntimeException(t);
}
}
/**
* Return true if each implementation implement the respective given interface.
*
* @param interfaces
* @param implementations
* @return
*/
private static boolean implementsRespectively(final Class[] interfaces, final Object[] implementations) {
if (interfaces.length != implementations.length) {
return false;
}
for (int i = 0; i < interfaces.length; i++) {
if (!interfaces[i].isAssignableFrom(implementations[i].getClass())) {
return false;
}
}
return true;
}
/**
* Compile a new proxy class and attach it to the lowest shared classloader of the given interfaces (as for JDK proxy).
*
* @param interfaces
* @param makeAdvisable
* @return
*/
private static Class getNewProxyClassFor(Class[] interfaces, boolean makeAdvisable) {
ClassLoader loader = getLowestClassLoader(interfaces);
String proxyClassName = getUniqueClassNameForProxy(interfaces);
if (makeAdvisable) {
Proxy.makeProxyAdvisable(proxyClassName, loader);
}
final byte[] bytes = ProxyDelegationCompiler.compileProxyFor(loader, interfaces, proxyClassName);
// 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 the lowest (childest) shared classloader or fail it detects parallel hierarchies.
*
* @param classes
* @return
*/
private static ClassLoader getLowestClassLoader(Class[] classes) {
ClassLoader loader = classes[0].getClassLoader();
for (int i = 1; i < classes.length; i++) {
Class other = classes[i];
if (SystemDefinitionContainer.isChildOf(other.getClassLoader(), loader)) {
loader = other.getClassLoader();
} else if (SystemDefinitionContainer.isChildOf(loader, other.getClassLoader())) {
;//loader is fine
} else {
throw new RuntimeException("parallel classloader hierarchy not supported");
}
}
return loader;
}
private static String getUniqueClassNameForProxy(Class[] interfaces) {
//TODO based on first interface. Do we need a naming strategy / prefix ?
//TODO see in pointcut matching how the classname is tweaked to get the class it refers to
return interfaces[0].getName().replace('.', '/') + PROXY_SUFFIX_START + new Long(Uuid.newUuid()).toString();
}
/**
* A composite key for the proxy cache.
*/
private static class CompositeClassKey {
private final Class[] m_interfaces;
CompositeClassKey(Class[] interfaces) {
m_interfaces = interfaces;
}
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof CompositeClassKey)) return false;
final CompositeClassKey compositeClassKey = (CompositeClassKey) o;
if (!Arrays.equals(m_interfaces, compositeClassKey.m_interfaces)) return false;
return true;
}
public int hashCode() {
int result = 1;
for (int i = 0; i < m_interfaces.length; i++) {
result = 31 * result + m_interfaces[i].hashCode();
}
return result;
}
}
}