package net.sourceforge.javautil.interceptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.interceptor.InterceptorBinding;
import javax.interceptor.Interceptors;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.util.ASMifierClassVisitor;
import net.sourceforge.javautil.bytecode.BytecodeCompiler;
import net.sourceforge.javautil.bytecode.IBytecodeFactory;
import net.sourceforge.javautil.bytecode.BytecodeCompiler.Version;
import net.sourceforge.javautil.bytecode.api.IBytecodeField;
import net.sourceforge.javautil.bytecode.api.IBytecodeResolvable;
import net.sourceforge.javautil.bytecode.api.MethodDescriptor;
import net.sourceforge.javautil.bytecode.api.TypeDescriptor;
import net.sourceforge.javautil.bytecode.api.TypeMemberAccess.Scope;
import net.sourceforge.javautil.bytecode.api.type.JavaClass;
import net.sourceforge.javautil.bytecode.api.type.JavaClassConcrete;
import net.sourceforge.javautil.bytecode.api.type.method.BytecodeBlock;
import net.sourceforge.javautil.bytecode.api.type.method.IBytecodeConstructor;
import net.sourceforge.javautil.bytecode.api.type.method.BytecodeConstructorBase;
import net.sourceforge.javautil.bytecode.api.type.method.BytecodeContextMethod;
import net.sourceforge.javautil.bytecode.api.type.method.IBytecodeMethod;
import net.sourceforge.javautil.common.CollectionUtil;
import net.sourceforge.javautil.common.VirtualArtifactUtil;
import net.sourceforge.javautil.common.io.IVirtualFile;
import net.sourceforge.javautil.common.reflection.ReflectionContext;
import net.sourceforge.javautil.common.reflection.cache.ClassCache;
import net.sourceforge.javautil.common.reflection.cache.ClassDescriptor;
import net.sourceforge.javautil.common.reflection.cache.ClassMethod;
import net.sourceforge.javautil.interceptor.annotation.Interceptorability;
import net.sourceforge.javautil.interceptor.asm.InterceptorHelper;
import net.sourceforge.javautil.interceptor.type.InterceptorInvocationHandler;
import net.sourceforge.javautil.interceptor.type.InterceptorProxyTypeAbstract;
import net.sourceforge.javautil.interceptor.type.InterceptorProxyTypeInterceptor;
import net.sourceforge.javautil.interceptor.type.cjc.InterceptorProxyConstructor;
import net.sourceforge.javautil.interceptor.type.cjc.InterceptorProxyMethod;
import net.sourceforge.javautil.interceptor.type.cjc.InterceptorProxyTypeCJC;
import net.sourceforge.javautil.interceptor.type.cjcw.InterceptorProxyTypeCJCW;
/**
* Interceptor proxy compiler based off the {@link IBytecodeFactory} API.
*
* @author elponderador
* @author $Author$
* @version $Id$
*/
public class InterceptorCompiler extends BytecodeCompiler implements IInterceptorFactory {
public enum InterceptorGranularity { PerClass, PerInstance }
protected final Map<Class<?>, Class<?>> interceptorTypes = new HashMap<Class<?>, Class<?>>();
protected final Map<Class<?>, Class<?>> interceptorWrapperTypes = new HashMap<Class<?>, Class<?>>();
protected final Map<Class<?>, IInterceptorManager> defaultInterceptors = new HashMap<Class<?>, IInterceptorManager>();
protected final Map<Class<? extends Annotation>, EnabledInterceptorBinding> interceptorBindings = new HashMap<Class<? extends Annotation>, EnabledInterceptorBinding>();
protected String proxySuffix = "NETSFProxy";
protected IInterceptorFactory interceptorFactory = this;
protected InterceptorGranularity defaultGranularity = InterceptorGranularity.PerClass;
public InterceptorCompiler(IBytecodeFactory factory) {
super(factory);
}
public InterceptorCompiler(ClassLoader parent, IBytecodeFactory factory, Version defaultVersion) {
super(parent, factory, defaultVersion);
}
public InterceptorCompiler(ClassLoader parent, IBytecodeFactory factory) {
super(parent, factory);
}
/**
* @return The suffix appended to class names for generated class proxies.
*/
public String getProxySuffix() { return proxySuffix; }
public void setProxySuffix(String proxySuffix) { this.proxySuffix = proxySuffix; }
/**
* @return The granularity for default {@link IInterceptorManager} creation.
*
* @see #getDefaultProxy(ClassLoader, IInterceptedInstanceWrapper, Class)
* @see #getDefaultProxyInstance(Class, Object...)
* @see #getDefaultWrapperProxyInstance(Class, IInterceptedInstanceWrapper, Object...)
*/
public InterceptorGranularity getDefaultGranularity() { return defaultGranularity; }
public void setDefaultGranularity(InterceptorGranularity defaultGranularity) { this.defaultGranularity = defaultGranularity; }
/**
* @return The factory used by this compiler for interceptor chain construction
*/
public IInterceptorFactory getInterceptorFactory() { return interceptorFactory; }
/**
* This will not be retro-active on proxies already instantiated by this compiler.
*
* @param interceptorFactory The new factory to use
*/
public void setInterceptorFactory(IInterceptorFactory interceptorFactory) {
this.interceptorFactory = interceptorFactory;
this.defaultInterceptors.clear();
}
public Object instantiateInterceptorLink(Class interceptorClass, IInterceptorLink next, Object intercepted) {
return IInterceptorLink.class.isAssignableFrom(interceptorClass) ?
ClassCache.getFor(interceptorClass).newInstance(next) :
ClassCache.getFor(interceptorClass).newInstance();
}
public IInterceptorLink buildCustomChain(Object intercepted, Class... interceptorClasses) {
return this.buildCustomChain(intercepted, new InterceptorLinkTarget(), interceptorClasses);
}
public IInterceptorLink buildCustomChain(Object intercepted, IInterceptorLink root, Class... interceptorClasses) {
for (Class clazz : interceptorClasses) {
Object interceptor = this.instantiateInterceptorLink(clazz, root, intercepted);
if (interceptor instanceof IInterceptorLink) {
root = (IInterceptorLink) interceptor;
} else {
root = new InterceptorLinkWrapper(root, interceptor);
}
}
return root;
}
public IInterceptorLink buildDefaultChain(AnnotatedElement element, Object intercepted) {
return this.buildDefaultChain(new InterceptorLinkTarget(), element, intercepted);
}
public IInterceptorLink buildDefaultChain(IInterceptorLink root, AnnotatedElement element, Object intercepted) {
List<Class> aspects = new ArrayList<Class>();
Interceptors declared = element.getAnnotation(Interceptors.class);
if (declared != null) {
aspects.addAll(Arrays.asList(declared.value()));
}
Set<Class<? extends Annotation>> bindings = this.collectBindings(element, new LinkedHashSet<Class<? extends Annotation>>());
List<EnabledInterceptorBinding> enabled = new ArrayList<EnabledInterceptorBinding>();
for (Class<? extends Annotation> binding : bindings) {
if (this.interceptorBindings.containsKey(binding)) {
if (!enabled.contains(this.interceptorBindings.get(binding)))
enabled.add(this.interceptorBindings.get(binding));
}
}
if (enabled.size() > 0) {
if (enabled.size() > 1) Collections.sort(enabled);
for (EnabledInterceptorBinding ei : enabled) {
aspects.add(ei.interceptor);
}
}
if (aspects.size() == 0) return root;
return this.buildCustomChain(root, aspects.toArray(new Class[aspects.size()]));
}
public IInterceptorManager createInterceptor(Object intercepted, IInterceptorLink root) {
return new InterceptorBase(this, root == null ? new InterceptorLinkTarget() : root);
}
public void bind(Class<?> interceptorClass) {
if (interceptorClass.getAnnotation(javax.interceptor.Interceptor.class) != null) {
for (Annotation annotation : interceptorClass.getAnnotations()) {
InterceptorBinding ib = annotation.annotationType().getAnnotation(InterceptorBinding.class);
if (ib != null) {
if (this.interceptorBindings.containsKey(annotation.annotationType())) {
throw new IllegalArgumentException("More than one interceptor defined with the same binding: " + annotation);
}
this.interceptorBindings.put(annotation.annotationType(), new EnabledInterceptorBinding(interceptorBindings.size(), interceptorClass));
}
}
} else {
throw new IllegalArgumentException("This is not an interceptor class");
}
}
/**
* @param annotated The annotated element
* @param annotations The current set of annotations
*/
protected Set<Class<? extends Annotation>> collectBindings (AnnotatedElement annotated, Set<Class<? extends Annotation>> annotations) {
for (Annotation annotation : annotated.getAnnotations()) {
if (annotation.annotationType().getAnnotation(InterceptorBinding.class) != null) {
annotations.add(annotation.annotationType());
this.collectBindings(annotation.annotationType(), annotations);
}
}
return annotations;
}
/**
* @param <T> The interface type
* @param proxyClassLoader The class loader to use for creating the proxy
* @param instance The instance to wrap, must implement the interface
* @param iface The interface type class
* @param interceptors The interceptors to use when proxied
* @return The proxy instance
*/
public <T> T getProxy (ClassLoader proxyClassLoader, IInterceptedInstanceWrapper<T> instance, Class<T> iface, IInterceptorManager interceptor) {
List<IInterceptorManipulator> abilities = this.getInterceptorAbilities(iface);
InterceptorInvocationHandler handler = new InterceptorInvocationHandler(instance, interceptor);
T proxy = (T) Proxy.newProxyInstance(proxyClassLoader, new Class[] { iface }, handler);
for (IInterceptorManipulator ability : abilities) {
ability.manipulate(iface, interceptor, proxy, InterceptorInvocationHandler.getStateMap(handler));
}
return proxy;
}
/**
* This will build the {@link IInterceptorManager} from defaults specified on the target.
*
* @see #getProxy(ClassLoader, Object, Class, IInterceptorManager)
*/
public <T> T getDefaultProxy (ClassLoader proxyClassLoader, IInterceptedInstanceWrapper<T> instance, Class<T> iface) {
return getProxy(proxyClassLoader, instance, iface, interceptorFactory.createInterceptor(instance, buildDefaultChain(instance.getClass(), instance)));
}
/**
* This will build the {@link IInterceptorManager} from the specified classes.
*
* @see #getProxy(ClassLoader, Object, Class, IInterceptorManager)
*/
public <T> T getProxy (ClassLoader proxyClassLoader, T instance, Class<T> iface, Class... interceptors) {
return getProxy(proxyClassLoader, instance, iface, interceptorFactory.createInterceptor(instance, buildCustomChain(instance, interceptors)));
}
/**
* This will wrap the instance in {@link InterceptedInstanceSimple}.
*
* @see #getProxy(ClassLoader, IInterceptedInstanceWrapper, Class, IInterceptorManager)
*/
public <T> T getProxy (ClassLoader proxyClassLoader, T instance, Class<T> iface, IInterceptorManager interceptor) {
return this.getProxy(proxyClassLoader, new InterceptedInstanceSimple<T>(instance), iface, interceptor);
}
/**
* This will wrap the instance in {@link InterceptedInstanceSimple}.
*
* @see #getDefaultProxy(ClassLoader, IInterceptedInstanceWrapper, Class)
*/
public <T> T getDefaultProxy (ClassLoader proxyClassLoader, T instance, Class<T> iface) {
return this.getDefaultProxy(proxyClassLoader, new InterceptedInstanceSimple<T>(instance), iface);
}
/**
* This will build the {@link IInterceptorManager} from the specified classes.
*
* @see #getProxy(ClassLoader, Object, Class, IInterceptorManager)
*/
public <T> T getProxy (ClassLoader proxyClassLoader, IInterceptedInstanceWrapper<T> instance, Class<T> iface, Class... interceptors) {
return getProxy(proxyClassLoader, instance, iface, interceptorFactory.createInterceptor(instance, buildCustomChain(instance, interceptors)));
}
/**
* @param <T> The type of CONCRETE class
* @param instance The instance to wrap
* @param chain The interceptors to use
* @param parameters The parameters to pass to the
* @return An instance of the proxy class wrapping the instance passed
*
* @see #getProxy(Class)
*/
public <T> T getProxyInstance (Class<T> type, IInterceptorManager interceptor, Object... parameters) throws IOException {
Class<? extends T> proxyClass = this.getProxyClass(type);
ClassDescriptor<? extends T> pcDesc = ClassCache.getFor(proxyClass);
T instance = pcDesc.newInstance(CollectionUtil.insert(parameters, 0, interceptor));
if (interceptor == null) {
ClassMethod method = ClassCache.getFor(proxyClass).findMethod("set$Interceptor", IInterceptorManager.class);
ReflectionContext.getReflectionManager().ensureAccesssibility(method.getJavaMember());
method.invoke(instance, this.getDefaultInterceptorManager(instance, type));
}
return instance;
}
/**
* This will create the {@link IInterceptorManager} from the specified classes
*
* @see #getProxyInstance(Object, IInterceptorLink, Object...)
*/
public <T> T getProxyInstance (Class<T> type, Class[] interceptors, Object... parameters) throws IOException {
return getProxyInstance(type, interceptorFactory.createInterceptor(null, buildCustomChain(null, interceptors)), parameters);
}
/**
* This will create the {@link IInterceptorManager} from the class specified interceptors.
*
* @see #getProxyInstance(Object, IInterceptorLink, Object...)
*/
public <T> T getDefaultProxyInstance (Class<T> type, Object... parameters) throws IOException {
return getProxyInstance(type, (IInterceptorManager) null, parameters);
}
/**
* @param <T> The type of proxy
* @param type The class of the type
* @param instance The instance wrapper
* @param interceptor The interceptor to use for this proxy instance
* @param parameters The parameters to pass to the constructor
* @return The proxy instance
*/
public <T> T getWrapperProxyInstance (Class<T> type, IInterceptedInstanceWrapper<T> instance, IInterceptorManager interceptor, Object... parameters) throws IOException {
Class<? extends T> proxyClass = this.getWrapperProxyClass(type);
ClassDescriptor<? extends T> pcDesc = ClassCache.getFor(proxyClass);
return pcDesc.newInstance(CollectionUtil.insert(parameters, 0, interceptor, instance));
}
/**
* Create an interceptor using the passed interceptor classes.
*
* @param interceptors The classes used to create the interceptor chain.
*
* @see #getWrapperProxyInstance(Class, IInterceptedInstanceWrapper, IInterceptorManager, Object...)
* @see #buildCustomChain(Class...)
*/
public <T> T getWrapperProxyInstance (Class<T> type, IInterceptedInstanceWrapper<T> instance, Class[] interceptors, Object... parameters) throws IOException {
return getWrapperProxyInstance(type, instance, interceptorFactory.createInterceptor(instance, buildCustomChain(instance, interceptors)), parameters);
}
/**
* Use the default interceptors for the proxy.
*
* @see #getWrapperProxyInstance(Class, IInterceptedInstanceWrapper, IInterceptorManager, Object...)
*/
public <T> T getDefaultWrapperProxyInstance (Class<T> type, IInterceptedInstanceWrapper<T> instance, Object... parameters) throws IOException {
return getWrapperProxyInstance(type, instance, this.getDefaultInterceptorManager(instance, type), parameters);
}
/**
* This uses {@link InterceptedInstanceSimple} to wrap the concrete instance.
*
* @see #getWrapperProxyInstance(Class, IInterceptedInstanceWrapper, IInterceptorManager, Object...)
*/
public <T> T getWrapperProxyInstance (Class<T> type, T instance, IInterceptorManager interceptor, Object... parameters) throws IOException {
return this.getWrapperProxyInstance(type, new InterceptedInstanceSimple<T>(instance), interceptor, parameters);
}
/**
* This uses {@link InterceptedInstanceSimple} to wrap the concrete instance.
*
* @see #getWrapperProxyInstance(Class, IInterceptedInstanceWrapper, Class[], Object...)
*/
public <T> T getWrapperProxyInstance (Class<T> type, T instance, Class[] interceptors, Object... parameters) throws IOException {
return getWrapperProxyInstance(type, new InterceptedInstanceSimple<T>(instance), interceptors, parameters);
}
/**
* This uses {@link InterceptedInstanceSimple} to wrap the concrete instance.
*
* @see #getDefaultWrapperProxyInstance(Class, IInterceptedInstanceWrapper, Object...)
*/
public <T> T getDefaultWrapperProxyInstance (Class<T> type, T instance, Object... parameters) throws IOException {
return this.getDefaultWrapperProxyInstance(type, new InterceptedInstanceSimple<T>(instance), parameters);
}
/**
* NOTE: It is preferrable that the class provide a no-argument constructor.
*
* @param <T> The type of CONCRETE class
* @param type The type's class
* @return A class of the same 'type', an extended class, for proxy creation with all constructors accepting two inserted arguments
* at the beginning of the parameter list of each constructor, the first of the actual type this is extending and the second
* an array of {@link IInterceptorLink}'s.
*
* @see #getProxyInstance(Object, IInterceptorLink[], Object...)
*/
public synchronized <T> Class<? extends T> getProxyClass (Class<T> type) throws IOException {
if (interceptorTypes.containsKey(type)) return (Class<? extends T>) interceptorTypes.get(type);
Class<? extends T> proxyClass = compile(this.getProxyClassTemplate(type, false));
this.interceptorTypes.put(type, proxyClass);
return proxyClass;
}
/**
* NOTE: It is preferrable that the class provide a no-argument constructor.
*
* @param <T> The type of CONCRETE class
* @param type The type's class
* @return A class of the same 'type', an extended class, for proxy creation with all constructors accepting two inserted arguments
* at the beginning of the parameter list of each constructor, the first of the actual type this is extending and the second
* an array of {@link IInterceptorLink}'s.
*
* @see #getProxyInstance(Object, IInterceptorLink[], Object...)
*/
public synchronized <T> Class<? extends T> getWrapperProxyClass (Class<T> type) throws IOException {
if (interceptorWrapperTypes.containsKey(type)) return (Class<? extends T>) interceptorWrapperTypes.get(type);
Class<? extends T> proxyClass = compile(this.getProxyClassTemplate(type, true));
this.interceptorWrapperTypes.put(type, proxyClass);
return proxyClass;
}
/**
* @param type The type the template is for
* @return The concrete class template
*/
protected JavaClassConcrete getProxyClassTemplate (Class<?> type, boolean wrapper) {
IBytecodeResolvable resolved = getPool().resolve(type.getName());
if (resolved.isFinal() || resolved.isAbstract())
throw new IllegalArgumentException("Cannot create class based proxies for final/non static/abstract classes");
final List<IInterceptorManipulator> abilities = this.getInterceptorAbilities(type);
InterceptorProxyTypeAbstract proxy = wrapper ?
new InterceptorProxyTypeCJCW(this, abilities, type, type.getName() + "$CJCW$" + proxySuffix) :
new InterceptorProxyTypeCJC(this, abilities, type, type.getName() + "$CJC$" + proxySuffix);
proxy.addProxyConstructors();
proxy.addProxyMethods();
proxy.addProxyAbilities();
return proxy;
}
/**
* @param type The type abilities are for
* @return The list of abilities for this type
*/
protected List<IInterceptorManipulator> getInterceptorAbilities (Class type) {
Set<Class<? extends IInterceptorManipulator>> abilityTypes = new LinkedHashSet<Class<? extends IInterceptorManipulator>>();
this.findInterceptorAbilities(type, abilityTypes);
List<IInterceptorManipulator> abilities = new ArrayList<IInterceptorManipulator>();
for (Class<? extends IInterceptorManipulator> atype : abilityTypes) {
abilities.add(ClassCache.getFor(atype).newInstance());
}
return abilities;
}
/**
* Recursive loop based search for annotations that added {@link IInterceptorManipulator}'s to a particular proxy class.
*
* @param type The type in question
* @param abilities The unique set of abilities for the particular type
*/
protected void findInterceptorAbilities (Class type, Set<Class<? extends IInterceptorManipulator>> abilityTypes) {
for (Annotation annotation : type.getAnnotations()) {
if (annotation.annotationType().getPackage().getName().startsWith("java")) continue;
Interceptorability ability = annotation.annotationType().getAnnotation(Interceptorability.class);
if (ability != null) {
abilityTypes.add(ability.value());
}
this.findInterceptorAbilities(annotation.annotationType(), abilityTypes);
}
}
/**
* @param instance The instance in question
* @param type The type of interceptor
* @return The manager for the particular type
*
* @see #getDefaultGranularity()
*/
protected IInterceptorManager getDefaultInterceptorManager (Object instance, Class type) {
if (this.defaultGranularity == InterceptorGranularity.PerInstance) {
return interceptorFactory.createInterceptor(instance, buildDefaultChain(type, instance));
} else {
IInterceptorManager interceptor = this.defaultInterceptors.get(type);
if (interceptor == null)
interceptor = interceptorFactory.createInterceptor(instance, buildDefaultChain(type, instance));
return interceptor;
}
}
/**
* Wrapper for enabled interceptors allowing interceptor ordering.
*
* @author elponderador
* @author $Author$
* @version $Id$
*/
protected class EnabledInterceptorBinding implements Comparable<EnabledInterceptorBinding> {
protected final int index;
protected final Class<?> interceptor;
public EnabledInterceptorBinding(int index, Class<?> interceptor) {
this.index = index;
this.interceptor = interceptor;
}
public int compareTo(EnabledInterceptorBinding o) {
int compare = o.index;
return index == compare ? 0 : (index > compare ? 1 : -1);
}
}
}