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

kusal pushed a commit to branch WW-5534-proxyutil
in repository https://gitbox.apache.org/repos/asf/struts.git

commit 4316c6e6280a15b37f0a3d71d169061ba826c41e
Author: Kusal Kithul-Godage <g...@kusal.io>
AuthorDate: Fri Feb 28 04:07:25 2025 +1100

    WW-5534 Simplify ProxyUtil, add OgnlCache#computeIfAbsent
---
 core/pom.xml                                       |   8 +-
 .../struts2/interceptor/ChainingInterceptor.java   |  24 +--
 .../java/org/apache/struts2/ognl/OgnlCache.java    |  50 ++++--
 .../org/apache/struts2/ognl/OgnlCaffeineCache.java |   4 +-
 .../org/apache/struts2/ognl/OgnlDefaultCache.java  |  17 +-
 .../java/org/apache/struts2/ognl/OgnlLRUCache.java |   4 +-
 .../java/org/apache/struts2/ognl/OgnlUtil.java     |  31 ++--
 .../java/org/apache/struts2/util/ProxyUtil.java    | 180 +++++++--------------
 .../java/org/apache/struts2/ognl/OgnlUtilTest.java |  28 ++--
 .../org/apache/struts2/json/DefaultJSONWriter.java |   6 +-
 10 files changed, 169 insertions(+), 183 deletions(-)

diff --git a/core/pom.xml b/core/pom.xml
index 2e10aafcc..a70b30133 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -187,13 +187,19 @@
             <artifactId>commons-text</artifactId>
         </dependency>
 
-        <!-- Optional used in org.apache.struts2.util.ProxyUtil to detect if 
object is HibernateProxy -->
         <dependency>
+            <!-- Optional used in org.apache.struts2.util.ProxyUtil to detect 
if object is HibernateProxy -->
             <groupId>org.hibernate</groupId>
             <artifactId>hibernate-core</artifactId>
             <version>5.6.15.Final</version>
             <optional>true</optional>
         </dependency>
+        <dependency>
+            <!-- Optional used in org.apache.struts2.util.ProxyUtil to detect 
if object is Spring proxy -->
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-aop</artifactId>
+            <optional>true</optional>
+        </dependency>
 
         <dependency>
             <groupId>org.springframework</groupId>
diff --git 
a/core/src/main/java/org/apache/struts2/interceptor/ChainingInterceptor.java 
b/core/src/main/java/org/apache/struts2/interceptor/ChainingInterceptor.java
index c114d65e1..2837d4c10 100644
--- a/core/src/main/java/org/apache/struts2/interceptor/ChainingInterceptor.java
+++ b/core/src/main/java/org/apache/struts2/interceptor/ChainingInterceptor.java
@@ -20,11 +20,11 @@ package org.apache.struts2.interceptor;
 
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
-import org.apache.struts2.result.ActionChainResult;
 import org.apache.struts2.ActionInvocation;
 import org.apache.struts2.StrutsConstants;
 import org.apache.struts2.Unchainable;
 import org.apache.struts2.inject.Inject;
+import org.apache.struts2.result.ActionChainResult;
 import org.apache.struts2.result.Result;
 import org.apache.struts2.util.CompoundRoot;
 import org.apache.struts2.util.ProxyUtil;
