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()");