Merge pull request #345 from JCgH4164838Gh792C124B5/localS2_26x_B3

Forward-port fix for WW-5026 to 2.6
This commit is contained in:
Lukasz Lenart
2019-04-01 08:27:17 +02:00
committed by GitHub
3 changed files with 71 additions and 6 deletions
@@ -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
@@ -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;
@@ -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();