You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by jg...@apache.org on 2010/02/13 15:50:45 UTC

svn commit: r909833 - in /openejb/trunk/openejb3/container/openejb-core/src: main/java/org/apache/openejb/util/proxy/ test/java/org/apache/openejb/util/proxy/

Author: jgallimore
Date: Sat Feb 13 14:50:44 2010
New Revision: 909833

URL: http://svn.apache.org/viewvc?rev=909833&view=rev
Log:
OPENEJB-1188 Adding @LocalBean proxy generation code

Added:
    openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/proxy/LocalBeanProxyGenerator.java
    openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/proxy/LocalBeanProxyGeneratorImpl.java
    openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/proxy/ProxyGenerationException.java
    openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/proxy/
    openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/proxy/LocalBeanProxyGeneratorImplTest.java
    openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/proxy/ProxyTestException.java
    openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/proxy/ProxyTestObject.java
    openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/proxy/SampleLocalBean.java

Added: openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/proxy/LocalBeanProxyGenerator.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/proxy/LocalBeanProxyGenerator.java?rev=909833&view=auto
==============================================================================
--- openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/proxy/LocalBeanProxyGenerator.java (added)
+++ openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/proxy/LocalBeanProxyGenerator.java Sat Feb 13 14:50:44 2010
@@ -0,0 +1,7 @@
+package org.apache.openejb.util.proxy;
+
+public interface LocalBeanProxyGenerator {
+	public byte[] generateProxy(Class<?> clsToProxy) throws ProxyGenerationException;
+
+	public byte[] generateProxy(Class<?> clsToProxy, String targetClassName) throws ProxyGenerationException;
+}

