/* | |
* 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.proxy2.javassist; | |
import java.lang.reflect.Method; | |
import javassist.CannotCompileException; | |
import javassist.CtClass; | |
import javassist.CtConstructor; | |
import javassist.CtMethod; | |
import org.apache.commons.proxy2.Interceptor; | |
import org.apache.commons.proxy2.Invoker; | |
import org.apache.commons.proxy2.ObjectProvider; | |
import org.apache.commons.proxy2.ProxyUtils; | |
import org.apache.commons.proxy2.exception.ProxyFactoryException; | |
import org.apache.commons.proxy2.impl.AbstractProxyClassGenerator; | |
import org.apache.commons.proxy2.impl.AbstractSubclassingProxyFactory; | |
import org.apache.commons.proxy2.impl.ProxyClassCache; | |
public class JavassistProxyFactory extends AbstractSubclassingProxyFactory | |
{ | |
//****************************************************************************************************************** | |
// Fields | |
//****************************************************************************************************************** | |
private static final String GET_METHOD_METHOD_NAME = "_javassistGetMethod"; | |
private static final ProxyClassCache DELEGATING_PROXY_CACHE = new ProxyClassCache( | |
new DelegatingProxyClassGenerator()); | |
private static final ProxyClassCache INTERCEPTOR_PROXY_CACHE = new ProxyClassCache( | |
new InterceptorProxyClassGenerator()); | |
private static final ProxyClassCache INVOKER_PROXY_CACHE = 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); | |
} | |
//****************************************************************************************************************** | |
// ProxyFactory Implementation | |
//****************************************************************************************************************** | |
/** | |
* {@inheritDoc} | |
*/ | |
@SuppressWarnings("unchecked") | |
public <T> T createDelegatorProxy(ClassLoader classLoader, ObjectProvider<?> targetProvider, | |
Class<?>... proxyClasses) | |
{ | |
try | |
{ | |
final Class<? extends T> clazz = (Class<? extends T>) DELEGATING_PROXY_CACHE.getProxyClass(classLoader, | |
proxyClasses); | |
return clazz.getConstructor(ObjectProvider.class).newInstance(targetProvider); | |
} | |
catch (Exception e) | |
{ | |
throw new ProxyFactoryException("Unable to instantiate proxy2 from generated proxy2 class.", e); | |
} | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
@SuppressWarnings("unchecked") | |
public <T> T createInterceptorProxy(ClassLoader classLoader, Object target, Interceptor interceptor, | |
Class<?>... proxyClasses) | |
{ | |
try | |
{ | |
final Class<? extends T> clazz = (Class<? extends T>) INTERCEPTOR_PROXY_CACHE.getProxyClass(classLoader, | |
proxyClasses); | |
return clazz.getConstructor(Object.class, Interceptor.class).newInstance(target, interceptor); | |
} | |
catch (Exception e) | |
{ | |
throw new ProxyFactoryException("Unable to instantiate proxy2 class instance.", e); | |
} | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
@SuppressWarnings("unchecked") | |
public <T> T createInvokerProxy(ClassLoader classLoader, Invoker invoker, Class<?>... proxyClasses) | |
{ | |
try | |
{ | |
final Class<? extends T> clazz = (Class<? extends T>) INVOKER_PROXY_CACHE.getProxyClass(classLoader, | |
proxyClasses); | |
return clazz.getConstructor(Invoker.class).newInstance(invoker); | |
} | |
catch (Exception e) | |
{ | |
throw new ProxyFactoryException("Unable to instantiate proxy2 from generated proxy2 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 (!ProxyUtils.isEqualsMethod(methods[i]) && !ProxyUtils.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, null); | |
} | |
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 (!ProxyUtils.isEqualsMethod(methods[i]) && !ProxyUtils.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() + "( this, target, " + GET_METHOD_METHOD_NAME + "(\"" | |
+ methods[i].getDeclaringClass().getName() + "\", \"" + methods[i].getName() | |
+ "\", $sig), $args ) );\n }"; | |
method.setBody(body); | |
proxyClass.addMethod(method); | |
} | |
} | |
return proxyClass.toClass(classLoader, null); | |
} | |
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 (!ProxyUtils.isEqualsMethod(methods[i]) && !ProxyUtils.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, null); | |
} | |
catch (CannotCompileException e) | |
{ | |
throw new ProxyFactoryException("Could not compile class.", e); | |
} | |
} | |
} | |
} |