You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@struts.apache.org by lu...@apache.org on 2026/07/14 08:25:05 UTC
(struts) 03/03: WW-5650 obtain a fresh JSONUtil per request in JSONInterceptor
This is an automated email from the ASF dual-hosted git repository.
lukaszlenart pushed a commit to branch WW-5650-json-per-request-reader-writer
in repository https://gitbox.apache.org/repos/asf/struts.git
commit b51dff6dd5ec45df6ecd16128f13016902dccb65
Author: Lukasz Lenart <lu...@apache.org>
AuthorDate: Tue Jul 14 10:15:15 2026 +0200
WW-5650 obtain a fresh JSONUtil per request in JSONInterceptor
---
.../org/apache/struts2/json/JSONInterceptor.java | 18 +++++++----
.../apache/struts2/json/JSONInterceptorTest.java | 37 ++++++++++------------
2 files changed, 29 insertions(+), 26 deletions(-)
diff --git a/plugins/json/src/main/java/org/apache/struts2/json/JSONInterceptor.java b/plugins/json/src/main/java/org/apache/struts2/json/JSONInterceptor.java
index b1c172388..8328196ad 100644
--- a/plugins/json/src/main/java/org/apache/struts2/json/JSONInterceptor.java
+++ b/plugins/json/src/main/java/org/apache/struts2/json/JSONInterceptor.java
@@ -22,6 +22,7 @@ import org.apache.struts2.action.Action;
import org.apache.struts2.action.ParameterNameAware;
import org.apache.struts2.action.ParameterValueAware;
import org.apache.struts2.ActionInvocation;
+import org.apache.struts2.inject.Container;
import org.apache.struts2.inject.Inject;
import org.apache.struts2.interceptor.AbstractInterceptor;
import org.apache.struts2.interceptor.parameter.ParameterAuthorizer;
@@ -77,7 +78,7 @@ public class JSONInterceptor extends AbstractInterceptor {
private String jsonContentType = "application/json";
private String jsonRpcContentType = "application/json-rpc";
- private JSONUtil jsonUtil;
+ private Container container;
private ParameterAuthorizer parameterAuthorizer;
private ExcludedPatternsChecker excludedPatterns;
private AcceptedPatternsChecker acceptedPatterns;
@@ -98,6 +99,7 @@ public class JSONInterceptor extends AbstractInterceptor {
String requestContentType = readContentType(request);
String requestContentTypeEncoding = readContentTypeEncoding(request);
+ JSONUtil jsonUtil = getJSONUtil();
Object rootObject = null;
final ValueStack stack = invocation.getStack();
@@ -111,7 +113,7 @@ public class JSONInterceptor extends AbstractInterceptor {
if (jsonContentType.equalsIgnoreCase(requestContentType)) {
// load JSON object
- applyLimitsToReader();
+ applyLimitsToReader(jsonUtil);
Object obj = jsonUtil.deserializeInput(request.getReader(), maxLength);
// JSON array (this.root cannot be null in this case)
@@ -157,7 +159,7 @@ public class JSONInterceptor extends AbstractInterceptor {
Object result;
if (this.enableSMD) {
// load JSON object
- applyLimitsToReader();
+ applyLimitsToReader(jsonUtil);
Object obj = jsonUtil.deserializeInput(request.getReader(), maxLength);
if (obj instanceof Map) {
@@ -208,7 +210,7 @@ public class JSONInterceptor extends AbstractInterceptor {
return invocation.invoke();
}
- private void applyLimitsToReader() {
+ private void applyLimitsToReader(JSONUtil jsonUtil) {
JSONReader reader = jsonUtil.getReader();
reader.setMaxElements(maxElements);
reader.setMaxDepth(maxDepth);
@@ -744,8 +746,12 @@ public class JSONInterceptor extends AbstractInterceptor {
}
@Inject
- public void setJsonUtil(JSONUtil jsonUtil) {
- this.jsonUtil = jsonUtil;
+ public void setContainer(Container container) {
+ this.container = container;
+ }
+
+ protected JSONUtil getJSONUtil() {
+ return container.getInstance(JSONUtil.class);
}
@Inject
diff --git a/plugins/json/src/test/java/org/apache/struts2/json/JSONInterceptorTest.java b/plugins/json/src/test/java/org/apache/struts2/json/JSONInterceptorTest.java
index e1aed361b..3bf9a33ce 100644
--- a/plugins/json/src/test/java/org/apache/struts2/json/JSONInterceptorTest.java
+++ b/plugins/json/src/test/java/org/apache/struts2/json/JSONInterceptorTest.java
@@ -44,10 +44,7 @@ public class JSONInterceptorTest extends StrutsTestCase {
private JSONInterceptor createInterceptor() {
JSONInterceptor interceptor = new JSONInterceptor();
- JSONUtil jsonUtil = new JSONUtil();
- jsonUtil.setReader(new StrutsJSONReader());
- jsonUtil.setWriter(new StrutsJSONWriter());
- interceptor.setJsonUtil(jsonUtil);
+ interceptor.setContainer(container);
// Default: allow all parameters (simulates requireAnnotations=false)
interceptor.setParameterAuthorizer((parameterName, target, action) -> true);
interceptor.setExcludedPatterns(new org.apache.struts2.security.DefaultExcludedPatternsChecker());
@@ -567,10 +564,7 @@ public class JSONInterceptorTest extends StrutsTestCase {
this.request.addHeader("Content-Type", "application/json");
JSONInterceptor interceptor = new JSONInterceptor();
- JSONUtil jsonUtil = new JSONUtil();
- jsonUtil.setReader(new StrutsJSONReader());
- jsonUtil.setWriter(new StrutsJSONWriter());
- interceptor.setJsonUtil(jsonUtil);
+ interceptor.setContainer(container);
// Only authorize "foo", reject "bar"
interceptor.setParameterAuthorizer((parameterName, target, action) -> "foo".equals(parameterName));
TestAction action = new TestAction();
@@ -589,10 +583,7 @@ public class JSONInterceptorTest extends StrutsTestCase {
// Simulate a custom JSON reader producing a Map with a non-String key.
// The authorizer should skip the entry rather than throw ClassCastException.
JSONInterceptor interceptor = new JSONInterceptor();
- JSONUtil jsonUtil = new JSONUtil();
- jsonUtil.setReader(new StrutsJSONReader());
- jsonUtil.setWriter(new StrutsJSONWriter());
- interceptor.setJsonUtil(jsonUtil);
+ interceptor.setContainer(container);
interceptor.setParameterAuthorizer((parameterName, target, action) -> true);
java.util.Map<Object, Object> mixedKeyMap = new java.util.LinkedHashMap<>();
@@ -658,10 +649,7 @@ public class JSONInterceptorTest extends StrutsTestCase {
this.request.addHeader("Content-Type", "application/json");
JSONInterceptor interceptor = new JSONInterceptor();
- JSONUtil jsonUtil = new JSONUtil();
- jsonUtil.setReader(new StrutsJSONReader());
- jsonUtil.setWriter(new StrutsJSONWriter());
- interceptor.setJsonUtil(jsonUtil);
+ interceptor.setContainer(container);
// Authorize "bean" (top-level) and "bean.stringField" (nested) but reject "bean.intField"
interceptor.setParameterAuthorizer((parameterName, target, action) ->
"bean".equals(parameterName) || "bean.stringField".equals(parameterName));
@@ -688,10 +676,7 @@ public class JSONInterceptorTest extends StrutsTestCase {
this.request.addHeader("Content-Type", "application/json");
JSONInterceptor interceptor = new JSONInterceptor();
- JSONUtil jsonUtil = new JSONUtil();
- jsonUtil.setReader(new StrutsJSONReader());
- jsonUtil.setWriter(new StrutsJSONWriter());
- interceptor.setJsonUtil(jsonUtil);
+ interceptor.setContainer(container);
interceptor.setRoot("bean");
// Reject all parameters — simulates strict requireAnnotations
interceptor.setParameterAuthorizer((parameterName, target, action) -> false);
@@ -905,6 +890,18 @@ public class JSONInterceptorTest extends StrutsTestCase {
assertEquals("b", action.getBar());
}
+ public void testObtainsFreshJSONUtilAndReaderPerInvocation() {
+ JSONInterceptor interceptor = new JSONInterceptor();
+ interceptor.setContainer(container); // StrutsTestCase-provided container
+
+ JSONUtil first = interceptor.getJSONUtil();
+ JSONUtil second = interceptor.getJSONUtil();
+
+ assertNotSame("interceptor must obtain a fresh JSONUtil per request", first, second);
+ assertNotSame("each fresh JSONUtil must carry its own reader (no shared parse state)",
+ first.getReader(), second.getReader());
+ }
+
@Override
protected void setUp() throws Exception {
super.setUp();