This is an automated email from the ASF dual-hosted git repository. lukaszlenart pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/struts.git
The following commit(s) were added to refs/heads/master by this push: new bb7cf09 Forward-port fix for WW-5026 to 2.6. - Equivalent to PR#342 for 2.5.x, fixes double-submit error 500 failure with - Fixes error 500 processing failures for double-submit results with TokenSessionStoreInterceptor processing - Fix to InvocationSessionStore, new unit test confirming fix in InvocationSessionStoreTest - Minor whitespace fix to TokenSessionStoreInterceptor new f251d1c Merge pull request #345 from JCgH4164838Gh792C124B5/localS2_26x_B3 bb7cf09 is described below commit bb7cf092bc9374af743de67ec9e2aa4d0f55cdf0 Author: JCgH4164838Gh792C124B5 <43964333+jcgh4164838gh792c12...@users.noreply.github.com> AuthorDate: Sun Mar 31 22:47:16 2019 -0400 Forward-port fix for WW-5026 to 2.6. - Equivalent to PR#342 for 2.5.x, fixes double-submit error 500 failure with - Fixes error 500 processing failures for double-submit results with TokenSessionStoreInterceptor processing - Fix to InvocationSessionStore, new unit test confirming fix in InvocationSessionStoreTest - Minor whitespace fix to TokenSessionStoreInterceptor --- .../interceptor/TokenSessionStoreInterceptor.java | 26 +++++++++++++++++- .../struts2/util/InvocationSessionStore.java | 20 ++++++++++---- .../struts2/util/InvocationSessionStoreTest.java | 31 ++++++++++++++++++++++ 3 files changed, 71 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/org/apache/struts2/interceptor/TokenSessionStoreInterceptor.java b/core/src/main/java/org/apache/struts2/interceptor/TokenSessionStoreInterceptor.java index e08fb07..863e804 100644 --- a/core/src/main/java/org/apache/struts2/interceptor/TokenSessionStoreInterceptor.java +++ b/core/src/main/java/org/apache/struts2/interceptor/TokenSessionStoreInterceptor.java @@ -112,6 +112,20 @@ public class TokenSessionStoreInterceptor extends TokenInterceptor { } } + /** + * Handles processing of invalid tokens. If a previously stored invocation is + * available, the method will attempt to return and render its result. Otherwise + * it will return INVALID_TOKEN_CODE. + * + * Note: Because a stored (previously completed) invocation's PageContext will be closed, + * this method must replace the stored PageContext with the current invocation's one (or a null). + * See {@link org.apache.struts2.util.InvocationSessionStore#loadInvocation(String key, String token)} for details. + * + * @param invocation + * + * @return + * @throws Exception + */ @Override protected String handleInvalidToken(ActionInvocation invocation) throws Exception { ActionContext ac = invocation.getInvocationContext(); @@ -130,7 +144,7 @@ public class TokenSessionStoreInterceptor extends TokenInterceptor { ActionInvocation savedInvocation = InvocationSessionStore.loadInvocation(sessionTokenName, token); if (savedInvocation != null) { - // set the valuestack to the request scope + // set the savedInvocation's valuestack to the request scope ValueStack stack = savedInvocation.getStack(); request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack); @@ -153,6 +167,16 @@ public class TokenSessionStoreInterceptor extends TokenInterceptor { return INVALID_TOKEN_CODE; } + /** + * Handles processing of valid tokens. Stores the current invocation for + * later use by {@link handleInvalidToken}. + * See {@link org.apache.struts2.util.InvocationSessionStore#storeInvocation(String key, String token, ActionInvocation invocation)} for details. + * + * @param invocation + * + * @return + * @throws Exception + */ @Override protected String handleValidToken(ActionInvocation invocation) throws Exception { // we know the token name and token must be there diff --git a/core/src/main/java/org/apache/struts2/util/InvocationSessionStore.java b/core/src/main/java/org/apache/struts2/util/InvocationSessionStore.java index 15662de..238df3c 100644 --- a/core/src/main/java/org/apache/struts2/util/InvocationSessionStore.java +++ b/core/src/main/java/org/apache/struts2/util/InvocationSessionStore.java @@ -20,6 +20,7 @@ package org.apache.struts2.util; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionInvocation; +import org.apache.struts2.ServletActionContext; import java.io.Serializable; import java.util.HashMap; @@ -55,11 +56,20 @@ public class InvocationSessionStore { return null; } - ActionInvocation savedInvocation = null; - if (invocationContext.invocation != null) { - savedInvocation = invocationContext.invocation; - ActionContext.setContext(savedInvocation.getInvocationContext()); - ActionContext.getContext().setValueStack(savedInvocation.getStack()); + final ActionInvocation savedInvocation = invocationContext.invocation; + if (savedInvocation != null) { + // WW-5026 - Preserve the previous PageContext (even if null) and restore it to the + // ActionContext after loading the savedInvocation context. The saved context's PageContext + // would already be closed at this point (causing failures if used for output). + final ActionContext savedActionContext = savedInvocation.getInvocationContext(); + final ActionContext previousActionContext = ActionContext.getContext(); + ActionContext.setContext(savedActionContext); + savedActionContext.setValueStack(savedInvocation.getStack()); + if (previousActionContext != null) { + savedActionContext.put(ServletActionContext.PAGE_CONTEXT, previousActionContext.get(ServletActionContext.PAGE_CONTEXT)); + } else { + savedActionContext.put(ServletActionContext.PAGE_CONTEXT, null); + } } return savedInvocation; diff --git a/core/src/test/java/org/apache/struts2/util/InvocationSessionStoreTest.java b/core/src/test/java/org/apache/struts2/util/InvocationSessionStoreTest.java index 8b5b8cd..8cfc3f4 100644 --- a/core/src/test/java/org/apache/struts2/util/InvocationSessionStoreTest.java +++ b/core/src/test/java/org/apache/struts2/util/InvocationSessionStoreTest.java @@ -31,6 +31,8 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.HashMap; import java.util.Map; +import org.apache.struts.mock.MockPageContext; +import org.apache.struts2.ServletActionContext; /** @@ -100,6 +102,35 @@ public class InvocationSessionStoreTest extends StrutsInternalTestCase { assertNull(savedInvocation);//Currently we don't support invocation restore from serialized session } + public void testStoreAndLoadPreservesPageContext() { + ActionContext actionContext = ActionContext.getContext(); + + // Create mock PageContext to put with the saved context (simulating a PageContext previously + // used and closed after generating JSP output). + MockPageContext mockSavedPageContext = new MockPageContext(); + actionContext.put(ServletActionContext.PAGE_CONTEXT, mockSavedPageContext); + assertEquals(mockSavedPageContext, ActionContext.getContext().get(ServletActionContext.PAGE_CONTEXT)); + + InvocationSessionStore.storeInvocation(INVOCATION_KEY, TOKEN_VALUE, invocation); + + ActionContext actionContext2 = new ActionContext(new HashMap<String, Object>()); + actionContext2.setSession(session); + ActionContext.setContext(actionContext2); + assertEquals(actionContext2, ActionContext.getContext()); + + // Create mock PageContext to put with the current context (simulating a PageContext + // associated with the current (active) process flow). In real-world processing it + // will usually be null, but if non-null it should be preserved/restored upon load of the + // saved context. + MockPageContext mockPreviousPageContext = new MockPageContext(); + actionContext2.put(ServletActionContext.PAGE_CONTEXT, mockPreviousPageContext); + assertEquals(mockPreviousPageContext, ActionContext.getContext().get(ServletActionContext.PAGE_CONTEXT)); + + InvocationSessionStore.loadInvocation(INVOCATION_KEY, TOKEN_VALUE); + assertEquals(actionContext, ActionContext.getContext()); + assertEquals(mockPreviousPageContext, ActionContext.getContext().get(ServletActionContext.PAGE_CONTEXT)); + } + protected void setUp() throws Exception { super.setUp(); stack = ActionContext.getContext().getValueStack();