WW-5414 Always call afterInvocation even in case of exception

This commit is contained in:
Lukasz Lenart
2024-05-11 08:23:56 +02:00
parent 649760d8b0
commit 4dfbe09343
4 changed files with 83 additions and 10 deletions
+6
View File
@@ -260,6 +260,12 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
@@ -20,6 +20,8 @@ package org.apache.struts2.interceptor.exec;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.Serializable;
@@ -30,6 +32,8 @@ public class StrutsBackgroundProcess implements BackgroundProcess, Serializable
private static final long serialVersionUID = 3884464776311686443L;
private static final Logger LOG = LogManager.getLogger(StrutsBackgroundProcess.class);
private final String threadName;
private final int threadPriority;
@@ -44,8 +48,8 @@ public class StrutsBackgroundProcess implements BackgroundProcess, Serializable
/**
* Constructs a background process
*
* @param invocation The action invocation
* @param threadName The name of background thread
* @param invocation The action invocation
* @param threadName The name of background thread
* @param threadPriority The priority of background thread
*/
public StrutsBackgroundProcess(ActionInvocation invocation, String threadName, int threadPriority) {
@@ -61,11 +65,19 @@ public class StrutsBackgroundProcess implements BackgroundProcess, Serializable
try {
beforeInvocation();
result = invocation.invokeActionOnly();
afterInvocation();
} catch (Exception e) {
LOG.warn("Exception during invokeActionOnly() execution", e);
exception = e;
} finally {
done = true;
try {
afterInvocation();
} catch (Exception ex) {
if (exception == null) {
exception = ex;
}
LOG.warn("Exception during afterInvocation() execution", ex);
}
done = true;
}
});
processThread.setName(threadName);
@@ -26,6 +26,7 @@ import org.apache.struts2.StrutsInternalTestCase;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.NotSerializableException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.security.SecureRandom;
@@ -41,6 +42,8 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import static org.awaitility.Awaitility.await;
/**
* Test case for BackgroundProcessTest.
*/
@@ -59,9 +62,9 @@ public class StrutsBackgroundProcessTest extends StrutsInternalTestCase {
invocation.setInvocationContext(ActionContext.getContext());
StrutsBackgroundProcess bp = (StrutsBackgroundProcess) new StrutsBackgroundProcess(
invocation,
"BackgroundProcessTest.testSerializeDeserialize",
Thread.MIN_PRIORITY
invocation,
"BackgroundProcessTest.testSerializeDeserialize",
Thread.MIN_PRIORITY
).prepare();
executor.execute(bp);
@@ -120,6 +123,31 @@ public class StrutsBackgroundProcessTest extends StrutsInternalTestCase {
assertEquals(100, mutableState.get());
}
public void testErrorableProcesses1() {
MockActionInvocationWithActionInvoker invocation = new MockActionInvocationWithActionInvoker(() -> {
throw new IllegalStateException("boom");
});
BackgroundProcess bp = new ErrorableBackgroundProcess(invocation, null).prepare();
executor.execute(bp);
await().atLeast(100, TimeUnit.MILLISECONDS).until(bp::isDone);
assertTrue("afterInvocation not called in case of exception", ((ErrorableBackgroundProcess) bp).isDoneAfter());
}
public void testErrorableProcesses2() {
MockActionInvocationWithActionInvoker invocation = new MockActionInvocationWithActionInvoker(() -> "done");
IllegalStateException expected = new IllegalStateException("after!");
BackgroundProcess bp = new ErrorableBackgroundProcess(invocation, expected).prepare();
executor.execute(bp);
await().atLeast(100, TimeUnit.MILLISECONDS).until(bp::isDone);
assertEquals(expected, bp.getException());
}
public void testUnpreparedProcess() throws ExecutionException, InterruptedException, TimeoutException {
// given
MockActionInvocationWithActionInvoker invocation = new MockActionInvocationWithActionInvoker(() -> "done");
@@ -147,7 +175,8 @@ public class StrutsBackgroundProcessTest extends StrutsInternalTestCase {
}
private static class NotSerializableException extends Exception {
private MockHttpServletRequest notSerializableField;
@SuppressWarnings("unused")
private final MockHttpServletRequest notSerializableField;
NotSerializableException(MockHttpServletRequest notSerializableField) {
this.notSerializableField = notSerializableField;
@@ -170,10 +199,29 @@ class LockBackgroundProcess extends StrutsBackgroundProcess {
super.run();
}
}
}
class ErrorableBackgroundProcess extends StrutsBackgroundProcess {
private final Exception afterException;
private boolean doneAfter;
public ErrorableBackgroundProcess(ActionInvocation invocation, Exception afterException) {
super(invocation, "errorabale process", Thread.NORM_PRIORITY);
this.afterException = afterException;
}
@Override
protected void afterInvocation() throws Exception {
super.afterInvocation();
lock.notify();
if (afterException != null) {
throw afterException;
} else {
super.afterInvocation();
doneAfter = true;
}
}
public boolean isDoneAfter() {
return doneAfter;
}
}
+7
View File
@@ -774,6 +774,13 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<version>4.2.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>