Author: dblevins
Date: Sat Sep 21 10:32:17 2013
New Revision: 1525217

URL: http://svn.apache.org/r1525217
Log:
TOMEE-1040 - Abstract Dynamic Beans

Added:
    
tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/dyni/
    
tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/dyni/DynamicSubclass.java
   (with props)
    
tomee/tomee/trunk/container/openejb-core/src/test/java/org/apache/openejb/dyni/
    
tomee/tomee/trunk/container/openejb-core/src/test/java/org/apache/openejb/dyni/ColorsBefore.java
   (with props)
    
tomee/tomee/trunk/container/openejb-core/src/test/java/org/apache/openejb/dyni/DynamicSingletonTest.java
   (with props)
    
tomee/tomee/trunk/container/openejb-core/src/test/java/org/apache/openejb/dyni/DynamicStatefulTest.java
   (with props)
    
tomee/tomee/trunk/container/openejb-core/src/test/java/org/apache/openejb/dyni/DynamicStatelessTest.java
   (with props)
    
tomee/tomee/trunk/container/openejb-core/src/test/java/org/apache/openejb/dyni/DynamicSubclassTest.java
   (with props)
    
tomee/tomee/trunk/server/openejb-cxf-rs/src/test/java/org/apache/openejb/server/cxf/rs/DynamicSubclassEjbDeploymentTest.java
      - copied, changed from r1524933, 
tomee/tomee/trunk/server/openejb-cxf-rs/src/test/java/org/apache/openejb/server/cxf/rs/EjbDeploymentTest.java
Modified:
    
tomee/tomee/trunk/container/openejb-api/src/main/java/org/apache/openejb/api/Proxy.java
    
tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/EnterpriseBeanBuilder.java
    
tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/config/AnnotationDeployer.java
    
tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/config/rules/CheckClasses.java
    
tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/util/proxy/LocalBeanProxyFactory.java

Modified: 
tomee/tomee/trunk/container/openejb-api/src/main/java/org/apache/openejb/api/Proxy.java
URL: 
http://svn.apache.org/viewvc/tomee/tomee/trunk/container/openejb-api/src/main/java/org/apache/openejb/api/Proxy.java?rev=1525217&r1=1525216&r2=1525217&view=diff
==============================================================================
--- 
tomee/tomee/trunk/container/openejb-api/src/main/java/org/apache/openejb/api/Proxy.java
 (original)
+++ 
tomee/tomee/trunk/container/openejb-api/src/main/java/org/apache/openejb/api/Proxy.java
 Sat Sep 21 10:32:17 2013
@@ -20,11 +20,12 @@ import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
+import java.lang.reflect.InvocationHandler;
 
 @Target({ElementType.TYPE, ElementType.METHOD})
 @Retention(RetentionPolicy.RUNTIME)
 public @interface Proxy {
 
-    Class<?> value();
+    Class<? extends InvocationHandler> value();
 
 }

Modified: 
tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/EnterpriseBeanBuilder.java
URL: 
http://svn.apache.org/viewvc/tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/EnterpriseBeanBuilder.java?rev=1525217&r1=1525216&r2=1525217&view=diff
==============================================================================
--- 
tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/EnterpriseBeanBuilder.java
 (original)
+++ 
tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/EnterpriseBeanBuilder.java
 Sat Sep 21 10:32:17 2013
@@ -22,6 +22,7 @@ import org.apache.openejb.Injection;
 import org.apache.openejb.ModuleContext;
 import org.apache.openejb.OpenEJBException;
 import org.apache.openejb.core.cmp.CmpUtil;
+import org.apache.openejb.dyni.DynamicSubclass;
 import org.apache.openejb.loader.SystemInstance;
 import org.apache.openejb.spi.ContainerSystem;
 import org.apache.openejb.util.Duration;
