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 2019/04/01 06:27:23 UTC

[struts] branch master updated: 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

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 <43...@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();