This is an automated email from the ASF dual-hosted git repository.
pefernan pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-kie.git
The following commit(s) were added to refs/heads/main by this push:
new e213190e63c [kie-issues #2410] WIH exception handling (#7166)
e213190e63c is described below
commit e213190e63c8f49245f4d2e8a5c532c5eadc2be9
Author: Christine-Jose <[email protected]>
AuthorDate: Thu Oct 15 18:15:14 2026 +0530
[kie-issues #2410] WIH exception handling (#7166)
* Changes for exception
* Changes for configuring exception handler
* Test failures
* fixing the retry bug
* Fixing the build failure
* Changes for config
* review comments
---
.../process/instance/LightWorkItemManager.java | 2 +
.../instance/node/WorkItemNodeInstance.java | 2 +-
.../WorkItemHandlerExceptionHandlingTest.java | 129 +++++++++++
.../ProcessWorkItemHandlerExceptionHandler.java | 117 ++++++++++
.../impl/KogitoDefaultWorkItemManager.java | 2 +
...ProcessWorkItemHandlerExceptionHandlerTest.java | 253 +++++++++++++++++++++
6 files changed, 504 insertions(+), 1 deletion(-)
diff --git
a/kogito-jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/LightWorkItemManager.java
b/kogito-jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/LightWorkItemManager.java
index 73c87902eb3..32f8cf107ad 100755
---
a/kogito-jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/LightWorkItemManager.java
+++
b/kogito-jbpm/jbpm-flow/src/main/java/org/jbpm/process/instance/LightWorkItemManager.java
@@ -29,6 +29,7 @@ import java.util.function.Function;
import java.util.stream.Stream;
import org.drools.core.process.WorkItem;
+import
org.jbpm.process.workitem.builtin.ProcessWorkItemHandlerExceptionHandler;
import org.kie.kogito.internal.process.event.KogitoProcessEventSupport;
import org.kie.kogito.internal.process.runtime.KogitoProcessInstance;
import org.kie.kogito.internal.process.workitem.KogitoWorkItem;
@@ -192,6 +193,7 @@ public class LightWorkItemManager implements
InternalKogitoWorkItemManager {
if (handler == null) {
throw new
KogitoWorkItemHandlerNotFoundException(workItem.getName());
}
+ handler = ProcessWorkItemHandlerExceptionHandler.wrapIfNeeded(handler,
workItem);
KogitoProcessInstance processInstance =
processInstanceManager.getProcessInstance(workItem.getProcessInstanceStringId());
WorkItemTransition lastTransition = null;
diff --git
a/kogito-jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/WorkItemNodeInstance.java
b/kogito-jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/WorkItemNodeInstance.java
index 20fa12d7e09..247669e5d78 100755
---
a/kogito-jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/WorkItemNodeInstance.java
+++
b/kogito-jbpm/jbpm-flow/src/main/java/org/jbpm/workflow/instance/node/WorkItemNodeInstance.java
@@ -176,7 +176,7 @@ public class WorkItemNodeInstance extends
StateBasedNodeInstance implements Even
getProcessInstance().setState(STATE_ABORTED);
throw wihnfe;
} catch (ProcessWorkItemHandlerException handlerException) {
- if (triggerCount++ < handlerException.getRetries() + 1) {
+ if (triggerCount++ <= handlerException.getRetries()) {
this.workItemId = workItem.getStringId();
handleWorkItemHandlerException(handlerException, workItem);
} else {
diff --git
a/kogito-jbpm/jbpm-tests/src/test/java/org/jbpm/bpmn2/handler/WorkItemHandlerExceptionHandlingTest.java
b/kogito-jbpm/jbpm-tests/src/test/java/org/jbpm/bpmn2/handler/WorkItemHandlerExceptionHandlingTest.java
index 90ce62bc5b5..d3b192aeca3 100755
---
a/kogito-jbpm/jbpm-tests/src/test/java/org/jbpm/bpmn2/handler/WorkItemHandlerExceptionHandlingTest.java
+++
b/kogito-jbpm/jbpm-tests/src/test/java/org/jbpm/bpmn2/handler/WorkItemHandlerExceptionHandlingTest.java
@@ -20,9 +20,11 @@ package org.jbpm.bpmn2.handler;
import java.util.HashMap;
import java.util.Map;
+import java.util.Optional;
import org.jbpm.bpmn2.objects.TestWorkItemHandler;
import org.jbpm.process.core.context.variable.VariableScope;
+import
org.jbpm.process.workitem.builtin.ProcessWorkItemHandlerExceptionHandler;
import org.jbpm.test.utils.ProcessTestHelper;
import org.jbpm.workflow.instance.impl.WorkflowProcessInstanceImpl;
import org.junit.jupiter.api.AfterAll;
@@ -35,6 +37,10 @@ import
org.kie.kogito.internal.process.event.DefaultKogitoProcessEventListener;
import org.kie.kogito.internal.process.event.KogitoProcessEventListener;
import org.kie.kogito.internal.process.runtime.KogitoProcessInstance;
import org.kie.kogito.internal.process.workitem.KogitoWorkItem;
+import org.kie.kogito.internal.process.workitem.KogitoWorkItemHandler;
+import org.kie.kogito.internal.process.workitem.KogitoWorkItemManager;
+import org.kie.kogito.internal.process.workitem.WorkItemTransition;
+import org.kie.kogito.process.workitems.impl.DefaultKogitoWorkItemHandler;
import static org.assertj.core.api.Assertions.assertThat;
import static
org.jbpm.process.core.context.variable.VariableScope.VARIABLE_STRICT_ENABLED_PROPERTY;
@@ -232,4 +238,127 @@ public class WorkItemHandlerExceptionHandlingTest {
ProcessTestHelper.completeWorkItem(exception, results);
}
+
+ @Test
+ public void testDecoratorWithStrategyComplete() throws Exception {
+ DefaultKogitoWorkItemHandler plainHandler = new
DefaultKogitoWorkItemHandler() {
+ @Override
+ public Optional<WorkItemTransition>
activateWorkItemHandler(KogitoWorkItemManager manager, KogitoWorkItemHandler
handler, KogitoWorkItem workItem, WorkItemTransition transition) {
+
workItem.getParameters().put(ProcessWorkItemHandlerExceptionHandler.ERROR_HANDLER_PROCESS_ID,
"ScriptTask");
+
workItem.getParameters().put(ProcessWorkItemHandlerExceptionHandler.ERROR_HANDLING_STRATEGY,
"COMPLETE");
+ if (workItem.getParameter("isCheckedCheckbox") != null) {
+ return
Optional.of(this.workItemLifeCycle.newTransition("complete",
workItem.getPhaseStatus(), workItem.getParameters()));
+ }
+ throw new RuntimeException("Simulated failure in wrapped WIH");
+ }
+ };
+
+ ProcessWorkItemHandlerExceptionHandler decoratedHandler = new
ProcessWorkItemHandlerExceptionHandler(plainHandler);
+
+ Application application = ProcessTestHelper.newApplication();
+ ProcessTestHelper.registerHandler(application, "Human Task",
decoratedHandler);
+ org.kie.kogito.process.Process<UserTaskWithBooleanOutputModel>
processDefinition = UserTaskWithBooleanOutputProcess.newProcess(application);
+ ScriptTaskProcess.newProcess(application);
+
+ UserTaskWithBooleanOutputModel model = processDefinition.createModel();
+ model.setIsChecked(false);
+ org.kie.kogito.process.ProcessInstance<UserTaskWithBooleanOutputModel>
instance = processDefinition.createInstance(model);
+ instance.start();
+
+
assertThat(instance.status()).isEqualTo(KogitoProcessInstance.STATE_COMPLETED);
+ assertThat(instance.variables().getIsChecked()).isTrue();
+ }
+
+ @Test
+ public void testDecoratorWithStrategyRetry() throws Exception {
+ DefaultKogitoWorkItemHandler plainHandler = new
DefaultKogitoWorkItemHandler() {
+ @Override
+ public Optional<WorkItemTransition>
activateWorkItemHandler(KogitoWorkItemManager manager, KogitoWorkItemHandler
handler, KogitoWorkItem workItem, WorkItemTransition transition) {
+
workItem.getParameters().put(ProcessWorkItemHandlerExceptionHandler.ERROR_HANDLER_PROCESS_ID,
"ScriptTask");
+
workItem.getParameters().put(ProcessWorkItemHandlerExceptionHandler.ERROR_HANDLING_STRATEGY,
"RETRY");
+ if (workItem.getParameter("isCheckedCheckbox") != null) {
+ return
Optional.of(this.workItemLifeCycle.newTransition("complete",
workItem.getPhaseStatus(), workItem.getParameters()));
+ }
+ throw new RuntimeException("Simulated failure in wrapped WIH");
+ }
+ };
+
+ ProcessWorkItemHandlerExceptionHandler decoratedHandler = new
ProcessWorkItemHandlerExceptionHandler(plainHandler);
+
+ Application application = ProcessTestHelper.newApplication();
+ ProcessTestHelper.registerHandler(application, "Human Task",
decoratedHandler);
+ org.kie.kogito.process.Process<UserTaskWithBooleanOutputModel>
processDefinition = UserTaskWithBooleanOutputProcess.newProcess(application);
+ ScriptTaskProcess.newProcess(application);
+
+ org.kie.kogito.process.ProcessInstance<UserTaskWithBooleanOutputModel>
instance = processDefinition.createInstance(processDefinition.createModel());
+ instance.start();
+
+
assertThat(instance.status()).isEqualTo(KogitoProcessInstance.STATE_COMPLETED);
+ assertThat(instance.variables().getIsChecked()).isTrue();
+ }
+
+ @Test
+ public void testDecoratorWithStrategyAbort() throws Exception {
+ DefaultKogitoWorkItemHandler plainHandler = new
DefaultKogitoWorkItemHandler() {
+ @Override
+ public Optional<WorkItemTransition>
activateWorkItemHandler(KogitoWorkItemManager manager, KogitoWorkItemHandler
handler, KogitoWorkItem workItem, WorkItemTransition transition) {
+
workItem.getParameters().put(ProcessWorkItemHandlerExceptionHandler.ERROR_HANDLER_PROCESS_ID,
"ScriptTask");
+
workItem.getParameters().put(ProcessWorkItemHandlerExceptionHandler.ERROR_HANDLING_STRATEGY,
"ABORT");
+ throw new RuntimeException("Simulated failure in wrapped WIH");
+ }
+ };
+
+ ProcessWorkItemHandlerExceptionHandler decoratedHandler = new
ProcessWorkItemHandlerExceptionHandler(plainHandler);
+
+ Application application = ProcessTestHelper.newApplication();
+ ProcessTestHelper.registerHandler(application, "Human Task",
decoratedHandler);
+ org.kie.kogito.process.Process<UserTaskWithBooleanOutputModel>
processDefinition = UserTaskWithBooleanOutputProcess.newProcess(application);
+ ScriptTaskProcess.newProcess(application);
+
+ UserTaskWithBooleanOutputModel model = processDefinition.createModel();
+ model.setIsChecked(false);
+ org.kie.kogito.process.ProcessInstance<UserTaskWithBooleanOutputModel>
instance = processDefinition.createInstance(model);
+ instance.start();
+
+
assertThat(instance.status()).isEqualTo(KogitoProcessInstance.STATE_COMPLETED);
+ assertThat(instance.variables().getIsChecked()).isFalse();
+ }
+
+ @Test
+ public void testDecoratorWithStrategyRethrow() throws Exception {
+ DefaultKogitoWorkItemHandler plainHandler = new
DefaultKogitoWorkItemHandler() {
+ @Override
+ public Optional<WorkItemTransition>
activateWorkItemHandler(KogitoWorkItemManager manager, KogitoWorkItemHandler
handler, KogitoWorkItem workItem, WorkItemTransition transition) {
+
workItem.getParameters().put(ProcessWorkItemHandlerExceptionHandler.ERROR_HANDLER_PROCESS_ID,
"ScriptTask");
+
workItem.getParameters().put(ProcessWorkItemHandlerExceptionHandler.ERROR_HANDLING_STRATEGY,
"RETHROW");
+ throw new RuntimeException("Simulated failure in wrapped WIH");
+ }
+ };
+
+ ProcessWorkItemHandlerExceptionHandler decoratedHandler = new
ProcessWorkItemHandlerExceptionHandler(plainHandler);
+
+ Application application = ProcessTestHelper.newApplication();
+ ProcessTestHelper.registerHandler(application, "Human Task",
decoratedHandler);
+ org.kie.kogito.process.Process<UserTaskWithBooleanOutputModel>
processDefinition = UserTaskWithBooleanOutputProcess.newProcess(application);
+ ScriptTaskProcess.newProcess(application);
+
+ UserTaskWithBooleanOutputModel model = processDefinition.createModel();
+ model.setIsChecked(false);
+ org.kie.kogito.process.ProcessInstance<UserTaskWithBooleanOutputModel>
instance = processDefinition.createInstance(model);
+ instance.start();
+
+
assertThat(instance.status()).isEqualTo(KogitoProcessInstance.STATE_ERROR);
+ assertThat(instance.variables().getIsChecked()).isFalse();
+ }
+
+ @Test
+ public void testRegisteredHandlerIsNotWrappedInManager() throws Exception {
+ TestWorkItemHandler rawHandler = new TestWorkItemHandler();
+ Application application = ProcessTestHelper.newApplication();
+ ProcessTestHelper.registerHandler(application, "MyCustomTask",
rawHandler);
+
+ org.kie.kogito.process.Process<UserTaskWithBooleanOutputModel>
processDefinition = UserTaskWithBooleanOutputProcess.newProcess(application);
+ KogitoWorkItemHandler retrievedHandler =
processDefinition.getKogitoWorkItemHandler("MyCustomTask");
+ assertThat(retrievedHandler).isSameAs(rawHandler);
+ }
}
diff --git
a/kogito-jbpm/process-workitems/src/main/java/org/jbpm/process/workitem/builtin/ProcessWorkItemHandlerExceptionHandler.java
b/kogito-jbpm/process-workitems/src/main/java/org/jbpm/process/workitem/builtin/ProcessWorkItemHandlerExceptionHandler.java
new file mode 100644
index 00000000000..a2e8a34cc3a
--- /dev/null
+++
b/kogito-jbpm/process-workitems/src/main/java/org/jbpm/process/workitem/builtin/ProcessWorkItemHandlerExceptionHandler.java
@@ -0,0 +1,117 @@
+/*
+ * 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.jbpm.process.workitem.builtin;
+
+import java.util.Arrays;
+
+import org.kie.api.runtime.process.ProcessWorkItemHandlerException;
+import org.kie.kogito.internal.process.workitem.KogitoWorkItem;
+import org.kie.kogito.internal.process.workitem.KogitoWorkItemHandler;
+import org.kie.kogito.internal.process.workitem.KogitoWorkItemManager;
+import org.kie.kogito.internal.process.workitem.WorkItemTransition;
+
+public class ProcessWorkItemHandlerExceptionHandler extends
AbstractExceptionHandlingTaskHandler {
+
+ public static final String ERROR_HANDLER_PROCESS_ID =
"ErrorHandlerProcessId";
+ public static final String ERROR_HANDLING_STRATEGY =
"ErrorHandlingStrategy";
+ public static final String ERROR_HANDLING_RETRIES = "ErrorHandlingRetries";
+
+ public static boolean canDecorate(KogitoWorkItemHandler handler,
KogitoWorkItem workItem) {
+ if (handler == null || handler instanceof
AbstractExceptionHandlingTaskHandler || workItem == null) {
+ return false;
+ }
+ Object processId = workItem.getParameter(ERROR_HANDLER_PROCESS_ID);
+ Object strategy = workItem.getParameter(ERROR_HANDLING_STRATEGY);
+ return processId != null && !processId.toString().trim().isEmpty()
+ && strategy != null && !strategy.toString().trim().isEmpty();
+ }
+
+ public static KogitoWorkItemHandler wrapIfNeeded(KogitoWorkItemHandler
handler, KogitoWorkItem workItem) {
+ if (canDecorate(handler, workItem)) {
+ return new ProcessWorkItemHandlerExceptionHandler(handler);
+ }
+ return handler;
+ }
+
+ public ProcessWorkItemHandlerExceptionHandler(KogitoWorkItemHandler
originalTaskHandler) {
+ super(originalTaskHandler);
+ }
+
+ public ProcessWorkItemHandlerExceptionHandler(Class<? extends
KogitoWorkItemHandler> originalTaskHandlerClass) {
+ super(originalTaskHandlerClass);
+ }
+
+ @Override
+ public void handleException(KogitoWorkItemManager manager,
KogitoWorkItemHandler originalTaskHandler, KogitoWorkItem workItem,
WorkItemTransition transition, Throwable cause) {
+ if (cause instanceof ProcessWorkItemHandlerException) {
+ throw (ProcessWorkItemHandlerException) cause;
+ }
+
+ Object processIdObj = workItem.getParameter(ERROR_HANDLER_PROCESS_ID);
+ Object strategyObj = workItem.getParameter(ERROR_HANDLING_STRATEGY);
+ Object retriesObj = workItem.getParameter(ERROR_HANDLING_RETRIES);
+
+ String processId = processIdObj != null ?
processIdObj.toString().trim() : null;
+ String strategyStr = strategyObj != null ?
strategyObj.toString().trim() : null;
+
+ if (processId != null && !processId.isEmpty() && strategyStr != null
&& !strategyStr.isEmpty()) {
+ ProcessWorkItemHandlerException.HandlingStrategy strategy;
+ if (strategyObj instanceof
ProcessWorkItemHandlerException.HandlingStrategy) {
+ strategy = (ProcessWorkItemHandlerException.HandlingStrategy)
strategyObj;
+ } else {
+ try {
+ strategy =
ProcessWorkItemHandlerException.HandlingStrategy.valueOf(strategyStr.toUpperCase());
+ } catch (IllegalArgumentException e) {
+ throw new IllegalArgumentException("Invalid " +
ERROR_HANDLING_STRATEGY + " value: '" + strategyObj + "'. Expected one of: "
+ +
Arrays.toString(ProcessWorkItemHandlerException.HandlingStrategy.values()) +
".", e);
+ }
+ }
+
+ int retries = 0;
+ if (retriesObj instanceof Number) {
+ retries = ((Number) retriesObj).intValue();
+ } else if (retriesObj instanceof String) {
+ String retriesStr = ((String) retriesObj).trim();
+ if (!retriesStr.isEmpty()) {
+ try {
+ retries = Integer.parseInt(retriesStr);
+ } catch (NumberFormatException nfe) {
+ throw new IllegalArgumentException("Invalid " +
ERROR_HANDLING_RETRIES + " value: '" + retriesObj + "'. Expected an integer.",
nfe);
+ }
+ }
+ } else if (retriesObj != null) {
+ throw new IllegalArgumentException("Invalid " +
ERROR_HANDLING_RETRIES + " value: '" + retriesObj + "'. Expected an integer.");
+ }
+
+ throw new ProcessWorkItemHandlerException(
+ processId,
+ strategy,
+ cause instanceof Exception ? (Exception) cause : new
RuntimeException(cause),
+ retries);
+ }
+
+ if (cause instanceof RuntimeException) {
+ throw (RuntimeException) cause;
+ }
+ if (cause instanceof Error) {
+ throw (Error) cause;
+ }
+ throw new RuntimeException(cause);
+ }
+}
diff --git
a/kogito-jbpm/process-workitems/src/main/java/org/kie/kogito/process/workitems/impl/KogitoDefaultWorkItemManager.java
b/kogito-jbpm/process-workitems/src/main/java/org/kie/kogito/process/workitems/impl/KogitoDefaultWorkItemManager.java
index 55304c9a2bc..5d01a0e5c7a 100755
---
a/kogito-jbpm/process-workitems/src/main/java/org/kie/kogito/process/workitems/impl/KogitoDefaultWorkItemManager.java
+++
b/kogito-jbpm/process-workitems/src/main/java/org/kie/kogito/process/workitems/impl/KogitoDefaultWorkItemManager.java
@@ -30,6 +30,7 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.drools.core.process.WorkItem;
+import
org.jbpm.process.workitem.builtin.ProcessWorkItemHandlerExceptionHandler;
import org.kie.internal.runtime.Closeable;
import org.kie.kogito.internal.process.runtime.KogitoProcessRuntime;
import org.kie.kogito.internal.process.workitem.KogitoWorkItem;
@@ -117,6 +118,7 @@ public class KogitoDefaultWorkItemManager implements
InternalKogitoWorkItemManag
if (handler == null) {
throw new
KogitoWorkItemHandlerNotFoundException(workItem.getName());
}
+ handler = ProcessWorkItemHandlerExceptionHandler.wrapIfNeeded(handler,
workItem);
WorkItemTransition lastTransition = null;
Optional<WorkItemTransition> nextTransition = Optional.of(transition);
while (nextTransition.isPresent()) {
diff --git
a/kogito-jbpm/process-workitems/src/test/java/org/jbpm/process/workitem/builtin/ProcessWorkItemHandlerExceptionHandlerTest.java
b/kogito-jbpm/process-workitems/src/test/java/org/jbpm/process/workitem/builtin/ProcessWorkItemHandlerExceptionHandlerTest.java
new file mode 100644
index 00000000000..0bb4bea0eff
--- /dev/null
+++
b/kogito-jbpm/process-workitems/src/test/java/org/jbpm/process/workitem/builtin/ProcessWorkItemHandlerExceptionHandlerTest.java
@@ -0,0 +1,253 @@
+/*
+ * 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.jbpm.process.workitem.builtin;
+
+import java.util.Collections;
+import java.util.Optional;
+
+import org.junit.jupiter.api.Test;
+import org.kie.api.runtime.process.ProcessWorkItemHandlerException;
+import org.kie.kogito.internal.process.workitem.KogitoWorkItem;
+import org.kie.kogito.internal.process.workitem.KogitoWorkItemHandler;
+import org.kie.kogito.internal.process.workitem.KogitoWorkItemManager;
+import org.kie.kogito.internal.process.workitem.WorkItemTransition;
+import org.kie.kogito.process.workitems.impl.DefaultKogitoWorkItemHandler;
+import org.kie.kogito.process.workitems.impl.KogitoWorkItemImpl;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+public class ProcessWorkItemHandlerExceptionHandlerTest {
+
+ private static class SuccessfulWorkItemHandler extends
DefaultKogitoWorkItemHandler {
+ @Override
+ public Optional<WorkItemTransition>
activateWorkItemHandler(KogitoWorkItemManager manager, KogitoWorkItemHandler
handler, KogitoWorkItem workItem, WorkItemTransition transition) {
+ return
Optional.of(this.workItemLifeCycle.newTransition("complete",
workItem.getPhaseStatus(), Collections.emptyMap()));
+ }
+ }
+
+ private static class FailingWorkItemHandler extends
DefaultKogitoWorkItemHandler {
+ private final RuntimeException exception;
+
+ public FailingWorkItemHandler(RuntimeException exception) {
+ this.exception = exception;
+ }
+
+ @Override
+ public Optional<WorkItemTransition>
activateWorkItemHandler(KogitoWorkItemManager manager, KogitoWorkItemHandler
handler, KogitoWorkItem workItem, WorkItemTransition transition) {
+ throw exception;
+ }
+ }
+
+ @Test
+ public void testSuccessfulExecution() {
+ SuccessfulWorkItemHandler originalHandler = new
SuccessfulWorkItemHandler();
+ ProcessWorkItemHandlerExceptionHandler handler = new
ProcessWorkItemHandlerExceptionHandler(originalHandler);
+ KogitoWorkItemImpl workItem = new KogitoWorkItemImpl();
+ Optional<WorkItemTransition> result = handler.transitionToPhase(null,
workItem, handler.startingTransition(Collections.emptyMap()));
+ assertThat(result).isPresent();
+ assertThat(result.get().id()).isEqualTo("complete");
+ }
+
+ @Test
+ public void testFailingExecutionWithoutErrorConfig() {
+ RuntimeException originalException = new
IllegalStateException("Service unavailable");
+ FailingWorkItemHandler originalHandler = new
FailingWorkItemHandler(originalException);
+ ProcessWorkItemHandlerExceptionHandler handler = new
ProcessWorkItemHandlerExceptionHandler(originalHandler);
+ KogitoWorkItemImpl workItem = new KogitoWorkItemImpl();
+
+ assertThatThrownBy(() -> handler.transitionToPhase(null, workItem,
handler.startingTransition(Collections.emptyMap())))
+ .isSameAs(originalException);
+ }
+
+ @Test
+ public void testFailingExecutionWithPartialConfig() {
+ RuntimeException originalException = new
IllegalStateException("Service unavailable");
+ FailingWorkItemHandler originalHandler = new
FailingWorkItemHandler(originalException);
+ ProcessWorkItemHandlerExceptionHandler handler = new
ProcessWorkItemHandlerExceptionHandler(originalHandler);
+
+ // Only process id
+ KogitoWorkItemImpl workItemOnlyProcessId = new KogitoWorkItemImpl();
+
workItemOnlyProcessId.setParameter(ProcessWorkItemHandlerExceptionHandler.ERROR_HANDLER_PROCESS_ID,
"error_handling");
+ assertThatThrownBy(() -> handler.transitionToPhase(null,
workItemOnlyProcessId, handler.startingTransition(Collections.emptyMap())))
+ .isSameAs(originalException);
+
+ // Only strategy
+ KogitoWorkItemImpl workItemOnlyStrategy = new KogitoWorkItemImpl();
+
workItemOnlyStrategy.setParameter(ProcessWorkItemHandlerExceptionHandler.ERROR_HANDLING_STRATEGY,
"RETRY");
+ assertThatThrownBy(() -> handler.transitionToPhase(null,
workItemOnlyStrategy, handler.startingTransition(Collections.emptyMap())))
+ .isSameAs(originalException);
+ }
+
+ @Test
+ public void testFailingExecutionWithErrorConfig() {
+ RuntimeException originalException = new
IllegalStateException("Service unavailable");
+ FailingWorkItemHandler originalHandler = new
FailingWorkItemHandler(originalException);
+ ProcessWorkItemHandlerExceptionHandler handler = new
ProcessWorkItemHandlerExceptionHandler(originalHandler);
+
+ KogitoWorkItemImpl workItem = new KogitoWorkItemImpl();
+
workItem.setParameter(ProcessWorkItemHandlerExceptionHandler.ERROR_HANDLER_PROCESS_ID,
"error_handling");
+
workItem.setParameter(ProcessWorkItemHandlerExceptionHandler.ERROR_HANDLING_STRATEGY,
"retry");
+
workItem.setParameter(ProcessWorkItemHandlerExceptionHandler.ERROR_HANDLING_RETRIES,
3);
+
+ assertThatThrownBy(() -> handler.transitionToPhase(null, workItem,
handler.startingTransition(Collections.emptyMap())))
+ .isInstanceOf(ProcessWorkItemHandlerException.class)
+ .satisfies(e -> {
+ ProcessWorkItemHandlerException ex =
(ProcessWorkItemHandlerException) e;
+ assertThat(ex.getProcessId()).isEqualTo("error_handling");
+
assertThat(ex.getStrategy()).isEqualTo(ProcessWorkItemHandlerException.HandlingStrategy.RETRY);
+ assertThat(ex.getRetries()).isEqualTo(3);
+ assertThat(ex.getCause()).isSameAs(originalException);
+ });
+ }
+
+ @Test
+ public void testFailingExecutionWithStringRetries() {
+ RuntimeException originalException = new
IllegalStateException("Service unavailable");
+ FailingWorkItemHandler originalHandler = new
FailingWorkItemHandler(originalException);
+ ProcessWorkItemHandlerExceptionHandler handler = new
ProcessWorkItemHandlerExceptionHandler(originalHandler);
+
+ KogitoWorkItemImpl workItem = new KogitoWorkItemImpl();
+
workItem.setParameter(ProcessWorkItemHandlerExceptionHandler.ERROR_HANDLER_PROCESS_ID,
"error_handling");
+
workItem.setParameter(ProcessWorkItemHandlerExceptionHandler.ERROR_HANDLING_STRATEGY,
"COMPLETE");
+
workItem.setParameter(ProcessWorkItemHandlerExceptionHandler.ERROR_HANDLING_RETRIES,
" 5 ");
+
+ assertThatThrownBy(() -> handler.transitionToPhase(null, workItem,
handler.startingTransition(Collections.emptyMap())))
+ .isInstanceOf(ProcessWorkItemHandlerException.class)
+ .satisfies(e -> {
+ ProcessWorkItemHandlerException ex =
(ProcessWorkItemHandlerException) e;
+ assertThat(ex.getProcessId()).isEqualTo("error_handling");
+
assertThat(ex.getStrategy()).isEqualTo(ProcessWorkItemHandlerException.HandlingStrategy.COMPLETE);
+ assertThat(ex.getRetries()).isEqualTo(5);
+ assertThat(ex.getCause()).isSameAs(originalException);
+ });
+ }
+
+ @Test
+ public void testFailingExecutionWithInvalidStrategy() {
+ RuntimeException originalException = new
IllegalStateException("Service unavailable");
+ FailingWorkItemHandler originalHandler = new
FailingWorkItemHandler(originalException);
+ ProcessWorkItemHandlerExceptionHandler handler = new
ProcessWorkItemHandlerExceptionHandler(originalHandler);
+
+ KogitoWorkItemImpl workItem = new KogitoWorkItemImpl();
+
workItem.setParameter(ProcessWorkItemHandlerExceptionHandler.ERROR_HANDLER_PROCESS_ID,
"error_handling");
+
workItem.setParameter(ProcessWorkItemHandlerExceptionHandler.ERROR_HANDLING_STRATEGY,
"INVALID_STRATEGY");
+
+ assertThatThrownBy(() -> handler.transitionToPhase(null, workItem,
handler.startingTransition(Collections.emptyMap())))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessageContaining("Invalid ErrorHandlingStrategy value:
'INVALID_STRATEGY'");
+ }
+
+ @Test
+ public void testFailingExecutionWithEnumStrategyAndObjectProcessId() {
+ RuntimeException originalException = new
IllegalStateException("Service unavailable");
+ FailingWorkItemHandler originalHandler = new
FailingWorkItemHandler(originalException);
+ ProcessWorkItemHandlerExceptionHandler handler = new
ProcessWorkItemHandlerExceptionHandler(originalHandler);
+
+ KogitoWorkItemImpl workItem = new KogitoWorkItemImpl();
+
workItem.setParameter(ProcessWorkItemHandlerExceptionHandler.ERROR_HANDLER_PROCESS_ID,
new StringBuilder("error_proc_obj"));
+
workItem.setParameter(ProcessWorkItemHandlerExceptionHandler.ERROR_HANDLING_STRATEGY,
ProcessWorkItemHandlerException.HandlingStrategy.ABORT);
+
workItem.setParameter(ProcessWorkItemHandlerExceptionHandler.ERROR_HANDLING_RETRIES,
2L);
+
+ assertThatThrownBy(() -> handler.transitionToPhase(null, workItem,
handler.startingTransition(Collections.emptyMap())))
+ .isInstanceOf(ProcessWorkItemHandlerException.class)
+ .satisfies(e -> {
+ ProcessWorkItemHandlerException ex =
(ProcessWorkItemHandlerException) e;
+ assertThat(ex.getProcessId()).isEqualTo("error_proc_obj");
+
assertThat(ex.getStrategy()).isEqualTo(ProcessWorkItemHandlerException.HandlingStrategy.ABORT);
+ assertThat(ex.getRetries()).isEqualTo(2);
+ assertThat(ex.getCause()).isSameAs(originalException);
+ });
+ }
+
+ @Test
+ public void testFailingExecutionWithInvalidRetries() {
+ RuntimeException originalException = new
IllegalStateException("Service unavailable");
+ FailingWorkItemHandler originalHandler = new
FailingWorkItemHandler(originalException);
+ ProcessWorkItemHandlerExceptionHandler handler = new
ProcessWorkItemHandlerExceptionHandler(originalHandler);
+
+ KogitoWorkItemImpl workItem = new KogitoWorkItemImpl();
+
workItem.setParameter(ProcessWorkItemHandlerExceptionHandler.ERROR_HANDLER_PROCESS_ID,
"error_handling");
+
workItem.setParameter(ProcessWorkItemHandlerExceptionHandler.ERROR_HANDLING_STRATEGY,
"RETRY");
+
workItem.setParameter(ProcessWorkItemHandlerExceptionHandler.ERROR_HANDLING_RETRIES,
"not-a-number");
+
+ assertThatThrownBy(() -> handler.transitionToPhase(null, workItem,
handler.startingTransition(Collections.emptyMap())))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessageContaining("Invalid ErrorHandlingRetries value:
'not-a-number'");
+ }
+
+ @Test
+ public void testFailingExecutionWithInvalidRetriesType() {
+ RuntimeException originalException = new
IllegalStateException("Service unavailable");
+ FailingWorkItemHandler originalHandler = new
FailingWorkItemHandler(originalException);
+ ProcessWorkItemHandlerExceptionHandler handler = new
ProcessWorkItemHandlerExceptionHandler(originalHandler);
+
+ KogitoWorkItemImpl workItem = new KogitoWorkItemImpl();
+
workItem.setParameter(ProcessWorkItemHandlerExceptionHandler.ERROR_HANDLER_PROCESS_ID,
"error_handling");
+
workItem.setParameter(ProcessWorkItemHandlerExceptionHandler.ERROR_HANDLING_STRATEGY,
"RETRY");
+
workItem.setParameter(ProcessWorkItemHandlerExceptionHandler.ERROR_HANDLING_RETRIES,
Boolean.TRUE);
+
+ assertThatThrownBy(() -> handler.transitionToPhase(null, workItem,
handler.startingTransition(Collections.emptyMap())))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessageContaining("Invalid ErrorHandlingRetries value:
'true'. Expected an integer.");
+ }
+
+ @Test
+ public void testDoNotDoubleWrapProcessWorkItemHandlerException() {
+ ProcessWorkItemHandlerException originalException = new
ProcessWorkItemHandlerException("sub_process",
ProcessWorkItemHandlerException.HandlingStrategy.ABORT, new RuntimeException());
+ FailingWorkItemHandler originalHandler = new
FailingWorkItemHandler(originalException);
+ ProcessWorkItemHandlerExceptionHandler handler = new
ProcessWorkItemHandlerExceptionHandler(originalHandler);
+
+ KogitoWorkItemImpl workItem = new KogitoWorkItemImpl();
+
workItem.setParameter(ProcessWorkItemHandlerExceptionHandler.ERROR_HANDLER_PROCESS_ID,
"error_handling");
+
workItem.setParameter(ProcessWorkItemHandlerExceptionHandler.ERROR_HANDLING_STRATEGY,
"RETRY");
+
+ assertThatThrownBy(() -> handler.transitionToPhase(null, workItem,
handler.startingTransition(Collections.emptyMap())))
+ .isSameAs(originalException);
+ }
+
+ @Test
+ public void testCanDecorateAndWrapIfNeeded() {
+ SuccessfulWorkItemHandler rawHandler = new SuccessfulWorkItemHandler();
+
+ // Null checks
+ assertThat(ProcessWorkItemHandlerExceptionHandler.canDecorate(null,
null)).isFalse();
+
assertThat(ProcessWorkItemHandlerExceptionHandler.canDecorate(rawHandler,
null)).isFalse();
+
+ // Work item without error params
+ KogitoWorkItemImpl workItem = new KogitoWorkItemImpl();
+
assertThat(ProcessWorkItemHandlerExceptionHandler.canDecorate(rawHandler,
workItem)).isFalse();
+
assertThat(ProcessWorkItemHandlerExceptionHandler.wrapIfNeeded(rawHandler,
workItem)).isSameAs(rawHandler);
+
+ // Work item with partial params
+
workItem.setParameter(ProcessWorkItemHandlerExceptionHandler.ERROR_HANDLER_PROCESS_ID,
"error_proc");
+
assertThat(ProcessWorkItemHandlerExceptionHandler.canDecorate(rawHandler,
workItem)).isFalse();
+
assertThat(ProcessWorkItemHandlerExceptionHandler.wrapIfNeeded(rawHandler,
workItem)).isSameAs(rawHandler);
+
+ // Work item with both params
+
workItem.setParameter(ProcessWorkItemHandlerExceptionHandler.ERROR_HANDLING_STRATEGY,
"RETRY");
+
assertThat(ProcessWorkItemHandlerExceptionHandler.canDecorate(rawHandler,
workItem)).isTrue();
+ KogitoWorkItemHandler wrapped =
ProcessWorkItemHandlerExceptionHandler.wrapIfNeeded(rawHandler, workItem);
+
assertThat(wrapped).isInstanceOf(ProcessWorkItemHandlerExceptionHandler.class);
+
+ // Already decorated handler should not be double-wrapped
+ assertThat(ProcessWorkItemHandlerExceptionHandler.canDecorate(wrapped,
workItem)).isFalse();
+
assertThat(ProcessWorkItemHandlerExceptionHandler.wrapIfNeeded(wrapped,
workItem)).isSameAs(wrapped);
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]