@@ -78,6 +79,10 @@ class EnterpriseBeanBuilder {
     public BeanContext build() throws OpenEJBException {
         Class ejbClass = loadClass(bean.ejbClass, "classNotFound.ejbClass");
 
+        if (DynamicSubclass.isDynamic(ejbClass)) {
+            ejbClass = DynamicSubclass.createSubclass(ejbClass, 
moduleContext.getClassLoader());
+        }
+
         Class home = null;
         Class remote = null;
         if (bean.home != null) {

Modified: 
tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/config/AnnotationDeployer.java
URL: 
http://svn.apache.org/viewvc/tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/config/AnnotationDeployer.java?rev=1525217&r1=1525216&r2=1525217&view=diff
==============================================================================
--- 
tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/config/AnnotationDeployer.java
 (original)
+++ 
tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/config/AnnotationDeployer.java
 Sat Sep 21 10:32:17 2013
@@ -22,8 +22,10 @@ import org.apache.openejb.api.LocalClien
 import org.apache.openejb.api.Proxy;
 import org.apache.openejb.api.RemoteClient;
 import org.apache.openejb.cdi.CdiBeanInfo;
+import org.apache.openejb.config.rules.CheckClasses;
 import org.apache.openejb.core.EmptyResourcesClassLoader;
 import org.apache.openejb.core.webservices.JaxWsUtils;
+import org.apache.openejb.dyni.DynamicSubclass;
 import org.apache.openejb.jee.ActivationConfig;
 import org.apache.openejb.jee.ActivationSpec;
 import org.apache.openejb.jee.AdminObject;
@@ -216,6 +218,7 @@ import java.io.IOException;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
+import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.net.MalformedURLException;
@@ -1736,17 +1739,16 @@ public class AnnotationDeployer implemen
                 }
             }
 
-            // not a dynamic proxy implemented bean
-            if (beanClass.getAnnotation(PersistenceContext.class) == null
-                    && beanClass.getAnnotation(Proxy.class) == null
-                    && beanClass.get().isInterface()) {
-                ejbModule.getValidation().fail(ejbName, 
"interfaceAnnotatedAsBean", annotationClass.getSimpleName(), 
beanClass.get().getName());
-                return false;
-            }
-
-            if (!beanClass.get().isInterface() && 
isAbstract(beanClass.get().getModifiers())) {
-                ejbModule.getValidation().fail(ejbName, 
"abstractAnnotatedAsBean", annotationClass.getSimpleName(), 
beanClass.get().getName());
-                return false;
+            if (beanClass.get().isInterface()) {
+                if (!CheckClasses.isAbstractAllowed(beanClass.get())) {
+                    ejbModule.getValidation().fail(ejbName, 
"interfaceAnnotatedAsBean", annotationClass.getSimpleName(), 
beanClass.get().getName());
+                    return false;
+                }
+            } else if (isAbstract(beanClass.get().getModifiers())) {
+                if (!CheckClasses.isAbstractAllowed(beanClass.get())) {
+                    ejbModule.getValidation().fail(ejbName, 
"abstractAnnotatedAsBean", annotationClass.getSimpleName(), 
beanClass.get().getName());
+                    return false;
+                }
             }
 
             return b;
@@ -2890,6 +2892,7 @@ public class AnnotationDeployer implemen
                                 !name.equals("groovy.lang.GroovyObject") &&
                                 !name.equals("java.io.Serializable") &&
                                 !name.equals("java.io.Externalizable") &&
+                                
!(name.equals(InvocationHandler.class.getName()) && 
DynamicSubclass.isDynamic(beanClass)) &&
                                 !name.startsWith("javax.ejb.") &&
                                 !descriptor.contains(interfce.getName()) &&
                                 !interfce.isSynthetic() &&

Modified: 
tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/config/rules/CheckClasses.java
URL: 
http://svn.apache.org/viewvc/tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/config/rules/CheckClasses.java?rev=1525217&r1=1525216&r2=1525217&view=diff
==============================================================================
--- 
tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/config/rules/CheckClasses.java
 (original)
+++ 
tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/config/rules/CheckClasses.java
 Sat Sep 21 10:32:17 2013
@@ -19,6 +19,7 @@ package org.apache.openejb.config.rules;
 import org.apache.openejb.OpenEJBException;
 import org.apache.openejb.OpenEJBRuntimeException;
 import org.apache.openejb.config.EjbModule;
+import org.apache.openejb.dyni.DynamicSubclass;
 import org.apache.openejb.jee.EnterpriseBean;
 import org.apache.openejb.jee.EntityBean;
 import org.apache.openejb.jee.Interceptor;
@@ -230,13 +231,19 @@ public class CheckClasses extends Valida
 
         if (isCmp(b)) return beanClass;
 
-        if (isAbstract(beanClass.getModifiers()) && !isDynamicProxyImpl){
+        if (isAbstract(beanClass.getModifiers()) && 
!isAbstractAllowed(beanClass)){
             fail(ejbName, "abstractDeclaredAsBean", beanClass.getName());
         }
 
         return beanClass;
     }
 
+    public static boolean isAbstractAllowed(Class clazz) {
+        if (DynamicProxyImplFactory.isKnownDynamicallyImplemented(clazz)) 
return true;
+        if (DynamicSubclass.isDynamic(clazz)) return true;
+        return false;
+    }
+
     private void check_hasInterceptorClass(Interceptor i) {
 
         lookForClass(i.getInterceptorClass(), "interceptor-class", 
"Interceptor");

Added: 
tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/dyni/DynamicSubclass.java
URL: 
http://svn.apache.org/viewvc/tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/dyni/DynamicSubclass.java?rev=1525217&view=auto
==============================================================================
--- 
tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/dyni/DynamicSubclass.java
 (added)
+++ 
tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/dyni/DynamicSubclass.java
 Sat Sep 21 10:32:17 2013
@@ -0,0 +1,321 @@
+/*
+ * 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.dyni;
+
+import org.apache.openejb.loader.IO;
+import org.apache.openejb.util.Debug;
+import org.apache.openejb.util.proxy.LocalBeanProxyFactory;
+import org.apache.openejb.util.proxy.ProxyGenerationException;
+import org.apache.xbean.asm4.AnnotationVisitor;
+import org.apache.xbean.asm4.ClassReader;
+import org.apache.xbean.asm4.ClassVisitor;
+import org.apache.xbean.asm4.ClassWriter;
+import org.apache.xbean.asm4.MethodVisitor;
+import org.apache.xbean.asm4.Opcodes;
+import org.apache.xbean.asm4.Type;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class DynamicSubclass implements Opcodes {
+
+    private static final ReentrantLock LOCK = new ReentrantLock();
+
+    public static boolean isDynamic(Class beanClass) {
+        return Modifier.isAbstract(beanClass.getModifiers()) && 
InvocationHandler.class.isAssignableFrom(beanClass);
+    }
+
+    public static Class createSubclass(final Class<?> abstractClass, final 
ClassLoader cl) {
+        final String proxyName = getSubclassName(abstractClass);
+
+        try {
+            return cl.loadClass(proxyName);
+        } catch (Exception e) {
+            // no-op
+        }
+
+        final ReentrantLock lock = LOCK;
+        lock.lock();
+
+        try {
+
+            try { // Try it again, another thread may have beaten this one...
+                return cl.loadClass(proxyName);
+            } catch (Exception e) {
+                // no-op
+            }
+
+            final byte[] bytes = generateBytes(abstractClass);
+            return LocalBeanProxyFactory.Unsafe.defineClass(abstractClass, 
proxyName, bytes);
+
+        } catch (Exception e) {
+            throw new InternalError(DynamicSubclass.class.getSimpleName() + 
".createSubclass: " + Debug.printStackTrace(e));
+        } finally {
+            lock.unlock();
+        }
+    }
+
+    private static byte[] generateBytes(final Class<?> classToProxy) throws 
ProxyGenerationException {
+
+        final Map<String, MethodVisitor> visitors = new HashMap<String, 
MethodVisitor>();
+
+        final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
+
+        final String proxyClassFileName = 
getSubclassName(classToProxy).replace('.', '/');
+        final String classFileName = classToProxy.getName().replace('.', '/');
+
+        cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, proxyClassFileName, null, 
classFileName, null);
+        cw.visitSource(classFileName + ".java", null);
+
+
+        // push InvocationHandler field
+        cw.visitField(ACC_FINAL + ACC_PRIVATE, "this$handler", 
"Ljava/lang/reflect/InvocationHandler;", null, null).visitEnd();
+
+        for (Constructor<?> constructor : classToProxy.getConstructors()) {
+            if (!Modifier.isPublic(constructor.getModifiers())) continue;
+
+            final MethodVisitor mv = visitConstructor(cw, proxyClassFileName, 
classFileName, constructor);
+            visitors.put("<init>" + 
Type.getConstructorDescriptor(constructor), mv);
+        }
+
+        final Map<String, List<Method>> methodMap = new HashMap<String, 
List<Method>>();
+
+        getNonPrivateMethods(classToProxy, methodMap);
+
+        // Iterate over the public methods
+        for (final Map.Entry<String, List<Method>> entry : 
methodMap.entrySet()) {
+
+            for (final Method method : entry.getValue()) {
+                if (Modifier.isAbstract(method.getModifiers())) {
+                    final MethodVisitor visitor = 
LocalBeanProxyFactory.visit(cw, method, proxyClassFileName, "this$handler");
+                    visitors.put(method.getName() + 
Type.getMethodDescriptor(method), visitor);
+                }
+            }
+        }
+
+        copyClassAnnotations(classToProxy, cw);
+
+        copyMethodAnnotations(classToProxy, visitors);
+
+        // This should never be reached, but just in case
+        for (MethodVisitor visitor : visitors.values()) {
+            visitor.visitEnd();
+        }
+
+        return cw.toByteArray();
+    }
+
+    private static MethodVisitor visitConstructor(ClassWriter cw, String 
proxyClassFileName, String classFileName, Constructor<?> constructor) {
+        final String descriptor = Type.getConstructorDescriptor(constructor);
+
+        final String[] exceptions = new 
String[constructor.getExceptionTypes().length];
+        for (int i = 0; i < exceptions.length; i++) {
+            exceptions[i] = 
Type.getInternalName(constructor.getExceptionTypes()[i]);
+        }
+
+        final MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", 
descriptor, null, exceptions);
+        mv.visitCode();
+        mv.visitVarInsn(ALOAD, 0);
+        int index = 1;
+
+        for (Type type : Type.getArgumentTypes(descriptor)) {
+            mv.visitVarInsn(type.getOpcode(ILOAD), index);
+            index += size(type);
+        }
+
+        mv.visitMethodInsn(INVOKESPECIAL, classFileName, "<init>", descriptor);
+
+        mv.visitVarInsn(ALOAD, 0);
+        mv.visitVarInsn(ALOAD, 0);
+        mv.visitFieldInsn(PUTFIELD, proxyClassFileName, "this$handler", 
"Ljava/lang/reflect/InvocationHandler;");
+        mv.visitInsn(RETURN);
+        mv.visitMaxs(2, 1);
+        return mv;
+    }
+
+    private static String getSubclassName(Class<?> classToProxy) {
+        return classToProxy.getName() + "$$Impl";
+    }
+
+    private static void getNonPrivateMethods(Class<?> clazz, final Map<String, 
List<Method>> methodMap) {
+        while (clazz != null) {
+            for (final Method method : clazz.getDeclaredMethods()) {
+                final 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();
+        }
+    }
+
+    private static 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;
+    }
+
+    public static int size(Type type) {
+        if (Type.VOID_TYPE.equals(type)) return 0;
+        if (Type.LONG_TYPE.equals(type) || Type.DOUBLE_TYPE.equals(type)) 
return 2;
+        return 1;
+    }
+
+    public static byte[] readClassFile(Class clazz) throws IOException {
+        return readClassFile(clazz.getClassLoader(), clazz);
+    }
+
+    public static byte[] readClassFile(ClassLoader classLoader, Class clazz) 
throws IOException {
+        final String internalName = clazz.getName().replace('.', '/') + 
".class";
+        final URL resource = classLoader.getResource(internalName);
+
+        final InputStream in = IO.read(resource);
+        final ByteArrayOutputStream out;
+        try {
+            out = new ByteArrayOutputStream();
+            IO.copy(in, out);
+        } finally {
+            IO.close(in);
+        }
+
+        return out.toByteArray();
+    }
+
+    private static void copyMethodAnnotations(Class<?> classToProxy, 
Map<String, MethodVisitor> visitors) throws ProxyGenerationException {
+        // Move all the annotations onto the newly implemented methods
+        // Ensures CDI and JAX-RS and JAX-WS still work
+        Class clazz = classToProxy;
+        while (clazz != null && !clazz.equals(Object.class)) {
+            try {
+                final ClassReader classReader = new 
ClassReader(readClassFile(clazz));
+                final ClassVisitor copyMethodAnnotations = new 
CopyMethodAnnotations(visitors);
+                classReader.accept(copyMethodAnnotations, 
ClassReader.SKIP_CODE);
+            } catch (IOException e) {
+                throw new ProxyGenerationException(e);
+            }
+            clazz = clazz.getSuperclass();
+        }
+    }
+
+    private static void copyClassAnnotations(Class<?> clazz, final 
ClassVisitor newClass) throws ProxyGenerationException {
+        try {
+            final ClassReader classReader = new 
ClassReader(readClassFile(clazz));
+            final ClassVisitor visitor = new CopyClassAnnotations(newClass);
+            classReader.accept(visitor, ClassReader.SKIP_CODE);
+        } catch (IOException e) {
+            throw new ProxyGenerationException(e);
+        }
+    }
+
+
+    public static class MoveAnnotationsVisitor extends MethodVisitor {
+
+        private MethodVisitor newMethod;
+
+        public MoveAnnotationsVisitor(MethodVisitor movedMethod, MethodVisitor 
newMethod) {
+            super(Opcodes.ASM4, movedMethod);
+            this.newMethod = newMethod;
+        }
+
+        @Override
+        public AnnotationVisitor visitAnnotation(String desc, boolean visible) 
{
+            return newMethod.visitAnnotation(desc, visible);
+        }
+
+        @Override
+        public AnnotationVisitor visitParameterAnnotation(int parameter, 
String desc, boolean visible) {
+            return newMethod.visitParameterAnnotation(parameter, desc, 
visible);
+        }
+
+        @Override
+        public void visitEnd() {
+            newMethod.visitEnd();
+            super.visitEnd();
+        }
+    }
+
+
+    private static class CopyClassAnnotations extends ClassVisitor {
+        private final ClassVisitor newClass;
+
+        public CopyClassAnnotations(ClassVisitor newClass) {
+            super(Opcodes.ASM4);
+            this.newClass = newClass;
+        }
+
+        @Override
+        public AnnotationVisitor visitAnnotation(String desc, boolean visible) 
{
+            return newClass.visitAnnotation(desc, visible);
+        }
+    }
+
+    private static class CopyMethodAnnotations extends ClassVisitor {
+        private final Map<String, MethodVisitor> visitors;
+
+        public CopyMethodAnnotations(Map<String, MethodVisitor> visitors) {
+            super(Opcodes.ASM4);
+            this.visitors = visitors;
+        }
+
+        @Override
+        public MethodVisitor visitMethod(int access, String name, String desc, 
String signature, String[] exceptions) {
+            final MethodVisitor newMethod = visitors.remove(name + desc);
+
+            if (newMethod == null) return null;
+
+            final MethodVisitor oldMethod = super.visitMethod(access, name, 
desc, signature, exceptions);
+
+            return new MoveAnnotationsVisitor(oldMethod, newMethod);
+        }
+    }
+}

Propchange: 
tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/dyni/DynamicSubclass.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: 
tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/util/proxy/LocalBeanProxyFactory.java
URL: 
http://svn.apache.org/viewvc/tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/util/proxy/LocalBeanProxyFactory.java?rev=1525217&r1=1525216&r2=1525217&view=diff
==============================================================================
--- 
tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/util/proxy/LocalBeanProxyFactory.java
 (original)
+++ 
tomee/tomee/trunk/container/openejb-core/src/main/java/org/apache/openejb/util/proxy/LocalBeanProxyFactory.java
 Sat Sep 21 10:32:17 2013
@@ -241,11 +241,15 @@ public class LocalBeanProxyFactory imple
         return false;
     }
 
-    private static void processMethod(final ClassWriter cw, final Method 
method, final String proxyName, final String handlerName) throws 
ProxyGenerationException {
+    public static void processMethod(final ClassWriter cw, final Method 
method, final String proxyName, final String handlerName) throws 
ProxyGenerationException {
         if ("<init>".equals(method.getName())) {
             return;
         }
 
+        visit(cw, method, proxyName, handlerName).visitEnd();
+    }
+
+    public static MethodVisitor visit(ClassWriter cw, Method method, String 
proxyName, String handlerName) throws ProxyGenerationException {
         final Class<?> returnType = method.getReturnType();
         final Class<?>[] parameterTypes = method.getParameterTypes();
         final Class<?>[] exceptionTypes = method.getExceptionTypes();
@@ -431,7 +435,7 @@ public class LocalBeanProxyFactory imple
 
         // finish this method
         mv.visitMaxs(0, 0);
-        mv.visitEnd();
+        return mv;
     }
 
     /**
@@ -694,7 +698,7 @@ public class LocalBeanProxyFactory imple
     /**
      * The methods of this class model sun.misc.Unsafe which is used 
reflectively
      */
-    private static class Unsafe {
+    public static class Unsafe {
 
         // sun.misc.Unsafe
         private static final Object unsafe;
@@ -812,7 +816,7 @@ public class LocalBeanProxyFactory imple
             }
         }
 
-        private static Class defineClass(final Class<?> clsToProxy, final 
String proxyName, final byte[] proxyBytes) throws IllegalAccessException, 
InvocationTargetException {
+        public static Class defineClass(final Class<?> clsToProxy, final 
String proxyName, final byte[] proxyBytes) throws IllegalAccessException, 
InvocationTargetException {
             return (Class<?>) defineClass.invoke(unsafe, proxyName, 
proxyBytes, 0, proxyBytes.length, clsToProxy.getClassLoader(), 
clsToProxy.getProtectionDomain());
         }
     }

Added: 
tomee/tomee/trunk/container/openejb-core/src/test/java/org/apache/openejb/dyni/ColorsBefore.java
URL: 
http://svn.apache.org/viewvc/tomee/tomee/trunk/container/openejb-core/src/test/java/org/apache/openejb/dyni/ColorsBefore.java?rev=1525217&view=auto
==============================================================================
--- 
tomee/tomee/trunk/container/openejb-core/src/test/java/org/apache/openejb/dyni/ColorsBefore.java
 (added)
+++ 
tomee/tomee/trunk/container/openejb-core/src/test/java/org/apache/openejb/dyni/ColorsBefore.java
 Sat Sep 21 10:32:17 2013
@@ -0,0 +1,23 @@
+/*
+ * 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.dyni;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class ColorsBefore {
+}

Propchange: 
tomee/tomee/trunk/container/openejb-core/src/test/java/org/apache/openejb/dyni/ColorsBefore.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
tomee/tomee/trunk/container/openejb-core/src/test/java/org/apache/openejb/dyni/DynamicSingletonTest.java
URL: 
http://svn.apache.org/viewvc/tomee/tomee/trunk/container/openejb-core/src/test/java/org/apache/openejb/dyni/DynamicSingletonTest.java?rev=1525217&view=auto
==============================================================================
--- 
tomee/tomee/trunk/container/openejb-core/src/test/java/org/apache/openejb/dyni/DynamicSingletonTest.java
 (added)
+++ 
tomee/tomee/trunk/container/openejb-core/src/test/java/org/apache/openejb/dyni/DynamicSingletonTest.java
 Sat Sep 21 10:32:17 2013
@@ -0,0 +1,72 @@
+/*
+ * 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.dyni;
+
+import org.apache.openejb.junit.ApplicationComposer;
+import org.apache.openejb.testing.Module;
+import org.apache.openejb.util.Join;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.ejb.EJB;
+import javax.ejb.Singleton;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+
+
+/**
+ * @version $Rev$ $Date$
+ */
+@RunWith(ApplicationComposer.class)
+public class DynamicSingletonTest extends Assert {
+
+    @EJB
+    private Colors colors;
+
+    @Module
+    public Class[] dynamic() {
+        return new Class[]{Colors.class};
+    }
+
+    @Test
+    public void test() throws Exception {
+
+        assertNotNull(colors);
+        assertEquals("red", colors.red());
+        assertEquals("handle:blue(hello)", colors.blue("hello"));
+        assertEquals("handle:green()", colors.green());
+    }
+
+
+    @Singleton
+    public static abstract class Colors implements InvocationHandler {
+
+        public String red() {
+            return "red";
+        }
+
+        public abstract String green();
+
+        public abstract String blue(String s);
+
+        @Override
+        public Object invoke(Object proxy, Method method, Object[] args) 
throws Throwable {
+            return "handle:" + method.getName() + "(" + Join.join(",", args) + 
")";
+        }
+    }
+}

Propchange: 
tomee/tomee/trunk/container/openejb-core/src/test/java/org/apache/openejb/dyni/DynamicSingletonTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
tomee/tomee/trunk/container/openejb-core/src/test/java/org/apache/openejb/dyni/DynamicStatefulTest.java
URL: 
http://svn.apache.org/viewvc/tomee/tomee/trunk/container/openejb-core/src/test/java/org/apache/openejb/dyni/DynamicStatefulTest.java?rev=1525217&view=auto
==============================================================================
--- 
tomee/tomee/trunk/container/openejb-core/src/test/java/org/apache/openejb/dyni/DynamicStatefulTest.java
 (added)
+++ 
tomee/tomee/trunk/container/openejb-core/src/test/java/org/apache/openejb/dyni/DynamicStatefulTest.java
 Sat Sep 21 10:32:17 2013
@@ -0,0 +1,72 @@
+/*
+ * 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.dyni;
+
+import org.apache.openejb.junit.ApplicationComposer;
+import org.apache.openejb.testing.Module;
+import org.apache.openejb.util.Join;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.ejb.EJB;
+import javax.ejb.Stateful;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+
+
+/**
+ * @version $Rev$ $Date$
+ */
+@RunWith(ApplicationComposer.class)
+public class DynamicStatefulTest extends Assert {
+
+    @EJB
+    private Colors colors;
+
+    @Module
+    public Class[] dynamic() {
+        return new Class[]{Colors.class};
+    }
+
+    @Test
+    public void test() throws Exception {
+
+        assertNotNull(colors);
+        assertEquals("red", colors.red());
+        assertEquals("handle:blue(hello)", colors.blue("hello"));
+        assertEquals("handle:green()", colors.green());
+    }
+
+
+    @Stateful
+    public static abstract class Colors implements InvocationHandler {
+
+        public String red() {
+            return "red";
+        }
+
+        public abstract String green();
+
+        public abstract String blue(String s);
+
+        @Override
+        public Object invoke(Object proxy, Method method, Object[] args) 
throws Throwable {
+            return "handle:" + method.getName() + "(" + Join.join(",", args) + 
")";
+        }
+    }
+}

Propchange: 
tomee/tomee/trunk/container/openejb-core/src/test/java/org/apache/openejb/dyni/DynamicStatefulTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
tomee/tomee/trunk/container/openejb-core/src/test/java/org/apache/openejb/dyni/DynamicStatelessTest.java
URL: 
http://svn.apache.org/viewvc/tomee/tomee/trunk/container/openejb-core/src/test/java/org/apache/openejb/dyni/DynamicStatelessTest.java?rev=1525217&view=auto
==============================================================================
--- 
tomee/tomee/trunk/container/openejb-core/src/test/java/org/apache/openejb/dyni/DynamicStatelessTest.java
 (added)
+++ 
tomee/tomee/trunk/container/openejb-core/src/test/java/org/apache/openejb/dyni/DynamicStatelessTest.java
 Sat Sep 21 10:32:17 2013
@@ -0,0 +1,73 @@
+/*
+ * 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.dyni;
+
+import org.apache.openejb.junit.ApplicationComposer;
+import org.apache.openejb.testing.Module;
+import org.apache.openejb.util.Join;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import javax.ejb.EJB;
+import javax.ejb.Singleton;
+import javax.ejb.Stateless;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+
+
+/**
+ * @version $Rev$ $Date$
+ */
+@RunWith(ApplicationComposer.class)
+public class DynamicStatelessTest extends Assert {
+
+    @EJB
+    private Colors colors;
+
+    @Module
+    public Class[] dynamic() {
+        return new Class[]{Colors.class};
+    }
+
+    @Test
+    public void test() throws Exception {
+
+        assertNotNull(colors);
+        assertEquals("red", colors.red());
+        assertEquals("handle:blue(hello)", colors.blue("hello"));
+        assertEquals("handle:green()", colors.green());
+    }
+
+
+    @Stateless
+    public static abstract class Colors implements InvocationHandler {
+
+        public String red() {
+            return "red";
+        }
+
+        public abstract String green();
+
+        public abstract String blue(String s);
+
+        @Override
+        public Object invoke(Object proxy, Method method, Object[] args) 
throws Throwable {
+            return "handle:" + method.getName() + "(" + Join.join(",", args) + 
")";
+        }
+    }
+}

Propchange: 
tomee/tomee/trunk/container/openejb-core/src/test/java/org/apache/openejb/dyni/DynamicStatelessTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
tomee/tomee/trunk/container/openejb-core/src/test/java/org/apache/openejb/dyni/DynamicSubclassTest.java
URL: 
http://svn.apache.org/viewvc/tomee/tomee/trunk/container/openejb-core/src/test/java/org/apache/openejb/dyni/DynamicSubclassTest.java?rev=1525217&view=auto
==============================================================================
--- 
tomee/tomee/trunk/container/openejb-core/src/test/java/org/apache/openejb/dyni/DynamicSubclassTest.java
 (added)
+++ 
tomee/tomee/trunk/container/openejb-core/src/test/java/org/apache/openejb/dyni/DynamicSubclassTest.java
 Sat Sep 21 10:32:17 2013
@@ -0,0 +1,205 @@
+/*
+ * 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.dyni;
+
+import org.apache.openejb.util.Join;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLClassLoader;
+
+/**
+ * Implementation of methods and constructors:
+ *
+ *  - Ensure all abstract methods were implemented to delegate to the 
InvocationHandler method
+ *  - Ensure all constructors were carried forward to subclass
+ *
+ * Preservation of annotations
+ *
+ *  - Ensure all annotations from the immediate parent class were copied
+ *  - Ensure all annotations from parent constructors and constructor params 
were copied
+ *  - Ensure all annotations from abstract ancestor methods and method params 
were copied
+ *
+ * @version $Rev$ $Date$
+ */
+public class DynamicSubclassTest extends Assert {
+
+    private static Invocation invocation;
+
+    @Test
+    public void test() throws Exception {
+        final URLClassLoader loader = new URLClassLoader(new URL[0]);
+
+        final Class subclass = DynamicSubclass.createSubclass(Blue.class, 
loader);
+
+        final Constructor constructor = subclass.getConstructor(long.class);
+        final Blue blue = (Blue) constructor.newInstance(1l);
+
+        final Class<?> generatedClass = blue.getClass();
+        assertNotEquals(Blue.class, generatedClass);
+
+        // Were class, constructor and constructor parameter annotations 
copied?
+        {
+            // class annotations?
+            assertEquals("blue", 
generatedClass.getAnnotation(Circle.class).value());
+
+            // constructor annotations?
+            assertEquals("blue()", 
generatedClass.getConstructor().getAnnotation(Oval.class).value());
+            assertEquals("blue(long)", 
generatedClass.getConstructor(long.class).getAnnotation(Oval.class).value());
+
+            // constructor parameter annotations?
+            final Annotation annotation = 
generatedClass.getConstructor(long.class).getParameterAnnotations()[0][0];
+            assertEquals("1", ((Triangle) annotation).value());
+        }
+
+        { // blue method
+            blue.blue(1);
+            assertNotNull(invocation);
+            final Method method = Blue.class.getDeclaredMethod("blue", 
int.class);
+            assertEquals(invocation.getMethod(), method);
+            assertEquals("1", Join.join(",", invocation.args));
+            assertEquals("blue", method.getAnnotation(Square.class).value());
+            assertEquals("blue", ((Triangle) 
method.getParameterAnnotations()[0][0]).value());
+        }
+
+        { // green method
+            blue.green("hello");
+            assertNotNull(invocation);
+            final Method method = Green.class.getDeclaredMethod("green", 
String.class);
+            assertEquals(invocation.getMethod(), method);
+            assertEquals("hello", Join.join(",", invocation.args));
+            assertEquals("green", method.getAnnotation(Square.class).value());
+            assertEquals("green", ((Triangle) 
method.getParameterAnnotations()[0][0]).value());
+        }
+
+        { // blue method
+            blue.red(URI.create("foo://bar"));
+            assertNotNull(invocation);
+            final Method method = Red.class.getDeclaredMethod("red", 
URI.class);
+            assertEquals(invocation.getMethod(), method);
+            assertEquals("foo://bar", Join.join(",", invocation.getArgs()));
+            assertEquals("red", method.getAnnotation(Square.class).value());
+            assertEquals("red", ((Triangle) 
method.getParameterAnnotations()[0][0]).value());
+        }
+    }
+
+    public static class Invocation {
+        private final Object proxy;
+        private final Method method;
+        private final Object[] args;
+
+        public Invocation(Object proxy, Method method, Object[] args) {
+            this.proxy = proxy;
+            this.method = method;
+            this.args = args;
+        }
+
+        public Object getProxy() {
+            return proxy;
+        }
+
+        public Method getMethod() {
+            return method;
+        }
+
+        public Object[] getArgs() {
+            return args;
+        }
+    }
+
+    public static abstract class Color implements InvocationHandler {
+
+        public Color() {
+        }
+
+        public Color(URI uri, long foo) {
+        }
+
+        // TODO: check to ensure this method is implemented, issue validation 
failure if not
+        @Override
+        public Object invoke(Object proxy, Method method, Object[] args) 
throws Throwable {
+            invocation = new Invocation(proxy, method, args);
+            return null;
+        }
+    }
+
+    @Circle("red")
+    public static abstract class Red extends Color {
+
+        @Square("red")
+        public abstract void red(@Triangle("red") URI uri);
+    }
+
+    @Circle("green")
+    public static abstract class Green extends Red {
+
+        @Square("green")
+        public abstract void green(@Triangle("green") String v);
+    }
+
+    @Circle("blue")
+    public static abstract class Blue extends Green {
+
+        @Oval("blue()")
+        public Blue() {
+        }
+
+        @Oval("blue(long)")
+        public Blue(@Triangle("1") long l) {
+        }
+
+        @Square("blue")
+        public abstract void blue(@Triangle("blue") int v);
+    }
+
+
+    @Target(value = ElementType.TYPE)
+    @Retention(value = RetentionPolicy.RUNTIME)
+    public static @interface Circle {
+        String value();
+    }
+
+    @Target(value = ElementType.CONSTRUCTOR)
+    @Retention(value = RetentionPolicy.RUNTIME)
+    public static @interface Oval {
+        String value();
+    }
+
+    @Target(value = ElementType.METHOD)
+    @Retention(value = RetentionPolicy.RUNTIME)
+    public static @interface Square {
+        String value();
+    }
+
+    @Target(value = ElementType.PARAMETER)
+    @Retention(value = RetentionPolicy.RUNTIME)
+    public static @interface Triangle {
+        String value();
+    }
+
+
+}

Propchange: 
tomee/tomee/trunk/container/openejb-core/src/test/java/org/apache/openejb/dyni/DynamicSubclassTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Copied: 
tomee/tomee/trunk/server/openejb-cxf-rs/src/test/java/org/apache/openejb/server/cxf/rs/DynamicSubclassEjbDeploymentTest.java
 (from r1524933, 
tomee/tomee/trunk/server/openejb-cxf-rs/src/test/java/org/apache/openejb/server/cxf/rs/EjbDeploymentTest.java)
URL: 
http://svn.apache.org/viewvc/tomee/tomee/trunk/server/openejb-cxf-rs/src/test/java/org/apache/openejb/server/cxf/rs/DynamicSubclassEjbDeploymentTest.java?p2=tomee/tomee/trunk/server/openejb-cxf-rs/src/test/java/org/apache/openejb/server/cxf/rs/DynamicSubclassEjbDeploymentTest.java&p1=tomee/tomee/trunk/server/openejb-cxf-rs/src/test/java/org/apache/openejb/server/cxf/rs/EjbDeploymentTest.java&r1=1524933&r2=1525217&rev=1525217&view=diff
==============================================================================
--- 
tomee/tomee/trunk/server/openejb-cxf-rs/src/test/java/org/apache/openejb/server/cxf/rs/EjbDeploymentTest.java
 (original)
+++ 
tomee/tomee/trunk/server/openejb-cxf-rs/src/test/java/org/apache/openejb/server/cxf/rs/DynamicSubclassEjbDeploymentTest.java
 Sat Sep 21 10:32:17 2013
@@ -33,14 +33,17 @@ import javax.ws.rs.GET;
 import javax.ws.rs.Path;
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Request;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
 import java.util.Properties;
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertNotNull;
 
-public class EjbDeploymentTest {
+public class DynamicSubclassEjbDeploymentTest {
+
     private static EJBContainer container;
-    private static RESTIsCool service;
+    private static RESTIsVeryCool service;
 
     @BeforeClass
     public static void start() throws Exception {
@@ -48,7 +51,7 @@ public class EjbDeploymentTest {
         properties.setProperty(DeploymentFilterable.CLASSPATH_INCLUDE, 
".*openejb-cxf-rs.*");
         properties.setProperty(OpenEjbContainer.OPENEJB_EMBEDDED_REMOTABLE, 
"true");
         container = EJBContainer.createEJBContainer(properties);
-        service = (RESTIsCool) 
container.getContext().lookup("java:/global/openejb-cxf-rs/RESTIsCool");
+        service = (RESTIsVeryCool) 
container.getContext().lookup("java:/global/openejb-cxf-rs/RESTIsVeryCool");
     }
 
     @AfterClass
@@ -87,23 +90,21 @@ public class EjbDeploymentTest {
 
     @Stateless
     @Path("/ejb")
-    public static class RESTIsCool {
+    public abstract static class RESTIsVeryCool implements InvocationHandler {
+
         @EJB
         private SimpleEJB simpleEJB;
+
         @javax.ws.rs.core.Context
         Request request;
 
         @Path("/normal")
         @GET
-        public String normal() {
-            return simpleEJB.ok();
-        }
+        public abstract String normal();
 
         @Path("/rest")
         @GET
-        public String rest() {
-            return simpleEJB.ok();
-        }
+        public abstract String rest();
 
         @Path("/param")
         @GET
@@ -116,5 +117,10 @@ public class EjbDeploymentTest {
         public boolean field() {
             return "GET".equals(request.getMethod());
         }
+
+        @Override
+        public Object invoke(Object proxy, Method method, Object[] args) 
throws Throwable {
+            return simpleEJB.ok();
+        }
     }
 }


Reply via email to