Added: openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/proxy/LocalBeanProxyGeneratorImpl.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/proxy/LocalBeanProxyGeneratorImpl.java?rev=909833&view=auto
==============================================================================
--- openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/proxy/LocalBeanProxyGeneratorImpl.java (added)
+++ openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/proxy/LocalBeanProxyGeneratorImpl.java Sat Feb 13 14:50:44 2010
@@ -0,0 +1,527 @@
+/**
+ * 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.openejb.util.proxy;
+
+import java.lang.reflect.Method;
+
+import org.apache.xbean.asm.ClassWriter;
+import org.apache.xbean.asm.FieldVisitor;
+import org.apache.xbean.asm.Label;
+import org.apache.xbean.asm.MethodVisitor;
+import org.apache.xbean.asm.Opcodes;
+import org.apache.xbean.asm.Type;
+
+
+public class LocalBeanProxyGeneratorImpl implements LocalBeanProxyGenerator, Opcodes {
+
+	private static Object nextUniqueNumberLock = new Object();
+	private final static String proxyClassNamePrefix = "$LocalBeanProxy";
+	private static long nextUniqueNumber = 0;
+	
+	private String generateProxyName() {
+		long num;
+		synchronized (nextUniqueNumberLock) {
+		    num = nextUniqueNumber++;
+		}
+		
+		return proxyClassNamePrefix + num;
+	}
+
+	public byte[] generateProxy(Class<?> clsToProxy) throws ProxyGenerationException {
+		String proxyName = generateProxyName();
+		return generateProxy(clsToProxy, proxyName);
+	}
+
+	public byte[] generateProxy(Class<?> clsToProxy, String proxyName) throws ProxyGenerationException {
+		ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
+		FieldVisitor fv;
+		MethodVisitor mv;
+
+		String clsToOverride = clsToProxy.getCanonicalName().replace('.', '/');
+
+		// push class signature
+		cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, proxyName, null, clsToOverride, null);
+		cw.visitSource(proxyName + ".java", null);
+
+		// push InvocationHandler field
+		fv = cw.visitField(ACC_FINAL + ACC_PRIVATE, "invocationHandler", "Ljava/lang/reflect/InvocationHandler;", null, null);
+		fv.visitEnd();
+		
+		// push constructor
+		mv = cw.visitMethod(ACC_PUBLIC, "<init>", "(Ljava/lang/reflect/InvocationHandler;)V", null, null);
+		mv.visitCode();
+		mv.visitVarInsn(ALOAD, 0);
+		mv.visitMethodInsn(INVOKESPECIAL, clsToOverride, "<init>", "()V");
+		mv.visitVarInsn(ALOAD, 0);
+		mv.visitVarInsn(ALOAD, 1);
+		mv.visitFieldInsn(PUTFIELD, proxyName, "invocationHandler", "Ljava/lang/reflect/InvocationHandler;");
+		mv.visitInsn(RETURN);
+		mv.visitMaxs(0, 0);
+		mv.visitEnd();
+
+		// loop through public methods, and push something to the class
+		Method[] methods = clsToProxy.getDeclaredMethods();
+		for (Method method : methods) {
+			processMethod(cw, method, proxyName);
+		}
+		
+		byte[] clsBytes = cw.toByteArray();
+		return clsBytes;
+	}
+
+
+	private void processMethod(ClassWriter cw, Method method, String proxyName) throws ProxyGenerationException {
+		if ("<init>".equals(method.getName())) {
+			return;
+		}
+		
+		Class<?> returnType = method.getReturnType();
+		Class<?>[] parameterTypes = method.getParameterTypes();
+		Class<?>[] exceptionTypes = method.getExceptionTypes();
+		
+		// push the method definition
+		MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, method.getName(), getMethodSignatureAsString(returnType, parameterTypes), null, null);
+		mv.visitCode();
+
+		// push try/catch block, to catch declared exceptions, and to catch java.lang.Throwable
+		Label l0 = new Label();
+		Label l1 = new Label();
+		Label l2 = new Label();
+		Label l3 = new Label();
+		
+		if (exceptionTypes.length > 0) {
+			mv.visitTryCatchBlock(l0, l1, l2, "java/lang/reflect/InvocationTargetException");
+		}
+		
+		mv.visitTryCatchBlock(l0, l1, l3, "java/lang/Throwable");
+		
+		// push try code
+		mv.visitLabel(l0);
+		mv.visitLdcInsn(Type.getType("L" + proxyName + ";"));
+		
+		// the following code generates the bytecode for this line of Java:
+		// Method method = <proxy>.class.getMethod("add", new Class[] { <array of function argument classes> });
+
+		// get the method name to invoke, and push to stack
+		mv.visitLdcInsn(method.getName());
+		
+		// create the Class[]
+		createArrayDefinition(mv, parameterTypes.length, Class.class);
+		
+		int length = 1;
+		
+		// push parameters into array
+		for (int i = 0; i < parameterTypes.length; i++) {
+			// keep copy of array on stack
+			mv.visitInsn(DUP);
+			
+			Class<?> parameterType = parameterTypes[i];
+			
+			// push number onto stack
+			pushIntOntoStack(mv, i);
+
+			if (parameterType.isPrimitive()) {
+				String wrapperType = getWrapperType(parameterType);
+				mv.visitFieldInsn(GETSTATIC, wrapperType, "TYPE", "Ljava/lang/Class;");
+			} else {
+				mv.visitLdcInsn(Type.getType(getAsmTypeAsString(parameterType, true)));
+			}
+			
+			mv.visitInsn(AASTORE);
+			
+			if (Long.TYPE.equals(parameterType) || Double.TYPE.equals(parameterType)) {
+				length += 2;
+			} else {
+				length++;
+			}
+		}
+		
+		// invoke getMethod() with the method name and the array of types		
+		mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;");
+	
+		// store the returned method for later
+		mv.visitVarInsn(ASTORE, length);
+		
+		// the following code generates bytecode equivalent to:
+		// return ((<returntype>) invocationHandler.invoke(this, method, new Object[] { <function arguments }))[.<primitive>Value()];
+		
+		Label l4 = new Label();
+		mv.visitLabel(l4);
+		mv.visitVarInsn(ALOAD, 0);
+		
+		// get the invocationHandler field from this class
+		mv.visitFieldInsn(GETFIELD, proxyName, "invocationHandler", "Ljava/lang/reflect/InvocationHandler;");
+		
+		// we want to pass "this" in as the first parameter
+		mv.visitVarInsn(ALOAD, 0);
+		
+		// and the method we fetched earlier
+		mv.visitVarInsn(ALOAD, length);
+		
+		// need to construct the array of objects passed in
+
+		// create the Object[]
+		createArrayDefinition(mv, parameterTypes.length, Object.class);
+
+		int index = 1;
+		// push parameters into array
+		for (int i = 0; i < parameterTypes.length; i++) {
+			// keep copy of array on stack
+			mv.visitInsn(DUP);
+			
+			Class<?> parameterType = parameterTypes[i];
+			
+			// push number onto stack
+			pushIntOntoStack(mv, i);
+
+			if (parameterType.isPrimitive()) {
+				String wrapperType = getWrapperType(parameterType);
+				mv.visitVarInsn(getVarInsn(parameterType), index);
+				
+				mv.visitMethodInsn(INVOKESTATIC, wrapperType, "valueOf", "(" + getPrimitiveLetter(parameterType) + ")L" + wrapperType + ";");
+				mv.visitInsn(AASTORE);
+				
+				if (Long.TYPE.equals(parameterType) || Double.TYPE.equals(parameterType)) {
+					index += 2;
+				} else {
+					index++;
+				}
+			} else {
+				mv.visitVarInsn(ALOAD, index);
+				mv.visitInsn(AASTORE);
+				index++;
+			}
+		}
+
+		// invoke the invocationHandler
+		mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/reflect/InvocationHandler", "invoke", "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;");
+		
+		// cast the result
+		mv.visitTypeInsn(CHECKCAST, getCastType(returnType));
+		
+		if (returnType.isPrimitive() && (!Void.TYPE.equals(returnType))) {
+			// get the primitive value		
+			mv.visitMethodInsn(INVOKEVIRTUAL, getWrapperType(returnType), getPrimitiveMethod(returnType), "()" + getPrimitiveLetter(returnType));
+		}
+		
+		// push return
+		mv.visitLabel(l1);
+		if (! Void.TYPE.equals(returnType)) {
+			mv.visitInsn(getReturnInsn(returnType));
+		} else {
+			mv.visitInsn(POP);
+			mv.visitInsn(RETURN);
+		}
+
+		// catch InvocationTargetException
+		if (exceptionTypes.length > 0) {
+			mv.visitLabel(l2);
+			mv.visitVarInsn(ASTORE, length);
+			Label l5 = new Label();
+			mv.visitLabel(l5);
+	
+			for (int i = 0; i < exceptionTypes.length; i++) {
+				Class<?> exceptionType = exceptionTypes[i];
+				
+				mv.visitLdcInsn(Type.getType("L" + exceptionType.getCanonicalName().replaceAll("\\.", "/") + ";"));
+				mv.visitVarInsn(ALOAD, length);
+				mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/reflect/InvocationTargetException", "getCause", "()Ljava/lang/Throwable;");
+				mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;");
+				mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "equals", "(Ljava/lang/Object;)Z");
+				Label l6 = new Label();
+				mv.visitJumpInsn(IFEQ, l6);
+				Label l7 = new Label();
+				mv.visitLabel(l7);
+				mv.visitVarInsn(ALOAD, length);
+				mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/reflect/InvocationTargetException", "getCause", "()Ljava/lang/Throwable;");
+				mv.visitTypeInsn(CHECKCAST, exceptionType.getCanonicalName().replaceAll("\\.", "/"));
+				mv.visitInsn(ATHROW);
+				mv.visitLabel(l6);
+				
+				if (i == (exceptionTypes.length - 1)) {
+					mv.visitTypeInsn(NEW, "java/lang/reflect/UndeclaredThrowableException");
+					mv.visitInsn(DUP);
+					mv.visitVarInsn(ALOAD, length);
+					mv.visitMethodInsn(INVOKESPECIAL, "java/lang/reflect/UndeclaredThrowableException", "<init>", "(Ljava/lang/Throwable;)V");
+					mv.visitInsn(ATHROW);
+				}
+			}
+		}
+
+		// wrap any other exceptions with an UndeclaredThrowableException
+		mv.visitLabel(l3);
+		mv.visitVarInsn(ASTORE, length);
+		Label l8 = new Label();
+		mv.visitLabel(l8);
+		mv.visitTypeInsn(NEW, "java/lang/reflect/UndeclaredThrowableException");
+		mv.visitInsn(DUP);
+		mv.visitVarInsn(ALOAD, length);
+		mv.visitMethodInsn(INVOKESPECIAL, "java/lang/reflect/UndeclaredThrowableException", "<init>", "(Ljava/lang/Throwable;)V");
+		mv.visitInsn(ATHROW);
+		Label l9 = new Label();
+		mv.visitLabel(l9);
+
+		// finish this method
+		mv.visitMaxs(0, 0);
+		mv.visitEnd();
+	}
+
+	/**
+	 * Gets the appropriate bytecode instruction for RETURN, according to what type we need to return
+	 * @param type Type the needs to be returned
+	 * @return The matching bytecode instruction
+	 */
+	private int getReturnInsn(Class<?> type) {
+		if (type.isPrimitive()) {
+			if (Integer.TYPE.equals(type)) {
+				return IRETURN;
+			} else if (Boolean.TYPE.equals(type)) {
+				return IRETURN;
+			} else if (Character.TYPE.equals(type)) {
+				return IRETURN;
+			} else if (Byte.TYPE.equals(type)) {
+				return IRETURN;
+			} else if (Short.TYPE.equals(type)) {
+				return IRETURN;
+			} else if (Float.TYPE.equals(type)) {
+				return FRETURN;
+			} else if (Long.TYPE.equals(type)) {
+				return LRETURN;
+			} else if (Double.TYPE.equals(type)) {
+				return DRETURN;
+			}
+		}
+		
+		return ARETURN;
+	}
+
+
+	/**
+	 * Returns the appropriate bytecode instruction to load a value from a variable to the stack
+	 * @param type Type to load
+	 * @return Bytecode instruction to use
+	 */
+	private int getVarInsn(Class<?> type) {
+		if (type.isPrimitive()) {
+			if (Integer.TYPE.equals(type)) {
+				return ILOAD;
+			} else if (Boolean.TYPE.equals(type)) {
+				return ILOAD;
+			} else if (Character.TYPE.equals(type)) {
+				return ILOAD;
+			} else if (Byte.TYPE.equals(type)) {
+				return ILOAD;
+			} else if (Short.TYPE.equals(type)) {
+				return ILOAD;
+			} else if (Float.TYPE.equals(type)) {
+				return FLOAD;
+			} else if (Long.TYPE.equals(type)) {
+				return LLOAD;
+			} else if (Double.TYPE.equals(type)) {
+				return DLOAD;
+			}
+		}
+		
+		throw new IllegalStateException("Type: " + type.getCanonicalName() + " is not a primitive type");
+	}
+
+	/**
+	 * Returns the name of the Java method to call to get the primitive value from an Object - e.g. intValue for java.lang.Integer
+	 * @param type Type whose primitive method we want to lookup
+	 * @return The name of the method to use
+	 */
+	private String getPrimitiveMethod(Class<?> type) {
+		if (Integer.TYPE.equals(type)) {
+			return "intValue";
+		} else if (Boolean.TYPE.equals(type)) {
+			return "booleanValue";
+		} else if (Character.TYPE.equals(type)) {
+			return "charValue";
+		} else if (Byte.TYPE.equals(type)) {
+			return "byteValue";
+		} else if (Short.TYPE.equals(type)) {
+			return "shortValue";
+		} else if (Float.TYPE.equals(type)) {
+			return "floatValue";
+		} else if (Long.TYPE.equals(type)) {
+			return "longValue";
+		} else if (Double.TYPE.equals(type)) {
+			return "doubleValue";
+		}
+		
+		throw new IllegalStateException("Type: " + type.getCanonicalName() + " is not a primitive type");
+	}
+
+	/**
+	 * Gets the string to use for CHECKCAST instruction, returning the correct value for any type, including primitives and arrays
+	 * @param returnType The type to cast to with CHECKCAST
+	 * @return CHECKCAST parameter
+	 */
+	String getCastType(Class<?> returnType) {
+		if (returnType.isPrimitive()) {
+			return getWrapperType(returnType);
+		} else {
+			return getAsmTypeAsString(returnType, false);
+		}
+	}
+
+	/**
+	 * Returns the wrapper type for a primitive, e.g. java.lang.Integer for int
+	 * @param type
+	 * @return
+	 */
+	private String getWrapperType(Class<?> type) {
+		if (Integer.TYPE.equals(type)) {
+			return Integer.class.getCanonicalName().replaceAll("\\.", "/");
+		} else if (Boolean.TYPE.equals(type)) {
+			return Boolean.class.getCanonicalName().replaceAll("\\.", "/");
+		} else if (Character.TYPE.equals(type)) {
+			return Character.class.getCanonicalName().replaceAll("\\.", "/");
+		} else if (Byte.TYPE.equals(type)) {
+			return Byte.class.getCanonicalName().replaceAll("\\.", "/");
+		} else if (Short.TYPE.equals(type)) {
+			return Short.class.getCanonicalName().replaceAll("\\.", "/");
+		} else if (Float.TYPE.equals(type)) {
+			return Float.class.getCanonicalName().replaceAll("\\.", "/");
+		} else if (Long.TYPE.equals(type)) {
+			return Long.class.getCanonicalName().replaceAll("\\.", "/");
+		} else if (Double.TYPE.equals(type)) {
+			return Double.class.getCanonicalName().replaceAll("\\.", "/");
+		} else if (Void.TYPE.equals(type)) {
+			return Void.class.getCanonicalName().replaceAll("\\.", "/");
+		}
+		
+		throw new IllegalStateException("Type: " + type.getCanonicalName() + " is not a primitive type");
+	}
+
+	/**
+	 * Invokes the most appropriate bytecode instruction to put a number on the stack
+	 * @param mv
+	 * @param i
+	 */
+	private void pushIntOntoStack(MethodVisitor mv, int i) {
+		if (i == 0) {
+			mv.visitInsn(ICONST_0);	
+		} else if (i == 1) {
+			mv.visitInsn(ICONST_1);
+		} else if (i == 2) {
+			mv.visitInsn(ICONST_2);
+		} else if (i == 3) {
+			mv.visitInsn(ICONST_3);
+		} else if (i == 4) {
+			mv.visitInsn(ICONST_4);
+		} else if (i == 5) {
+			mv.visitInsn(ICONST_5);
+		} else if (i > 5 && i <= 255) {
+			mv.visitIntInsn(BIPUSH, i);
+		} else {
+			mv.visitIntInsn(SIPUSH, i);
+		}
+	}
+
+	/**
+	 * pushes an array of the specified size to the method visitor. The generated bytecode will leave
+	 * the new array at the top of the stack.
+	 * 
+	 * @param mv MethodVisitor to use
+	 * @param size Size of the array to create
+	 * @param type Type of array to create
+	 * @throws ProxyGenerationException 
+	 */
+	private void createArrayDefinition(MethodVisitor mv, int size, Class<?> type) throws ProxyGenerationException {
+		// create a new array of java.lang.class (2)
+		
+		if (size < 0) {
+			throw new ProxyGenerationException("Array size cannot be less than zero");
+		}
+		
+		pushIntOntoStack(mv, size);
+		
+		mv.visitTypeInsn(ANEWARRAY, type.getCanonicalName().replaceAll("\\.", "/"));
+	}
+
+
+	String getMethodSignatureAsString(Class<?> returnType, Class<?>[] parameterTypes) {
+		StringBuilder builder = new StringBuilder();
+		builder.append("(");
+		for (Class<?> parameterType : parameterTypes) {
+			builder.append(getAsmTypeAsString(parameterType, true));
+		}
+		
+		builder.append(")");
+		builder.append(getAsmTypeAsString(returnType, true));
+		
+		return builder.toString();
+	}
+
+	/**
+	 * Returns the single letter that matches the given primitive in bytecode instructions 
+	 * @param type
+	 * @return
+	 */
+	private String getPrimitiveLetter(Class<?> type) {
+		if (Integer.TYPE.equals(type)) {
+			return "I";
+		} else if (Void.TYPE.equals(type)) {
+			return "V";
+		} else if (Boolean.TYPE.equals(type)) {
+			return "Z";
+		} else if (Character.TYPE.equals(type)) {
+			return "C";
+		} else if (Byte.TYPE.equals(type)) {
+			return "B";
+		} else if (Short.TYPE.equals(type)) {
+			return "S";
+		} else if (Float.TYPE.equals(type)) {
+			return "F";
+		} else if (Long.TYPE.equals(type)) {
+			return "J";
+		} else if (Double.TYPE.equals(type)) {
+			return "D";
+		}
+		
+		throw new IllegalStateException("Type: " + type.getCanonicalName() + " is not a primitive type");
+	}
+
+	/**
+	 * Converts a class to a String suitable for ASM.
+	 * @param parameterType Class to convert
+	 * @param wrap True if a non-array object should be wrapped with L and ; - e.g. Ljava/lang/Integer;
+	 * @return String to use for ASM
+	 */
+	public String getAsmTypeAsString(Class<?> parameterType, boolean wrap) {
+		if (parameterType.isArray()) {
+			if (parameterType.getComponentType().isPrimitive()) {
+				Class<?> componentType = parameterType.getComponentType();
+				return "[" + getPrimitiveLetter(componentType);
+			} else {
+				return "[" + getAsmTypeAsString(parameterType.getComponentType(), true);
+			}
+		} else {
+			if (! parameterType.isPrimitive()) {
+				if (wrap) {
+					return "L" + parameterType.getCanonicalName().replaceAll("\\.", "/") + ";";
+				} else {
+					return parameterType.getCanonicalName().replaceAll("\\.", "/");
+				}
+			} else {
+				return getPrimitiveLetter(parameterType);
+			}
+		}
+	}
+
+}

