blob: 7a532f223fd9f5e7686d3b5037613810496fe2a2 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.proxy.factory.javassist;
import javassist.CannotCompileException;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtMethod;
import org.apache.commons.proxy.Interceptor;
import org.apache.commons.proxy.Invoker;
import org.apache.commons.proxy.ObjectProvider;
import org.apache.commons.proxy.exception.ProxyFactoryException;
import org.apache.commons.proxy.factory.util.AbstractProxyClassGenerator;
import org.apache.commons.proxy.factory.util.AbstractSubclassingProxyFactory;
import org.apache.commons.proxy.factory.util.ProxyClassCache;
import java.lang.reflect.Method;
/**
* A <a href="http://www.jboss.org/products/javassist">Javassist</a>-based {@link org.apache.commons.proxy.ProxyFactory}
* implementation.
* <p/>
* <b>Dependencies</b>: <ul> <li>Javassist version 3.0 or greater</li> </ul> </p>
*
* @author James Carman
* @since 1.0
*/
public class JavassistProxyFactory extends AbstractSubclassingProxyFactory
{
//**********************************************************************************************************************
// Fields
//**********************************************************************************************************************
private static final String GET_METHOD_METHOD_NAME = "_javassistGetMethod";
private static final ProxyClassCache delegatingProxyClassCache = new ProxyClassCache(
new DelegatingProxyClassGenerator());
private static final ProxyClassCache interceptorProxyClassCache = new ProxyClassCache(
new InterceptorProxyClassGenerator());
private static final ProxyClassCache invocationHandlerProxyClassCache = new ProxyClassCache(
new InvokerProxyClassGenerator());
//**********************************************************************************************************************
// Static Methods
//**********************************************************************************************************************
private static void addGetMethodMethod(CtClass proxyClass) throws CannotCompileException
{
final CtMethod method = new CtMethod(JavassistUtils.resolve(Method.class), GET_METHOD_METHOD_NAME,
JavassistUtils.resolve(new Class[]{String.class, String.class, Class[].class}), proxyClass);
final String body = "try { return Class.forName($1).getMethod($2, $3); } catch( Exception e ) " +
"{ throw new RuntimeException(\"Unable to look up method.\", e); }";
method.setBody(body);
proxyClass.addMethod(method);
}
//**********************************************************************************************************************
// Other Methods
//**********************************************************************************************************************
public Object createDelegatorProxy(ClassLoader classLoader, ObjectProvider targetProvider,
Class[] proxyClasses)
{
try
{
final Class clazz = delegatingProxyClassCache.getProxyClass(classLoader, proxyClasses);
return clazz.getConstructor(new Class[]{ObjectProvider.class})
.newInstance(new Object[]{targetProvider});
}
catch (Exception e)
{
throw new ProxyFactoryException("Unable to instantiate proxy from generated proxy class.", e);
}
}
public Object createInterceptorProxy(ClassLoader classLoader, Object target, Interceptor interceptor,
Class[] proxyClasses)
{
try
{
final Class clazz = interceptorProxyClassCache.getProxyClass(classLoader, proxyClasses);
return clazz.getConstructor(new Class[]{Object.class, Interceptor.class})
.newInstance(new Object[]{target, interceptor});
}
catch (Exception e)
{
throw new ProxyFactoryException("Unable to instantiate proxy class instance.", e);
}
}
public Object createInvokerProxy(ClassLoader classLoader, Invoker invoker,
Class[] proxyClasses)
{
try
{
final Class clazz = invocationHandlerProxyClassCache.getProxyClass(classLoader, proxyClasses);
return clazz.getConstructor(new Class[]{Invoker.class})
.newInstance(new Object[]{invoker});
}
catch (Exception e)
{
throw new ProxyFactoryException("Unable to instantiate proxy from generated proxy class.", e);
}
}
//**********************************************************************************************************************
// Inner Classes
//**********************************************************************************************************************
private static class DelegatingProxyClassGenerator extends AbstractProxyClassGenerator
{
public Class generateProxyClass(ClassLoader classLoader, Class[] proxyClasses)
{
try
{
final CtClass proxyClass = JavassistUtils.createClass(getSuperclass(proxyClasses));
JavassistUtils.addField(ObjectProvider.class, "provider", proxyClass);
final CtConstructor proxyConstructor = new CtConstructor(
JavassistUtils.resolve(new Class[]{ObjectProvider.class}),
proxyClass);
proxyConstructor.setBody("{ this.provider = $1; }");
proxyClass.addConstructor(proxyConstructor);
JavassistUtils.addInterfaces(proxyClass, toInterfaces(proxyClasses));
addHashCodeMethod(proxyClass);
addEqualsMethod(proxyClass);
final Method[] methods = getImplementationMethods(proxyClasses);
for (int i = 0; i < methods.length; ++i)
{
if (!isEqualsMethod(methods[i]) && !isHashCode(methods[i]))
{
final Method method = methods[i];
final CtMethod ctMethod = new CtMethod(JavassistUtils.resolve(method.getReturnType()),
method.getName(),
JavassistUtils.resolve(method.getParameterTypes()),
proxyClass);
final String body = "{ return ( $r ) ( ( " + method.getDeclaringClass().getName() +
" )provider.getObject() )." +
method.getName() + "($$); }";
ctMethod.setBody(body);
proxyClass.addMethod(ctMethod);
}
}
return proxyClass.toClass(classLoader);
}
catch (CannotCompileException e)
{
throw new ProxyFactoryException("Could not compile class.", e);
}
}
}
private static class InterceptorProxyClassGenerator extends AbstractProxyClassGenerator
{
public Class generateProxyClass(ClassLoader classLoader, Class[] proxyClasses)
{
try
{
final CtClass proxyClass = JavassistUtils.createClass(getSuperclass(proxyClasses));
final Method[] methods = getImplementationMethods(proxyClasses);
JavassistUtils.addInterfaces(proxyClass, toInterfaces(proxyClasses));
JavassistUtils.addField(Object.class, "target", proxyClass);
JavassistUtils.addField(Interceptor.class, "interceptor", proxyClass);
addGetMethodMethod(proxyClass);
addHashCodeMethod(proxyClass);
addEqualsMethod(proxyClass);
final CtConstructor proxyConstructor = new CtConstructor(
JavassistUtils.resolve(
new Class[]{Object.class, Interceptor.class}),
proxyClass);
proxyConstructor
.setBody(
"{\n\tthis.target = $1;\n\tthis.interceptor = $2; }");
proxyClass.addConstructor(proxyConstructor);
for (int i = 0; i < methods.length; ++i)
{
if (!isEqualsMethod(methods[i]) && !isHashCode(methods[i]))
{
final CtMethod method = new CtMethod(JavassistUtils.resolve(methods[i].getReturnType()),
methods[i].getName(),
JavassistUtils.resolve(methods[i].getParameterTypes()),
proxyClass);
final Class invocationClass = JavassistInvocation
.getMethodInvocationClass(classLoader, methods[i]);
final String body = "{\n\t return ( $r ) interceptor.intercept( new " + invocationClass.getName() +
"( " + GET_METHOD_METHOD_NAME + "(\"" + methods[i].getDeclaringClass().getName() +
"\", \"" + methods[i].getName() + "\", $sig), target, $args ) );\n }";
method.setBody(body);
proxyClass.addMethod(method);
}
}
return proxyClass.toClass(classLoader);
}
catch (CannotCompileException e)
{
throw new ProxyFactoryException("Could not compile class.", e);
}
}
}
private static void addEqualsMethod(CtClass proxyClass)
throws CannotCompileException
{
final CtMethod equalsMethod = new CtMethod(JavassistUtils.resolve(Boolean.TYPE), "equals",
JavassistUtils.resolve(new Class[]{Object.class}), proxyClass);
final String body = "{\n\treturn this == $1;\n}";
equalsMethod.setBody(body);
proxyClass.addMethod(equalsMethod);
}
private static void addHashCodeMethod(CtClass proxyClass)
throws CannotCompileException
{
final CtMethod hashCodeMethod = new CtMethod(JavassistUtils.resolve(Integer.TYPE), "hashCode",
new CtClass[0], proxyClass);
hashCodeMethod.setBody("{\n\treturn System.identityHashCode(this);\n}");
proxyClass.addMethod(hashCodeMethod);
}
private static class InvokerProxyClassGenerator extends AbstractProxyClassGenerator
{
public Class generateProxyClass(ClassLoader classLoader, Class[] proxyClasses)
{
try
{
final CtClass proxyClass = JavassistUtils.createClass(getSuperclass(proxyClasses));
final Method[] methods = getImplementationMethods(proxyClasses);
JavassistUtils.addInterfaces(proxyClass, toInterfaces(proxyClasses));
JavassistUtils.addField(Invoker.class, "invoker", proxyClass);
final CtConstructor proxyConstructor = new CtConstructor(
JavassistUtils.resolve(
new Class[]{Invoker.class}),
proxyClass);
proxyConstructor
.setBody("{\n\tthis.invoker = $1; }");
proxyClass.addConstructor(proxyConstructor);
addGetMethodMethod(proxyClass);
addHashCodeMethod(proxyClass);
addEqualsMethod(proxyClass);
for (int i = 0; i < methods.length; ++i)
{
if (!isEqualsMethod(methods[i]) && !isHashCode(methods[i]))
{
final CtMethod method = new CtMethod(JavassistUtils.resolve(methods[i].getReturnType()),
methods[i].getName(),
JavassistUtils.resolve(methods[i].getParameterTypes()),
proxyClass);
final String body = "{\n\t return ( $r ) invoker.invoke( this, " + GET_METHOD_METHOD_NAME + "(\"" +
methods[i].getDeclaringClass().getName() +
"\", \"" + methods[i].getName() + "\", $sig), $args );\n }";
method.setBody(body);
proxyClass.addMethod(method);
}
}
return proxyClass.toClass(classLoader);
}
catch (CannotCompileException e)
{
throw new ProxyFactoryException("Could not compile class.", e);
}
}
}
}