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

reta pushed a commit to branch 3.2.x-fixes
in repository https://gitbox.apache.org/repos/asf/cxf.git


The following commit(s) were added to refs/heads/3.2.x-fixes by this push:
     new 22db276  CXF-8009: CXF should not rely on ClassUtils for CGLIB proxy 
checks (#529)
22db276 is described below

commit 22db27632e3fc1df1b402c887711cf2085b679d2
Author: Andriy Redko <[email protected]>
AuthorDate: Thu Apr 4 16:16:21 2019 -0400

    CXF-8009: CXF should not rely on ClassUtils for CGLIB proxy checks (#529)
---
 .../org/apache/cxf/common/util/ClassHelper.java    | 65 +++++++++++-----
 .../org/apache/cxf/common/util/ClassUnwrapper.java | 23 +++++-
 ...pClassHelper.java => SpringClassUnwrapper.java} | 42 ++++++++---
 .../apache/cxf/common/util/ClassHelperTest.java    | 88 +++++++++++++++++++++-
 .../java/org/apache/cxf/cdi/CdiClassUnwrapper.java |  5 ++
 5 files changed, 190 insertions(+), 33 deletions(-)

diff --git a/core/src/main/java/org/apache/cxf/common/util/ClassHelper.java 
b/core/src/main/java/org/apache/cxf/common/util/ClassHelper.java
index 91be9e8..34a4156 100644
--- a/core/src/main/java/org/apache/cxf/common/util/ClassHelper.java
+++ b/core/src/main/java/org/apache/cxf/common/util/ClassHelper.java
@@ -33,17 +33,47 @@ public class ClassHelper {
     public static final String USE_DEFAULT_CLASS_HELPER = 
"org.apache.cxf.useDefaultClassHelpers";
 
     static final ClassHelper HELPER;
-    static final ClassHelper DEFAULT_HELPER;
+    static final ClassUnwrapper DEFAULT_UNWRAPPER;
+    static final ClassUnwrapper UNWRAPPER;
+    
+    /**
+     * Default class unwrapper implementation which delegates to the 
ClassHelper
+     * internal methods.
+     *
+     */
+    private static class DefaultClassUnwrapper implements ClassUnwrapper {
+        private final ClassHelper helper;
+        
+        DefaultClassUnwrapper(ClassHelper helper) {
+            this.helper = helper;
+        }
+        
+        @Override
+        public Class<?> getRealClassFromClass(Class<?> clazz) {
+            return helper.getRealClassFromClassInternal(clazz);
+        }
+        
+        @Override
+        public Class<?> getRealClass(Object o) {
+            return helper.getRealClassInternal(o);
+        }
+        
+        @Override
+        public Object getRealObject(Object o) {
+            return helper.getRealObjectInternal(o);
+        }
+    }
 
     static {
-        DEFAULT_HELPER = new ClassHelper();
-        HELPER = getClassHelper(DEFAULT_HELPER);
+        HELPER = new ClassHelper();
+        DEFAULT_UNWRAPPER = new DefaultClassUnwrapper(HELPER);
+        UNWRAPPER = getClassUnwrapper(DEFAULT_UNWRAPPER);
     }
 
     protected ClassHelper() {
     }
 
-    private static ClassHelper getClassHelper(ClassHelper defaultHelper) {
+    private static ClassUnwrapper getClassUnwrapper(ClassUnwrapper 
defaultHelper) {
         boolean useSpring = true;
         String s = 
SystemPropertyAction.getPropertyOrNull("org.apache.cxf.useSpringClassHelpers");
         if (!StringUtils.isEmpty(s)) {
@@ -51,7 +81,7 @@ public class ClassHelper {
         }
         if (useSpring) {
             try {
-                return new SpringAopClassHelper();
+                return new SpringClassUnwrapper();
             } catch (Throwable ex) {
                 // ignore
             }
@@ -59,15 +89,15 @@ public class ClassHelper {
         return defaultHelper;
     }
 
-    protected Class<?> getRealClassInternal(Object o) {
+    private Class<?> getRealClassInternal(Object o) {
         return getRealObjectInternal(o).getClass();
     }
 
-    protected Class<?> getRealClassFromClassInternal(Class<?> cls) {
+    private Class<?> getRealClassFromClassInternal(Class<?> cls) {
         return cls;
     }
 
-    protected Object getRealObjectInternal(Object o) {
+    private Object getRealObjectInternal(Object o) {
         return o instanceof Proxy ? Proxy.getInvocationHandler(o) : o;
     }
 
@@ -80,26 +110,23 @@ public class ClassHelper {
     }
 
     public static Class<?> getRealClassFromClass(Bus bus, Class<?> cls) {
-        bus = getBus(bus);
-        return getContextClassHelper(bus).getRealClassFromClassInternal(cls);
+        return 
getContextClassUnwrapper(getBus(bus)).getRealClassFromClass(cls);
     }
 
     public static Object getRealObject(Object o) {
-        Bus bus = getBus(null);
-        return getContextClassHelper(bus).getRealObjectInternal(o);
+        return getContextClassUnwrapper(getBus(null)).getRealObject(o);
     }
 
     public static Class<?> getRealClass(Bus bus, Object o) {
-        bus = getBus(bus);
+        return getContextClassUnwrapper(getBus(bus)).getRealClass(o);
+    }
+
+    private static ClassUnwrapper getContextClassUnwrapper(Bus bus) {
         if (bus != null && bus.getProperty(ClassUnwrapper.class.getName()) != 
null) {
-            ClassUnwrapper unwrapper = (ClassUnwrapper) 
bus.getProperty(ClassUnwrapper.class.getName());
-            return unwrapper.getRealClass(o);
+            return (ClassUnwrapper) 
bus.getProperty(ClassUnwrapper.class.getName());
         }
-        return getContextClassHelper(bus).getRealClassInternal(o);
-    }
 
-    private static ClassHelper getContextClassHelper(Bus bus) {
-        return (DEFAULT_HELPER == HELPER || checkUseDefaultClassHelper(bus)) ? 
DEFAULT_HELPER : HELPER;
+        return (DEFAULT_UNWRAPPER == UNWRAPPER || 
checkUseDefaultClassHelper(bus)) ? DEFAULT_UNWRAPPER : UNWRAPPER;
     }
 
     private static Bus getBus(Bus bus) {
diff --git a/core/src/main/java/org/apache/cxf/common/util/ClassUnwrapper.java 
b/core/src/main/java/org/apache/cxf/common/util/ClassUnwrapper.java
index c3f8361..a94f356 100644
--- a/core/src/main/java/org/apache/cxf/common/util/ClassUnwrapper.java
+++ b/core/src/main/java/org/apache/cxf/common/util/ClassUnwrapper.java
@@ -18,8 +18,29 @@
  */
 package org.apache.cxf.common.util;
 
-public interface ClassUnwrapper {
+import java.lang.reflect.Proxy;
 
+public interface ClassUnwrapper {
+    /**
+     * Return a real class for the instance, possibly a proxy.
+     * @param o instance to get real class for
+     * @return real class for the instance
+     */
     Class<?> getRealClass(Object o);
 
+    /**
+     * Return a real class for the class, possibly a proxy class.
+     * @param clazz class to get real class for
+     * @return real class for the class
+     */
+    Class<?> getRealClassFromClass(Class<?> clazz);
+    
+    /**
+     * Return a real class for the instance, possibly a proxy.
+     * @param o instance to get real class for
+     * @return real class for the instance
+     */
+    default Object getRealObject(Object o) {
+        return (o instanceof Proxy) ? Proxy.getInvocationHandler(o) : o;
+    }
 }
diff --git 
a/core/src/main/java/org/apache/cxf/common/util/SpringAopClassHelper.java 
b/core/src/main/java/org/apache/cxf/common/util/SpringClassUnwrapper.java
similarity index 62%
rename from 
core/src/main/java/org/apache/cxf/common/util/SpringAopClassHelper.java
rename to 
core/src/main/java/org/apache/cxf/common/util/SpringClassUnwrapper.java
index 495d3d1..af0ad76 100644
--- a/core/src/main/java/org/apache/cxf/common/util/SpringAopClassHelper.java
+++ b/core/src/main/java/org/apache/cxf/common/util/SpringClassUnwrapper.java
@@ -28,26 +28,34 @@ import org.springframework.util.ClassUtils;
 /**
  *
  */
-class SpringAopClassHelper extends ClassHelper {
-    SpringAopClassHelper() throws Exception {
+class SpringClassUnwrapper implements ClassUnwrapper {
+    SpringClassUnwrapper() throws Exception {
         Class.forName("org.springframework.aop.support.AopUtils");
         Class.forName("org.springframework.aop.framework.Advised");
     }
 
-    protected Class<?> getRealClassFromClassInternal(Class<?> cls) {
+    @Override
+    public Class<?> getRealClassFromClass(Class<?> cls) {
         if (ClassUtils.isCglibProxyClass(cls)) {
-            return getRealClassFromClassInternal(cls.getSuperclass());
+            final Class<?> superclass = cls.getSuperclass();
+            // Lambda's generated class names also contain $$ which makes them 
trigger CGLIB
+            // proxy path. Adding more checks to handle this particular case.
+            if (superclass != null && (superclass != Object.class || 
wasCglibEnhanced(cls))) {
+                return getRealClassFromClass(superclass);
+            }
         }
         return cls;
     }
-    protected Object getRealObjectInternal(Object o) {
+
+    @Override
+    public Object getRealObject(Object o) {
         if (o instanceof Advised) {
             try {
 
                 Advised advised = (Advised)o;
                 Object target = advised.getTargetSource().getTarget();
                 //could be a proxy of a proxy.....
-                return getRealObjectInternal(target);
+                return getRealObject(target);
             } catch (Exception ex) {
                 // ignore
             }
@@ -55,7 +63,8 @@ class SpringAopClassHelper extends ClassHelper {
         return o;
     }
 
-    protected Class<?> getRealClassInternal(Object o) {
+    @Override
+    public Class<?> getRealClass(Object o) {
         if (AopUtils.isAopProxy(o) && (o instanceof Advised)) {
             Advised advised = (Advised)o;
             try {
@@ -68,25 +77,36 @@ class SpringAopClassHelper extends ClassHelper {
                 } catch (BeanCreationException ex) {
                     // some scopes such as 'request' may not
                     // be active on the current thread yet
-                    return 
getRealClassFromClassInternal(targetSource.getTargetClass());
+                    return 
getRealClassFromClass(targetSource.getTargetClass());
                 }
 
                 if (target == null) {
                     Class<?> targetClass = AopUtils.getTargetClass(o);
                     if (targetClass != null) {
-                        return getRealClassFromClassInternal(targetClass);
+                        return getRealClassFromClass(targetClass);
                     }
                 } else {
-                    return getRealClassInternal(target);
+                    return getRealClass(target);
                 }
             } catch (Exception ex) {
                 // ignore
             }
 
         } else if (ClassUtils.isCglibProxyClass(o.getClass())) {
-            return getRealClassFromClassInternal(AopUtils.getTargetClass(o));
+            return getRealClassFromClass(AopUtils.getTargetClass(o));
         }
+        
         return o.getClass();
     }
 
+    /**
+     * This additional check is not very reliable since CGLIB allows to
+     * supply own NamingPolicy implementations. However, it works with native
+     * CGLIB proxies ("byCGLIB$$") as well as Spring CGLIB proxies (by 
"BySpringCGLIB$$").
+     * More expensive approach is to use reflection and inspect the class 
declared methods, 
+     * looking for CGLIB-specific ones like CGLIB$BIND_CALLBACKS. 
+     */
+    private static boolean wasCglibEnhanced(Class<?> cls) {
+        return cls.getName().contains("CGLIB");
+    }
 }
diff --git a/core/src/test/java/org/apache/cxf/common/util/ClassHelperTest.java 
b/core/src/test/java/org/apache/cxf/common/util/ClassHelperTest.java
index dc3e184..7755b13 100644
--- a/core/src/test/java/org/apache/cxf/common/util/ClassHelperTest.java
+++ b/core/src/test/java/org/apache/cxf/common/util/ClassHelperTest.java
@@ -21,7 +21,10 @@ package org.apache.cxf.common.util;
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
+import java.util.function.Function;
 
+import net.sf.cglib.proxy.Callback;
+import net.sf.cglib.proxy.Enhancer;
 import org.apache.cxf.Bus;
 import org.apache.cxf.BusFactory;
 import org.springframework.aop.AfterReturningAdvice;
@@ -34,12 +37,12 @@ import org.junit.Before;
 import org.junit.Test;
 
 
-
 public class ClassHelperTest extends Assert {
-
     private Object proxiedObject;
 
     private Object springAopObject;
+    
+    private Object cglibProxyObject;
 
     private InvocationHandler realObjectInternalProxy;
 
@@ -48,6 +51,8 @@ public class ClassHelperTest extends Assert {
     private Bus bus;
 
     private Bus currentThreadBus;
+    
+    private Function<String, Integer> fn;
 
     @Before
     public void setUp() {
@@ -73,6 +78,16 @@ public class ClassHelperTest extends Assert {
         });
 
         springAopObject = proxyFactory.getProxy();
+        
+        final Callback callback = new net.sf.cglib.proxy.InvocationHandler() {
+            @Override
+            public Object invoke(Object proxy, Method method, Object[] args) 
throws Throwable {
+                return null;
+            }
+        };
+        cglibProxyObject = Enhancer.create(Object.class, new Class[] 
{Function.class}, callback);
+        
+        fn = Integer::parseInt;
 
         currentThreadBus = BusFactory.getThreadDefaultBus();
 
@@ -116,6 +131,7 @@ public class ClassHelperTest extends Assert {
     public void getRealClassFromClassPropertyWasSetInBus() {
 
         
EasyMock.expect(bus.getProperty(ClassHelper.USE_DEFAULT_CLASS_HELPER)).andReturn(true);
+        
EasyMock.expect(bus.getProperty(ClassUnwrapper.class.getName())).andReturn(null);
         EasyMock.replay(bus);
 
         assertSame(proxiedObject.getClass(), 
ClassHelper.getRealClassFromClass(proxiedObject.getClass()));
@@ -128,6 +144,7 @@ public class ClassHelperTest extends Assert {
     public void getRealClassFromClassPropertyWasNotSetInBus() {
 
         
EasyMock.expect(bus.getProperty(ClassHelper.USE_DEFAULT_CLASS_HELPER)).andReturn(false);
+        
EasyMock.expect(bus.getProperty(ClassUnwrapper.class.getName())).andReturn(null);
         EasyMock.replay(bus);
 
         assertSame(realObjectInternalSpring.getClass(), 
ClassHelper.getRealClassFromClass(springAopObject.getClass()));
@@ -141,6 +158,7 @@ public class ClassHelperTest extends Assert {
     public void getRealObjectPropertyWasSetInBus() {
 
         
EasyMock.expect(bus.getProperty(ClassHelper.USE_DEFAULT_CLASS_HELPER)).andReturn(true);
+        
EasyMock.expect(bus.getProperty(ClassUnwrapper.class.getName())).andReturn(null);
         EasyMock.replay(bus);
 
         assertSame(realObjectInternalProxy, 
ClassHelper.getRealObject(proxiedObject));
@@ -153,6 +171,7 @@ public class ClassHelperTest extends Assert {
     public void getRealObjectPropertyWasNotSetInBus() {
 
         
EasyMock.expect(bus.getProperty(ClassHelper.USE_DEFAULT_CLASS_HELPER)).andReturn(false);
+        
EasyMock.expect(bus.getProperty(ClassUnwrapper.class.getName())).andReturn(null);
         EasyMock.replay(bus);
 
         assertSame(realObjectInternalSpring, 
ClassHelper.getRealObject(springAopObject));
@@ -161,6 +180,71 @@ public class ClassHelperTest extends Assert {
 
     }
 
+    @Test
+    public void getRealLambdaClassPropertyWasNotSetInBus() {
+        
EasyMock.expect(bus.getProperty(ClassHelper.USE_DEFAULT_CLASS_HELPER)).andReturn(false);
+        
EasyMock.expect(bus.getProperty(ClassUnwrapper.class.getName())).andReturn(null);
+        EasyMock.replay(bus);
+
+        assertSame(fn.getClass(), ClassHelper.getRealClass(fn));
+
+        EasyMock.verify(bus);
+    }
+    
+    public void getRealLambdaClassPropertyWasSetInBus() {
+        
EasyMock.expect(bus.getProperty(ClassHelper.USE_DEFAULT_CLASS_HELPER)).andReturn(true);
+        
EasyMock.expect(bus.getProperty(ClassUnwrapper.class.getName())).andReturn(null);
+        EasyMock.replay(bus);
+
+        assertSame(fn.getClass(), ClassHelper.getRealClass(fn));
+
+        EasyMock.verify(bus);
+    }
+
+    @Test
+    public void getRealLambdaClassFromClassPropertyWasSetInBus() {
+        
EasyMock.expect(bus.getProperty(ClassHelper.USE_DEFAULT_CLASS_HELPER)).andReturn(true);
+        
EasyMock.expect(bus.getProperty(ClassUnwrapper.class.getName())).andReturn(null);
+        EasyMock.replay(bus);
+
+        assertSame(fn.getClass(), 
ClassHelper.getRealClassFromClass(fn.getClass()));
+
+        EasyMock.verify(bus);
+    }
+
+    @Test
+    public void getRealLambdaClassFromClassPropertyWasNotSetInBus() {
+        
EasyMock.expect(bus.getProperty(ClassHelper.USE_DEFAULT_CLASS_HELPER)).andReturn(false);
+        
EasyMock.expect(bus.getProperty(ClassUnwrapper.class.getName())).andReturn(null);
+        EasyMock.replay(bus);
+
+        assertSame(fn.getClass(), 
ClassHelper.getRealClassFromClass(fn.getClass()));
+
+        EasyMock.verify(bus);
+    }
+
+    @Test
+    public void getRealCglibClassFromClassPropertyWasNotSetInBus() {
+        
EasyMock.expect(bus.getProperty(ClassHelper.USE_DEFAULT_CLASS_HELPER)).andReturn(false);
+        
EasyMock.expect(bus.getProperty(ClassUnwrapper.class.getName())).andReturn(null);
+        EasyMock.replay(bus);
+
+        assertSame(Object.class, 
ClassHelper.getRealClassFromClass(cglibProxyObject.getClass()));
+
+        EasyMock.verify(bus);
+    }
+
+    @Test
+    public void getRealCglibClassFromClassPropertyWasSetInBus() {
+        
EasyMock.expect(bus.getProperty(ClassHelper.USE_DEFAULT_CLASS_HELPER)).andReturn(true);
+        
EasyMock.expect(bus.getProperty(ClassUnwrapper.class.getName())).andReturn(null);
+        EasyMock.replay(bus);
+
+        assertSame(cglibProxyObject.getClass(), 
ClassHelper.getRealClassFromClass(cglibProxyObject.getClass()));
+
+        EasyMock.verify(bus);
+    }
+
     public interface AnyInterface {
         void anyMethod();
     }
diff --git 
a/integration/cdi/src/main/java/org/apache/cxf/cdi/CdiClassUnwrapper.java 
b/integration/cdi/src/main/java/org/apache/cxf/cdi/CdiClassUnwrapper.java
index ac54da9..a94b109 100644
--- a/integration/cdi/src/main/java/org/apache/cxf/cdi/CdiClassUnwrapper.java
+++ b/integration/cdi/src/main/java/org/apache/cxf/cdi/CdiClassUnwrapper.java
@@ -42,6 +42,11 @@ class CdiClassUnwrapper implements ClassUnwrapper {
     @Override
     public Class<?> getRealClass(Object o) {
         Class<?> clazz = o.getClass();
+        return getRealClassFromClass(clazz);
+    }
+    
+    @Override
+    public Class<?> getRealClassFromClass(Class<?> clazz) {
         if (PROXY_PATTERN.matcher(clazz.getSimpleName()).matches()) {
             return clazz.getSuperclass();
         }

Reply via email to