Added: openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/proxy/ProxyGenerationException.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/proxy/ProxyGenerationException.java?rev=909833&view=auto
==============================================================================
--- openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/proxy/ProxyGenerationException.java (added)
+++ openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/proxy/ProxyGenerationException.java Sat Feb 13 14:50:44 2010
@@ -0,0 +1,36 @@
+/**
+ * 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.openejb.util.proxy;
+
+public class ProxyGenerationException extends Exception {
+
+	public ProxyGenerationException() {
+	}
+
+	public ProxyGenerationException(String message) {
+		super(message);
+	}
+
+	public ProxyGenerationException(Throwable cause) {
+		super(cause);
+	}
+
+	public ProxyGenerationException(String message, Throwable cause) {
+		super(message, cause);
+	}
+
+}

Added: openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/proxy/LocalBeanProxyGeneratorImplTest.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/proxy/LocalBeanProxyGeneratorImplTest.java?rev=909833&view=auto
==============================================================================
--- openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/proxy/LocalBeanProxyGeneratorImplTest.java (added)
+++ openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/proxy/LocalBeanProxyGeneratorImplTest.java Sat Feb 13 14:50:44 2010
@@ -0,0 +1,874 @@
+/**
+ * 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.openejb.util.proxy;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.junit.Test;
+
+public class LocalBeanProxyGeneratorImplTest extends TestCase {
+	
+	public class Call {
+		private String methodName;
+		private Class<?>[] parameterTypes;
+		
+		public String getMethodName() {
+			return methodName;
+		}
+		
+		public void setMethodName(String methodName) {
+			this.methodName = methodName;
+		}
+		
+		public Class<?>[] getParameterTypes() {
+			return parameterTypes;
+		}
+		
+		public void setParameterTypes(Class<?>[] parameterTypes) {
+			this.parameterTypes = parameterTypes;
+		}
+
+		public Call() {
+			super();
+		}
+
+		public Call(String methodName, Class<?>[] parameterTypes) {
+			this.parameterTypes = parameterTypes;
+			this.methodName = methodName;
+		}
+	}
+
+	private class TestInvocationHandler implements InvocationHandler {
+		
+		private final Object object;
+		private List<Call> calls = new ArrayList<Call>();
+
+		public TestInvocationHandler(Object object) {
+			super();
+			this.object = object;
+		}
+
+		public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+			StringBuilder builder = new StringBuilder();
+			builder.append(method.getName());
+			builder.append("(");
+			
+			Class<?>[] parameterTypes = method.getParameterTypes();
+			for (int i = 0; i < parameterTypes.length; i++) {
+				Class<?> parameterType = parameterTypes[i];
+				
+				if (i > 0) {
+					builder.append(",");
+				}
+				
+				builder.append(getObjectType(parameterType));				
+			}
+			
+			builder.append(")");
+			
+			System.out.println(builder.toString());
+			
+			Method m = object.getClass().getMethod(method.getName(), method.getParameterTypes());
+			calls.add(new Call(m.getName(), m.getParameterTypes()));
+			return m.invoke(object, args);
+		}
+
+		private String getObjectType(Class<?> parameterType) {
+			String type = "";
+			
+			if (parameterType.isPrimitive()) {
+				if (Boolean.TYPE.equals(parameterType)) {
+					type = "boolean";
+				}
+				
+				if (Character.TYPE.equals(parameterType)) {
+					type = "character";
+				}
+				
+				if (Byte.TYPE.equals(parameterType)) {
+					type = "byte";
+				}
+				
+				if (Short.TYPE.equals(parameterType)) {
+					type = "short";
+				}
+				
+				if (Integer.TYPE.equals(parameterType)) {
+					type = "int";
+				}
+				
+				if (Long.TYPE.equals(parameterType)) {
+					type = "long";
+				}
+				
+				if (Float.TYPE.equals(parameterType)) {
+					type = "float";
+				}
+				
+				if (Double.TYPE.equals(parameterType)) {
+					type = "double";
+				}
+				
+				if (Void.TYPE.equals(parameterType)) {
+					type = "void";
+				}
+				
+				if (Boolean.TYPE.equals(parameterType)) {
+					type = "boolean";
+				}
+			} else {
+				type = parameterType.getCanonicalName();				
+			}
+			
+			if (parameterType.isArray()) {
+				type = type + "[]";
+			}
+			
+			return type;
+		}
+
+		public Call[] getCalls() {
+			return calls.toArray(new Call[calls.size()]);
+		}
+	}
+
+	private class TestClassLoader extends ClassLoader {
+		
+		public TestClassLoader() {
+			super();
+		}
+
+		public TestClassLoader(ClassLoader parent) {
+			super(parent);
+		}
+
+		public Class addClass(String name, byte[] cls) {
+			Class c = defineClass(name, cls, 0, cls.length);
+			return c;
+		}
+	}
+	
+	private SampleLocalBean loadProxy() throws Exception {
+		SampleLocalBean bean = new SampleLocalBean();
+		TestInvocationHandler invocationHandler = new TestInvocationHandler(bean);
+		
+		return loadProxy(invocationHandler);
+	}
+
+	private SampleLocalBean loadProxy(TestInvocationHandler invocationHandler) throws Exception {
+		String name = "TestProxy";
+		byte[] cls = new LocalBeanProxyGeneratorImpl().generateProxy(SampleLocalBean.class, name);
+		
+		FileOutputStream os = new FileOutputStream("C:\\Temp\\TestProxy.class");
+		os.write(cls);
+		os.close();
+		
+		ClassLoader oldCl = Thread.currentThread().getContextClassLoader();
+		TestClassLoader newCl = new TestClassLoader(oldCl);
+		Thread.currentThread().setContextClassLoader(newCl);
+		newCl.addClass(name, cls);
+		
+		Class<?> proxyClass = newCl.loadClass(name);
+		
+		SampleLocalBean proxy = (SampleLocalBean) proxyClass.getConstructor(new Class[] { InvocationHandler.class }).newInstance(invocationHandler);
+		return proxy;
+	}
+
+	public void testShouldReturnCorrectMethodSignatures() throws Exception {
+		LocalBeanProxyGeneratorImpl proxyGenerator = new LocalBeanProxyGeneratorImpl();
+		assertEquals("(II)I", proxyGenerator.getMethodSignatureAsString(Integer.TYPE, new Class<?>[] { Integer.TYPE, Integer.TYPE }));
+		assertEquals("(ZZ)Z", proxyGenerator.getMethodSignatureAsString(Boolean.TYPE, new Class<?>[] { Boolean.TYPE, Boolean.TYPE }));
+		assertEquals("(CC)C", proxyGenerator.getMethodSignatureAsString(Character.TYPE, new Class<?>[] { Character.TYPE, Character.TYPE }));		
+		assertEquals("(BB)B", proxyGenerator.getMethodSignatureAsString(Byte.TYPE, new Class<?>[] { Byte.TYPE, Byte.TYPE }));		
+		assertEquals("(SS)S", proxyGenerator.getMethodSignatureAsString(Short.TYPE, new Class<?>[] { Short.TYPE, Short.TYPE }));		
+		assertEquals("(JJ)J", proxyGenerator.getMethodSignatureAsString(Long.TYPE, new Class<?>[] { Long.TYPE, Long.TYPE }));		
+		assertEquals("(FF)F", proxyGenerator.getMethodSignatureAsString(Float.TYPE, new Class<?>[] { Float.TYPE, Float.TYPE }));		
+		assertEquals("(DD)D", proxyGenerator.getMethodSignatureAsString(Double.TYPE, new Class<?>[] { Double.TYPE, Double.TYPE }));		
+		assertEquals("()V", proxyGenerator.getMethodSignatureAsString(Void.TYPE, new Class<?>[] { }));		
+
+		assertEquals("([I[I)[I", proxyGenerator.getMethodSignatureAsString(int[].class, new Class<?>[] { int[].class, int[].class }));
+		assertEquals("([Z[Z)[Z", proxyGenerator.getMethodSignatureAsString(boolean[].class, new Class<?>[] { boolean[].class, boolean[].class }));
+		assertEquals("([C[C)[C", proxyGenerator.getMethodSignatureAsString(char[].class, new Class<?>[] { char[].class, char[].class }));		
+		assertEquals("([B[B)[B", proxyGenerator.getMethodSignatureAsString(byte[].class, new Class<?>[] { byte[].class, byte[].class }));		
+		assertEquals("([S[S)[S", proxyGenerator.getMethodSignatureAsString(short[].class, new Class<?>[] { short[].class, short[].class }));		
+		assertEquals("([J[J)[J", proxyGenerator.getMethodSignatureAsString(long[].class, new Class<?>[] { long[].class, long[].class }));		
+		assertEquals("([F[F)[F", proxyGenerator.getMethodSignatureAsString(float[].class, new Class<?>[] { float[].class, float[].class }));		
+		assertEquals("([D[D)[D", proxyGenerator.getMethodSignatureAsString(double[].class, new Class<?>[] { double[].class, double[].class }));		
+		
+		assertEquals("(Ljava/lang/Integer;Ljava/lang/Integer;)Ljava/lang/Integer;", proxyGenerator.getMethodSignatureAsString(Integer.class, new Class<?>[] { Integer.class, Integer.class }));
+		assertEquals("([Ljava/lang/Integer;[Ljava/lang/Integer;)[Ljava/lang/Integer;", proxyGenerator.getMethodSignatureAsString(Integer[].class, new Class<?>[] { Integer[].class, Integer[].class }));
+	}
+	
+	@Test
+	public void testDoWork() throws Exception {
+		TestInvocationHandler invocationHandler = new TestInvocationHandler(new SampleLocalBean());
+		SampleLocalBean proxy = loadProxy(invocationHandler);
+		proxy.doWork();
+		
+		assertEquals(1, invocationHandler.getCalls().length);
+		Call call = invocationHandler.getCalls()[0];
+		assertEquals("doWork", call.getMethodName());
+		assertTrue(Arrays.equals(new Class<?>[] { }, call.getParameterTypes()));
+	}
+
+	@Test
+	public void testEcho() throws Exception {
+		TestInvocationHandler invocationHandler = new TestInvocationHandler(new SampleLocalBean());
+		SampleLocalBean proxy = loadProxy(invocationHandler);
+		String result = proxy.echo("Some text");
+
+		assertEquals("Some text", result);
+		assertEquals(1, invocationHandler.getCalls().length);
+		Call call = invocationHandler.getCalls()[0];
+		assertEquals("echo", call.getMethodName());
+		assertTrue(Arrays.equals(new Class<?>[] { String.class }, call.getParameterTypes()));
+	}
+
+	@Test
+	public void testAddIntInt() throws Exception {
+		int value1 = 32;
+		int value2 = 64;
+		int expectedResult = 96;
+		
+		TestInvocationHandler invocationHandler = new TestInvocationHandler(new SampleLocalBean());
+		SampleLocalBean proxy = loadProxy(invocationHandler);
+		int result = proxy.add(value1, value2);
+		assertEquals(expectedResult, result);
+		assertEquals(1, invocationHandler.getCalls().length);
+		Call call = invocationHandler.getCalls()[0];
+		assertEquals("add", call.getMethodName());
+		assertTrue(Arrays.equals(new Class<?>[] { Integer.TYPE, Integer.TYPE }, call.getParameterTypes()));
+	}
+
+	@Test
+	public void testAddIntegerInteger() throws Exception {
+		Integer value1 = 32;
+		Integer value2 = 64;
+		Integer expectedResult = 96;
+		
+		TestInvocationHandler invocationHandler = new TestInvocationHandler(new SampleLocalBean());
+		SampleLocalBean proxy = loadProxy(invocationHandler);
+		Integer result = proxy.add(value1, value2);
+		assertEquals(expectedResult, result);
+		assertEquals(1, invocationHandler.getCalls().length);
+		Call call = invocationHandler.getCalls()[0];
+		assertEquals("add", call.getMethodName());
+		assertTrue(Arrays.equals(new Class<?>[] { Integer.class, Integer.class }, call.getParameterTypes()));
+	}
+
+	@Test
+	public void testIsTrueBoolean() throws Exception {
+		TestInvocationHandler invocationHandler = new TestInvocationHandler(new SampleLocalBean());
+		SampleLocalBean proxy = loadProxy(invocationHandler);
+		boolean result = proxy.isTrue(new Boolean(true).booleanValue());
+
+		assertTrue(result);
+		assertEquals(1, invocationHandler.getCalls().length);
+		Call call = invocationHandler.getCalls()[0];
+		assertEquals("isTrue", call.getMethodName());
+		assertTrue(Arrays.equals(new Class<?>[] { Boolean.TYPE }, call.getParameterTypes()));
+	}
+
+	@Test
+	public void testIsTrueBoolean1() throws Exception {
+		TestInvocationHandler invocationHandler = new TestInvocationHandler(new SampleLocalBean());
+		SampleLocalBean proxy = loadProxy(invocationHandler);
+		Boolean result = proxy.isTrue(new Boolean(true));
+
+		assertTrue(result);
+		assertEquals(1, invocationHandler.getCalls().length);
+		Call call = invocationHandler.getCalls()[0];
+		assertEquals("isTrue", call.getMethodName());
+		assertTrue(Arrays.equals(new Class<?>[] { Boolean.class }, call.getParameterTypes()));
+	}
+
+	@Test
+	public void testNextCharChar() throws Exception {
+		TestInvocationHandler invocationHandler = new TestInvocationHandler(new SampleLocalBean());
+		SampleLocalBean proxy = loadProxy(invocationHandler);
+		char result = proxy.nextChar(new Character('A').charValue());
+
+		assertEquals('B', result);
+		assertEquals(1, invocationHandler.getCalls().length);
+		Call call = invocationHandler.getCalls()[0];
+		assertEquals("nextChar", call.getMethodName());
+		assertTrue(Arrays.equals(new Class<?>[] { Character.TYPE }, call.getParameterTypes()));
+	}
+
+	@Test
+	public void testNextCharCharacter() throws Exception {
+		TestInvocationHandler invocationHandler = new TestInvocationHandler(new SampleLocalBean());
+		SampleLocalBean proxy = loadProxy(invocationHandler);
+		Character result = proxy.nextChar(new Character('A'));
+
+		assertEquals(new Character('B'), result);
+		assertEquals(1, invocationHandler.getCalls().length);
+		Call call = invocationHandler.getCalls()[0];
+		assertEquals("nextChar", call.getMethodName());
+		assertTrue(Arrays.equals(new Class<?>[] { Character.class }, call.getParameterTypes()));
+	}
+
+	@Test
+	public void testAddShortShort() throws Exception {
+		short value1 = 32;
+		short value2 = 64;
+		short expectedResult = 96;
+		
+		TestInvocationHandler invocationHandler = new TestInvocationHandler(new SampleLocalBean());
+		SampleLocalBean proxy = loadProxy(invocationHandler);
+		short result = proxy.add(value1, value2);
+		assertEquals(expectedResult, result);
+		assertEquals(1, invocationHandler.getCalls().length);
+		Call call = invocationHandler.getCalls()[0];
+		assertEquals("add", call.getMethodName());
+		assertTrue(Arrays.equals(new Class<?>[] { Short.TYPE, Short.TYPE }, call.getParameterTypes()));
+	}
+
+	@Test
+	public void testAddShortShort1() throws Exception {
+		Short value1 = 32;
+		Short value2 = 64;
+		Short expectedResult = 96;
+		
+		TestInvocationHandler invocationHandler = new TestInvocationHandler(new SampleLocalBean());
+		SampleLocalBean proxy = loadProxy(invocationHandler);
+		Short result = proxy.add(value1, value2);
+		assertEquals(expectedResult, result);
+		assertEquals(1, invocationHandler.getCalls().length);
+		Call call = invocationHandler.getCalls()[0];
+		assertEquals("add", call.getMethodName());
+		assertTrue(Arrays.equals(new Class<?>[] { Short.class, Short.class }, call.getParameterTypes()));
+	}
+
+	@Test
+	public void testAddLongLong() throws Exception {
+		long value1 = 32;
+		long value2 = 64;
+		long expectedResult = 96;
+		
+		TestInvocationHandler invocationHandler = new TestInvocationHandler(new SampleLocalBean());
+		SampleLocalBean proxy = loadProxy(invocationHandler);
+		long result = proxy.add(value1, value2);
+		assertEquals(expectedResult, result);
+		assertEquals(1, invocationHandler.getCalls().length);
+		Call call = invocationHandler.getCalls()[0];
+		assertEquals("add", call.getMethodName());
+		assertTrue(Arrays.equals(new Class<?>[] { Long.TYPE, Long.TYPE }, call.getParameterTypes()));
+	}
+
+	@Test
+	public void testAddLongLong1() throws Exception {
+		Long value1 = 32L;
+		Long value2 = 64L;
+		Long expectedResult = 96L;
+		
+		TestInvocationHandler invocationHandler = new TestInvocationHandler(new SampleLocalBean());
+		SampleLocalBean proxy = loadProxy(invocationHandler);
+		Long result = proxy.add(value1, value2);
+		assertEquals(expectedResult, result);
+		assertEquals(1, invocationHandler.getCalls().length);
+		Call call = invocationHandler.getCalls()[0];
+		assertEquals("add", call.getMethodName());
+		assertTrue(Arrays.equals(new Class<?>[] { Long.class, Long.class }, call.getParameterTypes()));
+	}
+
+	@Test
+	public void testAddDoubleDouble() throws Exception {
+		double value1 = 32;
+		double value2 = 64;
+		double expectedResult = 96;
+		
+		TestInvocationHandler invocationHandler = new TestInvocationHandler(new SampleLocalBean());
+		SampleLocalBean proxy = loadProxy(invocationHandler);
+		double result = proxy.add(value1, value2);
+		assertEquals(expectedResult, result);
+		assertEquals(1, invocationHandler.getCalls().length);
+		Call call = invocationHandler.getCalls()[0];
+		assertEquals("add", call.getMethodName());
+		assertTrue(Arrays.equals(new Class<?>[] { Double.TYPE, Double.TYPE }, call.getParameterTypes()));
+	}
+
+	@Test
+	public void testAddDoubleDouble1() throws Exception {
+		Double value1 = 32d;
+		Double value2 = 64d;
+		Double expectedResult = 96d;
+		
+		TestInvocationHandler invocationHandler = new TestInvocationHandler(new SampleLocalBean());
+		SampleLocalBean proxy = loadProxy(invocationHandler);
+		Double result = proxy.add(value1, value2);
+		assertEquals(expectedResult, result);
+		assertEquals(1, invocationHandler.getCalls().length);
+		Call call = invocationHandler.getCalls()[0];
+		assertEquals("add", call.getMethodName());
+		assertTrue(Arrays.equals(new Class<?>[] { Double.class, Double.class }, call.getParameterTypes()));
+	}
+
+	@Test
+	public void testAddFloatFloat() throws Exception {
+		float value1 = 32f;
+		float value2 = 64f;
+		float expectedResult = 96f;
+		
+		TestInvocationHandler invocationHandler = new TestInvocationHandler(new SampleLocalBean());
+		SampleLocalBean proxy = loadProxy(invocationHandler);
+		float result = proxy.add(value1, value2);
+		assertEquals(expectedResult, result);
+		assertEquals(1, invocationHandler.getCalls().length);
+		Call call = invocationHandler.getCalls()[0];
+		assertEquals("add", call.getMethodName());
+		assertTrue(Arrays.equals(new Class<?>[] { Float.TYPE, Float.TYPE }, call.getParameterTypes()));
+	}
+
+	@Test
+	public void testAddFloatFloat1() throws Exception {
+		Float value1 = 32f;
+		Float value2 = 64f;
+		Float expectedResult = 96f;
+		
+		TestInvocationHandler invocationHandler = new TestInvocationHandler(new SampleLocalBean());
+		SampleLocalBean proxy = loadProxy(invocationHandler);
+		Float result = proxy.add(value1, value2);
+		assertEquals(expectedResult, result);
+		assertEquals(1, invocationHandler.getCalls().length);
+		Call call = invocationHandler.getCalls()[0];
+		assertEquals("add", call.getMethodName());
+		assertTrue(Arrays.equals(new Class<?>[] { Float.class, Float.class }, call.getParameterTypes()));
+	}
+
+	@Test
+	public void testCreateSomeObjects() throws Exception {
+		ProxyTestObject[] expectedResult = new ProxyTestObject[] { new ProxyTestObject("object1"), new ProxyTestObject("object2") };
+		
+		TestInvocationHandler invocationHandler = new TestInvocationHandler(new SampleLocalBean());
+		SampleLocalBean proxy = loadProxy(invocationHandler);
+		ProxyTestObject[] result = proxy.createSomeObjects();
+		
+		assertTrue(Arrays.equals(expectedResult, result));
+		assertEquals(1, invocationHandler.getCalls().length);
+		Call call = invocationHandler.getCalls()[0];
+		assertEquals("createSomeObjects", call.getMethodName());
+		assertTrue(Arrays.equals(new Class<?>[] { }, call.getParameterTypes()));
+	}
+
+	@Test
+	public void testReturnFirst() throws Exception {
+		ProxyTestObject expectedResult = new ProxyTestObject("object1");
+		
+		TestInvocationHandler invocationHandler = new TestInvocationHandler(new SampleLocalBean());
+		SampleLocalBean proxy = loadProxy(invocationHandler);
+		ProxyTestObject result = proxy.returnFirst(new ProxyTestObject[] { new ProxyTestObject("object1"), new ProxyTestObject("object2") });
+		
+		assertEquals(expectedResult, result);
+		assertEquals(1, invocationHandler.getCalls().length);
+		Call call = invocationHandler.getCalls()[0];
+		assertEquals("returnFirst", call.getMethodName());
+		assertTrue(Arrays.equals(new Class<?>[] { ProxyTestObject[].class }, call.getParameterTypes()));
+	}
+
+	@Test
+	public void testReverseIntArray() throws Exception {
+		int value1 = 2;
+		int value2 = 4;
+		int value3 = 6;
+		int value4 = 8;
+		int value5 = 10;
+		
+		int[] value = new int[] { value1, value2, value3, value4, value5 };
+		int[] expectedResult = new int[] { value5, value4, value3, value2, value1 };
+		
+		TestInvocationHandler invocationHandler = new TestInvocationHandler(new SampleLocalBean());
+		SampleLocalBean proxy = loadProxy(invocationHandler);
+		int[] result = proxy.reverse(value);
+		
+		assertTrue(Arrays.equals(expectedResult, result));
+		assertEquals(1, invocationHandler.getCalls().length);
+		Call call = invocationHandler.getCalls()[0];
+		assertEquals("reverse", call.getMethodName());
+		assertTrue(Arrays.equals(new Class<?>[] { int[].class }, call.getParameterTypes()));
+	}
+
+	@Test
+	public void testReverseIntegerArray() throws Exception {
+		Integer value1 = 2;
+		Integer value2 = 4;
+		Integer value3 = 6;
+		Integer value4 = 8;
+		Integer value5 = 10;
+		
+		Integer[] value = new Integer[] { value1, value2, value3, value4, value5 };
+		Integer[] expectedResult = new Integer[] { value5, value4, value3, value2, value1 };
+		
+		TestInvocationHandler invocationHandler = new TestInvocationHandler(new SampleLocalBean());
+		SampleLocalBean proxy = loadProxy(invocationHandler);
+		Integer[] result = proxy.reverse(value);
+		
+		assertTrue(Arrays.equals(expectedResult, result));
+		assertEquals(1, invocationHandler.getCalls().length);
+		Call call = invocationHandler.getCalls()[0];
+		assertEquals("reverse", call.getMethodName());
+		assertTrue(Arrays.equals(new Class<?>[] { Integer[].class }, call.getParameterTypes()));
+	}
+
+	@Test
+	public void testReverseBooleanArray() throws Exception {
+		boolean value1 = true;
+		boolean value2 = false;
+		
+		boolean[] value = new boolean[] { value1, value2 };
+		boolean[] expectedResult = new boolean[] { value2, value1 };
+		
+		TestInvocationHandler invocationHandler = new TestInvocationHandler(new SampleLocalBean());
+		SampleLocalBean proxy = loadProxy(invocationHandler);
+		boolean[] result = proxy.reverse(value);
+		
+		assertTrue(Arrays.equals(expectedResult, result));
+		assertEquals(1, invocationHandler.getCalls().length);
+		Call call = invocationHandler.getCalls()[0];
+		assertEquals("reverse", call.getMethodName());
+		assertTrue(Arrays.equals(new Class<?>[] { boolean[].class }, call.getParameterTypes()));
+	}
+
+	@Test
+	public void testReverseBooleanArray1() throws Exception {
+		Boolean value1 = true;
+		Boolean value2 = false;
+		
+		Boolean[] value = new Boolean[] { value1, value2 };
+		Boolean[] expectedResult = new Boolean[] { value2, value1 };
+		
+		TestInvocationHandler invocationHandler = new TestInvocationHandler(new SampleLocalBean());
+		SampleLocalBean proxy = loadProxy(invocationHandler);
+		Boolean[] result = proxy.reverse(value);
+		
+		assertTrue(Arrays.equals(expectedResult, result));
+		assertEquals(1, invocationHandler.getCalls().length);
+		Call call = invocationHandler.getCalls()[0];
+		assertEquals("reverse", call.getMethodName());
+		assertTrue(Arrays.equals(new Class<?>[] { Boolean[].class }, call.getParameterTypes()));
+	}
+
+	@Test
+	public void testReverseCharArray() throws Exception {
+		char value1 = 'j';
+		char value2 = 'o';
+		char value3 = 'n';
+		
+		char[] value = new char[] { value1, value2, value3 };
+		char[] expectedResult = new char[] { value3, value2, value1 };
+		
+		TestInvocationHandler invocationHandler = new TestInvocationHandler(new SampleLocalBean());
+		SampleLocalBean proxy = loadProxy(invocationHandler);
+		char[] result = proxy.reverse(value);
+		
+		assertTrue(Arrays.equals(expectedResult, result));
+		assertEquals(1, invocationHandler.getCalls().length);
+		Call call = invocationHandler.getCalls()[0];
+		assertEquals("reverse", call.getMethodName());
+		assertTrue(Arrays.equals(new Class<?>[] { char[].class }, call.getParameterTypes()));
+	}
+
+	@Test
+	public void testReverseCharacterArray() throws Exception {
+		Character value1 = 'j';
+		Character value2 = 'o';
+		Character value3 = 'n';
+		
+		Character[] value = new Character[] { value1, value2, value3 };
+		Character[] expectedResult = new Character[] { value3, value2, value1 };
+		
+		TestInvocationHandler invocationHandler = new TestInvocationHandler(new SampleLocalBean());
+		SampleLocalBean proxy = loadProxy(invocationHandler);
+		Character[] result = proxy.reverse(value);
+		
+		assertTrue(Arrays.equals(expectedResult, result));
+		assertEquals(1, invocationHandler.getCalls().length);
+		Call call = invocationHandler.getCalls()[0];
+		assertEquals("reverse", call.getMethodName());
+		assertTrue(Arrays.equals(new Class<?>[] { Character[].class }, call.getParameterTypes()));
+	}
+
+	@Test
+	public void testReverseShortArray() throws Exception {
+		short value1 = 2;
+		short value2 = 4;
+		short value3 = 6;
+		short value4 = 8;
+		short value5 = 10;
+		
+		short[] value = new short[] { value1, value2, value3, value4, value5 };
+		short[] expectedResult = new short[] { value5, value4, value3, value2, value1 };
+		
+		TestInvocationHandler invocationHandler = new TestInvocationHandler(new SampleLocalBean());
+		SampleLocalBean proxy = loadProxy(invocationHandler);
+		short[] result = proxy.reverse(value);
+		
+		assertTrue(Arrays.equals(expectedResult, result));
+		assertEquals(1, invocationHandler.getCalls().length);
+		Call call = invocationHandler.getCalls()[0];
+		assertEquals("reverse", call.getMethodName());
+		assertTrue(Arrays.equals(new Class<?>[] { short[].class }, call.getParameterTypes()));
+	}
+
+	@Test
+	public void testReverseShortArray1() throws Exception {
+		Short value1 = 2;
+		Short value2 = 4;
+		Short value3 = 6;
+		Short value4 = 8;
+		Short value5 = 10;
+		
+		Short[] value = new Short[] { value1, value2, value3, value4, value5 };
+		Short[] expectedResult = new Short[] { value5, value4, value3, value2, value1 };
+		
+		TestInvocationHandler invocationHandler = new TestInvocationHandler(new SampleLocalBean());
+		SampleLocalBean proxy = loadProxy(invocationHandler);
+		Short[] result = proxy.reverse(value);
+		
+		assertTrue(Arrays.equals(expectedResult, result));
+		assertEquals(1, invocationHandler.getCalls().length);
+		Call call = invocationHandler.getCalls()[0];
+		assertEquals("reverse", call.getMethodName());
+		assertTrue(Arrays.equals(new Class<?>[] { Short[].class }, call.getParameterTypes()));
+	}
+
+	@Test
+	public void testReverseLongArray() throws Exception {
+		long value1 = 2L;
+		long value2 = 4L;
+		long value3 = 6L;
+		long value4 = 8L;
+		long value5 = 10L;
+		
+		long[] value = new long[] { value1, value2, value3, value4, value5 };
+		long[] expectedResult = new long[] { value5, value4, value3, value2, value1 };
+		
+		TestInvocationHandler invocationHandler = new TestInvocationHandler(new SampleLocalBean());
+		SampleLocalBean proxy = loadProxy(invocationHandler);
+		long[] result = proxy.reverse(value);
+		
+		assertTrue(Arrays.equals(expectedResult, result));
+		assertEquals(1, invocationHandler.getCalls().length);
+		Call call = invocationHandler.getCalls()[0];
+		assertEquals("reverse", call.getMethodName());
+		assertTrue(Arrays.equals(new Class<?>[] { long[].class }, call.getParameterTypes()));
+	}
+
+	@Test
+	public void testReverseLongArray1() throws Exception {
+		Long value1 = 2L;
+		Long value2 = 4L;
+		Long value3 = 6L;
+		Long value4 = 8L;
+		Long value5 = 10L;
+		
+		Long[] value = new Long[] { value1, value2, value3, value4, value5 };
+		Long[] expectedResult = new Long[] { value5, value4, value3, value2, value1 };
+		
+		TestInvocationHandler invocationHandler = new TestInvocationHandler(new SampleLocalBean());
+		SampleLocalBean proxy = loadProxy(invocationHandler);
+		Long[] result = proxy.reverse(value);
+		
+		assertTrue(Arrays.equals(expectedResult, result));
+		assertEquals(1, invocationHandler.getCalls().length);
+		Call call = invocationHandler.getCalls()[0];
+		assertEquals("reverse", call.getMethodName());
+		assertTrue(Arrays.equals(new Class<?>[] { Long[].class }, call.getParameterTypes()));
+	}
+
+	@Test
+	public void testReverseDoubleArray() throws Exception {
+		double value1 = 2d;
+		double value2 = 4d;
+		double value3 = 6d;
+		double value4 = 8d;
+		double value5 = 10d;
+		
+		double[] value = new double[] { value1, value2, value3, value4, value5 };
+		double[] expectedResult = new double[] { value5, value4, value3, value2, value1 };
+		
+		TestInvocationHandler invocationHandler = new TestInvocationHandler(new SampleLocalBean());
+		SampleLocalBean proxy = loadProxy(invocationHandler);
+		double[] result = proxy.reverse(value);
+		
+		assertTrue(Arrays.equals(expectedResult, result));
+		assertEquals(1, invocationHandler.getCalls().length);
+		Call call = invocationHandler.getCalls()[0];
+		assertEquals("reverse", call.getMethodName());
+		assertTrue(Arrays.equals(new Class<?>[] { double[].class }, call.getParameterTypes()));
+	}
+
+	@Test
+	public void testReverseDoubleArray1() throws Exception {
+		Double value1 = 2d;
+		Double value2 = 4d;
+		Double value3 = 6d;
+		Double value4 = 8d;
+		Double value5 = 10d;
+		
+		Double[] value = new Double[] { value1, value2, value3, value4, value5 };
+		Double[] expectedResult = new Double[] { value5, value4, value3, value2, value1 };
+		
+		TestInvocationHandler invocationHandler = new TestInvocationHandler(new SampleLocalBean());
+		SampleLocalBean proxy = loadProxy(invocationHandler);
+		Double[] result = proxy.reverse(value);
+		
+		assertTrue(Arrays.equals(expectedResult, result));
+		assertEquals(1, invocationHandler.getCalls().length);
+		Call call = invocationHandler.getCalls()[0];
+		assertEquals("reverse", call.getMethodName());
+		assertTrue(Arrays.equals(new Class<?>[] { Double[].class }, call.getParameterTypes()));
+	}
+
+	@Test
+	public void testReverseFloatArray() throws Exception {
+		float value1 = 2f;
+		float value2 = 4f;
+		float value3 = 6f;
+		float value4 = 8f;
+		float value5 = 10f;
+		
+		float[] value = new float[] { value1, value2, value3, value4, value5 };
+		float[] expectedResult = new float[] { value5, value4, value3, value2, value1 };
+		
+		TestInvocationHandler invocationHandler = new TestInvocationHandler(new SampleLocalBean());
+		SampleLocalBean proxy = loadProxy(invocationHandler);
+		float[] result = proxy.reverse(value);
+		
+		assertTrue(Arrays.equals(expectedResult, result));
+		assertEquals(1, invocationHandler.getCalls().length);
+		Call call = invocationHandler.getCalls()[0];
+		assertEquals("reverse", call.getMethodName());
+		assertTrue(Arrays.equals(new Class<?>[] { float[].class }, call.getParameterTypes()));
+	}
+
+	@Test
+	public void testReverseFloatArray1() throws Exception {
+		Float value1 = 2f;
+		Float value2 = 4f;
+		Float value3 = 6f;
+		Float value4 = 8f;
+		Float value5 = 10f;
+		
+		Float[] value = new Float[] { value1, value2, value3, value4, value5 };
+		Float[] expectedResult = new Float[] { value5, value4, value3, value2, value1 };
+		
+		TestInvocationHandler invocationHandler = new TestInvocationHandler(new SampleLocalBean());
+		SampleLocalBean proxy = loadProxy(invocationHandler);
+		Float[] result = proxy.reverse(value);
+		
+		assertTrue(Arrays.equals(expectedResult, result));
+		assertEquals(1, invocationHandler.getCalls().length);
+		Call call = invocationHandler.getCalls()[0];
+		assertEquals("reverse", call.getMethodName());
+		assertTrue(Arrays.equals(new Class<?>[] { Float[].class }, call.getParameterTypes()));
+	}
+
+	@Test
+	public void testThrowAnException() throws Exception {
+		TestInvocationHandler invocationHandler = new TestInvocationHandler(new SampleLocalBean());
+		SampleLocalBean proxy = loadProxy(invocationHandler);
+
+		try {
+			proxy.throwAnException();
+		} catch (ProxyTestException e) {
+		}
+		
+		assertEquals(1, invocationHandler.getCalls().length);
+		Call call = invocationHandler.getCalls()[0];
+		assertEquals("throwAnException", call.getMethodName());
+		assertTrue(Arrays.equals(new Class<?>[] { }, call.getParameterTypes()));
+	}
+	
+	@Test
+	public void testThrowAnotherException() throws Exception {
+		TestInvocationHandler invocationHandler = new TestInvocationHandler(new SampleLocalBean());
+		SampleLocalBean proxy = loadProxy(invocationHandler);
+
+		try {
+			proxy.throwAnotherException();
+		} catch (IOException e) {
+		}
+		
+		assertEquals(1, invocationHandler.getCalls().length);
+		Call call = invocationHandler.getCalls()[0];
+		assertEquals("throwAnotherException", call.getMethodName());
+		assertTrue(Arrays.equals(new Class<?>[] { }, call.getParameterTypes()));
+	}
+	
+	@Test
+	public void testGenericCollections() throws Exception {
+		List<ProxyTestObject> value = new ArrayList<ProxyTestObject>();
+		value.add(new ProxyTestObject("test1"));
+		value.add(new ProxyTestObject("test2"));
+
+		List<ProxyTestObject> expectedResult = new ArrayList<ProxyTestObject>();
+		expectedResult.add(new ProxyTestObject("test2"));
+		expectedResult.add(new ProxyTestObject("test1"));
+		
+		TestInvocationHandler invocationHandler = new TestInvocationHandler(new SampleLocalBean());
+		SampleLocalBean proxy = loadProxy(invocationHandler);
+		List<ProxyTestObject> result = proxy.reverseList(value);
+		
+		assertEquals(expectedResult, result);
+		assertEquals(1, invocationHandler.getCalls().length);
+		Call call = invocationHandler.getCalls()[0];
+		assertEquals("reverseList", call.getMethodName());
+		assertTrue(Arrays.equals(new Class<?>[] { List.class }, call.getParameterTypes()));
+	}
+
+	@Test
+	public void testMultiDimensionArrays() throws Exception {
+		int[][] value = new int[][] { new int[] { 1, 2, 3, 4 }, new int[] { 5, 6, 7, 8 } };
+		int[][] expectedResult = new int[][] { new int[] { 8, 7, 6, 5 }, new int[] { 4, 3, 2, 1 } };
+		
+		TestInvocationHandler invocationHandler = new TestInvocationHandler(new SampleLocalBean());
+		SampleLocalBean proxy = loadProxy(invocationHandler);
+		int[][] result = proxy.reverseAll(value);
+		
+		assertTrue(Arrays.deepEquals(expectedResult, result));
+		assertEquals(1, invocationHandler.getCalls().length);
+		Call call = invocationHandler.getCalls()[0];
+		assertEquals("reverseAll", call.getMethodName());
+		assertTrue(Arrays.equals(new Class<?>[] { int[][].class }, call.getParameterTypes()));
+	}
+	
+	@Test
+	public void testAreThoseTwoMethodsTheSame() throws Exception {
+		LocalBeanProxyGeneratorImpl proxyGenerator = new LocalBeanProxyGeneratorImpl();
+		
+		assertEquals("[I", proxyGenerator.getAsmTypeAsString(int[].class, true));
+		assertEquals("[[I", proxyGenerator.getAsmTypeAsString(int[][].class, false));
+		assertEquals("Lorg/apache/openejb/util/proxy/ProxyTestObject;", proxyGenerator.getAsmTypeAsString(ProxyTestObject.class, true));
+		assertEquals("org/apache/openejb/util/proxy/ProxyTestObject", proxyGenerator.getAsmTypeAsString(ProxyTestObject.class, false));
+		assertEquals("[Lorg/apache/openejb/util/proxy/ProxyTestObject;", proxyGenerator.getAsmTypeAsString(ProxyTestObject[].class, true));
+		assertEquals("[Lorg/apache/openejb/util/proxy/ProxyTestObject;", proxyGenerator.getAsmTypeAsString(ProxyTestObject[].class, false));
+		
+		assertEquals("java/lang/Integer", proxyGenerator.getCastType(Integer.TYPE));
+		assertEquals("java/lang/Integer", proxyGenerator.getCastType(Integer.class));
+		assertEquals("org/apache/openejb/util/proxy/ProxyTestObject", proxyGenerator.getCastType(ProxyTestObject.class));
+	}
+}

Added: openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/proxy/ProxyTestException.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/proxy/ProxyTestException.java?rev=909833&view=auto
==============================================================================
--- openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/proxy/ProxyTestException.java (added)
+++ openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/proxy/ProxyTestException.java Sat Feb 13 14:50:44 2010
@@ -0,0 +1,37 @@
+/**
+ * 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.openejb.util.proxy;
+
+public class ProxyTestException extends Exception {
+
+	public ProxyTestException() {
+		super();
+	}
+
+	public ProxyTestException(String message, Throwable throwable) {
+		super(message, throwable);
+	}
+
+	public ProxyTestException(String message) {
+		super(message);
+	}
+
+	public ProxyTestException(Throwable throwable) {
+		super(throwable);
+	}
+
+}

Added: openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/proxy/ProxyTestObject.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/proxy/ProxyTestObject.java?rev=909833&view=auto
==============================================================================
--- openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/proxy/ProxyTestObject.java (added)
+++ openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/proxy/ProxyTestObject.java Sat Feb 13 14:50:44 2010
@@ -0,0 +1,60 @@
+/**
+ * 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.openejb.util.proxy;
+
+public class ProxyTestObject {
+	private String info;
+
+	public ProxyTestObject() {
+		super();
+	}
+
+	public ProxyTestObject(String info) {
+		this.info = info;
+	}
+
+	public String getInfo() {
+		return info;
+	}
+
+	public void setInfo(String info) {
+		this.info = info;
+	}
+
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + ((info == null) ? 0 : info.hashCode());
+		return result;
+	}
+
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		ProxyTestObject other = (ProxyTestObject) obj;
+		if (info == null) {
+			if (other.info != null)
+				return false;
+		} else if (!info.equals(other.info))
+			return false;
+		return true;
+	}
+}

Added: openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/proxy/SampleLocalBean.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/proxy/SampleLocalBean.java?rev=909833&view=auto
==============================================================================
--- openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/proxy/SampleLocalBean.java (added)
+++ openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/proxy/SampleLocalBean.java Sat Feb 13 14:50:44 2010
@@ -0,0 +1,360 @@
+/**
+ * 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.openejb.util.proxy;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.ejb.LocalBean;
+
+
+@LocalBean
+public class SampleLocalBean {
+
+	public SampleLocalBean() {
+		super();
+	}
+	
+	/* 1. void return, no arg */
+	public void doWork() {
+		System.out.println("void doWork()");
+	}
+	
+	/* 2. simple object */
+	public String echo(String input) {
+		return input;
+	}
+	
+	/* 3a int primitive */
+	public int add(int no1, int no2) {
+		return no1 + no2;
+	}
+
+	/* 3b int object */
+	public Integer add(Integer no1, Integer no2) {
+		return no1 + no2;
+	}
+	
+	/* 3c bool primitive */
+	public boolean isTrue (boolean value) {
+		return value;
+	}
+
+	/* 3d bool object */
+	public Boolean isTrue (Boolean value) {
+		return value;
+	}
+	
+	/* 3e char primitive */
+	public char nextChar (char value) {
+		if (value > 'z') return 'A';
+		char next = value;
+		next++;
+		return next;
+	}
+	
+	/* 3f char object */
+	public Character nextChar (Character value) {
+		if (value > 'z') return 'A';
+		char next = value;
+		next++;
+		return next;
+	}
+	
+	/* 3g short primitive */
+	public short add(short no1, short no2) {
+		return (short) (no1 + no2);
+	}
+	
+	/* 3h short object */
+	public Short add(Short no1, Short no2) {
+		return new Short((short) (no1 + no2));
+	}
+
+	/* 3i long primitive */
+	public long add(long no1, long no2) {
+		return (long) (no1 + no2);
+	}
+
+	/* 3j long object */
+	public Long add(Long no1, Long no2) {
+		return (long) (no1 + no2);
+	}
+	
+	/* 3k double primitive */
+	public double add(double no1, double no2) {
+		return (double) (no1 + no2);
+	}
+
+	/* 3l double object */
+	public Double add(Double no1, Double no2) {
+		return (Double) (no1 + no2);
+	}
+	
+	/* 3m float primitive */
+	public float add(float no1, float no2) {
+		return (float) (no1 + no2);
+	}
+	
+	/* 3n float object*/
+	public Float add(Float no1, Float no2) {
+		return (Float) (no1 + no2);
+	}
+	
+	/* 4a simple object array */
+	public ProxyTestObject[] createSomeObjects() {
+		ProxyTestObject object1 = new ProxyTestObject("object1");
+		ProxyTestObject object2 = new ProxyTestObject("object2");
+		
+		return new ProxyTestObject[] { object1, object2 };
+	}
+	
+	/* 4b simple object array */
+	public ProxyTestObject returnFirst(ProxyTestObject[] objects) {
+		if (objects == null || objects.length == 0) {
+			return null;
+		}
+		
+		return objects[0];
+	}
+
+	/* 5a int primitive */
+	public int[] reverse(int[] value) {
+		if (value == null || value.length == 0) {
+			return value;
+		}
+		
+		int[] result = new int[value.length];
+		for (int i = 0; i < value.length; i++) {
+			result[(value.length - 1) - i] = value[i];
+		}
+		
+		return result;
+	}
+
+	/* 5b int object */
+	public Integer[] reverse(Integer[] value) {
+		if (value == null || value.length == 0) {
+			return value;
+		}
+		
+		Integer[] result = new Integer[value.length];
+		for (int i = 0; i < value.length; i++) {
+			result[(value.length - 1) - i] = value[i];
+		}
+		
+		return result;
+	}
+	
+	/* 5c bool primitive */
+	public boolean[] reverse (boolean[] value) {
+		if (value == null || value.length == 0) {
+			return value;
+		}
+		
+		boolean[] result = new boolean[value.length];
+		for (int i = 0; i < value.length; i++) {
+			result[(value.length - 1) - i] = value[i];
+		}
+		
+		return result;
+	}
+
+	/* 5d bool object */
+	public Boolean[] reverse (Boolean[] value) {
+		if (value == null || value.length == 0) {
+			return value;
+		}
+		
+		Boolean[] result = new Boolean[value.length];
+		for (int i = 0; i < value.length; i++) {
+			result[(value.length - 1) - i] = value[i];
+		}
+		
+		return result;
+	}
+	
+	/* 5e char primitive */
+	public char[] reverse (char[] value) {
+		if (value == null || value.length == 0) {
+			return value;
+		}
+		
+		char[] result = new char[value.length];
+		for (int i = 0; i < value.length; i++) {
+			result[(value.length - 1) - i] = value[i];
+		}
+		
+		return result;
+	}
+	
+	/* 5f char object */
+	public Character[] reverse (Character[] value) {
+		if (value == null || value.length == 0) {
+			return value;
+		}
+		
+		Character[] result = new Character[value.length];
+		for (int i = 0; i < value.length; i++) {
+			result[(value.length - 1) - i] = value[i];
+		}
+		
+		return result;
+	}
+	
+	/* 5g short primitive */
+	public short[] reverse(short[] value) {
+		if (value == null || value.length == 0) {
+			return value;
+		}
+		
+		short[] result = new short[value.length];
+		for (int i = 0; i < value.length; i++) {
+			result[(value.length - 1) - i] = value[i];
+		}
+		
+		return result;
+	}
+	
+	/* 5h short object */
+	public Short[] reverse(Short[] value) {
+		if (value == null || value.length == 0) {
+			return value;
+		}
+		
+		Short[] result = new Short[value.length];
+		for (int i = 0; i < value.length; i++) {
+			result[(value.length - 1) - i] = value[i];
+		}
+		
+		return result;
+	}
+
+	/* 5i long primitive */
+	public long[] reverse(long[] value) {
+		if (value == null || value.length == 0) {
+			return value;
+		}
+		
+		long[] result = new long[value.length];
+		for (int i = 0; i < value.length; i++) {
+			result[(value.length - 1) - i] = value[i];
+		}
+		
+		return result;
+	}
+
+	/* 5j long object */
+	public Long[] reverse(Long[] value) {
+		if (value == null || value.length == 0) {
+			return value;
+		}
+		
+		Long[] result = new Long[value.length];
+		for (int i = 0; i < value.length; i++) {
+			result[(value.length - 1) - i] = value[i];
+		}
+		
+		return result;
+	}
+	
+	/* 5k double primitive */
+	public double[] reverse(double[] value) {
+		if (value == null || value.length == 0) {
+			return value;
+		}
+		
+		double[] result = new double[value.length];
+		for (int i = 0; i < value.length; i++) {
+			result[(value.length - 1) - i] = value[i];
+		}
+		
+		return result;
+	}
+
+	/* 5l double object */
+	public Double[] reverse(Double[] value) {
+		if (value == null || value.length == 0) {
+			return value;
+		}
+		
+		Double[] result = new Double[value.length];
+		for (int i = 0; i < value.length; i++) {
+			result[(value.length - 1) - i] = value[i];
+		}
+		
+		return result;
+	}
+	
+	/* 5m float primitive */
+	public float[] reverse(float[] value) {
+		if (value == null || value.length == 0) {
+			return value;
+		}
+		
+		float[] result = new float[value.length];
+		for (int i = 0; i < value.length; i++) {
+			result[(value.length - 1) - i] = value[i];
+		}
+		
+		return result;
+	}
+	
+	/* 5n float object*/
+	public Float[] reverse(Float[] value) {
+		if (value == null || value.length == 0) {
+			return value;
+		}
+		
+		Float[] result = new Float[value.length];
+		for (int i = 0; i < value.length; i++) {
+			result[(value.length - 1) - i] = value[i];
+		}
+		
+		return result;
+	}
+
+	/* 6 throw and exception */
+	public String throwAnException() throws ProxyTestException {
+		throw new ProxyTestException();
+	}
+	
+	public String throwAnotherException() throws ProxyTestException, IOException {
+		throw new IOException();
+	}
+	
+	/* 7 test generics */
+	public List<ProxyTestObject> reverseList(List<ProxyTestObject> objectList) {
+		List<ProxyTestObject> result = new ArrayList<ProxyTestObject>();
+		for (int i = objectList.size() - 1; i >= 0; i--) {
+			result.add(objectList.get(i));
+		}
+		
+		return result;
+	}
+	
+	/* 8 test multi dimension array */
+	public int[][] reverseAll(int[][] value) {
+		int[][] result = new int[value.length][];
+		for (int i = 0; i < value.length; i++) {
+			result[i] = reverse(value[(value.length - 1) - i]);
+		}
+		
+		return result;
+	}
+}