From dda3facc265d4e1c3b1c60fa2bcc4240953ee35c Mon Sep 17 00:00:00 2001 From: Yasser Zamani Date: Wed, 13 Dec 2017 12:41:22 +0330 Subject: [PATCH] WW-4900 Makes BackgroundProcess transient --- .../interceptor/BackgroundProcess.java | 16 ++--- .../ExecuteAndWaitInterceptor.java | 6 ++ .../interceptor/BackgroundProcessTest.java | 65 +++++++++++++++++++ .../ExecuteAndWaitInterceptorTest.java | 39 +++++++++++ 4 files changed, 118 insertions(+), 8 deletions(-) create mode 100644 core/src/test/java/org/apache/struts2/interceptor/BackgroundProcessTest.java diff --git a/core/src/main/java/org/apache/struts2/interceptor/BackgroundProcess.java b/core/src/main/java/org/apache/struts2/interceptor/BackgroundProcess.java index 20b330eae..5210c4072 100644 --- a/core/src/main/java/org/apache/struts2/interceptor/BackgroundProcess.java +++ b/core/src/main/java/org/apache/struts2/interceptor/BackgroundProcess.java @@ -31,10 +31,11 @@ public class BackgroundProcess implements Serializable { private static final long serialVersionUID = 3884464776311686443L; - protected Object action; - protected ActionInvocation invocation; + //WW-4900 transient since 2.5.15 + transient protected ActionInvocation invocation; + transient protected Exception exception; + protected String result; - protected Exception exception; protected boolean done; /** @@ -44,9 +45,8 @@ public class BackgroundProcess implements Serializable { * @param invocation The action invocation * @param threadPriority The thread priority */ - public BackgroundProcess(String threadName, final ActionInvocation invocation, int threadPriority) { + BackgroundProcess(String threadName, final ActionInvocation invocation, int threadPriority) { this.invocation = invocation; - this.action = invocation.getAction(); try { final Thread t = new Thread(new Runnable() { public void run() { @@ -75,7 +75,7 @@ public class BackgroundProcess implements Serializable { * * @throws Exception any exception thrown will be thrown, in turn, by the ExecuteAndWaitInterceptor */ - protected void beforeInvocation() throws Exception { + private void beforeInvocation() throws Exception { ActionContext.setContext(invocation.getInvocationContext()); } @@ -86,7 +86,7 @@ public class BackgroundProcess implements Serializable { * * @throws Exception any exception thrown will be thrown, in turn, by the ExecuteAndWaitInterceptor */ - protected void afterInvocation() throws Exception { + private void afterInvocation() throws Exception { ActionContext.setContext(null); } @@ -96,7 +96,7 @@ public class BackgroundProcess implements Serializable { * @return the action. */ public Object getAction() { - return action; + return invocation.getAction(); } /** diff --git a/core/src/main/java/org/apache/struts2/interceptor/ExecuteAndWaitInterceptor.java b/core/src/main/java/org/apache/struts2/interceptor/ExecuteAndWaitInterceptor.java index 2b3e063e0..b49ebcae7 100644 --- a/core/src/main/java/org/apache/struts2/interceptor/ExecuteAndWaitInterceptor.java +++ b/core/src/main/java/org/apache/struts2/interceptor/ExecuteAndWaitInterceptor.java @@ -243,6 +243,12 @@ public class ExecuteAndWaitInterceptor extends MethodFilterInterceptor { synchronized (httpSession) { BackgroundProcess bp = (BackgroundProcess) session.get(KEY + name); + //WW-4900 Checks if from a de-serialized session? so background thread missed, let's start a new one. + if (bp != null && bp.getInvocation() == null) { + session.remove(KEY + name); + bp = null; + } + if ((!executeAfterValidationPass || secondTime) && bp == null) { bp = getNewBackgroundProcess(name, actionInvocation, threadPriority); session.put(KEY + name, bp); diff --git a/core/src/test/java/org/apache/struts2/interceptor/BackgroundProcessTest.java b/core/src/test/java/org/apache/struts2/interceptor/BackgroundProcessTest.java new file mode 100644 index 000000000..ceeda9e2a --- /dev/null +++ b/core/src/test/java/org/apache/struts2/interceptor/BackgroundProcessTest.java @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.struts2.interceptor; + +import com.opensymphony.xwork2.ActionContext; +import com.opensymphony.xwork2.mock.MockActionInvocation; +import org.apache.struts2.StrutsInternalTestCase; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +/** + * Test case for BackgroundProcessTest. + */ +public class BackgroundProcessTest extends StrutsInternalTestCase { + + public void testSerializeDeserialize() throws Exception { + MockActionInvocation invocation = new MockActionInvocation(); + invocation.setResultCode("BackgroundProcessTest.testSerializeDeserialize"); + invocation.setInvocationContext(ActionContext.getContext()); + + BackgroundProcess bp = new BackgroundProcess("BackgroundProcessTest.testSerializeDeserialize", invocation + , Thread.MIN_PRIORITY); + + bp.exception = new Exception(); + bp.done = true; + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject(bp); + oos.close(); + assertTrue("should have serialized data", baos.size() > 0); + byte b[] = baos.toByteArray(); + baos.close(); + + ByteArrayInputStream bais = new ByteArrayInputStream(b); + ObjectInputStream ois = new ObjectInputStream(bais); + BackgroundProcess deserializedBp = (BackgroundProcess) ois.readObject(); + ois.close(); + bais.close(); + + assertNull("invocation should not be serialized", deserializedBp.invocation); + assertNull("exception should not be serialized", deserializedBp.exception); + assertEquals(bp.result, deserializedBp.result); + assertEquals(bp.done, deserializedBp.done); + } +} \ No newline at end of file diff --git a/core/src/test/java/org/apache/struts2/interceptor/ExecuteAndWaitInterceptorTest.java b/core/src/test/java/org/apache/struts2/interceptor/ExecuteAndWaitInterceptorTest.java index 9106a1b6a..c99cf0825 100644 --- a/core/src/test/java/org/apache/struts2/interceptor/ExecuteAndWaitInterceptorTest.java +++ b/core/src/test/java/org/apache/struts2/interceptor/ExecuteAndWaitInterceptorTest.java @@ -38,6 +38,10 @@ import org.apache.struts2.views.jsp.StrutsMockHttpServletRequest; import org.apache.struts2.views.jsp.StrutsMockHttpSession; import javax.servlet.http.HttpSession; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.util.HashMap; import java.util.Map; @@ -159,6 +163,41 @@ public class ExecuteAndWaitInterceptorTest extends StrutsInternalTestCase { assertTrue("Job done already after 500 so there should not be such long delay", diff <= 1000); } + public void testFromDeserializedSession() throws Exception { + waitInterceptor.setDelay(0); + waitInterceptor.setDelaySleepInterval(0); + + ActionProxy proxy = buildProxy("action1"); + String result = proxy.execute(); + assertEquals("wait", result); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject(session);//WW-4900 action1 and invocation are not serializable but we should not fail at this line + oos.close(); + byte b[] = baos.toByteArray(); + baos.close(); + + ByteArrayInputStream bais = new ByteArrayInputStream(b); + ObjectInputStream ois = new ObjectInputStream(bais); + session = (Map) ois.readObject(); + context.put(ActionContext.SESSION, session); + ois.close(); + bais.close(); + + Thread.sleep(1000); + + ActionProxy proxy2 = buildProxy("action1"); + String result2 = proxy2.execute(); + assertEquals("wait", result2);//WW-4900 A new thread should be started when background thread missed + + Thread.sleep(1000); + + ActionProxy proxy3 = buildProxy("action1"); + String result3 = proxy3.execute(); + assertEquals("success", result3); + } + protected ActionProxy buildProxy(String actionName) throws Exception { return actionProxyFactory.createActionProxy("", actionName, null, context); }