You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by mb...@apache.org on 2010/09/21 18:03:32 UTC

svn commit: r999477 - in /commons/proper/proxy/branches/version-2.0-work/stub/src: main/java/org/apache/commons/proxy2/stub/ test/java/org/apache/commons/proxy2/stub/

Author: mbenson
Date: Tue Sep 21 16:03:31 2010
New Revision: 999477

URL: http://svn.apache.org/viewvc?rev=999477&view=rev
Log:
refactor AnnotationFactory to always use a custom java.lang.reflect.Proxy-based ProxyFactory implementation that ensures generated annotation proxies correctly implement equals/hashCode/toString

Added:
    commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/AnnotationFactoryTest.java
      - copied, changed from r997852, commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/AbstractAnnotationFactoryTest.java
Removed:
    commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/AbstractAnnotationFactoryTest.java
    commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/CglibAnnotationFactoryTest.java
    commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/JavassistAnnotationFactoryTest.java
    commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/JdkAnnotationFactoryTest.java
Modified:
    commons/proper/proxy/branches/version-2.0-work/stub/src/main/java/org/apache/commons/proxy2/stub/AnnotationFactory.java

Modified: commons/proper/proxy/branches/version-2.0-work/stub/src/main/java/org/apache/commons/proxy2/stub/AnnotationFactory.java
URL: http://svn.apache.org/viewvc/commons/proper/proxy/branches/version-2.0-work/stub/src/main/java/org/apache/commons/proxy2/stub/AnnotationFactory.java?rev=999477&r1=999476&r2=999477&view=diff
==============================================================================
--- commons/proper/proxy/branches/version-2.0-work/stub/src/main/java/org/apache/commons/proxy2/stub/AnnotationFactory.java (original)
+++ commons/proper/proxy/branches/version-2.0-work/stub/src/main/java/org/apache/commons/proxy2/stub/AnnotationFactory.java Tue Sep 21 16:03:31 2010
@@ -17,40 +17,140 @@
 
 package org.apache.commons.proxy2.stub;
 
+import java.io.Serializable;
 import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
 
+import org.apache.commons.lang3.AnnotationUtils;
+import org.apache.commons.proxy2.Interceptor;
+import org.apache.commons.proxy2.Invocation;
 import org.apache.commons.proxy2.Invoker;
+import org.apache.commons.proxy2.ObjectProvider;
 import org.apache.commons.proxy2.ProxyFactory;
 import org.apache.commons.proxy2.ProxyUtils;
