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 2013/01/06 01:57:25 UTC
svn commit: r1429448 - in /openwebbeans/trunk/webbeans-impl/src:
main/java/org/apache/webbeans/proxy/
test/java/org/apache/webbeans/newtests/interceptors/factory/
test/java/org/apache/webbeans/newtests/interceptors/factory/beans/
test/java/org/apache/w...
Author: struberg
Date: Sun Jan 6 00:57:24 2013
New Revision: 1429448
URL: http://svn.apache.org/viewvc?rev=1429448&view=rev
Log:
OWB-344 create ASM proxy for NormalScoped beans
Added:
openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/proxy/NormalScopeProxyFactory.java
openwebbeans/trunk/webbeans-impl/src/test/java/org/apache/webbeans/newtests/interceptors/factory/NormalScopeProxyFactoryTest.java
Modified:
openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/proxy/AbstractProxyFactory.java
openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/proxy/InterceptorDecoratorProxyFactory.java
openwebbeans/trunk/webbeans-impl/src/test/java/org/apache/webbeans/newtests/interceptors/factory/beans/ClassInterceptedClass.java
openwebbeans/trunk/webbeans-impl/src/test/java/org/apache/webbeans/newtests/interceptors/resolution/InterceptorResolutionTest.java
Modified: openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/proxy/AbstractProxyFactory.java
URL: http://svn.apache.org/viewvc/openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/proxy/AbstractProxyFactory.java?rev=1429448&r1=1429447&r2=1429448&view=diff
==============================================================================
--- openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/proxy/AbstractProxyFactory.java (original)
+++ openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/proxy/AbstractProxyFactory.java Sun Jan 6 00:57:24 2013
@@ -32,7 +32,15 @@ import org.objectweb.asm.Type;
*/
public abstract class AbstractProxyFactory
{
+ /**
+ * @return the marker interface which should be used for this proxy.
+ * TODO this must be a list and for NormalScopeProxy we need add Serializable
+ */
+ protected abstract Class getMarkerInterface();
+ /**
+ * generate the bytecode for creating the instance variables of the class
+ */
protected abstract void createInstanceVariables(ClassWriter cw, Class<?> classToProxy, String classFileName);
/**
@@ -47,9 +55,15 @@ public abstract class AbstractProxyFacto
protected abstract void createConstructor(ClassWriter cw, String proxyClassFileName, Class<?> classToProxy, String classFileName)
throws ProxyGenerationException;
+ /**
+ * generate the bytecode for invoking all intercepted methods
+ */
protected abstract void delegateInterceptedMethods(ClassWriter cw, String proxyClassFileName, Class<?> classToProxy, Method[] interceptedMethods)
throws ProxyGenerationException;
+ /**
+ * generate the bytecode for invoking all non-intercepted methods
+ */
protected abstract void delegateNonInterceptedMethods(ClassWriter cw, String proxyClassFileName, Class<?> classToProxy, Method[] noninterceptedMethods)
throws ProxyGenerationException;
@@ -82,7 +96,7 @@ public abstract class AbstractProxyFacto
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
String classFileName = classToProxy.getName().replace('.', '/');
- String[] interfaceNames = new String[]{Type.getInternalName(OwbInterceptorProxy.class)};
+ String[] interfaceNames = new String[]{Type.getInternalName(getMarkerInterface())};
cw.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER + Opcodes.ACC_SYNTHETIC, proxyClassFileName, null, classFileName, interfaceNames);
cw.visitSource(classFileName + ".java", null);
@@ -107,6 +121,7 @@ public abstract class AbstractProxyFacto
+
/**
* 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
@@ -373,4 +388,55 @@ public abstract class AbstractProxyFacto
return Type.getInternalName(returnType);
}
}
+
+ /**
+ * 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
+ */
+ protected String getPrimitiveMethod(final 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");
+ }
+
+ protected void generateReturn(MethodVisitor mv, Method delegatedMethod)
+ {
+ final Class<?> returnType = delegatedMethod.getReturnType();
+ mv.visitInsn(getReturnInsn(returnType));
+ }
+
}
Modified: openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/proxy/InterceptorDecoratorProxyFactory.java
URL: http://svn.apache.org/viewvc/openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/proxy/InterceptorDecoratorProxyFactory.java?rev=1429448&r1=1429447&r2=1429448&view=diff
==============================================================================
--- openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/proxy/InterceptorDecoratorProxyFactory.java (original)
+++ openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/proxy/InterceptorDecoratorProxyFactory.java Sun Jan 6 00:57:24 2013
@@ -58,9 +58,6 @@ public class InterceptorDecoratorProxyFa
//X TODO add caching of created proxy classes. This is needed to prevent class loading clashes.
//X a generated proxy cannot easily get redefined later!
- public InterceptorDecoratorProxyFactory()
- {
- }
public <T> T createProxyInstance(Class<T> proxyClass, T instance, InterceptorHandler interceptorDecoratorStack)
throws ProxyGenerationException
@@ -163,7 +160,11 @@ public class InterceptorDecoratorProxyFa
return clazz;
}
-
+ @Override
+ protected Class getMarkerInterface()
+ {
+ return OwbInterceptorProxy.class;
+ }
protected void createInstanceVariables(ClassWriter cw, Class<?> classToProxy, String classFileName)
{
@@ -499,57 +500,6 @@ public class InterceptorDecoratorProxyFa
mv.visitEnd();
}
- /**
- * 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(final 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");
- }
-
-
-
- private void generateReturn(MethodVisitor mv, Method delegatedMethod)
- {
- final Class<?> returnType = delegatedMethod.getReturnType();
- mv.visitInsn(getReturnInsn(returnType));
- }
/**
* pushes an array of the specified size to the method visitor. The generated bytecode will leave
Added: openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/proxy/NormalScopeProxyFactory.java
URL: http://svn.apache.org/viewvc/openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/proxy/NormalScopeProxyFactory.java?rev=1429448&view=auto
==============================================================================
--- openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/proxy/NormalScopeProxyFactory.java (added)
+++ openwebbeans/trunk/webbeans-impl/src/main/java/org/apache/webbeans/proxy/NormalScopeProxyFactory.java Sun Jan 6 00:57:24 2013
@@ -0,0 +1,196 @@
+/*
+ * 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.webbeans.proxy;
+
+import javax.inject.Provider;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+/**
+ * This factory creates proxies which delegate the
+ * method invocations 1:1 to an instance which gets
+ * resolved via a {@link javax.inject.Provider}.
+ */
+public class NormalScopeProxyFactory extends AbstractProxyFactory
+{
+ /** the name of the field which stores the {@link Provider} for the Contextual Instance */
+ public static final String FIELD_INSTANCE_PROVIDER = "owbContextualInstanceProvider";
+
+
+ @Override
+ protected Class getMarkerInterface()
+ {
+ return OwbNormalScopeProxy.class;
+ }
+
+ /**
+ * @param classLoader to use for creating the class in
+ * @param classToProxy the class for which a subclass will get generated
+ * @param nonInterceptedMethods all methods which are <b>not</b> intercepted nor decorated and shall get delegated directly
+ * @param <T>
+ * @return the proxy class
+ */
+ public synchronized <T> Class<T> createProxyClass(ClassLoader classLoader, Class<T> classToProxy,
+ Method[] nonInterceptedMethods)
+ throws ProxyGenerationException
+ {
+ String proxyClassName = classToProxy.getName() + "$OwbNormalScopeProxy";
+
+ Class<T> clazz = createProxyClass(classLoader, proxyClassName, classToProxy, null, nonInterceptedMethods);
+
+ return clazz;
+ }
+
+ public <T> T createProxyInstance(Class<T> proxyClass, Provider provider)
+ throws ProxyGenerationException
+ {
+ try
+ {
+ T proxy = proxyClass.newInstance();
+
+ Field delegateField = proxy.getClass().getDeclaredField(FIELD_INSTANCE_PROVIDER);
+ delegateField.setAccessible(true);
+ delegateField.set(proxy, provider);
+
+ return proxy;
+ }
+ catch (InstantiationException e)
+ {
+ throw new ProxyGenerationException(e);
+ }
+ catch (IllegalAccessException e)
+ {
+ throw new ProxyGenerationException(e);
+ }
+ catch (NoSuchFieldException e)
+ {
+ throw new ProxyGenerationException(e);
+ }
+ }
+
+
+ @Override
+ protected void createConstructor(ClassWriter cw, String proxyClassFileName, Class<?> classToProxy, String classFileName) throws ProxyGenerationException
+ {
+ try
+ {
+ Constructor superDefaultCt = classToProxy.getConstructor(null);
+
+ final String descriptor = Type.getConstructorDescriptor(superDefaultCt);
+ 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_INSTANCE_PROVIDER, Type.getDescriptor(Provider.class));
+
+ mv.visitInsn(Opcodes.RETURN);
+ mv.visitMaxs(-1, -1);
+ mv.visitEnd();
+ }
+ catch (NoSuchMethodException e)
+ {
+ throw new ProxyGenerationException(e);
+ }
+ }
+
+ @Override
+ protected void createInstanceVariables(ClassWriter cw, Class<?> classToProxy, String classFileName)
+ {
+ // variable #1, the Provider<?> for the Contextual Instance
+ cw.visitField(Opcodes.ACC_PRIVATE,
+ FIELD_INSTANCE_PROVIDER, Type.getDescriptor(Provider.class), null, null).visitEnd();
+ }
+
+ @Override
+ protected void delegateInterceptedMethods(ClassWriter cw, String proxyClassFileName, Class<?> classToProxy, Method[] interceptedMethods) throws ProxyGenerationException
+ {
+ // nothing to do ;)
+ }
+
+ @Override
+ protected void delegateNonInterceptedMethods(ClassWriter cw, String proxyClassFileName, Class<?> classToProxy, Method[] noninterceptedMethods) throws ProxyGenerationException
+ {
+ for (Method delegatedMethod : noninterceptedMethods)
+ {
+ if (unproxyableMethod(delegatedMethod))
+ {
+ continue;
+ }
+
+ String methodDescriptor = Type.getMethodDescriptor(delegatedMethod);
+
+ //X TODO handle generic exception types?
+ Class[] exceptionTypes = delegatedMethod.getExceptionTypes();
+ String[] exceptionTypeNames = new String[exceptionTypes.length];
+ for (int i = 0; i < exceptionTypes.length; i++)
+ {
+ exceptionTypeNames[i] = Type.getType(exceptionTypes[i]).getInternalName();
+ }
+
+ int targetModifiers = delegatedMethod.getModifiers() & (Modifier.PROTECTED | Modifier.PUBLIC);
+
+ MethodVisitor mv = cw.visitMethod(targetModifiers, delegatedMethod.getName(), methodDescriptor, null, exceptionTypeNames);
+
+ // fill method body
+ mv.visitCode();
+
+ // load the contextual instance Provider
+ mv.visitVarInsn(Opcodes.ALOAD, 0);
+ mv.visitFieldInsn(Opcodes.GETFIELD, proxyClassFileName, FIELD_INSTANCE_PROVIDER, Type.getDescriptor(Provider.class));
+
+ // invoke the get() method on the Provider
+ mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, Type.getInternalName(Provider.class), "get", "()Ljava/lang/Object;");
+
+ // and convert the Object to the target class type
+ mv.visitTypeInsn(Opcodes.CHECKCAST, Type.getInternalName(classToProxy));
+
+
+ // now calculate the parameters
+ int offset = 1;
+ for (Class<?> aClass : delegatedMethod.getParameterTypes())
+ {
+ final Type type = Type.getType(aClass);
+ mv.visitVarInsn(type.getOpcode(Opcodes.ILOAD), offset);
+ offset += type.getSize();
+ }
+
+ // and finally invoke the target method on the provided Contextual Instance
+ final Type declaringClass = Type.getType(delegatedMethod.getDeclaringClass());
+ mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, declaringClass.getInternalName(), delegatedMethod.getName(), methodDescriptor);
+
+//X mv.visitInsn(Opcodes.POP);
+
+ generateReturn(mv, delegatedMethod);
+
+ mv.visitMaxs(-1, -1);
+
+ mv.visitEnd();
+ }
+ }
+}
Added: openwebbeans/trunk/webbeans-impl/src/test/java/org/apache/webbeans/newtests/interceptors/factory/NormalScopeProxyFactoryTest.java
URL: http://svn.apache.org/viewvc/openwebbeans/trunk/webbeans-impl/src/test/java/org/apache/webbeans/newtests/interceptors/factory/NormalScopeProxyFactoryTest.java?rev=1429448&view=auto
==============================================================================
--- openwebbeans/trunk/webbeans-impl/src/test/java/org/apache/webbeans/newtests/interceptors/factory/NormalScopeProxyFactoryTest.java (added)
+++ openwebbeans/trunk/webbeans-impl/src/test/java/org/apache/webbeans/newtests/interceptors/factory/NormalScopeProxyFactoryTest.java Sun Jan 6 00:57:24 2013
@@ -0,0 +1,115 @@
+/*
+ * 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.webbeans.newtests.interceptors.factory;
+
+import javax.inject.Provider;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.List;
+
+import org.apache.webbeans.newtests.AbstractUnitTest;
+import org.apache.webbeans.newtests.interceptors.factory.beans.ClassInterceptedClass;
+import org.apache.webbeans.proxy.NormalScopeProxyFactory;
+import org.apache.webbeans.util.ClassUtil;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Test for the {@link NormalScopeProxyFactory}
+ */
+public class NormalScopeProxyFactoryTest extends AbstractUnitTest
+{
+
+ @Test
+ public void textSimpleProxyCreation() throws Exception
+ {
+ NormalScopeProxyFactory pf = new NormalScopeProxyFactory();
+
+ // we take a fresh URLClassLoader to not blur the test classpath with synthetic classes.
+ ClassLoader classLoader = new URLClassLoader(new URL[0]);
+
+ List<Method> methods = ClassUtil.getNonPrivateMethods(ClassInterceptedClass.class);
+
+ Method[] nonInterceptedMethods = methods.toArray(new Method[methods.size()]);;
+ //X Method[] nonInterceptedMethods = new Method[]{methods.get(0)};
+
+ Class<ClassInterceptedClass> proxyClass = pf.createProxyClass(classLoader, ClassInterceptedClass.class, nonInterceptedMethods);
+ Assert.assertNotNull(proxyClass);
+
+ ClassInterceptedClass internalInstance = new ClassInterceptedClass();
+ internalInstance.init();
+
+ TestContextualInstanceProvider provider = new TestContextualInstanceProvider(internalInstance);
+
+ ClassInterceptedClass proxy = pf.createProxyInstance(proxyClass, provider);
+
+ Assert.assertEquals(42, proxy.getMeaningOfLife());
+ Assert.assertTrue(provider.gotInvoked());
+
+ Assert.assertEquals(internalInstance.getFloat(), proxy.getFloat(), 0f);
+ Assert.assertTrue(provider.gotInvoked());
+
+ Assert.assertEquals('c', proxy.getChar());
+ Assert.assertTrue(provider.gotInvoked());
+
+ Assert.assertEquals(internalInstance, proxy.getSelf());
+ Assert.assertTrue(provider.gotInvoked());
+
+ try
+ {
+ proxy.doThaBlowup();
+ Assert.fail("NumberFormatException expected!");
+ }
+ catch (NumberFormatException nfe)
+ {
+ Assert.assertEquals("should fit", nfe.getMessage());
+ }
+
+ }
+
+
+ public static class TestContextualInstanceProvider<T> implements Provider<T>
+ {
+ private T instance;
+ private boolean gotInvoked = false;
+
+ public TestContextualInstanceProvider(T instance)
+ {
+ this.instance = instance;
+ }
+
+ public boolean gotInvoked()
+ {
+ boolean invoked = gotInvoked;
+ gotInvoked = false;
+
+ return invoked;
+ }
+
+ @Override
+ public T get()
+ {
+ System.out.println("TestContextualInstanceProvider#get() got invoked!");
+ gotInvoked = true;
+
+ return instance;
+ }
+ }
+}
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=1429448&r1=1429447&r2=1429448&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 Sun Jan 6 00:57:24 2013
@@ -56,9 +56,10 @@ public class ClassInterceptedClass
{
f = 2.4f;
c = 'c';
+ meaningOfLife = 42;
}
- public int getMeaningOfLife()
+ public int getMeaningOfLife() throws NumberFormatException
{
System.out.println("answering the question about life, the universe and everything!");
System.out.println("and being in " + this.getClass());
@@ -85,4 +86,9 @@ public class ClassInterceptedClass
return c;
}
+ public String doThaBlowup() throws NumberFormatException
+ {
+ throw new NumberFormatException("should fit");
+ }
+
}
Modified: openwebbeans/trunk/webbeans-impl/src/test/java/org/apache/webbeans/newtests/interceptors/resolution/InterceptorResolutionTest.java
URL: http://svn.apache.org/viewvc/openwebbeans/trunk/webbeans-impl/src/test/java/org/apache/webbeans/newtests/interceptors/resolution/InterceptorResolutionTest.java?rev=1429448&r1=1429447&r2=1429448&view=diff
==============================================================================
--- openwebbeans/trunk/webbeans-impl/src/test/java/org/apache/webbeans/newtests/interceptors/resolution/InterceptorResolutionTest.java (original)
+++ openwebbeans/trunk/webbeans-impl/src/test/java/org/apache/webbeans/newtests/interceptors/resolution/InterceptorResolutionTest.java Sun Jan 6 00:57:24 2013
@@ -74,7 +74,7 @@ public class InterceptorResolutionTest
Map<Method, InterceptorResolution.MethodInterceptorInfo> methodInterceptorInfos = interceptorInfo.getBusinessMethodsInfo();
Assert.assertNotNull(methodInterceptorInfos);
- Assert.assertEquals(6, methodInterceptorInfos.size());
+ Assert.assertEquals(7, methodInterceptorInfos.size());
for (InterceptorResolution.MethodInterceptorInfo mi : methodInterceptorInfos.values())
{