diff --git a/plugins/json/src/main/java/org/apache/struts2/json/JSONResult.java b/plugins/json/src/main/java/org/apache/struts2/json/JSONResult.java index dfa0d6cff..c0ae8a6e6 100644 --- a/plugins/json/src/main/java/org/apache/struts2/json/JSONResult.java +++ b/plugins/json/src/main/java/org/apache/struts2/json/JSONResult.java @@ -20,12 +20,16 @@ */ package org.apache.struts2.json; -import com.opensymphony.xwork2.ActionContext; -import com.opensymphony.xwork2.ActionInvocation; -import com.opensymphony.xwork2.Result; -import com.opensymphony.xwork2.inject.Inject; -import com.opensymphony.xwork2.util.ValueStack; -import com.opensymphony.xwork2.util.WildcardUtil; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.regex.Pattern; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -33,13 +37,12 @@ import org.apache.struts2.StrutsConstants; import org.apache.struts2.StrutsStatics; import org.apache.struts2.json.smd.SMDGenerator; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.regex.Pattern; +import com.opensymphony.xwork2.ActionContext; +import com.opensymphony.xwork2.ActionInvocation; +import com.opensymphony.xwork2.Result; +import com.opensymphony.xwork2.inject.Inject; +import com.opensymphony.xwork2.util.ValueStack; +import com.opensymphony.xwork2.util.WildcardUtil; /** *

This result serializes an action @@ -84,6 +87,7 @@ public class JSONResult implements Result { private boolean ignoreInterfaces = true; private boolean enumAsBean = JSONWriter.ENUM_AS_BEAN_DEFAULT; private boolean noCache = false; + private boolean cacheBeanInfo = true; private boolean excludeNullProperties = false; private String defaultDateFormat = null; private int statusCode; @@ -92,12 +96,18 @@ public class JSONResult implements Result { private String contentType; private String wrapPrefix; private String wrapSuffix; - + private boolean devMode = false; + @Inject(StrutsConstants.STRUTS_I18N_ENCODING) public void setDefaultEncoding(String val) { this.defaultEncoding = val; } - + + @Inject(StrutsConstants.STRUTS_DEVMODE) + public void setDevMode(String val) { + this.devMode = BooleanUtils.toBoolean(val); + } + /** * Gets a list of regular expressions of properties to exclude from the JSON * output. @@ -171,7 +181,10 @@ public class JSONResult implements Result { ActionContext actionContext = invocation.getInvocationContext(); HttpServletRequest request = (HttpServletRequest) actionContext.get(StrutsStatics.HTTP_REQUEST); HttpServletResponse response = (HttpServletResponse) actionContext.get(StrutsStatics.HTTP_RESPONSE); - + + // only permit caching bean information when struts devMode = false + cacheBeanInfo = !devMode; + try { Object rootObject; rootObject = readRootObject(invocation); @@ -202,7 +215,7 @@ public class JSONResult implements Result { protected String createJSONString(HttpServletRequest request, Object rootObject) throws JSONException { String json = JSONUtil.serialize(rootObject, excludeProperties, includeProperties, ignoreHierarchy, - enumAsBean, excludeNullProperties, defaultDateFormat); + enumAsBean, excludeNullProperties, defaultDateFormat, cacheBeanInfo); json = addCallbackIfApplicable(request, json); return json; } diff --git a/plugins/json/src/main/java/org/apache/struts2/json/JSONUtil.java b/plugins/json/src/main/java/org/apache/struts2/json/JSONUtil.java index 0ee024e3d..5c2e9fcc7 100644 --- a/plugins/json/src/main/java/org/apache/struts2/json/JSONUtil.java +++ b/plugins/json/src/main/java/org/apache/struts2/json/JSONUtil.java @@ -20,20 +20,34 @@ */ package org.apache.struts2.json; -import com.opensymphony.xwork2.util.TextParseUtil; -import com.opensymphony.xwork2.util.WildcardUtil; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintWriter; +import java.io.Reader; +import java.io.Writer; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; +import java.util.zip.GZIPOutputStream; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.struts2.json.annotations.SMDMethod; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.*; -import java.lang.reflect.Method; -import java.util.*; -import java.util.regex.Pattern; -import java.util.zip.GZIPOutputStream; +import com.opensymphony.xwork2.util.TextParseUtil; +import com.opensymphony.xwork2.util.WildcardUtil; /** * Wrapper for JSONWriter with some utility methods. @@ -41,19 +55,23 @@ import java.util.zip.GZIPOutputStream; public class JSONUtil { public final static String RFC3339_FORMAT = "yyyy-MM-dd'T'HH:mm:ss"; - + public static final boolean CACHE_BEAN_INFO_DEFAULT = true; + private static final Logger LOG = LogManager.getLogger(JSONUtil.class); - + /** * Serializes an object into JSON. * * @param object * to be serialized + * @param cacheBeanInfo + * Specifies whether to cache bean info in the JSONWriter * @return JSON string * @throws JSONException */ - public static String serialize(Object object) throws JSONException { + public static String serialize(Object object, boolean cacheBeanInfo) throws JSONException { JSONWriter writer = new JSONWriter(); + writer.setCacheBeanInfo(cacheBeanInfo); return writer.write(object); } @@ -74,8 +92,33 @@ public class JSONUtil { public static String serialize(Object object, Collection excludeProperties, Collection includeProperties, boolean ignoreHierarchy, boolean excludeNullProperties) throws JSONException { + return serialize(object, excludeProperties, includeProperties, + ignoreHierarchy, excludeNullProperties, CACHE_BEAN_INFO_DEFAULT); + } + + /** + * Serializes an object into JSON, excluding any properties matching any of + * the regular expressions in the given collection. + * + * @param object + * to be serialized + * @param excludeProperties + * Patterns matching properties to exclude + * @param ignoreHierarchy + * whether to ignore properties defined on base classes of the + * root object + * @param cacheBeanInfo + * Specifies whether to cache bean info in the JSONWriter + * @return JSON string + * @throws JSONException + */ + public static String serialize(Object object, Collection excludeProperties, + Collection includeProperties, boolean ignoreHierarchy, boolean excludeNullProperties, + boolean cacheBeanInfo) + throws JSONException { JSONWriter writer = new JSONWriter(); writer.setIgnoreHierarchy(ignoreHierarchy); + writer.setCacheBeanInfo(cacheBeanInfo); return writer.write(object, excludeProperties, includeProperties, excludeNullProperties); } @@ -100,10 +143,38 @@ public class JSONUtil { public static String serialize(Object object, Collection excludeProperties, Collection includeProperties, boolean ignoreHierarchy, boolean enumAsBean, boolean excludeNullProperties, String defaultDateFormat) throws JSONException { + return serialize(object, excludeProperties, includeProperties, ignoreHierarchy, enumAsBean, + excludeNullProperties, defaultDateFormat, CACHE_BEAN_INFO_DEFAULT); + } + + /** + * Serializes an object into JSON, excluding any properties matching any of + * the regular expressions in the given collection. + * + * @param object + * to be serialized + * @param excludeProperties + * Patterns matching properties to exclude + * @param ignoreHierarchy + * whether to ignore properties defined on base classes of the + * root object + * @param enumAsBean + * whether to serialized enums a Bean or name=value pair + * @param defaultDateFormat + * date format used to serialize dates + * @param cacheBeanInfo + * Specifies whether to cache bean info in the JSONWriter + * @return JSON string + * @throws JSONException + */ + public static String serialize(Object object, Collection excludeProperties, + Collection includeProperties, boolean ignoreHierarchy, boolean enumAsBean, + boolean excludeNullProperties, String defaultDateFormat, boolean cacheBeanInfo) throws JSONException { JSONWriter writer = new JSONWriter(); writer.setIgnoreHierarchy(ignoreHierarchy); writer.setEnumAsBean(enumAsBean); writer.setDateFormatter(defaultDateFormat); + writer.setCacheBeanInfo(cacheBeanInfo); return writer.write(object, excludeProperties, includeProperties, excludeNullProperties); } @@ -118,7 +189,23 @@ public class JSONUtil { * @throws JSONException */ public static void serialize(Writer writer, Object object) throws IOException, JSONException { - writer.write(serialize(object)); + serialize(writer, object, CACHE_BEAN_INFO_DEFAULT); + } + + /** + * Serializes an object into JSON to the given writer. + * + * @param writer + * Writer to serialize the object to + * @param object + * object to be serialized + * @param cacheBeanInfo + * Specifies whether to cache bean info in the JSONWriter + * @throws IOException + * @throws JSONException + */ + public static void serialize(Writer writer, Object object, boolean cacheBeanInfo) throws IOException, JSONException { + writer.write(serialize(object, cacheBeanInfo)); } /** @@ -138,7 +225,29 @@ public class JSONUtil { public static void serialize(Writer writer, Object object, Collection excludeProperties, Collection includeProperties, boolean excludeNullProperties) throws IOException, JSONException { - writer.write(serialize(object, excludeProperties, includeProperties, true, excludeNullProperties)); + serialize(writer, object, excludeProperties, includeProperties, excludeNullProperties, CACHE_BEAN_INFO_DEFAULT); + } + + /** + * Serializes an object into JSON to the given writer, excluding any + * properties matching any of the regular expressions in the given + * collection. + * + * @param writer + * Writer to serialize the object to + * @param object + * object to be serialized + * @param excludeProperties + * Patterns matching properties to ignore + * @param cacheBeanInfo + * Specifies whether to cache bean info in the JSONWriter + * @throws IOException + * @throws JSONException + */ + public static void serialize(Writer writer, Object object, Collection excludeProperties, + Collection includeProperties, boolean excludeNullProperties, boolean cacheBeanInfo) + throws IOException, JSONException { + writer.write(serialize(object, excludeProperties, includeProperties, true, excludeNullProperties, cacheBeanInfo)); } /** 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 154508401..c3b888911 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 @@ -74,6 +74,7 @@ public class JSONWriter { private DateFormat formatter; private boolean enumAsBean = ENUM_AS_BEAN_DEFAULT; private boolean excludeNullProperties; + private boolean cacheBeanInfo = true; /** * @param object Object to be serialized into JSON @@ -620,6 +621,10 @@ public class JSONWriter { this.formatter = new SimpleDateFormat(defaultDateFormat); } } + + public void setCacheBeanInfo(boolean cacheBeanInfo) { + this.cacheBeanInfo = cacheBeanInfo; + } protected static class JSONAnnotationFinder { private boolean serialize = true; diff --git a/plugins/json/src/test/java/org/apache/struts2/json/JSONResultTest.java b/plugins/json/src/test/java/org/apache/struts2/json/JSONResultTest.java index bcd31e682..e9ca28353 100644 --- a/plugins/json/src/test/java/org/apache/struts2/json/JSONResultTest.java +++ b/plugins/json/src/test/java/org/apache/struts2/json/JSONResultTest.java @@ -20,16 +20,6 @@ */ package org.apache.struts2.json; -import com.opensymphony.xwork2.ActionContext; -import com.opensymphony.xwork2.mock.MockActionInvocation; -import com.opensymphony.xwork2.util.ValueStack; -import org.apache.struts2.StrutsStatics; -import org.apache.struts2.StrutsTestCase; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.mock.web.MockServletContext; - -import javax.servlet.http.HttpServletResponse; import java.math.BigDecimal; import java.math.BigInteger; import java.text.SimpleDateFormat; @@ -45,6 +35,18 @@ import java.util.Map; import java.util.Set; import java.util.regex.Pattern; +import javax.servlet.http.HttpServletResponse; + +import org.apache.struts2.StrutsStatics; +import org.apache.struts2.StrutsTestCase; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.mock.web.MockServletContext; + +import com.opensymphony.xwork2.ActionContext; +import com.opensymphony.xwork2.mock.MockActionInvocation; +import com.opensymphony.xwork2.util.ValueStack; + /** * JSONResultTest */ @@ -60,7 +62,7 @@ public class JSONResultTest extends StrutsTestCase { Map map = new HashMap(); map.put("createtime", new Date()); try { - JSONUtil.serialize(map); + JSONUtil.serialize(map, JSONUtil.CACHE_BEAN_INFO_DEFAULT); } catch (JSONException e) { fail(e.getMessage()); } diff --git a/plugins/json/src/test/java/org/apache/struts2/json/JSONUtilTest.java b/plugins/json/src/test/java/org/apache/struts2/json/JSONUtilTest.java index 2df5e47e6..dab592453 100644 --- a/plugins/json/src/test/java/org/apache/struts2/json/JSONUtilTest.java +++ b/plugins/json/src/test/java/org/apache/struts2/json/JSONUtilTest.java @@ -45,7 +45,7 @@ public class JSONUtilTest extends TestCase { bean1.setEnumField(AnEnum.ValueA); bean1.setEnumBean(AnEnumBean.Two); - String json = JSONUtil.serialize(bean1); + String json = JSONUtil.serialize(bean1, JSONUtil.CACHE_BEAN_INFO_DEFAULT); Map result = (Map) JSONUtil.deserialize(json); assertEquals("str", result.get("stringField"));