You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by jc...@apache.org on 2005/08/31 20:59:51 UTC
svn commit: r265552 - in /jakarta/commons/sandbox/proxy/trunk/src:
java/org/apache/commons/proxy/factory/
java/org/apache/commons/proxy/factory/javassist/
test/org/apache/commons/proxy/factory/
test/org/apache/commons/proxy/factory/javassist/ test/org/...
Author: jcarman
Date: Wed Aug 31 11:59:43 2005
New Revision: 265552
URL: http://svn.apache.org/viewcvs?rev=265552&view=rev
Log:
Improved test coverage/refactored JavassistProxyFactory for better maintainability.
Added:
jakarta/commons/sandbox/proxy/trunk/src/java/org/apache/commons/proxy/factory/javassist/JavassistMethodInvocation.java (with props)
jakarta/commons/sandbox/proxy/trunk/src/java/org/apache/commons/proxy/factory/javassist/JavassistUtils.java (with props)
jakarta/commons/sandbox/proxy/trunk/src/test/org/apache/commons/proxy/util/DuplicateEcho.java (with props)
Modified:
jakarta/commons/sandbox/proxy/trunk/src/java/org/apache/commons/proxy/factory/AbstractProxyFactory.java
jakarta/commons/sandbox/proxy/trunk/src/java/org/apache/commons/proxy/factory/javassist/JavassistProxyFactory.java
jakarta/commons/sandbox/proxy/trunk/src/test/org/apache/commons/proxy/factory/AbstractProxyFactoryTestCase.java
jakarta/commons/sandbox/proxy/trunk/src/test/org/apache/commons/proxy/factory/javassist/TestJavassistProxyFactory.java
jakarta/commons/sandbox/proxy/trunk/src/test/org/apache/commons/proxy/util/Echo.java
jakarta/commons/sandbox/proxy/trunk/src/test/org/apache/commons/proxy/util/EchoImpl.java
Modified: jakarta/commons/sandbox/proxy/trunk/src/java/org/apache/commons/proxy/factory/AbstractProxyFactory.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/sandbox/proxy/trunk/src/java/org/apache/commons/proxy/factory/AbstractProxyFactory.java?rev=265552&r1=265551&r2=265552&view=diff
==============================================================================
--- jakarta/commons/sandbox/proxy/trunk/src/java/org/apache/commons/proxy/factory/AbstractProxyFactory.java (original)
+++ jakarta/commons/sandbox/proxy/trunk/src/java/org/apache/commons/proxy/factory/AbstractProxyFactory.java Wed Aug 31 11:59:43 2005
@@ -22,6 +22,13 @@
import org.apache.commons.proxy.DelegateProvider;
import org.apache.commons.proxy.ProxyFactory;
+import java.lang.reflect.Method;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.List;
+import java.util.LinkedList;
+import java.util.Arrays;
+
/**
* A helpful superclass for {@link org.apache.commons.proxy.ProxyFactory} implementations.
*
@@ -51,5 +58,70 @@
public Object createDelegatingProxy( DelegateProvider targetProvider, Class... proxyInterfaces )
{
return createDelegatingProxy( Thread.currentThread().getContextClassLoader(), targetProvider, proxyInterfaces );
+ }
+
+ protected Method[] getImplementationMethods( Class... proxyInterfaces )
+ {
+ final Set<MethodSignature> signatures = new HashSet<MethodSignature>();
+ final List<Method> resultingMethods = new LinkedList<Method>();
+ for( int i = 0; i < proxyInterfaces.length; i++ )
+ {
+ Class proxyInterface = proxyInterfaces[i];
+ final Method[] methods = proxyInterface.getDeclaredMethods();
+ for( int j = 0; j < methods.length; j++ )
+ {
+ final MethodSignature signature = new MethodSignature( methods[j] );
+ if( !signatures.contains( signature ) )
+ {
+ signatures.add( signature );
+ resultingMethods.add( methods[j] );
+ }
+ }
+ }
+ final Method[] results = new Method[resultingMethods.size()];
+ return resultingMethods.toArray( results );
+ }
+
+ private static class MethodSignature
+ {
+ private final String name;
+ private final List<Class> parameterTypes;
+
+ public MethodSignature( Method method )
+ {
+ this.name = method.getName();
+ this.parameterTypes = Arrays.<Class>asList( method.getParameterTypes() );
+ }
+
+ public boolean equals( Object o )
+ {
+ if( this == o )
+ {
+ return true;
+ }
+ if( o == null || getClass() != o.getClass() )
+ {
+ return false;
+ }
+ final MethodSignature that = ( MethodSignature ) o;
+ if( name != null ? !name.equals( that.name ) : that.name != null )
+ {
+ return false;
+ }
+ if( parameterTypes != null ? !parameterTypes.equals( that.parameterTypes ) :
+ that.parameterTypes != null )
+ {
+ return false;
+ }
+ return true;
+ }
+
+ public int hashCode()
+ {
+ int result;
+ result = ( name != null ? name.hashCode() : 0 );
+ result = 29 * result + ( parameterTypes != null ? parameterTypes.hashCode() : 0 );
+ return result;
+ }
}
}
Added: jakarta/commons/sandbox/proxy/trunk/src/java/org/apache/commons/proxy/factory/javassist/JavassistMethodInvocation.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/sandbox/proxy/trunk/src/java/org/apache/commons/proxy/factory/javassist/JavassistMethodInvocation.java?rev=265552&view=auto
==============================================================================
--- jakarta/commons/sandbox/proxy/trunk/src/java/org/apache/commons/proxy/factory/javassist/JavassistMethodInvocation.java (added)
+++ jakarta/commons/sandbox/proxy/trunk/src/java/org/apache/commons/proxy/factory/javassist/JavassistMethodInvocation.java Wed Aug 31 11:59:43 2005
@@ -0,0 +1,237 @@
+/* $Id$
+ *
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed 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.aopalliance.intercept.MethodInvocation;
+
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Method;
+import java.util.WeakHashMap;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.List;
+import java.util.LinkedList;
+import java.util.Arrays;
+
+/**
+ * @author James Carman
+ * @version 1.0
+ */
+public abstract class JavassistMethodInvocation implements MethodInvocation
+{
+ protected final Method method;
+ protected final Object target;
+ protected final Object[] arguments;
+
+ public JavassistMethodInvocation( Method method, Object target, Object[] arguments )
+ {
+ this.method = method;
+ this.target = target;
+ this.arguments = ( arguments == null || arguments.length == 0 ? null : arguments );
+ }
+
+ public Method getMethod()
+ {
+ return method;
+ }
+
+ public Object[] getArguments()
+ {
+ return arguments;
+ }
+
+ public Object getThis()
+ {
+ return target;
+ }
+
+ public AccessibleObject getStaticPart()
+ {
+ return method;
+ }
+
+ private static final WeakHashMap invocationClassCache = new WeakHashMap();
+
+ public synchronized static Class getMethodInvocationClass( ClassLoader classLoader, Method interfaceMethod )
+ throws CannotCompileException
+ {
+ final CacheKey key = new CacheKey( classLoader, interfaceMethod );
+ Class invocationClass = ( Class ) invocationClassCache.get( key );
+ if( invocationClass == null )
+ {
+ final CtClass ctClass = JavassistUtils.createClass(
+ interfaceMethod.getDeclaringClass().getSimpleName() + "_" + interfaceMethod.getName() +
+ "_invocation",
+ JavassistMethodInvocation.class );
+ final CtConstructor constructor = new CtConstructor(
+ JavassistUtils.resolve( new Class[]{Method.class, Object.class, Object[].class} ), ctClass );
+ constructor.setBody( "{\n\tsuper($$);\n}" );
+ ctClass.addConstructor( constructor );
+ final CtMethod proceedMethod = new CtMethod( JavassistUtils.resolve( Object.class ), "proceed",
+ JavassistUtils.resolve( new Class[0] ), ctClass );
+ final Class[] argumentTypes = interfaceMethod.getParameterTypes();
+ final StringBuffer proceedBody = new StringBuffer( "{\n" );
+ if( !Void.TYPE.equals( interfaceMethod.getReturnType() ) )
+ {
+ proceedBody.append( "\treturn " );
+ }
+ else
+ {
+ proceedBody.append( "\t" );
+ }
+ proceedBody.append( "( (" );
+ proceedBody.append( interfaceMethod.getDeclaringClass().getName() );
+ proceedBody.append( " )target )." );
+ proceedBody.append( interfaceMethod.getName() );
+ proceedBody.append( "(" );
+ for( int i = 0; i < argumentTypes.length; ++i )
+ {
+ proceedBody.append( "(" );
+ proceedBody.append( argumentTypes[i].getName() );
+ proceedBody.append( ")arguments[" );
+ proceedBody.append( i );
+ proceedBody.append( "]" );
+ if( i != argumentTypes.length - 1 )
+ {
+ proceedBody.append( ", " );
+ }
+ }
+ proceedBody.append( ");\n" );
+ if( Void.TYPE.equals( interfaceMethod.getReturnType() ) )
+ {
+ proceedBody.append( "\treturn null;\n" );
+ }
+ proceedBody.append( "}" );
+ proceedMethod.setBody( proceedBody.toString() );
+ ctClass.addMethod( proceedMethod );
+ invocationClass = ctClass.toClass( classLoader );
+ invocationClassCache.put( key, invocationClass );
+ }
+ return invocationClass;
+ }
+
+ private static class CacheKey
+ {
+ private final ClassLoader classLoader;
+ private final Method method;
+
+ public CacheKey( ClassLoader classLoader, Method method )
+ {
+ this.classLoader = classLoader;
+ this.method = method;
+ }
+
+ public boolean equals( Object o )
+ {
+ if( this == o )
+ {
+ return true;
+ }
+ if( o == null || getClass() != o.getClass() )
+ {
+ return false;
+ }
+ final CacheKey cacheKey = ( CacheKey ) o;
+ if( classLoader != null ? !classLoader.equals( cacheKey.classLoader ) : cacheKey.classLoader != null )
+ {
+ return false;
+ }
+ if( method != null ? !method.equals( cacheKey.method ) : cacheKey.method != null )
+ {
+ return false;
+ }
+ return true;
+ }
+
+ public int hashCode()
+ {
+ int result;
+ result = ( classLoader != null ? classLoader.hashCode() : 0 );
+ result = 29 * result + ( method != null ? method.hashCode() : 0 );
+ return result;
+ }
+ }
+
+ public static Method[] getImplementationMethods( Class... proxyInterfaces )
+ {
+ final Set<MethodSignature> signatures = new HashSet<MethodSignature>();
+ final List<Method> resultingMethods = new LinkedList<Method>();
+ for( int i = 0; i < proxyInterfaces.length; i++ )
+ {
+ Class proxyInterface = proxyInterfaces[i];
+ final Method[] methods = proxyInterface.getDeclaredMethods();
+ for( int j = 0; j < methods.length; j++ )
+ {
+ final MethodSignature signature = new MethodSignature( methods[j] );
+ if( !signatures.contains( signature ) )
+ {
+ signatures.add( signature );
+ resultingMethods.add( methods[j] );
+ }
+ }
+ }
+ final Method[] results = new Method[resultingMethods.size()];
+ return resultingMethods.toArray( results );
+ }
+
+ private static class MethodSignature
+ {
+ private final String name;
+ private final List<Class> parameterTypes;
+
+ public MethodSignature( Method method )
+ {
+ this.name = method.getName();
+ this.parameterTypes = Arrays.<Class>asList( method.getParameterTypes() );
+ }
+
+ public boolean equals( Object o )
+ {
+ if( this == o )
+ {
+ return true;
+ }
+ if( o == null || getClass() != o.getClass() )
+ {
+ return false;
+ }
+ final MethodSignature that = ( MethodSignature ) o;
+ if( name != null ? !name.equals( that.name ) : that.name != null )
+ {
+ return false;
+ }
+ if( parameterTypes != null ? !parameterTypes.equals( that.parameterTypes ) :
+ that.parameterTypes != null )
+ {
+ return false;
+ }
+ return true;
+ }
+
+ public int hashCode()
+ {
+ int result;
+ result = ( name != null ? name.hashCode() : 0 );
+ result = 29 * result + ( parameterTypes != null ? parameterTypes.hashCode() : 0 );
+ return result;
+ }
+ }
+}
Propchange: jakarta/commons/sandbox/proxy/trunk/src/java/org/apache/commons/proxy/factory/javassist/JavassistMethodInvocation.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jakarta/commons/sandbox/proxy/trunk/src/java/org/apache/commons/proxy/factory/javassist/JavassistMethodInvocation.java
------------------------------------------------------------------------------
svn:keywords = Id
Modified: jakarta/commons/sandbox/proxy/trunk/src/java/org/apache/commons/proxy/factory/javassist/JavassistProxyFactory.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/sandbox/proxy/trunk/src/java/org/apache/commons/proxy/factory/javassist/JavassistProxyFactory.java?rev=265552&r1=265551&r2=265552&view=diff
==============================================================================
--- jakarta/commons/sandbox/proxy/trunk/src/java/org/apache/commons/proxy/factory/javassist/JavassistProxyFactory.java (original)
+++ jakarta/commons/sandbox/proxy/trunk/src/java/org/apache/commons/proxy/factory/javassist/JavassistProxyFactory.java Wed Aug 31 11:59:43 2005
@@ -17,21 +17,19 @@
package org.apache.commons.proxy.factory.javassist;
import javassist.CannotCompileException;
-import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
-import javassist.CtField;
import javassist.CtMethod;
-import javassist.NotFoundException;
import org.aopalliance.intercept.MethodInterceptor;
-import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.proxy.DelegateProvider;
-import org.apache.commons.proxy.exception.DelegateProviderException;
import org.apache.commons.proxy.exception.ProxyFactoryException;
import org.apache.commons.proxy.factory.AbstractProxyFactory;
-import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
/**
* A <a href="http://www.jboss.org/products/javassist">Javassist</a>-based {@link org.apache.commons.proxy.ProxyFactory}
@@ -42,303 +40,180 @@
*/
public class JavassistProxyFactory extends AbstractProxyFactory
{
- private static int classNumber = 0;
- private static final ClassPool classPool = ClassPool.getDefault();
-
- private void addField( Class fieldType, String fieldName, CtClass enclosingClass )
- {
- try
- {
- enclosingClass.addField( new CtField( resolve( fieldType ), fieldName, enclosingClass ) );
- }
- catch( CannotCompileException e )
- {
- throw new ProxyFactoryException( "Unable to add field named " + fieldName + " of type " +
- fieldType.getName() + " to class " + enclosingClass.getName(), e );
- }
- }
+ private static HashMap<ProxyClassDescriptor, Class> delegatingProxyClassCache = new HashMap<ProxyClassDescriptor, Class>();
+ private static HashMap<ProxyClassDescriptor, Class> interceptingProxyClassCache = new HashMap<ProxyClassDescriptor, Class>();
public Object createInterceptingProxy( ClassLoader classLoader, Object target, MethodInterceptor interceptor,
Class... proxyInterfaces )
{
- try
+ synchronized( interceptingProxyClassCache )
{
- final CtClass proxyClass = createClass();
- addField( target.getClass(), "target", proxyClass );
- addField( MethodInterceptor.class, "interceptor", proxyClass );
- final CtConstructor proxyConstructor = new CtConstructor(
- resolve( new Class[]{target.getClass(), MethodInterceptor.class} ), proxyClass );
- proxyConstructor.setBody( "{ this.target = $1;\nthis.interceptor = $2; }" );
- proxyClass.addConstructor( proxyConstructor );
- for( Class proxyInterface : proxyInterfaces )
- {
- proxyClass.addInterface( resolve( proxyInterface ) );
- final Method[] methods = proxyInterface.getMethods();
- for( int i = 0; i < methods.length; ++i )
+ final Method[] methods = getImplementationMethods( proxyInterfaces );
+ final ProxyClassDescriptor key = new ProxyClassDescriptor( methods, classLoader );
+ Class clazz = interceptingProxyClassCache.get( key );
+ if( clazz == null )
+ {
+ log.debug( "Generating intercepting proxy class for interfaces " + Arrays.asList( proxyInterfaces ) +
+ " using class loader " + classLoader + "..." );
+ try
+ {
+ final CtClass proxyClass = JavassistUtils.createClass();
+ JavassistUtils.addInterfaces( proxyClass, proxyInterfaces );
+ JavassistUtils.addField( Method[].class, "methods", proxyClass );
+ JavassistUtils.addField( Object.class, "target", proxyClass );
+ JavassistUtils.addField( MethodInterceptor.class, "interceptor", proxyClass );
+ final CtConstructor proxyConstructor = new CtConstructor(
+ JavassistUtils.resolve(
+ new Class[]{Method[].class, Object.class, MethodInterceptor.class} ),
+ proxyClass );
+ proxyConstructor
+ .setBody( "{\n\tthis.methods = $1;\n\tthis.target = $2;\n\tthis.interceptor = $3; }" );
+ proxyClass.addConstructor( proxyConstructor );
+ for( int i = 0; i < methods.length; ++i )
+ {
+ final CtMethod method = new CtMethod( JavassistUtils.resolve( methods[i].getReturnType() ),
+ methods[i].getName(),
+ JavassistUtils.resolve( methods[i].getParameterTypes() ),
+ proxyClass );
+ final Class invocationClass = JavassistMethodInvocation
+ .getMethodInvocationClass( classLoader, methods[i] );
+ final String body = "{\n\t return ( $r ) interceptor.invoke( new " + invocationClass.getName() +
+ "( methods[" + i + "], target, $args ) );\n }";
+ method.setBody( body );
+ proxyClass.addMethod( method );
+ }
+ clazz = proxyClass.toClass( classLoader );
+ interceptingProxyClassCache.put( key, clazz );
+ }
+ catch( CannotCompileException e )
{
- final CtMethod method = new CtMethod( resolve( methods[i].getReturnType() ), methods[i].getName(),
- resolve( methods[i].getParameterTypes() ), proxyClass );
- final Class invocationClass = createMethodInvocationClass( methods[i], target.getClass(),
- classLoader );
- final String body = "{\n\t return ( $r ) interceptor.invoke( new " + invocationClass.getName() +
- "( target, $$ ) );\n }";
- log.debug( method.getName() + "() method body:\n" + body );
- method.setBody( body );
- proxyClass.addMethod( method );
+ throw new ProxyFactoryException( "Could not compile class.", e );
}
}
- final Class clazz = proxyClass.toClass( classLoader );
- return clazz.getConstructor( target.getClass(), MethodInterceptor.class )
- .newInstance( target, interceptor );
- }
- catch( CannotCompileException e )
- {
- throw new ProxyFactoryException( "Could not compile class.", e );
- }
- catch( NoSuchMethodException e )
- {
- throw new ProxyFactoryException( "Could not find constructor in generated proxy class.", e );
- }
- catch( Exception e )
- {
- throw new ProxyFactoryException( "Unable to instantiate proxy from generated proxy class.", e );
+ try
+ {
+ return clazz.getConstructor( Method[].class, Object.class, MethodInterceptor.class )
+ .newInstance( methods, target, interceptor );
+ }
+ catch( Exception e )
+ {
+ throw new ProxyFactoryException( "Unable to instantiate proxy class instance.", e );
+
+ }
}
}
- private Class createMethodInvocationClass( Method method, Class targetClass, ClassLoader classLoader )
+ public Object createDelegatingProxy( ClassLoader classLoader, DelegateProvider targetProvider,
+ Class... proxyInterfaces )
{
- try
+ synchronized( delegatingProxyClassCache )
{
- final CtClass invocationClass = createClass();
- invocationClass.addInterface( resolve( MethodInvocation.class ) );
- addField( method.getDeclaringClass(), "target", invocationClass );
- addField( Object[].class, "arguments", invocationClass );
- final Class[] argumentTypes = method.getParameterTypes();
- final Class[] constructorArgs = new Class[argumentTypes.length + 1];
- constructorArgs[0] = targetClass;
- for( int i = 0; i < argumentTypes.length; i++ )
- {
- constructorArgs[i + 1] = argumentTypes[i];
- }
- final CtConstructor constructor = new CtConstructor( resolve( constructorArgs ), invocationClass );
- final StringBuffer constructorBody = new StringBuffer( "{\n" );
- constructorBody.append( "\tthis.target = $1;\n" );
- if( argumentTypes.length == 0 )
- {
- constructorBody.append( "\tthis.arguments = null;\n" );
+ final Method[] methods = getImplementationMethods( proxyInterfaces );
+ final ProxyClassDescriptor key = new ProxyClassDescriptor( methods, classLoader );
+ Class clazz = delegatingProxyClassCache.get( key );
+ if( clazz == null )
+ {
+ log.debug( "Generating delegating proxy class for interfaces " + Arrays.asList( proxyInterfaces ) +
+ " using class loader " + classLoader + "..." );
+ try
+ {
+ final CtClass proxyClass = JavassistUtils.createClass();
+ JavassistUtils.addField( DelegateProvider.class, "provider", proxyClass );
+ final CtConstructor proxyConstructor = new CtConstructor(
+ JavassistUtils.resolve( new Class[]{DelegateProvider.class} ),
+ proxyClass );
+ proxyConstructor.setBody( "{ this.provider = $1; }" );
+ proxyClass.addConstructor( proxyConstructor );
+ addMethods( proxyInterfaces, proxyClass, new DelegatingMethodBodyProvider() );
+ clazz = proxyClass.toClass( classLoader );
+ delegatingProxyClassCache.put( key, clazz );
+ }
+ catch( CannotCompileException e )
+ {
+ throw new ProxyFactoryException( "Could not compile class.", e );
+ }
}
- else
+ try
{
- constructorBody.append( "\tthis.arguments = new Object[" );
- constructorBody.append( argumentTypes.length );
- constructorBody.append( "];\n" );
+ return clazz.getConstructor( DelegateProvider.class ).newInstance( targetProvider );
}
- for( int i = 0; i < argumentTypes.length; i++ )
+ catch( Exception e )
{
- constructorBody.append( "\tthis.arguments[" );
- constructorBody.append( i );
- constructorBody.append( "] = $" );
- constructorBody.append( i + 2 );
- constructorBody.append( ";\n" );
+ throw new ProxyFactoryException( "Unable to instantiate proxy from generated proxy class.", e );
}
- constructorBody.append( "}" );
- log.debug( "Constructor body:\n" + constructorBody );
- constructor.setBody( constructorBody.toString() );
- invocationClass.addConstructor( constructor );
- // proceed()...
- final CtMethod proceedMethod = new CtMethod( resolve( Object.class ), "proceed", new CtClass[0],
- invocationClass );
- final String proceedBody = generateProceedBody( method, argumentTypes );
- log.debug( "Proceed method body:\n" + proceedBody );
- proceedMethod.setBody( proceedBody.toString() );
- invocationClass.addMethod( proceedMethod );
- addGetMethodMethod( invocationClass, argumentTypes, method );
- addGetArgumentsMethod( invocationClass );
- addGetStaticPartMethod( invocationClass );
- addGetThisMethod( invocationClass );
- return invocationClass.toClass( classLoader );
- }
- catch( CannotCompileException e )
- {
- throw new ProxyFactoryException( "Could not compile Javassist generated class.", e );
}
}
- private void addGetMethodMethod( CtClass invocationClass, Class[] argumentTypes, Method method )
+ private void addMethods( Class[] proxyInterfaces, CtClass proxyClass, MethodBodyProvider methodBodyProvider )
throws CannotCompileException
{
- final CtMethod getMethodMethod = new CtMethod( resolve( Method.class ), "getMethod", resolve( new Class[0] ),
- invocationClass );
- final StringBuffer getMethodBody = new StringBuffer();
- getMethodBody.append( "{\n\tfinal Class[] parameterTypes = new Class[" );
- getMethodBody.append( argumentTypes.length );
- getMethodBody.append( "];\n" );
- for( int i = 0; i < argumentTypes.length; ++i )
+ JavassistUtils.addInterfaces( proxyClass, proxyInterfaces );
+ final Method[] methods = getImplementationMethods( proxyInterfaces );
+ for( int i = 0; i < methods.length; ++i )
{
- getMethodBody.append( "\tparameterTypes[" );
- getMethodBody.append( i );
- getMethodBody.append( "] = " );
- getMethodBody.append( argumentTypes[i].getName() );
- getMethodBody.append( ".class;\n" );
+ final Method method = methods[i];
+ final CtMethod ctMethod = new CtMethod( JavassistUtils.resolve( method.getReturnType() ),
+ method.getName(),
+ JavassistUtils.resolve( method.getParameterTypes() ),
+ proxyClass );
+ ctMethod.setBody( methodBodyProvider.getMethodBody( method ) );
+ proxyClass.addMethod( ctMethod );
}
- getMethodBody.append( "\ttry\n\t{\n\t\treturn " );
- getMethodBody.append( method.getDeclaringClass().getName() );
- getMethodBody.append( ".class.getMethod(\"" );
- getMethodBody.append( method.getName() );
- getMethodBody
- .append( "\", parameterTypes );\n\t}\n\tcatch( NoSuchMethodException e )\n\t{\n\t\treturn null;\n\t}" );
- getMethodBody.append( "}" );
- log.debug( "getMethod() body:\n" + getMethodBody.toString() );
- getMethodMethod.setBody( getMethodBody.toString() );
- invocationClass.addMethod( getMethodMethod );
}
- private void addGetStaticPartMethod( CtClass invocationClass ) throws CannotCompileException
+ private interface MethodBodyProvider
{
- final CtMethod getStaticPartMethod = new CtMethod( resolve( AccessibleObject.class ), "getStaticPart",
- resolve( new Class[0] ), invocationClass );
- final String getStaticPartBody = "{\n\treturn getMethod();\n}";
- log.debug( "getStaticPart() body:\n" + getStaticPartBody );
- getStaticPartMethod.setBody( getStaticPartBody );
- invocationClass.addMethod( getStaticPartMethod );
+ public String getMethodBody( Method method );
}
- private void addGetThisMethod( CtClass invocationClass ) throws CannotCompileException
+ private class DelegatingMethodBodyProvider implements MethodBodyProvider
{
- final CtMethod getThisMethod = new CtMethod( resolve( Object.class ), "getThis", resolve( new Class[0] ),
- invocationClass );
- final String getThisMethodBody = "{\n\treturn target;\n}";
- log.debug( "getThis() body:\n" + getThisMethodBody );
- getThisMethod.setBody( getThisMethodBody );
- invocationClass.addMethod( getThisMethod );
+ public String getMethodBody( Method method )
+ {
+ return "{ return ( $r ) ( ( " + method.getDeclaringClass().getName() + " )provider.getDelegate() )." +
+ method.getName() + "($$); }";
+ }
}
- private void addGetArgumentsMethod( CtClass invocationClass ) throws CannotCompileException
+ private static class ProxyClassDescriptor
{
- final CtMethod method = new CtMethod( resolve( Object[].class ), "getArguments", resolve( new Class[0] ),
- invocationClass );
- final String body = "{\n\treturn arguments;\n}";
- log.debug( "getArguments() body:\n" + body );
- method.setBody( body );
- invocationClass.addMethod( method );
- }
+ private final List<Method> methods;
+ private final ClassLoader classLoader;
- private String generateProceedBody( Method method, Class[] argumentTypes )
- {
- final StringBuffer proceedBody = new StringBuffer( "{\n" );
- if( !Void.TYPE.equals( method.getReturnType() ) )
- {
- proceedBody.append( "\treturn " );
- }
- else
+ public ProxyClassDescriptor( Method[] methods, ClassLoader classLoader )
{
- proceedBody.append( "\t" );
+ this.methods = new ArrayList<Method>( Arrays.asList( methods ) );
+ this.classLoader = classLoader;
}
- proceedBody.append( "target." );
- proceedBody.append( method.getName() );
- proceedBody.append( "(" );
- for( int i = 0; i < argumentTypes.length; ++i )
+
+ public boolean equals( Object o )
{
- proceedBody.append( "(" );
- proceedBody.append( argumentTypes[i].getName() );
- proceedBody.append( ")arguments[" );
- proceedBody.append( i );
- proceedBody.append( "]" );
- if( i != argumentTypes.length - 1 )
+ if( this == o )
{
- proceedBody.append( ", " );
+ return true;
}
- }
- proceedBody.append( ");\n" );
- if( Void.TYPE.equals( method.getReturnType() ) )
- {
- proceedBody.append( "\treturn null;\n" );
- }
- proceedBody.append( "}" );
- return proceedBody.toString();
- }
-
- public Object createDelegatingProxy( ClassLoader classLoader, DelegateProvider targetProvider,
- Class... proxyInterfaces )
- {
- try
- {
- final CtClass proxyClass = createClass();
- final CtField providerField = new CtField( resolve( targetProvider.getClass() ), "provider", proxyClass );
- proxyClass.addField( providerField );
- final CtConstructor proxyConstructor = new CtConstructor( resolve( new Class[]{targetProvider.getClass()} ),
- proxyClass );
- proxyConstructor.setBody( "{ this.provider = $1; }" );
- proxyClass.addConstructor( proxyConstructor );
- for( Class proxyInterface : proxyInterfaces )
+ if( o == null || getClass() != o.getClass() )
{
- proxyClass.addInterface( resolve( proxyInterface ) );
- final Method[] methods = proxyInterface.getMethods();
- for( int i = 0; i < methods.length; ++i )
- {
- final CtMethod method = new CtMethod( resolve( methods[i].getReturnType() ), methods[i].getName(),
- resolve( methods[i].getParameterTypes() ), proxyClass );
- method.setBody( "{ return ( $r ) ( ( " + proxyInterface.getName() + " )provider.getDelegate() )." +
- methods[i].getName() + "($$); }" );
- proxyClass.addMethod( method );
- }
+ return false;
}
- final Class clazz = proxyClass.toClass( classLoader );
- return clazz.getConstructor( targetProvider.getClass() ).newInstance( targetProvider );
- }
- catch( CannotCompileException e )
- {
- throw new ProxyFactoryException( "Could not compile class.", e );
- }
- catch( NoSuchMethodException e )
- {
- throw new ProxyFactoryException( "Could not find constructor in generated proxy class.", e );
- }
- catch( Exception e )
- {
- throw new ProxyFactoryException( "Unable to instantiate proxy from generated proxy class.", e );
- }
- }
-
- public static CtClass resolve( Class clazz )
- {
- try
- {
- return classPool.get( getJavaClassName( clazz ) );
- }
- catch( NotFoundException e )
- {
- throw new DelegateProviderException(
- "Unable to find class " + clazz.getName() + " in default Javassist class pool.", e );
- }
- }
-
- public static CtClass[] resolve( Class[] classes )
- {
- final CtClass[] ctClasses = new CtClass[classes.length];
- for( int i = 0; i < ctClasses.length; ++i )
- {
- ctClasses[i] = resolve( classes[i] );
+ final ProxyClassDescriptor that = ( ProxyClassDescriptor ) o;
+ if( classLoader != null ? !classLoader.equals( that.classLoader ) : that.classLoader != null )
+ {
+ return false;
+ }
+ if( methods != null ? !methods.equals( that.methods ) : that.methods != null )
+ {
+ return false;
+ }
+ return true;
}
- return ctClasses;
- }
-
- public static CtClass createClass()
- {
- return createClass( Object.class );
- }
- public static CtClass createClass( Class superclass )
- {
- return classPool.makeClass( "JavassistProxyFactoryGenerated_" + ( ++classNumber ), resolve( superclass ) );
- }
-
- public static String getJavaClassName( Class inputClass )
- {
- if( inputClass.isArray() )
+ public int hashCode()
{
- return getJavaClassName( inputClass.getComponentType() ) + "[]";
+ int result;
+ result = ( methods != null ? methods.hashCode() : 0 );
+ result = 29 * result + ( classLoader != null ? classLoader.hashCode() : 0 );
+ return result;
}
- return inputClass.getName();
}
}
Added: jakarta/commons/sandbox/proxy/trunk/src/java/org/apache/commons/proxy/factory/javassist/JavassistUtils.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/sandbox/proxy/trunk/src/java/org/apache/commons/proxy/factory/javassist/JavassistUtils.java?rev=265552&view=auto
==============================================================================
--- jakarta/commons/sandbox/proxy/trunk/src/java/org/apache/commons/proxy/factory/javassist/JavassistUtils.java (added)
+++ jakarta/commons/sandbox/proxy/trunk/src/java/org/apache/commons/proxy/factory/javassist/JavassistUtils.java Wed Aug 31 11:59:43 2005
@@ -0,0 +1,105 @@
+/* $Id$
+ *
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed 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.ClassPool;
+import javassist.CtClass;
+import javassist.CtField;
+import javassist.NotFoundException;
+import org.apache.commons.proxy.exception.DelegateProviderException;
+
+/**
+ * @author James Carman
+ * @version 1.0
+ */
+public class JavassistUtils
+{
+ private static int classNumber = 0;
+ private static final ClassPool classPool = ClassPool.getDefault();
+ public static final String DEFAULT_BASE_NAME = "JavassistUtilsGenerated";
+
+ public static void addInterfaces( CtClass ctClass, Class... proxyInterfaces )
+ {
+ for( int i = 0; i < proxyInterfaces.length; i++ )
+ {
+ Class proxyInterface = proxyInterfaces[i];
+ ctClass.addInterface( resolve( proxyInterface ) );
+ }
+ }
+
+ public static void addField( Class fieldType, String fieldName, CtClass enclosingClass ) throws
+ CannotCompileException
+ {
+ enclosingClass.addField( new CtField( resolve( fieldType ), fieldName, enclosingClass ) );
+
+ }
+
+ public static CtClass resolve( Class clazz )
+ {
+ try
+ {
+ return classPool.get( getJavaClassName( clazz ) );
+ }
+ catch( NotFoundException e )
+ {
+ throw new DelegateProviderException(
+ "Unable to find class " + clazz.getName() + " in default Javassist class pool.", e );
+ }
+ }
+
+ public static CtClass[] resolve( Class[] classes )
+ {
+ final CtClass[] ctClasses = new CtClass[classes.length];
+ for( int i = 0; i < ctClasses.length; ++i )
+ {
+ ctClasses[i] = resolve( classes[i] );
+ }
+ return ctClasses;
+ }
+
+ public static CtClass createClass()
+ {
+ return createClass( DEFAULT_BASE_NAME );
+ }
+
+ public static CtClass createClass( Class superclass )
+ {
+ return createClass( DEFAULT_BASE_NAME, superclass );
+ }
+
+ public static CtClass createClass( String baseName )
+ {
+ return createClass( baseName, Object.class );
+ }
+
+ public synchronized static CtClass createClass( String baseName, Class superclass )
+ {
+ return classPool.makeClass( baseName + "_" + classNumber++, resolve( superclass ) );
+ }
+
+ public static String getJavaClassName( Class inputClass )
+ {
+ if( inputClass.isArray() )
+ {
+ return getJavaClassName( inputClass.getComponentType() ) + "[]";
+ }
+ return inputClass.getName();
+ }
+
+
+}
Propchange: jakarta/commons/sandbox/proxy/trunk/src/java/org/apache/commons/proxy/factory/javassist/JavassistUtils.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jakarta/commons/sandbox/proxy/trunk/src/java/org/apache/commons/proxy/factory/javassist/JavassistUtils.java
------------------------------------------------------------------------------
svn:keywords = Id
Modified: jakarta/commons/sandbox/proxy/trunk/src/test/org/apache/commons/proxy/factory/AbstractProxyFactoryTestCase.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/sandbox/proxy/trunk/src/test/org/apache/commons/proxy/factory/AbstractProxyFactoryTestCase.java?rev=265552&r1=265551&r2=265552&view=diff
==============================================================================
--- jakarta/commons/sandbox/proxy/trunk/src/test/org/apache/commons/proxy/factory/AbstractProxyFactoryTestCase.java (original)
+++ jakarta/commons/sandbox/proxy/trunk/src/test/org/apache/commons/proxy/factory/AbstractProxyFactoryTestCase.java Wed Aug 31 11:59:43 2005
@@ -19,14 +19,19 @@
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.proxy.ProxyFactory;
import static org.apache.commons.proxy.provider.ProviderUtils.*;
+import org.apache.commons.proxy.provider.ConstantProvider;
import org.apache.commons.proxy.util.AbstractTestCase;
import org.apache.commons.proxy.util.Echo;
import org.apache.commons.proxy.util.EchoImpl;
import org.apache.commons.proxy.util.SuffixMethodInterceptor;
+import org.apache.commons.proxy.util.DuplicateEcho;
import java.io.IOException;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.List;
+import java.util.LinkedList;
/**
* @author James Carman
@@ -34,14 +39,23 @@
*/
public abstract class AbstractProxyFactoryTestCase extends AbstractTestCase
{
- private final ProxyFactory factory;
+ protected final ProxyFactory factory;
protected AbstractProxyFactoryTestCase( ProxyFactory factory )
{
this.factory = factory;
}
- public void testCreateProxy()
+ public void testDelegatingProxyInterfaceOrder()
+ {
+ final Echo echo = ( Echo ) factory.createDelegatingProxy( singletonProvider( beanProvider( EchoImpl.class ) ), Echo.class, DuplicateEcho.class );
+ final List expected = new LinkedList( Arrays.asList( Echo.class, DuplicateEcho.class ) );
+ final List actual = new LinkedList( Arrays.asList( echo.getClass().getInterfaces() ) );
+ actual.retainAll( expected ); // Doesn't alter order!
+ assertEquals( expected, actual );
+ }
+
+ public void testCreateDelegatingProxy()
{
final Echo echo = ( Echo ) factory.createDelegatingProxy( singletonProvider( beanProvider( EchoImpl.class ) ), Echo.class );
echo.echo();
@@ -49,6 +63,12 @@
assertEquals( "ab", echo.echoBack( "a", "b" ) );
}
+ public void testPrimitiveParameter()
+ {
+ final Echo echo = ( Echo ) factory.createDelegatingProxy( singletonProvider( beanProvider( EchoImpl.class ) ), Echo.class );
+ assertEquals( 1, echo.echoBack( 1 ) );
+ }
+
public void testCreateInterceptorProxy()
{
final Echo target = ( Echo ) factory.createDelegatingProxy( singletonProvider( beanProvider( EchoImpl.class ) ), Echo.class );
@@ -80,6 +100,41 @@
assertEquals( Echo.class.getMethod( "echoBack", String.class, String.class ), tester.method );
}
+ public void testMethodInvocationDuplicateMethods() throws Exception
+ {
+ final MethodInvocationTester tester = new MethodInvocationTester();
+ final EchoImpl target = new EchoImpl();
+ final Echo proxy = ( Echo ) factory.createInterceptingProxy( target, tester, Echo.class, DuplicateEcho.class );
+ proxy.echoBack( "hello" );
+ assertEquals( Echo.class.getMethod( "echoBack", String.class ), tester.method );
+ }
+
+
+ public void testMethodInvocationClassCaching() throws Exception
+ {
+ final MethodInvocationTester tester = new MethodInvocationTester();
+ final EchoImpl target = new EchoImpl();
+ final Echo proxy = ( Echo ) factory.createInterceptingProxy( target, tester, Echo.class );
+ proxy.echoBack( "hello1" );
+ final Class invocationClass1 = tester.invocationClass;
+ proxy.echoBack( "hello2" );
+ assertEquals( invocationClass1, tester.invocationClass );
+ }
+
+ public void testDelegatingProxyClassCaching() throws Exception
+ {
+ final Echo proxy1 = ( Echo ) factory.createDelegatingProxy( constantProvider( new EchoImpl() ), Echo.class );
+ final Echo proxy2 = ( Echo ) factory.createDelegatingProxy( constantProvider( new EchoImpl() ), Echo.class );
+ assertEquals( proxy1.getClass(), proxy2.getClass() );
+ }
+
+ public void testInterceptingProxyClassCaching() throws Exception
+ {
+ final Echo proxy1 = ( Echo ) factory.createInterceptingProxy( new EchoImpl(), new NoOpMethodInterceptor(), Echo.class );
+ final Echo proxy2 = ( Echo ) factory.createInterceptingProxy( new EchoImpl(), new NoOpMethodInterceptor(), Echo.class );
+ assertEquals( proxy1.getClass(), proxy2.getClass() );
+ }
+
public void testProxyWithCheckedException() throws Exception
{
final Echo proxy = ( Echo ) factory.createDelegatingProxy( constantProvider( new EchoImpl() ), Echo.class );
@@ -172,6 +227,7 @@
private Method method;
private Object target;
private AccessibleObject staticPart;
+ private Class invocationClass;
public Object invoke( MethodInvocation methodInvocation ) throws Throwable
{
@@ -179,6 +235,7 @@
this.method = methodInvocation.getMethod();
this.target = methodInvocation.getThis();
this.staticPart = methodInvocation.getStaticPart();
+ this.invocationClass = methodInvocation.getClass();
return methodInvocation.proceed();
}
}
Modified: jakarta/commons/sandbox/proxy/trunk/src/test/org/apache/commons/proxy/factory/javassist/TestJavassistProxyFactory.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/sandbox/proxy/trunk/src/test/org/apache/commons/proxy/factory/javassist/TestJavassistProxyFactory.java?rev=265552&r1=265551&r2=265552&view=diff
==============================================================================
--- jakarta/commons/sandbox/proxy/trunk/src/test/org/apache/commons/proxy/factory/javassist/TestJavassistProxyFactory.java (original)
+++ jakarta/commons/sandbox/proxy/trunk/src/test/org/apache/commons/proxy/factory/javassist/TestJavassistProxyFactory.java Wed Aug 31 11:59:43 2005
@@ -16,6 +16,12 @@
package org.apache.commons.proxy.factory.javassist;
import org.apache.commons.proxy.factory.AbstractProxyFactoryTestCase;
+import org.apache.commons.proxy.util.Echo;
+import org.apache.commons.proxy.util.DuplicateEcho;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.LinkedList;
/**
* @author James Carman
Added: jakarta/commons/sandbox/proxy/trunk/src/test/org/apache/commons/proxy/util/DuplicateEcho.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/sandbox/proxy/trunk/src/test/org/apache/commons/proxy/util/DuplicateEcho.java?rev=265552&view=auto
==============================================================================
--- jakarta/commons/sandbox/proxy/trunk/src/test/org/apache/commons/proxy/util/DuplicateEcho.java (added)
+++ jakarta/commons/sandbox/proxy/trunk/src/test/org/apache/commons/proxy/util/DuplicateEcho.java Wed Aug 31 11:59:43 2005
@@ -0,0 +1,26 @@
+/* $Id$
+ *
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed 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.util;
+
+/**
+ * @author James Carman
+ * @version 1.0
+ */
+public interface DuplicateEcho
+{
+ public String echoBack( String message );
+}
Propchange: jakarta/commons/sandbox/proxy/trunk/src/test/org/apache/commons/proxy/util/DuplicateEcho.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jakarta/commons/sandbox/proxy/trunk/src/test/org/apache/commons/proxy/util/DuplicateEcho.java
------------------------------------------------------------------------------
svn:keywords = Id
Modified: jakarta/commons/sandbox/proxy/trunk/src/test/org/apache/commons/proxy/util/Echo.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/sandbox/proxy/trunk/src/test/org/apache/commons/proxy/util/Echo.java?rev=265552&r1=265551&r2=265552&view=diff
==============================================================================
--- jakarta/commons/sandbox/proxy/trunk/src/test/org/apache/commons/proxy/util/Echo.java (original)
+++ jakarta/commons/sandbox/proxy/trunk/src/test/org/apache/commons/proxy/util/Echo.java Wed Aug 31 11:59:43 2005
@@ -25,6 +25,7 @@
{
public String echoBack( String message );
public String echoBack( String message1, String message2 );
+ public int echoBack( int i );
public void echo();
public void ioException() throws IOException;
Modified: jakarta/commons/sandbox/proxy/trunk/src/test/org/apache/commons/proxy/util/EchoImpl.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/sandbox/proxy/trunk/src/test/org/apache/commons/proxy/util/EchoImpl.java?rev=265552&r1=265551&r2=265552&view=diff
==============================================================================
--- jakarta/commons/sandbox/proxy/trunk/src/test/org/apache/commons/proxy/util/EchoImpl.java (original)
+++ jakarta/commons/sandbox/proxy/trunk/src/test/org/apache/commons/proxy/util/EchoImpl.java Wed Aug 31 11:59:43 2005
@@ -21,7 +21,7 @@
* @author James Carman
* @version 1.0
*/
-public class EchoImpl implements Echo
+public class EchoImpl implements Echo, DuplicateEcho
{
public String echoBack( String message )
{
@@ -31,6 +31,11 @@
public String echoBack( String message1, String message2 )
{
return message1 + message2;
+ }
+
+ public int echoBack( int i )
+ {
+ return i;
}
public void echo()
---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org