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();
}