This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/master by this push:
     new c995488  JUNEAU-137 - Juneau cannot reflect parameterized types on 
Spring bean methods.
c995488 is described below

commit c99548873f8f429f7136794b318565f5c755cbec
Author: JamesBognar <[email protected]>
AuthorDate: Thu Sep 5 20:01:26 2019 -0400

    JUNEAU-137 - Juneau cannot reflect parameterized types on Spring bean
    methods.
---
 .../juneau/reflection/ConstructorInfoTest.java     |  4 +-
 .../apache/juneau/reflection/MethodInfoTest.java   |  4 +-
 .../org/apache/juneau/internal/ClassUtils.java     |  4 +-
 .../java/org/apache/juneau/reflect/ClassInfo.java  | 80 +++++++++++++++++++---
 .../org/apache/juneau/reflect/ConstructorInfo.java | 12 ++--
 .../org/apache/juneau/reflect/ExecutableInfo.java  | 10 +--
 .../java/org/apache/juneau/reflect/MethodInfo.java | 12 ++--
 .../java/org/apache/juneau/rest/RestContext.java   | 16 ++---
 8 files changed, 105 insertions(+), 37 deletions(-)

diff --git 
a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/reflection/ConstructorInfoTest.java
 
b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/reflection/ConstructorInfoTest.java
index 9712a30..d552ff8 100644
--- 
a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/reflection/ConstructorInfoTest.java
+++ 
b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/reflection/ConstructorInfoTest.java
@@ -67,7 +67,7 @@ public class ConstructorInfoTest {
 
        @Test
        public void of_withDeclaringClass() throws Exception {
-               check("A()", ConstructorInfo.of(ClassInfo.of(A.class), 
a.inner()));
+               check("A()", ConstructorInfo.of(ClassInfo.of(A.class), 
a.inner(), null));
        }
 
        @Test
@@ -83,7 +83,7 @@ public class ConstructorInfoTest {
        @Test
        public void of_null() throws Exception {
                check(null, ConstructorInfo.of(null));
-               check(null, ConstructorInfo.of(null, null));
+               check(null, ConstructorInfo.of(null, null, null));
        }
 
        
//-----------------------------------------------------------------------------------------------------------------
diff --git 
a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/reflection/MethodInfoTest.java
 
b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/reflection/MethodInfoTest.java
index 3e752fd..c37097c 100644
--- 
a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/reflection/MethodInfoTest.java
+++ 
b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/reflection/MethodInfoTest.java
@@ -120,7 +120,7 @@ public class MethodInfoTest {
        @Test
        public void of_withDeclaringClass() {
                check("A1.m()", a_m);
-               check("A1.m()", MethodInfo.of(ClassInfo.of(A1.class), 
a_m.inner()));
+               check("A1.m()", MethodInfo.of(ClassInfo.of(A1.class), 
a_m.inner(), null));
        }
 
        @Test
@@ -132,7 +132,7 @@ public class MethodInfoTest {
        @Test
        public void of_null() {
                check(null, MethodInfo.of(null));
-               check(null, MethodInfo.of(null, null));
+               check(null, MethodInfo.of(null, null, null));
        }
 
        
//-----------------------------------------------------------------------------------------------------------------
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassUtils.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassUtils.java
index b2d72e1..c1f7005 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassUtils.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/ClassUtils.java
@@ -55,7 +55,7 @@ public final class ClassUtils {
        }
 
        /**
-        * Shortcut for calling {@link MethodInfo#of(ClassInfo, Method)}.
+        * Shortcut for calling {@link MethodInfo#of(ClassInfo, Method, 
Method)}.
         *
         * @param c
         *      The class containing the method.
@@ -65,7 +65,7 @@ public final class ClassUtils {
         * @return The wrapped method.
         */
        public static MethodInfo getMethodInfo(Class<?> c, Method m) {
-               return MethodInfo.of(ClassInfo.of(c), m);
+               return MethodInfo.of(ClassInfo.of(c), m, m);
        }
 
        /**
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/reflect/ClassInfo.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/reflect/ClassInfo.java
index f226c67..8ed4f3e 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/reflect/ClassInfo.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/reflect/ClassInfo.java
@@ -58,6 +58,7 @@ public final class ClassInfo {
 
        private final Type t;
        final Class<?> c;
+       private ClassInfo proxyFor;
        private final boolean isParameterizedType;
        private List<ClassInfo> interfaces, declaredInterfaces, parents, 
allParents;
        private List<MethodInfo> publicMethods, declaredMethods, allMethods, 
allMethodsParentFirst;
@@ -76,10 +77,12 @@ public final class ClassInfo {
         *
         * @param c The class type.
         * @param t The generic type (if parameterized type).
+        * @param proxyFor If the class is a CGLIB proxy, this is the 
underlying wrapped class.
         */
-       protected ClassInfo(Class<?> c, Type t) {
+       protected ClassInfo(Class<?> c, Type t, Class<?> proxyFor) {
                this.t = t;
                this.c = c;
+               this.proxyFor = proxyFor == null ? null : 
ClassInfo.of(proxyFor);
                this.isParameterizedType = t == null ? false : (t instanceof 
ParameterizedType);
        }
 
@@ -92,7 +95,7 @@ public final class ClassInfo {
        public static ClassInfo of(Type t) {
                if (t == null)
                        return null;
-               return new ClassInfo(ClassUtils.toClass(t), t);
+               return new ClassInfo(ClassUtils.toClass(t), t, null);
        }
 
        /**
@@ -104,7 +107,7 @@ public final class ClassInfo {
        public static ClassInfo of(Class<?> c) {
                if (c == null)
                        return null;
-               return new ClassInfo(c, c);
+               return new ClassInfo(c, c, null);
        }
 
        /**
@@ -115,7 +118,7 @@ public final class ClassInfo {
         * @return The constructed class info, or <jk>null</jk> if the type was 
<jk>null</jk>.
         */
        public static ClassInfo of(Class<?> c, Type t) {
-               return new ClassInfo(c, t);
+               return new ClassInfo(c, t, null);
        }
 
        /**
@@ -127,7 +130,28 @@ public final class ClassInfo {
        public static ClassInfo of(Object o) {
                if (o == null)
                        return null;
-               return new ClassInfo(o.getClass(), o.getClass());
+               return new ClassInfo(o.getClass(), o.getClass(), 
getProxyFor(o));
+       }
+
+       /**
+        * When this metadata is against a CGLIB proxy, this method finds the 
underlying "real" class.
+        * 
+        * @param o The class instance.
+        * @return The non-proxy class, or <jk>null</jk> if it's not a CGLIB 
proxy.
+        */
+       private static Class<?> getProxyFor(Object o) {
+               Class<?> c = o.getClass();
+               String s = c.getName();
+               if (s.indexOf('$') == -1 || ! 
s.contains("$$EnhancerBySpringCGLIB$$"))
+                       return null;
+               for (Method m : c.getMethods()) {
+                       if (m.getName().equals("getTargetClass") && 
m.getParameterCount() == 0 && m.getReturnType().equals(Class.class)) {
+                               try {
+                                       return (Class<?>) m.invoke(o);
+                               } catch (Exception e) {}
+                       }
+               }
+               return null;
        }
 
        /**
@@ -160,6 +184,26 @@ public final class ClassInfo {
                return this;
        }
 
+       /**
+        * Identifies the inner target class when this class info represents a 
CGLIB proxy class.
+        *
+        * @param proxyFor The inner non-proxied class.
+        * @return This object (for method chaining).
+        */
+       public ClassInfo proxyFor(Class<?> proxyFor) {
+               this.proxyFor = ClassInfo.of(proxyFor);
+               return this;
+       }
+
+       /**
+        * Returns the non-proxied inner class of a CGLIB proxy class.
+        *
+        * @return The non-proxied inner class of a CGLIB proxy class, or the 
inner class if it's not a proxy.
+        */
+       public Class<?> getProxiedClass() {
+               return proxyFor == null ? c : proxyFor.inner();
+       }
+
        
//-----------------------------------------------------------------------------------------------------------------
        // Parent classes and interfaces.
        
//-----------------------------------------------------------------------------------------------------------------
@@ -308,13 +352,31 @@ public final class ClassInfo {
                        List<MethodInfo> l = new ArrayList<>(mm.length);
                        for (Method m : mm)
                                if (m.getDeclaringClass() != Object.class)
-                                       l.add(MethodInfo.of(this, m));
+                                       l.add(MethodInfo.of(this, m, 
getProxyTarget(m)));
                        l.sort(null);
                        publicMethods = Collections.unmodifiableList(l);
                }
                return publicMethods;
        }
 
+       private Method getProxyTarget(Method m) {
+               if (proxyFor != null) {
+                       MethodInfo m2 = proxyFor.getMethod(m.getName(), 
m.getParameterTypes());
+                       if (m2 != null)
+                               return m2.inner();
+               }
+               return m;
+       }
+
+       private Constructor<?> getProxyTarget(Constructor<?> c) {
+               if (proxyFor != null) {
+                       ConstructorInfo c2 = 
proxyFor.getConstructor(Visibility.PRIVATE, c.getParameterTypes());
+                       if (c2 != null)
+                               return c2.inner();
+               }
+               return c;
+       }
+
        /**
         * Returns the public method with the specified method name and 
argument types.
         *
@@ -358,7 +420,7 @@ public final class ClassInfo {
                        List<MethodInfo> l = new ArrayList<>(mm.length);
                        for (Method m : mm)
                                if (! "$jacocoInit".equals(m.getName())) // 
Jacoco adds its own simulated methods.
-                                       l.add(MethodInfo.of(this, m));
+                                       l.add(MethodInfo.of(this, m, 
getProxyTarget(m)));
                        l.sort(null);
                        declaredMethods = Collections.unmodifiableList(l);
                }
@@ -516,7 +578,7 @@ public final class ClassInfo {
                        Constructor<?>[] cc = c == null ? new Constructor[0] : 
c.getConstructors();
                        List<ConstructorInfo> l = new ArrayList<>(cc.length);
                        for (Constructor<?> ccc : cc)
-                               l.add(ConstructorInfo.of(this, ccc));
+                               l.add(ConstructorInfo.of(this, ccc, 
getProxyTarget(ccc)));
                        l.sort(null);
                        publicConstructors = Collections.unmodifiableList(l);
                }
@@ -575,7 +637,7 @@ public final class ClassInfo {
                        Constructor<?>[] cc = c == null ? new Constructor[0] : 
c.getDeclaredConstructors();
                        List<ConstructorInfo> l = new ArrayList<>(cc.length);
                        for (Constructor<?> ccc : cc)
-                               l.add(ConstructorInfo.of(this, ccc));
+                               l.add(ConstructorInfo.of(this, ccc, 
getProxyTarget(ccc)));
                        l.sort(null);
                        declaredConstructors = Collections.unmodifiableList(l);
                }
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/reflect/ConstructorInfo.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/reflect/ConstructorInfo.java
index cb66dd5..36c01b9 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/reflect/ConstructorInfo.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/reflect/ConstructorInfo.java
@@ -34,9 +34,10 @@ public final class ConstructorInfo extends ExecutableInfo 
implements Comparable<
         *
         * @param declaringClass The class that declares this method.
         * @param c The constructor being wrapped.
+        * @param rc The "real" constructor if the constructor above is defined 
against a CGLIB proxy.
         */
-       protected ConstructorInfo(ClassInfo declaringClass, Constructor<?> c) {
-               super(declaringClass, c);
+       protected ConstructorInfo(ClassInfo declaringClass, Constructor<?> c, 
Constructor<?> rc) {
+               super(declaringClass, c, rc);
                this.c = c;
        }
 
@@ -45,12 +46,13 @@ public final class ConstructorInfo extends ExecutableInfo 
implements Comparable<
         *
         * @param declaringClass The class that declares this method.
         * @param c The constructor being wrapped.
+        * @param rc The "real" constructor if the constructor above is defined 
against a CGLIB proxy.
         * @return A new {@link ConstructorInfo} object, or <jk>null</jk> if 
the method was null;
         */
-       public static ConstructorInfo of(ClassInfo declaringClass, 
Constructor<?> c) {
+       public static ConstructorInfo of(ClassInfo declaringClass, 
Constructor<?> c, Constructor<?> rc) {
                if (c == null)
                        return null;
-               return new ConstructorInfo(declaringClass, c);
+               return new ConstructorInfo(declaringClass, c, rc);
        }
 
        /**
@@ -62,7 +64,7 @@ public final class ConstructorInfo extends ExecutableInfo 
implements Comparable<
        public static ConstructorInfo of(Constructor<?> c) {
                if (c == null)
                        return null;
-               return new ConstructorInfo(ClassInfo.of(c.getDeclaringClass()), 
c);
+               return new ConstructorInfo(ClassInfo.of(c.getDeclaringClass()), 
c, c);
        }
 
        /**
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/reflect/ExecutableInfo.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/reflect/ExecutableInfo.java
index df1d89c..f07c120 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/reflect/ExecutableInfo.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/reflect/ExecutableInfo.java
@@ -28,7 +28,7 @@ import org.apache.juneau.internal.*;
 public abstract class ExecutableInfo {
 
        final ClassInfo declaringClass;
-       final Executable e;
+       final Executable e, re;
        final boolean isConstructor;
 
        private List<ParamInfo> params;
@@ -43,10 +43,12 @@ public abstract class ExecutableInfo {
         *
         * @param declaringClass The class that declares this method or 
constructor.
         * @param e The constructor or method that this info represents.
+        * @param re The "real" constructor if the constructor above is defined 
against a CGLIB proxy.
         */
-       protected ExecutableInfo(ClassInfo declaringClass, Executable e) {
+       protected ExecutableInfo(ClassInfo declaringClass, Executable e, 
Executable re) {
                this.declaringClass = declaringClass;
                this.e = e;
+               this.re = re == null ? e : re;
                this.isConstructor = e instanceof Constructor;
        }
 
@@ -259,13 +261,13 @@ public abstract class ExecutableInfo {
 
        Type[] rawGenericParamTypes() {
                if (rawGenericParamTypes == null)
-                       rawGenericParamTypes = e.getGenericParameterTypes();
+                       rawGenericParamTypes = re.getGenericParameterTypes();
                return rawGenericParamTypes;
        }
 
        Parameter[] rawParameters() {
                if (rawParameters == null)
-                       rawParameters = e.getParameters();
+                       rawParameters = re.getParameters();
                return rawParameters;
        }
 
diff --git 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/reflect/MethodInfo.java
 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/reflect/MethodInfo.java
index 861d99d..2d189cb 100644
--- 
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/reflect/MethodInfo.java
+++ 
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/reflect/MethodInfo.java
@@ -43,9 +43,10 @@ public final class MethodInfo extends ExecutableInfo 
implements Comparable<Metho
         *
         * @param declaringClass The class that declares this method.
         * @param m The method being wrapped.
+        * @param rm The "real" method if the method above is defined against a 
CGLIB proxy.
         */
-       protected MethodInfo(ClassInfo declaringClass, Method m) {
-               super(declaringClass, m);
+       protected MethodInfo(ClassInfo declaringClass, Method m, Method rm) {
+               super(declaringClass, m, rm);
                this.m = m;
        }
 
@@ -54,12 +55,13 @@ public final class MethodInfo extends ExecutableInfo 
implements Comparable<Metho
         *
         * @param declaringClass The class that declares this method.
         * @param m The method being wrapped.
+        * @param rm The "real" method if the method above is defined against a 
CGLIB proxy.
         * @return A new {@link MethodInfo} object, or <jk>null</jk> if the 
method was null;
         */
-       public static MethodInfo of(ClassInfo declaringClass, Method m) {
+       public static MethodInfo of(ClassInfo declaringClass, Method m, Method 
rm) {
                if (m == null)
                        return null;
-               return new MethodInfo(declaringClass, m);
+               return new MethodInfo(declaringClass, m, rm);
        }
 
        /**
@@ -71,7 +73,7 @@ public final class MethodInfo extends ExecutableInfo 
implements Comparable<Metho
        public static MethodInfo of(Method m) {
                if (m == null)
                        return null;
-               return new MethodInfo(ClassInfo.of(m.getDeclaringClass()), m);
+               return new MethodInfo(ClassInfo.of(m.getDeclaringClass()), m, 
m);
        }
 
        /**
diff --git 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
index 57cb7b2..ad65e00 100644
--- 
a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
+++ 
b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/RestContext.java
@@ -3600,8 +3600,8 @@ public final class RestContext extends BeanContext {
                        VarResolverSession vrs = 
this.varResolver.createSession();
                        config = builder.config.resolving(vrs);
 
-                       Class<?> resourceClass = resource.getClass();
-                       ClassInfo rci = getClassInfo(resourceClass);
+                       ClassInfo rci = getClassInfo(resource);
+
                        PropertyStore ps = getPropertyStore();
 
                        uriContext = 
nullIfEmpty(getStringProperty(REST_uriContext, null));
@@ -3683,7 +3683,7 @@ public final class RestContext extends BeanContext {
 
                        ClasspathResourceFinder rf = 
getInstanceProperty(REST_classpathResourceFinder, 
ClasspathResourceFinder.class, ClasspathResourceFinderBasic.class, 
resourceResolver, this);
                        useClasspathResourceCaching = 
getProperty(REST_useClasspathResourceCaching, boolean.class, true);
-                       staticResourceManager = new 
ClasspathResourceManager(resourceClass, rf, useClasspathResourceCaching);
+                       staticResourceManager = new 
ClasspathResourceManager(rci.getProxiedClass(), rf, 
useClasspathResourceCaching);
 
                        consumes = getListProperty(REST_consumes, 
MediaType.class, parsers.getSupportedMediaTypes());
                        produces = getListProperty(REST_produces, 
MediaType.class, serializers.getSupportedMediaTypes());
@@ -3696,11 +3696,11 @@ public final class RestContext extends BeanContext {
 
                        MessageBundleLocation[] mbl = 
getInstanceArrayProperty(REST_messages, MessageBundleLocation.class, new 
MessageBundleLocation[0]);
                        if (mbl.length == 0)
-                               msgs = new MessageBundle(resourceClass, "");
+                               msgs = new MessageBundle(rci.getProxiedClass(), 
"");
                        else {
-                               msgs = new MessageBundle(mbl[0] != null ? 
mbl[0].baseClass : resourceClass, mbl[0].bundlePath);
+                               msgs = new MessageBundle(mbl[0] != null ? 
mbl[0].baseClass : rci.getProxiedClass(), mbl[0].bundlePath);
                                for (int i = 1; i < mbl.length; i++)
-                                       msgs.addSearchPath(mbl[i] != null ? 
mbl[i].baseClass : resourceClass, mbl[i].bundlePath);
+                                       msgs.addSearchPath(mbl[i] != null ? 
mbl[i].baseClass : rci.getProxiedClass(), mbl[i].bundlePath);
                        }
 
                        this.fullPath = (builder.parentContext == null ? "" : 
(builder.parentContext.fullPath + '/')) + builder.getPath();
@@ -3743,7 +3743,7 @@ public final class RestContext extends BeanContext {
                                        methodsFound.add(mi.getSimpleName() + 
"," + emptyIfNull(firstNonEmpty(a.name(), a.method())) + "," + 
fixMethodPath(a.path()));
                                        try {
                                                if (mi.isNotPublic())
-                                                       throw new 
RestServletException("@RestMethod method {0}.{1} must be defined as public.", 
resourceClass.getName(), mi.getSimpleName());
+                                                       throw new 
RestServletException("@RestMethod method {0}.{1} must be defined as public.", 
rci.getProxiedClass().getName(), mi.getSimpleName());
 
                                                RestMethodContextBuilder rmcb = 
new RestMethodContextBuilder(resource, mi.inner(), this);
                                                RestMethodContext sm = new 
RestMethodContext(rmcb);
@@ -3816,7 +3816,7 @@ public final class RestContext extends BeanContext {
                                                        addToRouter(routers, 
httpMethod, sm);
                                                }
                                        } catch (Throwable e) {
-                                               throw new 
RestServletException("Problem occurred trying to serialize methods on class 
{0}, methods={1}", resourceClass.getName(), 
SimpleJsonSerializer.DEFAULT.serialize(methodsFound)).initCause(e);
+                                               throw new 
RestServletException("Problem occurred trying to serialize methods on class 
{0}, methods={1}", rci.getProxiedClass().getName(), 
SimpleJsonSerializer.DEFAULT.serialize(methodsFound)).initCause(e);
                                        }
                                }
                        }

Reply via email to