You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openwebbeans.apache.org by st...@apache.org on 2012/12/31 02:19:46 UTC
svn commit: r1427033 - in /openwebbeans/trunk/webbeans-impl/src:
main/java/org/apache/webbeans/proxy/asm/
test/java/org/apache/webbeans/newtests/interceptors/factory/
test/java/org/apache/webbeans/newtests/interceptors/factory/beans/
Author: struberg
Date: Mon Dec 31 01:19:45 2012
New Revision: 1427033
URL: http://svn.apache.org/viewvc?rev=1427033&view=rev
Log:
OWB-344 simple interceptor who 1:1 delegates through
the great thing: without reflection but with pure bytecode magic
Modified:
openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/proxy/asm/InterceptorDecoratorProxyFactory.java
openwebbeans/trunk/webbeans-impl/src/test/java/org/apache/webbeans/newtests/interceptors/factory/InterceptorDecoratorProxyFactoryTest.java
openwebbeans/trunk/webbeans-impl/src/test/java/org/apache/webbeans/newtests/interceptors/factory/beans/ClassInterceptedClass.java
Modified: openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/proxy/asm/InterceptorDecoratorProxyFactory.java
URL: http://svn.apache.org/viewvc/openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/proxy/asm/InterceptorDecoratorProxyFactory.java?rev=1427033&r1=1427032&r2=1427033&view=diff
==============================================================================
--- openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/proxy/asm/InterceptorDecoratorProxyFactory.java (original)
+++ openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/proxy/asm/InterceptorDecoratorProxyFactory.java Mon Dec 31 01:19:45 2012
@@ -20,8 +20,15 @@ package org.apache.webbeans.proxy.asm;
import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import org.apache.webbeans.proxy.ProxyGenerationException;
import org.objectweb.asm.ClassWriter;
@@ -39,10 +46,43 @@ import org.objectweb.asm.Type;
* All intercepted and decorated methods will get invoked via an InvocationHandler chain.
*
* This factory will create and cache the proxy classes for a given type.
+ *
+ * TODO: clarify how serialisation works! The proxy classes might need to get created on deserialisation!
*/
public class InterceptorDecoratorProxyFactory
{
+ /** the name of the field which stores the proxied instance */
+ public static final String FIELD_PROXIED_INSTANCE = "owbIntDecProxiedInstance";
+
+ public <T> T createProxyInstance(Class<T> proxyClass, T instance)
+ throws ProxyGenerationException
+ {
+ try
+ {
+ T proxy = proxyClass.newInstance();
+
+ Field delegateField = proxy.getClass().getDeclaredField(FIELD_PROXIED_INSTANCE);
+ delegateField.setAccessible(true);
+ delegateField.set(proxy, instance);
+
+ return proxy;
+ }
+ catch (InstantiationException e)
+ {
+ throw new ProxyGenerationException(e);
+ }
+ catch (IllegalAccessException e)
+ {
+ throw new ProxyGenerationException(e);
+ }
+ catch (NoSuchFieldException e)
+ {
+ throw new ProxyGenerationException(e);
+ }
+ }
+
+
/**
* Create a decorator and interceptor proxy for the given type.
*
@@ -57,43 +97,66 @@ public class InterceptorDecoratorProxyFa
* @return the proxy class
*/
public synchronized <T> Class<T> createInterceptorDecoratorProxyClass(ClassLoader classLoader, Class<T> classToProxy)
+ throws ProxyGenerationException
{
- String proxyName = classToProxy.getName() + "$OwbInterceptProxy";
- String proxyClassFileName = proxyName.replace('.', '/');
+ String proxyClassName = classToProxy.getName() + "$OwbInterceptProxy";
+ String proxyClassFileName = proxyClassName.replace('.', '/');
- try
- {
- final byte[] proxyBytes = generateProxy(classToProxy, proxyName, proxyClassFileName);
- return defineAndLoadClass(classLoader, proxyName, proxyBytes);
- }
- catch (Exception e)
- {
- final InternalError internalError = new InternalError();
- internalError.initCause(e);
- throw internalError;
- }
+ final byte[] proxyBytes = generateProxy(classToProxy, proxyClassName, proxyClassFileName);
+ return defineAndLoadClass(classLoader, proxyClassName, proxyBytes);
}
- private byte[] generateProxy(Class<?> classToProxy, String proxyName, String proxyClassFileName)
+ private byte[] generateProxy(Class<?> classToProxy, String proxyClassName, String proxyClassFileName)
throws ProxyGenerationException
{
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
String classFileName = classToProxy.getName().replace('.', '/');
- String[] interfaceNames = new String[0]; //X TODO there might be some more in the future
+ String[] interfaceNames = new String[]{}; //X TODO there might be some more in the future
cw.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER + Opcodes.ACC_SYNTHETIC, proxyClassFileName, null, classFileName, interfaceNames);
cw.visitSource(classFileName + ".java", null);
- propagateDefaultConstructor(cw, classToProxy, classFileName);
+ createInstanceVariables(cw, classToProxy, classFileName);
+
+ createConstructor(cw, proxyClassFileName, classToProxy, classFileName);
+
+
+ //X TODO filter out clone() and handle it seperately?
+ Map<String, List<Method>> methodMap = getNonPrivateMethods(classToProxy);
+
+ //X TODO select all non-intercepted and non-decorated methods
+
+ delegateNonInterceptedMethods(cw, proxyClassFileName, classToProxy, classFileName, methodMap);
+
+
+
+ //X TODO invoke all
//X TODO continue;
return cw.toByteArray();
}
- private void propagateDefaultConstructor(ClassWriter cw, Class<?> classToProxy, String classFileName)
+ private void createInstanceVariables(ClassWriter cw, Class<?> classToProxy, String classFileName)
+ {
+ // variable #1
+ createDelegationPoint(cw, classToProxy, classFileName);
+ }
+
+ /**
+ * Each of our interceptor/decorator proxies has exactly 1 constructor
+ * which invokes the super ct + sets the delegation field.
+ *
+ * //X TODO set delegation instance
+ *
+ * @param cw
+ * @param classToProxy
+ * @param classFileName
+ * @throws ProxyGenerationException
+ */
+ private void createConstructor(ClassWriter cw, String proxyClassFileName, Class<?> classToProxy, String classFileName)
throws ProxyGenerationException
{
try
@@ -104,8 +167,12 @@ public class InterceptorDecoratorProxyFa
final MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", descriptor, null, null);
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
-
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, classFileName, "<init>", descriptor);
+
+ mv.visitVarInsn(Opcodes.ALOAD, 0);
+ mv.visitInsn(Opcodes.ACONST_NULL);
+ mv.visitFieldInsn(Opcodes.PUTFIELD, proxyClassFileName, FIELD_PROXIED_INSTANCE, Type.getDescriptor(classToProxy));
+
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(-1, -1);
mv.visitEnd();
@@ -117,6 +184,134 @@ public class InterceptorDecoratorProxyFa
}
/**
+ * Create the private field to point to the internal contextual instance.
+ * @param cw
+ * @param classToProxy
+ * @param classFileName
+ */
+ private void createDelegationPoint(ClassWriter cw, Class<?> classToProxy, String classFileName)
+ {
+ cw.visitField(Opcodes.ACC_FINAL | Opcodes.ACC_PRIVATE, FIELD_PROXIED_INSTANCE, Type.getDescriptor(classToProxy), null, null).visitEnd();
+ }
+
+
+ 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())
+ {
+ final int modifiers = method.getModifiers();
+
+ if (Modifier.isFinal(modifiers) || Modifier.isPrivate(modifiers) ||
+ Modifier.isStatic(modifiers) || Modifier.isAbstract(modifiers) ||
+ Modifier.isNative(modifiers)) //X TODO deal with proxying native methods (clone) later
+ {
+ continue;
+ }
+
+ if ("finalize".equals(method.getName()))
+ {
+ // we do not proxy finalize()
+ 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(final List<Method> methods, final Method method)
+ {
+ for (final Method m : methods)
+ {
+ if (Arrays.equals(m.getParameterTypes(), method.getParameterTypes()))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Directly delegate all non intercepted nor decorated methods to the internal instance.
+ *
+ * @param noninterceptedMethods all methods which are neither intercepted nor decorated
+ */
+ private static void delegateNonInterceptedMethods(ClassWriter cw, String proxyClassFileName, Class<?> classToProxy, String classFileName,
+ Map<String, List<Method>> noninterceptedMethods)
+ {
+ for (List<Method> methodsPerName : noninterceptedMethods.values())
+ {
+ for (Method proxiedMethod : methodsPerName)
+ {
+ String methodDescriptor = Type.getMethodDescriptor(proxiedMethod);
+
+ //X TODO handle generic exception types?
+ Class[] exceptionTypes = proxiedMethod.getExceptionTypes();
+ String[] exceptionTypeNames = new String[exceptionTypes.length];
+ for (int i = 0; i < exceptionTypes.length; i++)
+ {
+ exceptionTypeNames[i] = Type.getType(exceptionTypes[i]).getInternalName();
+ }
+
+ MethodVisitor mv = cw.visitMethod(proxiedMethod.getModifiers(), proxiedMethod.getName(), methodDescriptor, null, exceptionTypeNames);
+
+ // fill method body
+ mv.visitCode();
+
+ // load the delegate variable
+ mv.visitVarInsn(Opcodes.ALOAD, 0);
+ mv.visitFieldInsn(Opcodes.GETFIELD, proxyClassFileName, FIELD_PROXIED_INSTANCE, Type.getDescriptor(classToProxy));
+
+ int offset = 1;
+ for (Class<?> aClass : proxiedMethod.getParameterTypes())
+ {
+ final Type type = Type.getType(aClass);
+ mv.visitVarInsn(type.getOpcode(Opcodes.ILOAD), offset);
+ offset += type.getSize();
+ }
+
+ final Type declaringClass = Type.getType(proxiedMethod.getDeclaringClass());
+ mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, declaringClass.getInternalName(), proxiedMethod.getName(), methodDescriptor);
+
+ final Type returnType = Type.getType(proxiedMethod.getReturnType());
+ mv.visitInsn(returnType.getOpcode(Opcodes.IRETURN));
+
+ mv.visitMaxs(-1, -1);
+
+ mv.visitEnd();
+
+ }
+ }
+ }
+
+
+ /**
* The 'defineClass' method on the ClassLoader is protected, thus we need to invoke it via reflection.
* @return the Class which got loaded in the classloader
*/
@@ -150,7 +345,15 @@ public class InterceptorDecoratorProxyFa
{
Class<T> definedClass = (Class<T>) defineClassMethod.invoke(classLoader, proxyName, proxyBytes, 0, proxyBytes.length);
- return definedClass;
+ try
+ {
+ Class<T> loadedClass = (Class<T>) classLoader.loadClass(definedClass.getName());
+ return loadedClass;
+ }
+ catch (ClassNotFoundException e)
+ {
+ throw new ProxyGenerationException(e);
+ }
}
catch (IllegalAccessException e)
{
Modified: openwebbeans/trunk/webbeans-impl/src/test/java/org/apache/webbeans/newtests/interceptors/factory/InterceptorDecoratorProxyFactoryTest.java
URL: http://svn.apache.org/viewvc/openwebbeans/trunk/webbeans-impl/src/test/java/org/apache/webbeans/newtests/interceptors/factory/InterceptorDecoratorProxyFactoryTest.java?rev=1427033&r1=1427032&r2=1427033&view=diff
==============================================================================
--- openwebbeans/trunk/webbeans-impl/src/test/java/org/apache/webbeans/newtests/interceptors/factory/InterceptorDecoratorProxyFactoryTest.java (original)
+++ openwebbeans/trunk/webbeans-impl/src/test/java/org/apache/webbeans/newtests/interceptors/factory/InterceptorDecoratorProxyFactoryTest.java Mon Dec 31 01:19:45 2012
@@ -18,6 +18,8 @@
*/
package org.apache.webbeans.newtests.interceptors.factory;
+import java.lang.reflect.Field;
+
import org.apache.webbeans.newtests.interceptors.factory.beans.ClassInterceptedClass;
import org.apache.webbeans.proxy.asm.InterceptorDecoratorProxyFactory;
@@ -41,12 +43,20 @@ public class InterceptorDecoratorProxyFa
Class<ClassInterceptedClass> proxyClass = pf.createInterceptorDecoratorProxyClass(classLoader, ClassInterceptedClass.class);
Assert.assertNotNull(proxyClass);
- ClassInterceptedClass instance = proxyClass.newInstance();
- Assert.assertNotNull(instance);
- Assert.assertTrue(instance.defaultCtInvoked);
+ ClassInterceptedClass proxy = pf.createProxyInstance(proxyClass, new ClassInterceptedClass());
+ Assert.assertNotNull(proxy);
+
+ // we need to get the field from the proxy via reflection
+ // otherwise we will end up seeing the proxied method on the internal state
+ Field field = proxy.getClass().getSuperclass().getDeclaredField("defaultCtInvoked");
+ Assert.assertNotNull(field);
+ field.setAccessible(true);
+
+ Boolean isDefaultCtInvoked = (Boolean) field.get(proxy);
+ Assert.assertTrue(isDefaultCtInvoked);
- instance.setMeaningOfLife(42);
+ proxy.setMeaningOfLife(42);
- Assert.assertEquals(42, instance.getMeaningOfLife());
+ Assert.assertEquals(42, proxy.getMeaningOfLife());
}
}
Modified: openwebbeans/trunk/webbeans-impl/src/test/java/org/apache/webbeans/newtests/interceptors/factory/beans/ClassInterceptedClass.java
URL: http://svn.apache.org/viewvc/openwebbeans/trunk/webbeans-impl/src/test/java/org/apache/webbeans/newtests/interceptors/factory/beans/ClassInterceptedClass.java?rev=1427033&r1=1427032&r2=1427033&view=diff
==============================================================================
--- openwebbeans/trunk/webbeans-impl/src/test/java/org/apache/webbeans/newtests/interceptors/factory/beans/ClassInterceptedClass.java (original)
+++ openwebbeans/trunk/webbeans-impl/src/test/java/org/apache/webbeans/newtests/interceptors/factory/beans/ClassInterceptedClass.java Mon Dec 31 01:19:45 2012
@@ -26,7 +26,7 @@ import org.apache.webbeans.test.componen
@Transactional
public class ClassInterceptedClass
{
- public boolean defaultCtInvoked = false;
+ private boolean defaultCtInvoked = false;
private int meaningOfLife;
@@ -37,6 +37,8 @@ public class ClassInterceptedClass
public int getMeaningOfLife()
{
+ System.out.println("answering the question about life, the universe and everything!");
+ System.out.println("and being in " + this.getClass());
return meaningOfLife;
}
@@ -44,4 +46,5 @@ public class ClassInterceptedClass
{
this.meaningOfLife = meaningOfLife;
}
+
}