You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by ga...@apache.org on 2010/08/19 19:04:24 UTC

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

Author: gawor
Date: Thu Aug 19 17:04:24 2010
New Revision: 987233

URL: http://svn.apache.org/viewvc?rev=987233&view=rev
Log:
OPENEJB-1334: Throw EJBException when calling non-public methods of local bean proxy

Modified:
    openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/proxy/LocalBeanProxyFactory.java
    openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/proxy/LocalBeanProxyGeneratorImpl.java
    openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/proxy/BaseLocalBean.java
    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/SampleLocalBean.java

Modified: openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/proxy/LocalBeanProxyFactory.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/proxy/LocalBeanProxyFactory.java?rev=987233&r1=987232&r2=987233&view=diff
==============================================================================
--- openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/proxy/LocalBeanProxyFactory.java (original)
+++ openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/proxy/LocalBeanProxyFactory.java Thu Aug 19 17:04:24 2010
@@ -19,14 +19,20 @@ package org.apache.openejb.util.proxy;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import javax.ejb.EJBException;
 
 public class LocalBeanProxyFactory {
 
+    private static final java.lang.reflect.InvocationHandler NON_BUSINESS_HANDLER = new NonBusinessHandler();
+    
     public static Object newProxyInstance(ClassLoader cl, Class interfce, java.lang.reflect.InvocationHandler h) throws IllegalArgumentException {
         try {
             Class proxyCls = new LocalBeanProxyGeneratorImpl().createProxy(interfce, cl);
-            Constructor constructor = proxyCls.getConstructor(java.lang.reflect.InvocationHandler.class);
-            Object object = constructor.newInstance(h);
+            Constructor constructor = proxyCls.getConstructor(java.lang.reflect.InvocationHandler.class, 
+                                                              java.lang.reflect.InvocationHandler.class);
+            Object object = constructor.newInstance(h, NON_BUSINESS_HANDLER);
             return object;
         } catch (NoSuchMethodException e) {
             throw new InternalError(e.toString());
@@ -41,7 +47,7 @@ public class LocalBeanProxyFactory {
 
     public static InvocationHandler getInvocationHandler(Object proxy) {
         try {
-            Field field = proxy.getClass().getDeclaredField("invocationHandler");
+            Field field = proxy.getClass().getDeclaredField(LocalBeanProxyGeneratorImpl.BUSSINESS_HANDLER_NAME);
             field.setAccessible(true);
             try {
                 return (InvocationHandler) field.get(proxy);
@@ -54,4 +60,12 @@ public class LocalBeanProxyFactory {
             throw new IllegalArgumentException(e);
         }
     }
+    
+    private static class NonBusinessHandler implements java.lang.reflect.InvocationHandler {
+
+        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+            throw new EJBException("Calling non-public methods of a local bean without any interfaces is not allowed");
+        }
+        
+    }
 }

Modified: 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=987233&r1=987232&r2=987233&view=diff
==============================================================================
--- openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/proxy/LocalBeanProxyGeneratorImpl.java (original)
+++ openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/util/proxy/LocalBeanProxyGeneratorImpl.java Thu Aug 19 17:04:24 2010
@@ -21,6 +21,11 @@ import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 import org.apache.xbean.asm.ClassWriter;
 import org.apache.xbean.asm.FieldVisitor;
@@ -31,6 +36,9 @@ import org.apache.xbean.asm.Type;
 
 public class LocalBeanProxyGeneratorImpl implements LocalBeanProxyGenerator, Opcodes {
 
+    static final String BUSSINESS_HANDLER_NAME = "businessHandler";    
+    static final String NON_BUSINESS_HANDLER_NAME = "nonBusinessHandler";
+    
     private static final sun.misc.Unsafe unsafe;
 
     static {
@@ -84,38 +92,107 @@ public class LocalBeanProxyGeneratorImpl
         cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, proxyClassName, null, clsToOverride, null);
 		cw.visitSource(clsToOverride + ".java", null);
 
-		// push InvocationHandler field
-		fv = cw.visitField(ACC_FINAL + ACC_PRIVATE, "invocationHandler", "Ljava/lang/reflect/InvocationHandler;", null, null);
+		// push InvocationHandler fields
+		fv = cw.visitField(ACC_FINAL + ACC_PRIVATE, BUSSINESS_HANDLER_NAME, "Ljava/lang/reflect/InvocationHandler;", null, null);
+		fv.visitEnd();
+		fv = cw.visitField(ACC_FINAL + ACC_PRIVATE, NON_BUSINESS_HANDLER_NAME, "Ljava/lang/reflect/InvocationHandler;", null, null);
 		fv.visitEnd();
 		
-		// push constructor
+		// push single argument constructor
 		mv = cw.visitMethod(ACC_PUBLIC, "<init>", "(Ljava/lang/reflect/InvocationHandler;)V", null, null);
 		mv.visitCode();
 		mv.visitVarInsn(ALOAD, 0);
+		mv.visitVarInsn(ALOAD, 1);
+		mv.visitVarInsn(ALOAD, 1);
+		mv.visitMethodInsn(INVOKESPECIAL, proxyName, "<init>", "(Ljava/lang/reflect/InvocationHandler;Ljava/lang/reflect/InvocationHandler;)V");
+		mv.visitInsn(RETURN);
+		mv.visitMaxs(0, 0);
+		mv.visitEnd();
+
+		// push double argument constructor
+		mv = cw.visitMethod(ACC_PUBLIC, "<init>", "(Ljava/lang/reflect/InvocationHandler;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.visitFieldInsn(PUTFIELD, proxyName, BUSSINESS_HANDLER_NAME, "Ljava/lang/reflect/InvocationHandler;");
+		mv.visitVarInsn(ALOAD, 0);
+		mv.visitVarInsn(ALOAD, 2);
+		mv.visitFieldInsn(PUTFIELD, proxyName, NON_BUSINESS_HANDLER_NAME, "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.getMethods();
-		for (Method method : methods) {
-		    if (Modifier.isFinal(method.getModifiers())) {
-		        // can't proxy final methods
-		        continue;
+		Map<String, List<Method>> methodMap = getNonPrivateMethods(clsToProxy);
+
+		for (Map.Entry<String, List<Method>> entry : methodMap.entrySet()) {
+		    for (Method method : entry.getValue()) {	
+		        String name = method.getName();
+                int modifiers = method.getModifiers();
+                if (Modifier.isPublic(modifiers) ||
+                    (method.getParameterTypes().length == 0 &&
+                            ("finalize".equals(name) || "clone".equals(name)))) {
+                    // forward invocations of any public methods or 
+                    // finalize/clone methods to businessHandler 
+                    processMethod(cw, method, proxyClassName, BUSSINESS_HANDLER_NAME);                    
+                } else {
+                    // forward invocations of any other methods to nonBusinessHandler
+                    processMethod(cw, method, proxyClassName, NON_BUSINESS_HANDLER_NAME);
+                }
 		    }
-			processMethod(cw, method, proxyClassName, clsToOverride);
 		}
 		
 		byte[] clsBytes = cw.toByteArray();
 		return clsBytes;
 	}
 
+	/*
+	 * Return all protected, public, and default methods of a given class
+	 * that are not final or static. The returned map includes the inherited methods
+	 * and ensures that overridden methods are included once.
+	 */
+	private Map<String, List<Method>> getNonPrivateMethods(Class<?> clazz) {
+	    Map<String, List<Method>> methodMap = new HashMap<String, List<Method>>();
+        while (clazz != null) {
+            for (Method method : clazz.getDeclaredMethods()) {
+                int modifiers = method.getModifiers();
+                if (Modifier.isFinal(modifiers) || 
+                    Modifier.isPrivate(modifiers) || 
+                    Modifier.isStatic(modifiers)) {
+                    continue;
+                }
+                
+                List<Method> methods = methodMap.get(method.getName());
+                if (methods == null) {
+                    methods = new ArrayList<Method>();
+                    methods.add(method);
+                    methodMap.put(method.getName(), methods);
+                } else {
+                    if (isOverridden(methods, method)) {
+                        // method is overridden in superclass, so do nothing
+                    } else {
+                        // method is not overridden, so add it
+                        methods.add(method);
+                    }
+                }
+            }
+            
+            clazz = clazz.getSuperclass();
+        }
+        return methodMap;
+	}
+	
+	private boolean isOverridden(List<Method> methods, Method method) {
+	    for (Method m : methods) {
+	        if (Arrays.equals(m.getParameterTypes(), method.getParameterTypes())) {
+	            return true;
+	        }
+	    }
+	    return false;
+	}
 
-	private void processMethod(ClassWriter cw, Method method, String proxyName, String clsToOverride) throws ProxyGenerationException {
+	private void processMethod(ClassWriter cw, Method method, String proxyName, String handlerName) throws ProxyGenerationException {
 		if ("<init>".equals(method.getName())) {
 			return;
 		}
@@ -123,9 +200,16 @@ public class LocalBeanProxyGeneratorImpl
 		Class<?> returnType = method.getReturnType();
 		Class<?>[] parameterTypes = method.getParameterTypes();
 		Class<?>[] exceptionTypes = method.getExceptionTypes();
+		int modifiers = method.getModifiers();
 		
 		// push the method definition
-		MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, method.getName(), getMethodSignatureAsString(returnType, parameterTypes), null, null);
+		int modifier = 0;
+		if (Modifier.isPublic(modifiers)) {
+		    modifier = ACC_PUBLIC;
+		} else if (Modifier.isProtected(modifiers)) {
+		    modifier = ACC_PROTECTED;
+		}
+		MethodVisitor mv = cw.visitMethod(modifier, method.getName(), getMethodSignatureAsString(returnType, parameterTypes), null, null);
 		mv.visitCode();
 
 		// push try/catch block, to catch declared exceptions, and to catch java.lang.Throwable
@@ -142,6 +226,7 @@ public class LocalBeanProxyGeneratorImpl
 		
 		// push try code
 		mv.visitLabel(l0);
+		String clsToOverride = method.getDeclaringClass().getName().replaceAll("\\.", "/");
 		mv.visitLdcInsn(Type.getType("L" + clsToOverride + ";"));
 		
 		// the following code generates the bytecode for this line of Java:
@@ -182,7 +267,7 @@ public class LocalBeanProxyGeneratorImpl
 		}
 		
 		// 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;");
+		mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getDeclaredMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;");
 	
 		// store the returned method for later
 		mv.visitVarInsn(ASTORE, length);
@@ -195,7 +280,7 @@ public class LocalBeanProxyGeneratorImpl
 		mv.visitVarInsn(ALOAD, 0);
 		
 		// get the invocationHandler field from this class
-		mv.visitFieldInsn(GETFIELD, proxyName, "invocationHandler", "Ljava/lang/reflect/InvocationHandler;");
+		mv.visitFieldInsn(GETFIELD, proxyName, handlerName, "Ljava/lang/reflect/InvocationHandler;");
 		
 		// we want to pass "this" in as the first parameter
 		mv.visitVarInsn(ALOAD, 0);

Modified: openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/proxy/BaseLocalBean.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/proxy/BaseLocalBean.java?rev=987233&r1=987232&r2=987233&view=diff
==============================================================================
--- openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/proxy/BaseLocalBean.java (original)
+++ openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/proxy/BaseLocalBean.java Thu Aug 19 17:04:24 2010
@@ -25,4 +25,14 @@ public class BaseLocalBean {
 	public final void finalMethod() {	    
 	}
 		
+	protected String protectedMethod() {
+	    return "protected method";
+	}
+	
+	protected String overriddenMethod() {
+	    return getClass().getName();
+	}
+	
+	public static void staticMethod() {	    
+	}
 }

Modified: 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=987233&r1=987232&r2=987233&view=diff
==============================================================================
--- openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/proxy/LocalBeanProxyGeneratorImplTest.java (original)
+++ openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/proxy/LocalBeanProxyGeneratorImplTest.java Thu Aug 19 17:04:24 2010
@@ -27,6 +27,8 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
+import javax.ejb.EJBException;
+
 public class LocalBeanProxyGeneratorImplTest extends TestCase {
 	
 	public class Call {
@@ -174,9 +176,8 @@ public class LocalBeanProxyGeneratorImpl
 
 	private SampleLocalBean loadProxy(TestInvocationHandler invocationHandler) throws Exception {
 		ClassLoader cl = Thread.currentThread().getContextClassLoader();
-
-        Class cls = new LocalBeanProxyGeneratorImpl().createProxy(SampleLocalBean.class, cl);
-        return (SampleLocalBean) cls.getConstructor(new Class[] { InvocationHandler.class }).newInstance(invocationHandler);
+		
+		return (SampleLocalBean) LocalBeanProxyFactory.newProxyInstance(cl, SampleLocalBean.class, invocationHandler);
 	}
 
 	public void testShouldReturnCorrectMethodSignatures() throws Exception {
@@ -205,6 +206,26 @@ public class LocalBeanProxyGeneratorImpl
 	}
 	
 	@Test
+	public void testNonPublicMethods() throws Exception {
+	    TestInvocationHandler invocationHandler = new TestInvocationHandler(new SampleLocalBean());
+	    SampleLocalBean proxy = loadProxy(invocationHandler);
+
+	    try {
+	        proxy.protectedMethod();
+	        fail("Protected method did not throw exception");
+	    } catch (EJBException e) {
+	        // that's what we expect
+	    }
+
+	    try {
+	        proxy.defaultMethod();
+	        fail("Default method did not throw exception");
+	    } catch (EJBException e) {
+	        // that's what we expect
+	    }
+	}
+	
+	@Test
 	public void testDoWork() throws Exception {
 		TestInvocationHandler invocationHandler = new TestInvocationHandler(new SampleLocalBean());
 		SampleLocalBean proxy = loadProxy(invocationHandler);
@@ -886,12 +907,16 @@ public class LocalBeanProxyGeneratorImpl
     public void testInheritedMethod() throws Exception {
         TestInvocationHandler invocationHandler = new TestInvocationHandler(new SampleLocalBean());
         SampleLocalBean proxy = loadProxy(invocationHandler);
-        String result = proxy.hello("Bob");
         
+        // call inherited method
+        String result = proxy.hello("Bob");
         assertEquals("Hello Bob", result);
         assertEquals(1, invocationHandler.getCalls().length);
         Call call = invocationHandler.getCalls()[0];
         assertEquals("hello", call.getMethodName());
+        
+        // call overridden method
+        assertEquals(SampleLocalBean.class.getName(), proxy.overriddenMethod());
     }
     
     public static class EnumParams {

Modified: 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=987233&r1=987232&r2=987233&view=diff
==============================================================================
--- openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/proxy/SampleLocalBean.java (original)
+++ openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/util/proxy/SampleLocalBean.java Thu Aug 19 17:04:24 2010
@@ -24,11 +24,20 @@ import javax.ejb.LocalBean;
 
 @LocalBean
 public class SampleLocalBean extends BaseLocalBean {
-
+    
 	public SampleLocalBean() {
 		super();
 	}
 	
+	int defaultMethod() {
+	    return -1;
+	}
+	
+	@Override
+	public String overriddenMethod() {
+	    return getClass().getName();
+	}
+	   
 	/* 1. void return, no arg */
 	public void doWork() {
 		System.out.println("void doWork()");