@@ -167,17 +167,18 @@ public class ChainingInterceptor extends 
AbstractInterceptor {
     }
 
     private void copyStack(ActionInvocation invocation, CompoundRoot root) {
-        List list = prepareList(root);
+        List<Object> list = prepareList(root);
         Map<String, Object> ctxMap = 
invocation.getInvocationContext().getContextMap();
         for (Object object : list) {
-            if (shouldCopy(object)) {
-                Object action = invocation.getAction();
-                Class<?> editable = null;
-                if(ProxyUtil.isProxy(action)) {
-                    editable = ProxyUtil.ultimateTargetClass(action);
-                }
-                reflectionProvider.copy(object, action, ctxMap, 
prepareExcludes(), includes, editable);
+            if (!shouldCopy(object)) {
+                continue;
+            }
+            Object action = invocation.getAction();
+            Class<?> editable = null;
+            if (ProxyUtil.isProxy(action)) {
+                editable = ProxyUtil.ultimateTargetClass(action);
             }
+            reflectionProvider.copy(object, action, ctxMap, prepareExcludes(), 
includes, editable);
         }
     }
 
@@ -204,9 +205,8 @@ public class ChainingInterceptor extends 
AbstractInterceptor {
         return o != null && !(o instanceof Unchainable);
     }
 
-    @SuppressWarnings("unchecked")
-    private List prepareList(CompoundRoot root) {
-        List list = new ArrayList(root);
+    private List<Object> prepareList(CompoundRoot root) {
+        var list = new ArrayList<>(root);
         list.remove(0);
         Collections.reverse(list);
         return list;
diff --git a/core/src/main/java/org/apache/struts2/ognl/OgnlCache.java 
b/core/src/main/java/org/apache/struts2/ognl/OgnlCache.java
index ec2d002e9..3c3e1d9b4 100644
--- a/core/src/main/java/org/apache/struts2/ognl/OgnlCache.java
+++ b/core/src/main/java/org/apache/struts2/ognl/OgnlCache.java
@@ -15,21 +15,53 @@
  */
 package org.apache.struts2.ognl;
 
+import java.util.function.Function;
+
+import static java.util.Objects.requireNonNull;
+
 /**
- * A basic cache interface for use with OGNL processing (such as Expression, 
BeanInfo).
- * All OGNL caches will have an eviction limit, but setting an extremely high 
value can
- * simulate an "effectively unlimited" cache.
+ * A basic cache interface for use with OGNL processing (such as Expression, 
BeanInfo). Implementation must be
+ * thread-safe. All OGNL caches will have an eviction limit, but setting an 
extremely high value can simulate an
+ * "effectively unlimited" cache.
  *
- * @param <Key> The type for the cache key entries
- * @param <Value> The type for the cache value entries
+ * @param <K> The type for the cache key entries
+ * @param <V> The type for the cache value entries
  */
-public interface OgnlCache<Key, Value> {
+public interface OgnlCache<K, V> {
+
+    V get(K key);
+
+    /**
+     * @since 7.1
+     */
+    default V computeIfAbsent(K key,
+                              Function<? super K, ? extends V> 
mappingFunction) {
+        requireNonNull(mappingFunction);
+        V oldValue, newValue;
+        return (oldValue = get(key)) == null
+                && (newValue = mappingFunction.apply(key)) != null
+                && (oldValue = putOnlyIfAbsent(key, newValue)) == null
+                ? newValue
+                : oldValue;
+    }
 
-    Value get(Key key);
+    void put(K key, V value);
 
-    void put(Key key, Value value);
+    /**
+     * @since 7.1
+     */
+    default V putOnlyIfAbsent(K key, V value) {
+        putIfAbsent(key, value);
+        return get(key);
+    }
 
-    void putIfAbsent(Key key, Value value);
+    /**
+     * @deprecated since 7.1, use {@link #putOnlyIfAbsent(Object, Object)} 
instead.
+     */
+    @Deprecated
+    default void putIfAbsent(K key, V value) {
+        putOnlyIfAbsent(key, value);
+    }
 
     int size();
 
diff --git a/core/src/main/java/org/apache/struts2/ognl/OgnlCaffeineCache.java 
b/core/src/main/java/org/apache/struts2/ognl/OgnlCaffeineCache.java
index 22ba4c62e..3d85ec188 100644
--- a/core/src/main/java/org/apache/struts2/ognl/OgnlCaffeineCache.java
+++ b/core/src/main/java/org/apache/struts2/ognl/OgnlCaffeineCache.java
@@ -52,8 +52,8 @@ public class OgnlCaffeineCache<K, V> implements OgnlCache<K, 
V> {
     }
 
     @Override
-    public void putIfAbsent(K key, V value) {
-        cache.asMap().putIfAbsent(key, value);
+    public V putOnlyIfAbsent(K key, V value) {
+        return cache.asMap().putIfAbsent(key, value);
     }
 
     @Override
diff --git a/core/src/main/java/org/apache/struts2/ognl/OgnlDefaultCache.java 
b/core/src/main/java/org/apache/struts2/ognl/OgnlDefaultCache.java
index 727c2177d..a5366bbdf 100644
--- a/core/src/main/java/org/apache/struts2/ognl/OgnlDefaultCache.java
+++ b/core/src/main/java/org/apache/struts2/ognl/OgnlDefaultCache.java
@@ -48,13 +48,16 @@ public class OgnlDefaultCache<K, V> implements OgnlCache<K, 
V> {
     @Override
     public void put(K key, V value) {
         ognlCache.put(key, value);
-        this.clearIfEvictionLimitExceeded();
+        clearIfEvictionLimitExceeded();
     }
 
     @Override
-    public void putIfAbsent(K key, V value) {
-        ognlCache.putIfAbsent(key, value);
-        this.clearIfEvictionLimitExceeded();
+    public V putOnlyIfAbsent(K key, V value) {
+        try {
+            return ognlCache.putIfAbsent(key, value);
+        } finally {
+            clearIfEvictionLimitExceeded();
+        }
     }
 
     @Override
@@ -69,12 +72,12 @@ public class OgnlDefaultCache<K, V> implements OgnlCache<K, 
V> {
 
     @Override
     public int getEvictionLimit() {
-        return this.cacheEvictionLimit.get();
+        return cacheEvictionLimit.get();
     }
 
     @Override
-    public void setEvictionLimit(int cacheEvictionLimit) {
-        this.cacheEvictionLimit.set(cacheEvictionLimit);
+    public void setEvictionLimit(int newCacheEvictionLimit) {
+        cacheEvictionLimit.set(newCacheEvictionLimit);
     }
 
     /**
diff --git a/core/src/main/java/org/apache/struts2/ognl/OgnlLRUCache.java 
b/core/src/main/java/org/apache/struts2/ognl/OgnlLRUCache.java
index 6b43d71f2..a484a9492 100644
--- a/core/src/main/java/org/apache/struts2/ognl/OgnlLRUCache.java
+++ b/core/src/main/java/org/apache/struts2/ognl/OgnlLRUCache.java
@@ -60,8 +60,8 @@ public class OgnlLRUCache<K, V> implements OgnlCache<K, V> {
     }
 
     @Override
-    public void putIfAbsent(K key, V value) {
-        ognlLRUCache.putIfAbsent(key, value);
+    public V putOnlyIfAbsent(K key, V value) {
+        return ognlLRUCache.putIfAbsent(key, value);
     }
 
     @Override
diff --git a/core/src/main/java/org/apache/struts2/ognl/OgnlUtil.java 
b/core/src/main/java/org/apache/struts2/ognl/OgnlUtil.java
index 95bee5f43..4d51ff294 100644
--- a/core/src/main/java/org/apache/struts2/ognl/OgnlUtil.java
+++ b/core/src/main/java/org/apache/struts2/ognl/OgnlUtil.java
@@ -18,12 +18,6 @@
  */
 package org.apache.struts2.ognl;
 
-import org.apache.struts2.conversion.impl.XWorkConverter;
-import org.apache.struts2.inject.Container;
-import org.apache.struts2.inject.Inject;
-import org.apache.struts2.ognl.accessor.RootAccessor;
-import org.apache.struts2.util.CompoundRoot;
-import org.apache.struts2.util.reflection.ReflectionException;
 import ognl.ClassResolver;
 import ognl.Ognl;
 import ognl.OgnlContext;
@@ -35,7 +29,12 @@ import org.apache.commons.lang3.BooleanUtils;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 import org.apache.struts2.StrutsConstants;
-import org.apache.struts2.ognl.OgnlGuard;
+import org.apache.struts2.conversion.impl.XWorkConverter;
+import org.apache.struts2.inject.Container;
+import org.apache.struts2.inject.Inject;
+import org.apache.struts2.ognl.accessor.RootAccessor;
+import org.apache.struts2.util.CompoundRoot;
+import org.apache.struts2.util.reflection.ReflectionException;
 
 import java.beans.BeanInfo;
 import java.beans.IntrospectionException;
@@ -676,13 +675,19 @@ public class OgnlUtil {
      * @throws IntrospectionException is thrown if an exception occurs during 
introspection.
      */
     public BeanInfo getBeanInfo(Class<?> clazz) throws IntrospectionException {
-        synchronized (beanInfoCache) {
-            BeanInfo beanInfo = beanInfoCache.get(clazz);
-            if (beanInfo == null) {
-                beanInfo = Introspector.getBeanInfo(clazz, Object.class);
-                beanInfoCache.putIfAbsent(clazz, beanInfo);
+        try {
+            return beanInfoCache.computeIfAbsent(clazz, k -> {
+                try {
+                    return Introspector.getBeanInfo(k, Object.class);
+                } catch (IntrospectionException e) {
+                    throw new IllegalArgumentException(e);
+                }
+            });
+        } catch (IllegalArgumentException e) {
+            if (e.getCause() instanceof IntrospectionException innerEx) {
+                throw innerEx;
             }
-            return beanInfo;
+            throw e;
         }
     }
 
diff --git a/core/src/main/java/org/apache/struts2/util/ProxyUtil.java 
b/core/src/main/java/org/apache/struts2/util/ProxyUtil.java
index 71823b47e..a574af1c2 100644
--- a/core/src/main/java/org/apache/struts2/util/ProxyUtil.java
+++ b/core/src/main/java/org/apache/struts2/util/ProxyUtil.java
@@ -26,15 +26,19 @@ import org.apache.struts2.ognl.OgnlCache;
 import org.apache.struts2.ognl.OgnlCacheFactory;
 import org.hibernate.Hibernate;
 import org.hibernate.proxy.HibernateProxy;
+import org.springframework.aop.SpringProxy;
+import org.springframework.aop.TargetClassAware;
+import org.springframework.aop.framework.Advised;
+import org.springframework.aop.framework.AopProxyUtils;
+import org.springframework.aop.support.AopUtils;
 
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.Member;
 import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.lang.reflect.Proxy;
 
 import static java.lang.reflect.Modifier.isPublic;
+import static java.lang.reflect.Modifier.isStatic;
 
 /**
  * <code>ProxyUtil</code>
@@ -44,17 +48,14 @@ import static java.lang.reflect.Modifier.isPublic;
  *
  */
 public class ProxyUtil {
-    private static final String SPRING_ADVISED_CLASS_NAME = 
"org.springframework.aop.framework.Advised";
-    private static final String SPRING_SPRINGPROXY_CLASS_NAME = 
"org.springframework.aop.SpringProxy";
-    private static final String SPRING_SINGLETONTARGETSOURCE_CLASS_NAME = 
"org.springframework.aop.target.SingletonTargetSource";
-    private static final String SPRING_TARGETCLASSAWARE_CLASS_NAME = 
"org.springframework.aop.TargetClassAware";
-    private static final String HIBERNATE_HIBERNATEPROXY_CLASS_NAME = 
"org.hibernate.proxy.HibernateProxy";
     private static final int CACHE_MAX_SIZE = 10000;
     private static final int CACHE_INITIAL_CAPACITY = 256;
     private static final OgnlCache<Class<?>, Boolean> isProxyCache = new 
DefaultOgnlCacheFactory<Class<?>, Boolean>(
             CACHE_MAX_SIZE, OgnlCacheFactory.CacheType.WTLFU, 
CACHE_INITIAL_CAPACITY).buildOgnlCache();
     private static final OgnlCache<Member, Boolean> isProxyMemberCache = new 
DefaultOgnlCacheFactory<Member, Boolean>(
             CACHE_MAX_SIZE, OgnlCacheFactory.CacheType.WTLFU, 
CACHE_INITIAL_CAPACITY).buildOgnlCache();
+    private static final OgnlCache<Object, Class<?>> targetClassCache = new 
DefaultOgnlCacheFactory<Object, Class<?>>(
+            CACHE_MAX_SIZE, OgnlCacheFactory.CacheType.WTLFU, 
CACHE_INITIAL_CAPACITY).buildOgnlCache();
 
     /**
      * Determine the ultimate target class of the given instance, traversing
@@ -65,15 +66,18 @@ public class ProxyUtil {
      * object as fallback; never {@code null})
      */
     public static Class<?> ultimateTargetClass(Object candidate) {
-        Class<?> result = null;
-        if (isSpringAopProxy(candidate))
-            result = springUltimateTargetClass(candidate);
-
-        if (result == null) {
-            result = candidate.getClass();
-        }
-
-        return result;
+        return targetClassCache.computeIfAbsent(candidate, k -> {
+            Class<?> result = null;
+            if (isSpringAopProxy(k)) {
+                result = springUltimateTargetClass(k);
+            } else if (isHibernateProxy(k)) {
+                result = getHibernateProxyTarget(k).getClass();
+            }
+            if (result == null) {
+                result = k.getClass();
+            }
+            return result;
+        });
     }
 
     /**
@@ -82,16 +86,8 @@ public class ProxyUtil {
      */
     public static boolean isProxy(Object object) {
         if (object == null) return false;
-        Class<?> clazz = object.getClass();
-        Boolean flag = isProxyCache.get(clazz);
-        if (flag != null) {
-            return flag;
-        }
-
-        boolean isProxy = isSpringAopProxy(object) || isHibernateProxy(object);
-
-        isProxyCache.put(clazz, isProxy);
-        return isProxy;
+        return isProxyCache.computeIfAbsent(object.getClass(),
+                k -> isSpringAopProxy(object) || isHibernateProxy(object));
     }
 
     /**
@@ -100,19 +96,11 @@ public class ProxyUtil {
      * @param object the object to check
      */
     public static boolean isProxyMember(Member member, Object object) {
-        if (!Modifier.isStatic(member.getModifiers()) && !isProxy(object) && 
!isHibernateProxy(object)) {
+        if (!isStatic(member.getModifiers()) && !isProxy(object)) {
             return false;
         }
-
-        Boolean flag = isProxyMemberCache.get(member);
-        if (flag != null) {
-            return flag;
-        }
-
-        boolean isProxyMember = isSpringProxyMember(member) || 
isHibernateProxyMember(member);
-
-        isProxyMemberCache.put(member, isProxyMember);
-        return isProxyMember;
+        return isProxyMemberCache.computeIfAbsent(member,
+                k -> isSpringProxyMember(member) || 
isHibernateProxyMember(member));
     }
 
     /**
@@ -123,7 +111,7 @@ public class ProxyUtil {
     public static boolean isHibernateProxy(Object object) {
         try {
             return object != null && 
HibernateProxy.class.isAssignableFrom(object.getClass());
-        } catch (NoClassDefFoundError ignored) {
+        } catch (LinkageError ignored) {
             return false;
         }
     }
@@ -135,12 +123,10 @@ public class ProxyUtil {
      */
     public static boolean isHibernateProxyMember(Member member) {
         try {
-            Class<?> clazz = 
ClassLoaderUtil.loadClass(HIBERNATE_HIBERNATEPROXY_CLASS_NAME, ProxyUtil.class);
-            return hasMember(clazz, member);
-        } catch (ClassNotFoundException ignored) {
+            return hasMember(HibernateProxy.class, member);
+        } catch (LinkageError ignored) {
+            return false;
         }
-
-        return false;
     }
 
     /**
@@ -152,20 +138,11 @@ public class ProxyUtil {
      * object as fallback; never {@code null})
      */
     private static Class<?> springUltimateTargetClass(Object candidate) {
-        Object current = candidate;
-        Class<?> result = null;
-        while (null != current && implementsInterface(current.getClass(), 
SPRING_TARGETCLASSAWARE_CLASS_NAME)) {
-            try {
-                result = (Class<?>) MethodUtils.invokeMethod(current, 
"getTargetClass");
-            } catch (Throwable ignored) {
-            }
-            current = getSingletonTarget(current);
-        }
-        if (result == null) {
-            Class<?> clazz = candidate.getClass();
-            result = (isCglibProxyClass(clazz) ? clazz.getSuperclass() : 
candidate.getClass());
+        try {
+            return AopProxyUtils.ultimateTargetClass(candidate);
+        } catch (LinkageError ignored) {
+            return candidate.getClass();
         }
-        return result;
     }
 
     /**
@@ -173,9 +150,11 @@ public class ProxyUtil {
      * @param object the object to check
      */
     private static boolean isSpringAopProxy(Object object) {
-        Class<?> clazz = object.getClass();
-        return (implementsInterface(clazz, SPRING_SPRINGPROXY_CLASS_NAME) && 
(Proxy.isProxyClass(clazz)
-                || isCglibProxyClass(clazz)));
+        try {
+            return AopUtils.isAopProxy(object);
+        } catch (LinkageError ignored) {
+            return false;
+        }
     }
 
     /**
@@ -184,79 +163,32 @@ public class ProxyUtil {
      */
     private static boolean isSpringProxyMember(Member member) {
         try {
-            Class<?> clazz = 
ClassLoaderUtil.loadClass(SPRING_ADVISED_CLASS_NAME, ProxyUtil.class);
-            if (hasMember(clazz, member))
+            if (hasMember(Advised.class, member))
                 return true;
-            clazz = 
ClassLoaderUtil.loadClass(SPRING_TARGETCLASSAWARE_CLASS_NAME, ProxyUtil.class);
-            if (hasMember(clazz, member))
+            if (hasMember(TargetClassAware.class, member))
                 return true;
-            clazz = ClassLoaderUtil.loadClass(SPRING_SPRINGPROXY_CLASS_NAME, 
ProxyUtil.class);
-            if (hasMember(clazz, member))
+            if (hasMember(SpringProxy.class, member))
                 return true;
-        } catch (ClassNotFoundException ignored) {
+        } catch (LinkageError ignored) {
         }
-
         return false;
     }
 
-    /**
-     * Obtain the singleton target object behind the given spring proxy, if 
any.
-     * @param candidate the (potential) spring proxy to check
-     * @return the singleton target object, or {@code null} in any other case
-     * (not a spring proxy, not an existing singleton target)
-     */
-    private static Object getSingletonTarget(Object candidate) {
-        try {
-            if (implementsInterface(candidate.getClass(), 
SPRING_ADVISED_CLASS_NAME)) {
-                Object targetSource = MethodUtils.invokeMethod(candidate, 
"getTargetSource");
-                if (implementsInterface(targetSource.getClass(), 
SPRING_SINGLETONTARGETSOURCE_CLASS_NAME)) {
-                    return MethodUtils.invokeMethod(targetSource, "getTarget");
-                }
-            }
-        } catch (Throwable ignored) {
-        }
-
-        return null;
-    }
-
-    /**
-     * Check whether the specified class is a CGLIB-generated class.
-     * @param clazz the class to check
-     */
-    private static boolean isCglibProxyClass(Class<?> clazz) {
-        return (clazz != null && clazz.getName().contains("$$"));
-    }
-
-    /**
-     * Check whether the given class implements an interface with a given 
class name.
-     * @param clazz the class to check
-     * @param ifaceClassName the interface class name to check
-     */
-    private static boolean implementsInterface(Class<?> clazz, String 
ifaceClassName) {
-        try {
-            Class<?> ifaceClass = ClassLoaderUtil.loadClass(ifaceClassName, 
ProxyUtil.class);
-            return ifaceClass.isAssignableFrom(clazz);
-        } catch (ClassNotFoundException e) {
-            return false;
-        }
-    }
-
     /**
      * Check whether the given class has a given member.
      * @param clazz the class to check
      * @param member the member to check
      */
     private static boolean hasMember(Class<?> clazz, Member member) {
-        if (member instanceof Method) {
-            return null != MethodUtils.getMatchingMethod(clazz, 
member.getName(), ((Method) member).getParameterTypes());
+        if (member instanceof Method method) {
+            return null != MethodUtils.getMatchingMethod(clazz, 
member.getName(), method.getParameterTypes());
         }
         if (member instanceof Field) {
             return null != FieldUtils.getField(clazz, member.getName(), true);
         }
-        if (member instanceof Constructor) {
-            return null != 
ConstructorUtils.getMatchingAccessibleConstructor(clazz, ((Constructor) 
member).getParameterTypes());
+        if (member instanceof Constructor<?> constructor) {
+            return null != 
ConstructorUtils.getMatchingAccessibleConstructor(clazz, 
constructor.getParameterTypes());
         }
-
         return false;
     }
 
@@ -266,26 +198,34 @@ public class ProxyUtil {
     public static Object getHibernateProxyTarget(Object object) {
         try {
             return Hibernate.unproxy(object);
-        } catch (NoClassDefFoundError ignored) {
+        } catch (LinkageError ignored) {
             return object;
         }
     }
 
     /**
-     * @return matching member on target object if one exists, otherwise the 
same member
+     * @deprecated since 7.1, use {@link #resolveTargetMember(Member, Class)} 
instead.
      */
+    @Deprecated
     public static Member resolveTargetMember(Member proxyMember, Object 
target) {
+        return resolveTargetMember(proxyMember, target.getClass());
+    }
+
+    /**
+     * @return matching member on target object if one exists, otherwise the 
same member
+     */
+    public static Member resolveTargetMember(Member proxyMember, Class<?> 
targetClass) {
         int mod = proxyMember.getModifiers();
         if (proxyMember instanceof Method) {
             if (isPublic(mod)) {
-                return 
MethodUtils.getMatchingAccessibleMethod(target.getClass(), 
proxyMember.getName(), ((Method) proxyMember).getParameterTypes());
+                return MethodUtils.getMatchingAccessibleMethod(targetClass, 
proxyMember.getName(), ((Method) proxyMember).getParameterTypes());
             } else {
-                return MethodUtils.getMatchingMethod(target.getClass(), 
proxyMember.getName(), ((Method) proxyMember).getParameterTypes());
+                return MethodUtils.getMatchingMethod(targetClass, 
proxyMember.getName(), ((Method) proxyMember).getParameterTypes());
             }
         } else if (proxyMember instanceof Field) {
-            return FieldUtils.getField(target.getClass(), 
proxyMember.getName(), isPublic(mod));
+            return FieldUtils.getField(targetClass, proxyMember.getName(), 
isPublic(mod));
         } else if (proxyMember instanceof Constructor && isPublic(mod)) {
-            return 
ConstructorUtils.getMatchingAccessibleConstructor(target.getClass(), 
((Constructor<?>) proxyMember).getParameterTypes());
+            return 
ConstructorUtils.getMatchingAccessibleConstructor(targetClass, 
((Constructor<?>) proxyMember).getParameterTypes());
         }
         return proxyMember;
     }
diff --git a/core/src/test/java/org/apache/struts2/ognl/OgnlUtilTest.java 
b/core/src/test/java/org/apache/struts2/ognl/OgnlUtilTest.java
index 9c526ffce..017baac33 100644
--- a/core/src/test/java/org/apache/struts2/ognl/OgnlUtilTest.java
+++ b/core/src/test/java/org/apache/struts2/ognl/OgnlUtilTest.java
@@ -18,8 +18,18 @@
  */
 package org.apache.struts2.ognl;
 
+import ognl.InappropriateExpressionException;
+import ognl.MethodFailedException;
+import ognl.NoSuchPropertyException;
+import ognl.NullHandler;
+import ognl.Ognl;
+import ognl.OgnlContext;
+import ognl.OgnlException;
+import ognl.OgnlRuntime;
+import ognl.SimpleNode;
 import org.apache.struts2.ActionContext;
-import org.apache.struts2.text.StubTextProvider;
+import org.apache.struts2.StrutsConstants;
+import org.apache.struts2.StrutsException;
 import org.apache.struts2.StubValueStack;
 import org.apache.struts2.XWorkTestCase;
 import org.apache.struts2.config.ConfigurationException;
@@ -30,6 +40,7 @@ import org.apache.struts2.ognl.accessor.CompoundRootAccessor;
 import org.apache.struts2.ognl.accessor.RootAccessor;
 import org.apache.struts2.test.StubConfigurationProvider;
 import org.apache.struts2.test.User;
+import org.apache.struts2.text.StubTextProvider;
 import org.apache.struts2.util.Bar;
 import org.apache.struts2.util.CompoundRoot;
 import org.apache.struts2.util.Foo;
@@ -37,17 +48,6 @@ import org.apache.struts2.util.Owner;
 import org.apache.struts2.util.ValueStack;
 import org.apache.struts2.util.location.LocatableProperties;
 import org.apache.struts2.util.reflection.ReflectionContextState;
-import ognl.InappropriateExpressionException;
-import ognl.MethodFailedException;
-import ognl.NoSuchPropertyException;
-import ognl.NullHandler;
-import ognl.Ognl;
-import ognl.OgnlContext;
-import ognl.OgnlException;
-import ognl.OgnlRuntime;
-import ognl.SimpleNode;
-import org.apache.struts2.StrutsConstants;
-import org.apache.struts2.StrutsException;
 
 import java.beans.BeanInfo;
 import java.beans.IntrospectionException;
@@ -1573,7 +1573,7 @@ public class OgnlUtilTest extends XWorkTestCase {
         assertNull("Lookup of value 0 (should have been evicted with 
everything) returned non-null value ?", lookupResult);
         lookupResult = defaultCache.get(3);
         assertNull("Lookup of value 3 (should have been evicted with 
everything) returned non-null value ?", lookupResult);
-        defaultCache.putIfAbsent(2, "Two");
+        defaultCache.putOnlyIfAbsent(2, "Two");
         lookupResult = defaultCache.get(2);
         assertEquals("Retrieved value does not match put value ?", "Two", 
lookupResult);
         defaultCache.clear();
@@ -1599,7 +1599,7 @@ public class OgnlUtilTest extends XWorkTestCase {
         assertEquals("LRU cache not size evictionlimit after adding  values 
?", lruCache.getEvictionLimit(), lruCache.size());
         lookupResult = lruCache.get(0);
         assertNull("Lookup of value 0 (should have dropped off LRU cache) 
returned non-null value ?", lookupResult);
-        lruCache.putIfAbsent(2, "Two");
+        lruCache.putOnlyIfAbsent(2, "Two");
         lookupResult = lruCache.get(2);
         assertEquals("Retrieved value does not match put value ?", "Two", 
lookupResult);
         lruCache.clear();
diff --git 
a/plugins/json/src/main/java/org/apache/struts2/json/DefaultJSONWriter.java 
b/plugins/json/src/main/java/org/apache/struts2/json/DefaultJSONWriter.java
index d6328af52..8e768bd91 100644
--- a/plugins/json/src/main/java/org/apache/struts2/json/DefaultJSONWriter.java
+++ b/plugins/json/src/main/java/org/apache/struts2/json/DefaultJSONWriter.java
@@ -18,15 +18,15 @@
  */
 package org.apache.struts2.json;
 
-import org.apache.struts2.inject.Inject;
-import org.apache.struts2.util.ProxyUtil;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
+import org.apache.struts2.inject.Inject;
 import org.apache.struts2.json.annotations.JSON;
 import org.apache.struts2.json.annotations.JSONFieldBridge;
 import org.apache.struts2.json.annotations.JSONParameter;
 import org.apache.struts2.json.bridge.FieldBridge;
 import org.apache.struts2.json.bridge.ParameterizedBridge;
+import org.apache.struts2.util.ProxyUtil;
 
 import java.beans.BeanInfo;
 import java.beans.IntrospectionException;
@@ -221,7 +221,7 @@ public class DefaultJSONWriter implements JSONWriter {
         BeanInfo info;
 
         try {
-            Class clazz = excludeProxyProperties ? 
ProxyUtil.ultimateTargetClass(object) : object.getClass();
+            Class<?> clazz = excludeProxyProperties ? 
ProxyUtil.ultimateTargetClass(object) : object.getClass();
 
             info = ((object == this.root) && this.ignoreHierarchy)
                     ? getBeanInfoIgnoreHierarchy(clazz)

Reply via email to