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 bcf569b37bf625dfdccbe84fa624f9a12ca40e0b 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 | 34 ++-- .../org/apache/struts2/ognl/OgnlCaffeineCache.java | 7 + .../org/apache/struts2/ognl/OgnlDefaultCache.java | 16 +- .../java/org/apache/struts2/ognl/OgnlLRUCache.java | 6 + .../java/org/apache/struts2/ognl/OgnlUtil.java | 31 ++-- .../java/org/apache/struts2/util/ProxyUtil.java | 180 +++++++-------------- .../java/org/apache/struts2/ognl/OgnlUtilTest.java | 26 +-- .../org/apache/struts2/json/DefaultJSONWriter.java | 6 +- 10 files changed, 162 insertions(+), 176 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..d7c88b336 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,37 @@ */ 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); - Value get(Key key); + /** + * @since 7.1 + */ + default V computeIfAbsent(K key, + Function<? super K, ? extends V> mappingFunction) { + requireNonNull(mappingFunction); + if (get(key) == null) { + putIfAbsent(key, mappingFunction.apply(key)); + } + return get(key); + } - void put(Key key, Value value); + void put(K key, V value); - void putIfAbsent(Key key, Value value); + void putIfAbsent(K key, V 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..b7f5b2750 100644 --- a/core/src/main/java/org/apache/struts2/ognl/OgnlCaffeineCache.java +++ b/core/src/main/java/org/apache/struts2/ognl/OgnlCaffeineCache.java @@ -18,6 +18,8 @@ package org.apache.struts2.ognl; import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; +import java.util.function.Function; + /** * <p>This OGNL Cache implementation is backed by {@link Caffeine} which uses the Window TinyLfu algorithm.</p> * @@ -46,6 +48,11 @@ public class OgnlCaffeineCache<K, V> implements OgnlCache<K, V> { return cache.getIfPresent(key); } + @Override + public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) { + return cache.asMap().computeIfAbsent(key, mappingFunction); + } + @Override public void put(K key, V value) { cache.put(key, value); 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..391944a10 100644 --- a/core/src/main/java/org/apache/struts2/ognl/OgnlDefaultCache.java +++ b/core/src/main/java/org/apache/struts2/ognl/OgnlDefaultCache.java @@ -17,6 +17,7 @@ package org.apache.struts2.ognl; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; /** * <p>Basic OGNL cache implementation.</p> @@ -45,16 +46,21 @@ public class OgnlDefaultCache<K, V> implements OgnlCache<K, V> { return ognlCache.get(key); } + @Override + public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) { + return ognlCache.computeIfAbsent(key, mappingFunction); + } + @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(); + clearIfEvictionLimitExceeded(); } @Override @@ -69,12 +75,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..fc995d438 100644 --- a/core/src/main/java/org/apache/struts2/ognl/OgnlLRUCache.java +++ b/core/src/main/java/org/apache/struts2/ognl/OgnlLRUCache.java @@ -19,6 +19,7 @@ import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; /** * <p>A basic OGNL LRU cache implementation.</p> @@ -54,6 +55,11 @@ public class OgnlLRUCache<K, V> implements OgnlCache<K, V> { return ognlLRUCache.get(key); } + @Override + public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) { + return ognlLRUCache.computeIfAbsent(key, mappingFunction); + } + @Override public void put(K key, V value) { ognlLRUCache.put(key, value); 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..2eff5939a 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; @@ -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)