OWB-1277 ensure we can proxy beans with a writeReplace method

git-svn-id: https://svn.apache.org/repos/asf/openwebbeans/trunk@1850347 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/webbeans-impl/src/main/java/org/apache/webbeans/proxy/InterceptorDecoratorProxyFactory.java b/webbeans-impl/src/main/java/org/apache/webbeans/proxy/InterceptorDecoratorProxyFactory.java
index a14e8e1..061dba8 100644
--- a/webbeans-impl/src/main/java/org/apache/webbeans/proxy/InterceptorDecoratorProxyFactory.java
+++ b/webbeans-impl/src/main/java/org/apache/webbeans/proxy/InterceptorDecoratorProxyFactory.java
@@ -338,6 +338,11 @@
         mv.visitEnd();
     }
 
+    private boolean isIgnoredMethod(final Method delegatedMethod)
+    {
+        return "writeReplace".equals(delegatedMethod.getName());
+    }
+
     /**
      * Directly delegate all non intercepted nor decorated methods to the internal instance.
      *
@@ -348,7 +353,7 @@
     {
         for (Method delegatedMethod : noninterceptedMethods)
         {
-            if (unproxyableMethod(delegatedMethod))
+            if (unproxyableMethod(delegatedMethod) || isIgnoredMethod(delegatedMethod))
             {
                 continue;
             }
@@ -422,6 +427,10 @@
         {
             return;
         }
+        if (isIgnoredMethod(method))
+        {
+            return;
+        }
 
         Class<?> returnType = method.getReturnType();
         Class<?>[] parameterTypes = method.getParameterTypes();
diff --git a/webbeans-impl/src/main/java/org/apache/webbeans/proxy/NormalScopeProxyFactory.java b/webbeans-impl/src/main/java/org/apache/webbeans/proxy/NormalScopeProxyFactory.java
index 73e25a8..c652284 100644
--- a/webbeans-impl/src/main/java/org/apache/webbeans/proxy/NormalScopeProxyFactory.java
+++ b/webbeans-impl/src/main/java/org/apache/webbeans/proxy/NormalScopeProxyFactory.java
@@ -387,6 +387,11 @@
 
         for (Method delegatedMethod : noninterceptedMethods)
         {
+            if (isIgnoredMethod(delegatedMethod))
+            {
+                return;
+            }
+
             String methodDescriptor = Type.getMethodDescriptor(delegatedMethod);
 
             //X TODO handle generic exception types?
@@ -439,8 +444,18 @@
 
     }
 
+    private boolean isIgnoredMethod(final Method delegatedMethod)
+    {
+        return "writeReplace".equals(delegatedMethod.getName());
+    }
+
     private void generateDelegationMethod(ClassWriter cw, Method method, int methodIndex, Class<?> classToProxy, String proxyClassFileName)
     {
+        if (isIgnoredMethod(method))
+        {
+            return;
+        }
+
         Class<?> returnType = method.getReturnType();
         Class<?>[] parameterTypes = method.getParameterTypes();
         int modifiers = method.getModifiers();
diff --git a/webbeans-impl/src/test/java/org/apache/webbeans/test/interceptors/factory/NormalScopeProxyFactoryTest.java b/webbeans-impl/src/test/java/org/apache/webbeans/test/interceptors/factory/NormalScopeProxyFactoryTest.java
index 734575e..bba0dcf 100644
--- a/webbeans-impl/src/test/java/org/apache/webbeans/test/interceptors/factory/NormalScopeProxyFactoryTest.java
+++ b/webbeans-impl/src/test/java/org/apache/webbeans/test/interceptors/factory/NormalScopeProxyFactoryTest.java
@@ -21,27 +21,39 @@
 import org.apache.webbeans.component.OwbBean;
 import org.apache.webbeans.component.WebBeansType;
 import org.apache.webbeans.config.WebBeansContext;
+import org.apache.webbeans.container.InjectableBeanManager;
+import org.apache.webbeans.context.ApplicationContext;
+import org.apache.webbeans.intercept.NormalScopedBeanInterceptorHandler;
 import org.apache.webbeans.test.AbstractUnitTest;
 import org.apache.webbeans.test.interceptors.factory.beans.ClassInterceptedClass;
 import org.apache.webbeans.test.interceptors.factory.beans.SomeBaseClass;
 import org.apache.webbeans.proxy.NormalScopeProxyFactory;
+import org.apache.webbeans.test.util.Serializations;
 import org.junit.Assert;
 import org.junit.Test;
 
 import javax.enterprise.context.ApplicationScoped;
+import javax.enterprise.context.spi.Context;
+import javax.enterprise.context.spi.Contextual;
 import javax.enterprise.context.spi.CreationalContext;
 import javax.enterprise.inject.spi.InjectionPoint;
 import javax.enterprise.inject.spi.Producer;
 import javax.inject.Provider;
+
+import java.io.ObjectStreamException;
+import java.io.Serializable;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Type;
 import java.net.URL;
 import java.net.URLClassLoader;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.Set;
 import org.apache.webbeans.test.interceptors.factory.beans.PartialBeanClass;
 import org.apache.webbeans.test.interceptors.factory.beans.PartialBeanInterface;
 
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
 
 /**
  * Test for the {@link NormalScopeProxyFactory}
@@ -209,6 +221,30 @@
 
     }
 
+    // ensure we don't get:
+    // java.lang.ClassFormatError: Duplicate method name "writeReplace" with signature
+    // "()Ljava.lang.Object;" in class file org/apache/webbeans/test/interceptors/factory/
+    // NormalScopeProxyFactoryTest$IHaveAWriteReplace$$OwbNormalScopeProxy0
+    @Test
+    public void writeReplaceIsIgnoredWhenPresentInDelegate() throws Exception
+    {
+        MapBackedProxyHandler.map = null;
+        final NormalScopeProxyFactory pf = new NormalScopeProxyFactory(new WebBeansContext());
+        final ClassLoader classLoader = new URLClassLoader(new URL[0]);
+        final Class<IHaveAWriteReplace> proxyClass = pf.createProxyClass(classLoader, IHaveAWriteReplace.class);
+        proxyClass.getDeclaredMethod("writeReplace"); // ensure it exists
+
+        MapBackedProxyHandler.map = new HashMap<>();
+        final IHaveAWriteReplace proxy = pf.createProxyInstance(proxyClass, new MapBackedProxyHandler());
+        assertNotNull(proxy);
+        MapBackedProxyHandler.map.put(IHaveAWriteReplace.class, proxy);
+
+        final Object deserialized = Serializations.deserialize(Serializations.serialize(proxy));
+        assertNotNull(deserialized);
+        assertSame(proxy, deserialized);
+        MapBackedProxyHandler.map = null;
+    }
+
     @Test
     public void testPartialBeanProxyCreation() throws Exception
     {
@@ -287,6 +323,22 @@
         Assert.assertEquals(Integer.valueOf(42), protectedUsage.getProtectedIntegerMeaningOfLife());
     }
 
+    public static class SerializableProvider<T> implements Serializable, Provider<T>
+    {
+
+        private final T value;
+
+        private SerializableProvider(final T value)
+        {
+            this.value = value;
+        }
+
+        @Override
+        public T get()
+        {
+            return value;
+        }
+    }
     public static class TestContextualInstanceProvider<T> implements Provider<T>
     {
         private T instance;
@@ -327,4 +379,43 @@
         Assert.assertNotNull(subPackageInstance);
         instance.getFloat();
     }
+
+    public static class IHaveAWriteReplace implements Serializable
+    {
+        Object writeReplace()
+        {
+            return null;
+        }
+    }
+
+    private static class MapBackedProxyHandler extends NormalScopedBeanInterceptorHandler
+    {
+        // ensure it is not serialized otherwise we break out test
+        private static Map<Class<?>, Object> map;
+
+        private MapBackedProxyHandler()
+        {
+            super(new InjectableBeanManager(null) // used as a mock without a WBC
+            {
+
+                @Override
+                public Context getContext(final Class<? extends Annotation> scope)
+                {
+                    return new ApplicationContext();
+                }
+
+                @Override
+                public <T> CreationalContext<T> createCreationalContext(final Contextual<T> contextual)
+                {
+                    return null;
+                }
+            }, null);
+        }
+
+        @Override
+        protected Object readResolve() throws ObjectStreamException
+        {
+            return map.get(IHaveAWriteReplace.class);
+        }
+    }
 }