+import org.apache.commons.proxy2.impl.AbstractProxyFactory;
 
 /**
  * {@link AnnotationFactory} provides a simplified API over {@link StubProxyFactory}
- * to stub a Java {@link Annotation}.  Non-stubbed methods including
- * {@link Annotation#annotationType()} will return the values that would have been
+ * to stub a Java {@link Annotation}.  Like "real" runtime proxies, instances created via
+ * {@link AnnotationFactory} are {@link Proxy}-based.  Non-stubbed methods including
+ * {@link Annotation#annotationType()} will return methods' default values and
+ * {@link Annotation#equals(Object)}/{@link Annotation#hashCode()}/{@link Annotation#toString()}
+ * return values consistent with those methods' documented expectations.
+ *  
+ *  that would have been
  * returned from a "real" annotation whose methods' values were unspecified.
  *
  * @author Matt Benson
  */
 public class AnnotationFactory {
+    //underlying proxyfactory implementation based on org.apache.commons.proxy2.jdk.JdkProxyFactory
+
+    private static class InterceptorInvocationHandler implements InvocationHandler, Serializable {
+        /** Serialization version */
+        private static final long serialVersionUID = 1L;
+
+        private final Object target;
+        private final Interceptor methodInterceptor;
+
+        public InterceptorInvocationHandler(Object target, Interceptor methodInterceptor) {
+            this.target = target;
+            this.methodInterceptor = methodInterceptor;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+            if (ProxyUtils.isHashCode(method)) {
+                return AnnotationUtils.hashCode((Annotation) proxy);
+            }
+            if (ProxyUtils.isEqualsMethod(method)) {
+                return args[0] instanceof Annotation
+                    && AnnotationUtils.equals((Annotation) proxy, (Annotation) args[0]);
+            }
+            if ("toString".equals(method.getName()) && method.getParameterTypes().length == 0) {
+                return AnnotationUtils.toString((Annotation) proxy);
+            }
+            final ReflectionInvocation invocation = new ReflectionInvocation(target, method, args);
+            return methodInterceptor.intercept(invocation);
+        }
+
+    }
+
+    private static class ReflectionInvocation implements Invocation, Serializable {
+        /** Serialization version */
+        private static final long serialVersionUID = 1L;
+
+        private final Method method;
+        private final Object[] arguments;
+        private final Object target;
+
+        public ReflectionInvocation(Object target, Method method, Object[] arguments) {
+            this.method = method;
+            this.arguments = (arguments == null ? ProxyUtils.EMPTY_ARGUMENTS : arguments);
+            this.target = target;
+        }
+
+        public Object[] getArguments() {
+            return arguments;
+        }
+
+        public Method getMethod() {
+            return method;
+        }
+
+        public Object getProxy() {
+            return target;
+        }
+
+        public Object proceed() throws Throwable {
+            try {
+                return method.invoke(target, arguments);
+            } catch (InvocationTargetException e) {
+                throw e.getTargetException();
+            }
+        }
+    }
+
+    private static final ProxyFactory PROXY_FACTORY = new AbstractProxyFactory() {
+
+        public <T> T createInvokerProxy(ClassLoader classLoader, final Invoker invoker, Class<?>... proxyClasses) {
+            throw new UnsupportedOperationException();
+        }
+
+        @SuppressWarnings("unchecked")
+        public <T> T createInterceptorProxy(ClassLoader classLoader, Object target, Interceptor interceptor,
+            Class<?>... proxyClasses) {
+            return (T) Proxy.newProxyInstance(classLoader, proxyClasses, new InterceptorInvocationHandler(target,
+                interceptor));
+        }
+
+        public <T> T createDelegatorProxy(ClassLoader classLoader, ObjectProvider<?> delegateProvider,
+            Class<?>... proxyClasses) {
+            throw new UnsupportedOperationException();
+        }
+    };
+
     private static final Invoker ANNOTATION_INVOKER = new Invoker() {
 
         /** Serialization version */
         private static final long serialVersionUID = 1L;
 
-        public Object invoke(Object proxy, Method method, Object[] arguments)
-                throws Throwable {
+        public Object invoke(Object proxy, Method method, Object[] arguments) throws Throwable {
             Object result = method.getDefaultValue();
             return result == null && method.getReturnType().isPrimitive() ? ProxyUtils
-                    .nullValue(method.getReturnType())
-                    : result;
+                .nullValue(method.getReturnType()) : result;
         }
     };
 
     private static final ThreadLocal<Object> CONFIGURER = new ThreadLocal<Object>();
 
     private static final StubConfigurer<Annotation> SHARED_CONFIGURER = new StubConfigurer<Annotation>() {
-        
+
         /**
          * {@inheritDoc}
          */
@@ -80,17 +180,7 @@ public class AnnotationFactory {
      * Create a new AnnotationFactory instance.
      */
     public AnnotationFactory() {
-        this(ProxyUtils.proxyFactory());
-    }
-
-    /**
-     * Create a new AnnotationFactory instance.
-     * @param proxyFactory
-     */
-    public AnnotationFactory(ProxyFactory proxyFactory) {
-        super();
-        this.proxyFactory = new StubProxyFactory(proxyFactory,
-                SHARED_CONFIGURER);
+        this.proxyFactory = new StubProxyFactory(PROXY_FACTORY, SHARED_CONFIGURER);
     }
 
     /**
@@ -99,14 +189,12 @@ public class AnnotationFactory {
      * @param configurer
      * @return stubbed annotation proxy
      */
-    public <A extends Annotation> A create(
-        StubConfigurer<A> configurer) {
+    public <A extends Annotation> A create(StubConfigurer<A> configurer) {
         @SuppressWarnings("unchecked")
-        final A result = (A) createInternal(Thread.currentThread()
-            .getContextClassLoader(), configurer);
+        final A result = (A) createInternal(Thread.currentThread().getContextClassLoader(), configurer);
         return result;
     }
-    
+
     /**
      * Create an annotation of the type supported by <code>configurer</code> in the specified classpath.
      * @param <A>
@@ -114,13 +202,12 @@ public class AnnotationFactory {
      * @param configurer
      * @return stubbed annotation proxy
      */
-    public <A extends Annotation> A create(ClassLoader classLoader,
-        StubConfigurer<A> configurer) {
+    public <A extends Annotation> A create(ClassLoader classLoader, StubConfigurer<A> configurer) {
         @SuppressWarnings("unchecked")
         final A result = (A) createInternal(classLoader, configurer);
         return result;
     }
-    
+
     /**
      * Create an annotation of <code>annotationType</code> with fully default behavior.
      * @param <A>
@@ -130,8 +217,7 @@ public class AnnotationFactory {
      */
     public <A extends Annotation> A create(Class<A> annotationType) {
         @SuppressWarnings("unchecked")
-        final A result = (A) createInternal(Thread.currentThread().getContextClassLoader(),
-                annotationType);
+        final A result = (A) createInternal(Thread.currentThread().getContextClassLoader(), annotationType);
         return result;
     }
 
@@ -142,21 +228,18 @@ public class AnnotationFactory {
      * @param annotationType
      * @return stubbed annotation proxy
      */
-    public <A extends Annotation> A create(ClassLoader classLoader,
-            Class<A> annotationType) {
+    public <A extends Annotation> A create(ClassLoader classLoader, Class<A> annotationType) {
         @SuppressWarnings("unchecked")
         final A result = (A) createInternal(classLoader, annotationType);
         return result;
     }
 
-    private <A extends Annotation> A createInternal(ClassLoader classLoader,
-            Object configurer) {
+    private <A extends Annotation> A createInternal(ClassLoader classLoader, Object configurer) {
         final Object existingConfigurer = CONFIGURER.get();
         try {
             CONFIGURER.set(configurer);
             @SuppressWarnings("unchecked")
-            final A result = (A) proxyFactory.createInvokerProxy(classLoader,
-                    ANNOTATION_INVOKER, getStubType());
+            final A result = (A) proxyFactory.createInvokerProxy(classLoader, ANNOTATION_INVOKER, getStubType());
             return result;
         } finally {
             if (existingConfigurer == null) {

Copied: commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/AnnotationFactoryTest.java (from r997852, commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/AbstractAnnotationFactoryTest.java)
URL: http://svn.apache.org/viewvc/commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/AnnotationFactoryTest.java?p2=commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/AnnotationFactoryTest.java&p1=commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/AbstractAnnotationFactoryTest.java&r1=997852&r2=999477&rev=999477&view=diff
==============================================================================
--- commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/AbstractAnnotationFactoryTest.java (original)
+++ commons/proper/proxy/branches/version-2.0-work/stub/src/test/java/org/apache/commons/proxy2/stub/AnnotationFactoryTest.java Tue Sep 21 16:03:31 2010
@@ -19,23 +19,20 @@ package org.apache.commons.proxy2.stub;
 
 import static org.junit.Assert.*;
 
-import org.apache.commons.proxy2.ProxyFactory;
 import org.junit.Before;
 import org.junit.Test;
 
 /**
  * Test {@link AnnotationFactory}.
  */
-public abstract class AbstractAnnotationFactoryTest {
+public class AnnotationFactoryTest {
     private AnnotationFactory annotationFactory;
 
     @Before
     public void setUp() {
-        annotationFactory = new AnnotationFactory(createProxyFactory());
+        annotationFactory = new AnnotationFactory();
     }
 
-    protected abstract ProxyFactory createProxyFactory();
-
     @Test
     public void testDefaultAnnotation() {
         CustomAnnotation customAnnotation = annotationFactory.create(CustomAnnotation.class);