From a25315b3d313d58537fc1329805b5b9bcc7492c6 Mon Sep 17 00:00:00 2001 From: Lukasz Lenart Date: Wed, 17 Oct 2012 18:33:22 +0000 Subject: [PATCH] WW-3902 adds cache for BeanInfo to improve performance and avoid dead locks git-svn-id: https://svn.apache.org/repos/asf/struts/struts2/trunk@1399375 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/struts2/json/JSONWriter.java | 66 +++++++++++++------ 1 file changed, 47 insertions(+), 19 deletions(-) diff --git a/plugins/json/src/main/java/org/apache/struts2/json/JSONWriter.java b/plugins/json/src/main/java/org/apache/struts2/json/JSONWriter.java index f838299c3..b77199f4a 100644 --- a/plugins/json/src/main/java/org/apache/struts2/json/JSONWriter.java +++ b/plugins/json/src/main/java/org/apache/struts2/json/JSONWriter.java @@ -29,6 +29,7 @@ import org.apache.struts2.json.bridge.FieldBridge; import org.apache.struts2.json.bridge.ParameterizedBridge; import java.beans.BeanInfo; +import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.lang.reflect.Array; @@ -38,6 +39,8 @@ import java.text.DateFormat; import java.text.SimpleDateFormat; import java.text.StringCharacterIterator; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import java.util.regex.Pattern; /** @@ -51,13 +54,17 @@ public class JSONWriter { private static final Logger LOG = LoggerFactory.getLogger(JSONWriter.class); /** - * By default, enums are serialzied as name=value pairs + * By default, enums are serialised as name=value pairs */ public static final boolean ENUM_AS_BEAN_DEFAULT = false; - static char[] hex = "0123456789ABCDEF".toCharArray(); + private static char[] hex = "0123456789ABCDEF".toCharArray(); + + private static final ConcurrentMap, BeanInfo> BEAN_INFO_CACHE_IGNORE_HIERARCHY = new ConcurrentHashMap, BeanInfo>(); + private static final ConcurrentMap, BeanInfo> BEAN_INFO_CACHE = new ConcurrentHashMap, BeanInfo>(); + private StringBuilder buf = new StringBuilder(); - private Stack stack = new Stack(); + private Stack stack = new Stack(); private boolean ignoreHierarchy = true; private Object root; private boolean buildExpr = true; @@ -182,8 +189,9 @@ public class JSONWriter { try { Class clazz = object.getClass(); - info = ((object == this.root) && this.ignoreHierarchy) ? Introspector.getBeanInfo(clazz, clazz - .getSuperclass()) : Introspector.getBeanInfo(clazz); + info = ((object == this.root) && this.ignoreHierarchy) + ? getBeanInfoIgnoreHierarchy(clazz) + : getBeanInfo(clazz); PropertyDescriptor[] props = info.getPropertyDescriptors(); @@ -241,6 +249,26 @@ public class JSONWriter { this.add("}"); } + protected BeanInfo getBeanInfoIgnoreHierarchy(final Class clazz) throws IntrospectionException { + BeanInfo beanInfo = BEAN_INFO_CACHE_IGNORE_HIERARCHY.get(clazz); + if (beanInfo != null) { + return beanInfo; + } + beanInfo = Introspector.getBeanInfo(clazz, clazz.getSuperclass()); + BEAN_INFO_CACHE_IGNORE_HIERARCHY.put(clazz, beanInfo); + return beanInfo; + } + + protected BeanInfo getBeanInfo(final Class clazz) throws IntrospectionException { + BeanInfo beanInfo = BEAN_INFO_CACHE.get(clazz); + if (beanInfo != null) { + return beanInfo; + } + beanInfo = Introspector.getBeanInfo(clazz); + BEAN_INFO_CACHE.put(clazz, beanInfo); + return beanInfo; + } + protected Object getBridgedValue(Method baseAccessor, Object value) throws InstantiationException, IllegalAccessException { JSONFieldBridge fieldBridgeAnn = baseAccessor.getAnnotation(JSONFieldBridge.class); if (fieldBridgeAnn != null) { @@ -261,7 +289,7 @@ public class JSONWriter { protected Method findBaseAccessor(Class clazz, Method accessor) { Method baseAccessor = null; - if (clazz.getName().indexOf("$$EnhancerByCGLIB$$") > -1) { + if (clazz.getName().contains("$$EnhancerByCGLIB$$")) { try { baseAccessor = Thread.currentThread().getContextClassLoader().loadClass( clazz.getName().substring(0, clazz.getName().indexOf("$$"))).getMethod( @@ -269,7 +297,7 @@ public class JSONWriter { } catch (Exception ex) { LOG.debug(ex.getMessage(), ex); } - } else if (clazz.getName().indexOf("$$_javassist") > -1) { + } else if (clazz.getName().contains("$$_javassist")) { try { baseAccessor = Class.forName( clazz.getName().substring(0, clazz.getName().indexOf("_$$"))) @@ -295,13 +323,12 @@ public class JSONWriter { } } - protected boolean shouldExcludeProperty(PropertyDescriptor prop) throws SecurityException, - NoSuchFieldException { + protected boolean shouldExcludeProperty(PropertyDescriptor prop) throws SecurityException, NoSuchFieldException { String name = prop.getName(); - - return name.equals("class") || name.equals("declaringClass") || name.equals("cachedSuperClass") + return name.equals("class") + || name.equals("declaringClass") + || name.equals("cachedSuperClass") || name.equals("metaClass"); - } protected String expandExpr(int i) { @@ -309,8 +336,9 @@ public class JSONWriter { } protected String expandExpr(String property) { - if (this.exprStack.length() == 0) + if (this.exprStack.length() == 0) { return property; + } return this.exprStack + "." + property; } @@ -324,8 +352,9 @@ public class JSONWriter { if (this.excludeProperties != null) { for (Pattern pattern : this.excludeProperties) { if (pattern.matcher(expr).matches()) { - if (LOG.isDebugEnabled()) + if (LOG.isDebugEnabled()) { LOG.debug("Ignoring property because of exclude rule: " + expr); + } return true; } } @@ -337,12 +366,11 @@ public class JSONWriter { return false; } } - - if (LOG.isDebugEnabled()) + if (LOG.isDebugEnabled()){ LOG.debug("Ignoring property because of include rule: " + expr); + } return true; } - return false; } @@ -353,7 +381,6 @@ public class JSONWriter { if (excludeNullProperties && value == null) { return false; } - if (hasData) { this.add(','); } @@ -591,7 +618,7 @@ public class JSONWriter { this.accessor = accessor; } - boolean shouldSerialize() { + public boolean shouldSerialize() { return serialize; } @@ -609,4 +636,5 @@ public class JSONWriter { return this; } } + }