From ce05e65d79562424c125a6fa797aa3dd701dd120 Mon Sep 17 00:00:00 2001 From: Christopher Welsch <75045836+WelschChristopher@users.noreply.github.com> Date: Mon, 26 Aug 2024 10:15:44 +0200 Subject: [PATCH] Add TriggerableJavaDelegate activity behavior to be able to control the leave from the delegate (#3949) Co-authored-by: Christopher Welsch --- ...askDelegateExpressionActivityBehavior.java | 20 +- ...askFutureJavaDelegateActivityBehavior.java | 59 +++- ...rviceTaskJavaDelegateActivityBehavior.java | 61 +++- .../impl/bpmn/helper/ClassDelegate.java | 18 +- .../delegate/TriggerableJavaDelegate.java | 36 +++ .../TriggerableJavaDelegateContextImpl.java | 67 +++++ .../LeavingFutureJavaDelegateServiceTask.java | 44 +++ .../bpmn/servicetask/LeavingJavaDelegate.java | 43 +++ ...tLeavingFutureJavaDelegateServiceTask.java | 40 +++ .../servicetask/NotLeavingJavaDelegate.java | 44 +++ ...rorTriggerableJavaDelegateServiceTask.java | 33 +++ .../TriggerableJavaDelegateServiceTask.java | 55 ++++ ...riggerableJavaDelegateServiceTaskTest.java | 265 ++++++++++++++++++ .../test/logging/ServiceTaskLoggingTest.java | 162 +++++++++++ ...xceptionWithoutWaitStateInCatch.bpmn20.xml | 31 ++ ...teServiceTaskTest.testAsyncJobs.bpmn20.xml | 22 ++ ...rviceTaskTest.testClassDelegate.bpmn20.xml | 22 ++ ...stClassDelegateTriggerException.bpmn20.xml | 29 ++ ...TaskTest.testDelegateExpression.bpmn20.xml | 29 ++ ...testFutureJavaDelegateIsLeaving.bpmn20.xml | 22 ++ ...estFutureJavaDelegateNotLeaving.bpmn20.xml | 22 ++ ...Test.testJavaDelegateNotLeaving.bpmn20.xml | 22 ++ ...askTest.testLeavingJavaDelegate.bpmn20.xml | 22 ++ ...askTest.throwBpmnErrorInTrigger.bpmn20.xml | 30 ++ ...neActivityBehaviorClassDelegate.bpmn20.xml | 22 ++ 25 files changed, 1212 insertions(+), 8 deletions(-) create mode 100644 modules/flowable-engine/src/main/java/org/flowable/engine/impl/delegate/TriggerableJavaDelegate.java create mode 100644 modules/flowable-engine/src/main/java/org/flowable/engine/impl/delegate/TriggerableJavaDelegateContextImpl.java create mode 100644 modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/LeavingFutureJavaDelegateServiceTask.java create mode 100644 modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/LeavingJavaDelegate.java create mode 100644 modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/NotLeavingFutureJavaDelegateServiceTask.java create mode 100644 modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/NotLeavingJavaDelegate.java create mode 100644 modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/ThrowBpmnErrorTriggerableJavaDelegateServiceTask.java create mode 100644 modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTask.java create mode 100644 modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.java create mode 100644 modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.classDelegateTriggerExceptionWithoutWaitStateInCatch.bpmn20.xml create mode 100644 modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testAsyncJobs.bpmn20.xml create mode 100644 modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testClassDelegate.bpmn20.xml create mode 100644 modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testClassDelegateTriggerException.bpmn20.xml create mode 100644 modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testDelegateExpression.bpmn20.xml create mode 100644 modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testFutureJavaDelegateIsLeaving.bpmn20.xml create mode 100644 modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testFutureJavaDelegateNotLeaving.bpmn20.xml create mode 100644 modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testJavaDelegateNotLeaving.bpmn20.xml create mode 100644 modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testLeavingJavaDelegate.bpmn20.xml create mode 100644 modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.throwBpmnErrorInTrigger.bpmn20.xml create mode 100644 modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableServiceTaskTest.testNoneActivityBehaviorClassDelegate.bpmn20.xml diff --git a/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/behavior/ServiceTaskDelegateExpressionActivityBehavior.java b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/behavior/ServiceTaskDelegateExpressionActivityBehavior.java index 0cb228c8243..d31d912b475 100644 --- a/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/behavior/ServiceTaskDelegateExpressionActivityBehavior.java +++ b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/behavior/ServiceTaskDelegateExpressionActivityBehavior.java @@ -36,6 +36,8 @@ import org.flowable.engine.impl.delegate.ActivityBehavior; import org.flowable.engine.impl.delegate.ActivityBehaviorInvocation; import org.flowable.engine.impl.delegate.TriggerableActivityBehavior; +import org.flowable.engine.impl.delegate.TriggerableJavaDelegate; +import org.flowable.engine.impl.delegate.TriggerableJavaDelegateContextImpl; import org.flowable.engine.impl.delegate.invocation.DelegateInvocation; import org.flowable.engine.impl.delegate.invocation.FutureJavaDelegateInvocation; import org.flowable.engine.impl.delegate.invocation.JavaDelegateInvocation; @@ -79,6 +81,7 @@ public void trigger(DelegateExecution execution, String signalName, Object signa Object delegate = DelegateExpressionUtil.resolveDelegateExpression(expression, execution, fieldDeclarations); ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(); boolean loggingSessionEnabled = processEngineConfiguration.isLoggingSessionEnabled(); + TriggerableJavaDelegateContextImpl triggerableJavaDelegateContext = null; try { if (triggerable && delegate instanceof TriggerableActivityBehavior) { if (loggingSessionEnabled) { @@ -92,7 +95,17 @@ public void trigger(DelegateExecution execution, String signalName, Object signa BpmnLoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_SERVICE_TASK_AFTER_TRIGGER, "Triggered service task with delegate " + delegate, execution); } - + } else if (triggerable && delegate instanceof TriggerableJavaDelegate triggerableJavaDelegate) { + if (loggingSessionEnabled) { + BpmnLoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_SERVICE_TASK_BEFORE_TRIGGER, + "Triggering service task with java delegate " + delegate, execution); + } + triggerableJavaDelegateContext = new TriggerableJavaDelegateContextImpl(execution, signalName, signalData); + triggerableJavaDelegate.trigger(triggerableJavaDelegateContext); + if (loggingSessionEnabled) { + BpmnLoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_SERVICE_TASK_AFTER_TRIGGER, + "Triggered service task with java delegate " + delegate, execution); + } } else if (loggingSessionEnabled) { if (!triggerable) { BpmnLoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_SERVICE_TASK_WRONG_TRIGGER, @@ -103,7 +116,10 @@ public void trigger(DelegateExecution execution, String signalName, Object signa } } - leave(execution); + + if (triggerableJavaDelegateContext == null || triggerableJavaDelegateContext.shouldLeave()) { + leave(execution); + } } catch (Exception exc) { handleException(exc, execution, loggingSessionEnabled); } diff --git a/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/behavior/ServiceTaskFutureJavaDelegateActivityBehavior.java b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/behavior/ServiceTaskFutureJavaDelegateActivityBehavior.java index f1d4335edc1..1ee2a2f8725 100644 --- a/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/behavior/ServiceTaskFutureJavaDelegateActivityBehavior.java +++ b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/behavior/ServiceTaskFutureJavaDelegateActivityBehavior.java @@ -32,6 +32,8 @@ import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; import org.flowable.engine.impl.delegate.ActivityBehavior; import org.flowable.engine.impl.delegate.TriggerableActivityBehavior; +import org.flowable.engine.impl.delegate.TriggerableJavaDelegate; +import org.flowable.engine.impl.delegate.TriggerableJavaDelegateContextImpl; import org.flowable.engine.impl.delegate.invocation.FutureJavaDelegateInvocation; import org.flowable.engine.impl.persistence.entity.ExecutionEntity; import org.flowable.engine.impl.util.BpmnLoggingSessionUtil; @@ -40,7 +42,7 @@ /** * @author Filip Hrisafov */ -public class ServiceTaskFutureJavaDelegateActivityBehavior extends TaskActivityBehavior implements ActivityBehavior { +public class ServiceTaskFutureJavaDelegateActivityBehavior extends TaskActivityBehavior implements ActivityBehavior, TriggerableJavaDelegate { private static final long serialVersionUID = 1L; @@ -77,7 +79,18 @@ public void trigger(DelegateExecution execution, String signalName, Object signa "Triggered service task with java class " + futureJavaDelegate.getClass().getName(), execution); } - leave(execution); + } else if (triggerable && futureJavaDelegate instanceof TriggerableJavaDelegate triggerableJavaDelegate) { + if (processEngineConfiguration.isLoggingSessionEnabled()) { + BpmnLoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_SERVICE_TASK_BEFORE_TRIGGER, + "Triggering service task with java class " + futureJavaDelegate.getClass().getName(), execution); + } + TriggerableJavaDelegateContextImpl context = new TriggerableJavaDelegateContextImpl(execution, null, null); + triggerableJavaDelegate.trigger(context); + + if (processEngineConfiguration.isLoggingSessionEnabled()) { + BpmnLoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_SERVICE_TASK_AFTER_TRIGGER, + "Triggered service task with java class " + futureJavaDelegate.getClass().getName(), execution); + } } else { if (processEngineConfiguration.isLoggingSessionEnabled()) { @@ -147,7 +160,49 @@ public void execute(DelegateExecution execution) { leave(execution); } } + } + + @Override + public void trigger(Context context) { + CommandContext commandContext = CommandContextUtil.getCommandContext(); + ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext); + if (triggerable && futureJavaDelegate instanceof TriggerableJavaDelegate triggerableJavaDelegate) { + if (processEngineConfiguration.isLoggingSessionEnabled()) { + BpmnLoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_SERVICE_TASK_BEFORE_TRIGGER, + "Triggering service task with java class " + futureJavaDelegate.getClass().getName(), context.getExecution()); + } + triggerableJavaDelegate.trigger(context); + + if (processEngineConfiguration.isLoggingSessionEnabled()) { + BpmnLoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_SERVICE_TASK_AFTER_TRIGGER, + "Triggered service task with java class " + futureJavaDelegate.getClass().getName(), context.getExecution()); + } + } else if (triggerable && futureJavaDelegate instanceof TriggerableActivityBehavior) { + if (processEngineConfiguration.isLoggingSessionEnabled()) { + BpmnLoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_SERVICE_TASK_BEFORE_TRIGGER, + "Triggering service task with java class " + futureJavaDelegate.getClass().getName(), context.getExecution()); + } + + ((TriggerableActivityBehavior) futureJavaDelegate).trigger(context.getExecution(), context.getSignalName(), context.getSignalData()); + + if (processEngineConfiguration.isLoggingSessionEnabled()) { + BpmnLoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_SERVICE_TASK_AFTER_TRIGGER, + "Triggered service task with java class " + futureJavaDelegate.getClass().getName(), context.getExecution()); + } + + } else { + if (processEngineConfiguration.isLoggingSessionEnabled()) { + if (!triggerable) { + BpmnLoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_SERVICE_TASK_WRONG_TRIGGER, + "Service task with java class triggered but not triggerable " + futureJavaDelegate.getClass().getName(), context.getExecution()); + } else { + BpmnLoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_SERVICE_TASK_WRONG_TRIGGER, + "Service task with java class triggered but not implementing TriggerableActivityBehavior " + futureJavaDelegate.getClass() + .getName(), context.getExecution()); + } + } + } } protected void handleException(Throwable throwable, DelegateExecution execution, boolean loggingSessionEnabled) { diff --git a/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/behavior/ServiceTaskJavaDelegateActivityBehavior.java b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/behavior/ServiceTaskJavaDelegateActivityBehavior.java index 3920e1b1b11..64528c96862 100644 --- a/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/behavior/ServiceTaskJavaDelegateActivityBehavior.java +++ b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/behavior/ServiceTaskJavaDelegateActivityBehavior.java @@ -23,6 +23,8 @@ import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; import org.flowable.engine.impl.delegate.ActivityBehavior; import org.flowable.engine.impl.delegate.TriggerableActivityBehavior; +import org.flowable.engine.impl.delegate.TriggerableJavaDelegate; +import org.flowable.engine.impl.delegate.TriggerableJavaDelegateContextImpl; import org.flowable.engine.impl.delegate.invocation.JavaDelegateInvocation; import org.flowable.engine.impl.util.BpmnLoggingSessionUtil; import org.flowable.engine.impl.util.CommandContextUtil; @@ -30,7 +32,7 @@ /** * @author Tom Baeyens */ -public class ServiceTaskJavaDelegateActivityBehavior extends TaskActivityBehavior implements ActivityBehavior, ExecutionListener { +public class ServiceTaskJavaDelegateActivityBehavior extends TaskActivityBehavior implements ActivityBehavior, ExecutionListener, TriggerableJavaDelegate { private static final long serialVersionUID = 1L; @@ -65,8 +67,19 @@ public void trigger(DelegateExecution execution, String signalName, Object signa "Triggered service task with java class " + javaDelegate.getClass().getName(), execution); } - leave(execution); - + } else if (triggerable && javaDelegate instanceof TriggerableJavaDelegate triggerableJavaDelegate) { + TriggerableJavaDelegateContextImpl triggerableJavaDelegateContext = null; + if (processEngineConfiguration.isLoggingSessionEnabled()) { + BpmnLoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_SERVICE_TASK_BEFORE_TRIGGER, + "Triggering service task with java delegate " + triggerableJavaDelegate, execution); + } + triggerableJavaDelegateContext = new TriggerableJavaDelegateContextImpl(execution, signalName, signalData); + triggerableJavaDelegate.trigger(triggerableJavaDelegateContext); + if (processEngineConfiguration.isLoggingSessionEnabled()) { + BpmnLoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_SERVICE_TASK_AFTER_TRIGGER, + "Triggered service task with delegate " + triggerableJavaDelegate, execution); + } + } else { if (processEngineConfiguration.isLoggingSessionEnabled()) { if (!triggerable) { @@ -132,4 +145,46 @@ public void execute(DelegateExecution execution) { public void notify(DelegateExecution execution) { execute(execution); } + + @Override + public void trigger(Context context) { + CommandContext commandContext = CommandContextUtil.getCommandContext(); + ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext); + + if (triggerable && javaDelegate instanceof TriggerableJavaDelegate triggerableJavaDelegate) { + if (processEngineConfiguration.isLoggingSessionEnabled()) { + BpmnLoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_SERVICE_TASK_BEFORE_TRIGGER, + "Triggering service task with java delegate " + triggerableJavaDelegate, context.getExecution()); + } + triggerableJavaDelegate.trigger(context); + if (processEngineConfiguration.isLoggingSessionEnabled()) { + BpmnLoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_SERVICE_TASK_AFTER_TRIGGER, + "Triggered service task with delegate " + triggerableJavaDelegate, context.getExecution()); + } + } else if (triggerable && javaDelegate instanceof TriggerableActivityBehavior) { + if (processEngineConfiguration.isLoggingSessionEnabled()) { + BpmnLoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_SERVICE_TASK_BEFORE_TRIGGER, + "Triggering service task with java class " + javaDelegate.getClass().getName(), context.getExecution()); + } + + ((TriggerableActivityBehavior) javaDelegate).trigger(context.getExecution(), context.getSignalName(), context.getSignalData()); + + if (processEngineConfiguration.isLoggingSessionEnabled()) { + BpmnLoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_SERVICE_TASK_AFTER_TRIGGER, + "Triggered service task with java class " + javaDelegate.getClass().getName(), context.getExecution()); + } + } else { + if (processEngineConfiguration.isLoggingSessionEnabled()) { + if (!triggerable) { + BpmnLoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_SERVICE_TASK_WRONG_TRIGGER, + "Service task with java class triggered but not triggerable " + javaDelegate.getClass().getName(), context.getExecution()); + + } else { + BpmnLoggingSessionUtil.addLoggingData(LoggingSessionConstants.TYPE_SERVICE_TASK_WRONG_TRIGGER, + "Service task with java class triggered but not implementing TriggerableActivityBehavior " + javaDelegate.getClass().getName(), + context.getExecution()); + } + } + } + } } diff --git a/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/helper/ClassDelegate.java b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/helper/ClassDelegate.java index 16b40fbacb4..36c93fb14e4 100644 --- a/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/helper/ClassDelegate.java +++ b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/helper/ClassDelegate.java @@ -42,6 +42,8 @@ import org.flowable.engine.impl.delegate.ActivityBehavior; import org.flowable.engine.impl.delegate.SubProcessActivityBehavior; import org.flowable.engine.impl.delegate.TriggerableActivityBehavior; +import org.flowable.engine.impl.delegate.TriggerableJavaDelegate; +import org.flowable.engine.impl.delegate.TriggerableJavaDelegateContextImpl; import org.flowable.engine.impl.delegate.invocation.TaskListenerInvocation; import org.flowable.engine.impl.persistence.entity.ExecutionEntity; import org.flowable.engine.impl.util.CommandContextUtil; @@ -208,7 +210,21 @@ public void trigger(DelegateExecution execution, String signalName, Object signa if (activityBehaviorInstance == null) { activityBehaviorInstance = getActivityBehaviorInstance(); } - if (activityBehaviorInstance instanceof TriggerableActivityBehavior) { + if (activityBehaviorInstance instanceof TriggerableJavaDelegate triggerableJavaDelegate) { + try { + TriggerableJavaDelegateContextImpl context = new TriggerableJavaDelegateContextImpl(execution, null, null); + triggerableJavaDelegate.trigger(context); + if (triggerable && context.shouldLeave()) { + leave(execution); + } + } catch (BpmnError error) { + ErrorPropagation.propagateError(error, execution); + } catch (RuntimeException e) { + if (!ErrorPropagation.mapException(e, (ExecutionEntity) execution, mapExceptions)) { + throw e; + } + } + } else if (activityBehaviorInstance instanceof TriggerableActivityBehavior) { try { ((TriggerableActivityBehavior) activityBehaviorInstance).trigger(execution, signalName, signalData); if (triggerable) { diff --git a/modules/flowable-engine/src/main/java/org/flowable/engine/impl/delegate/TriggerableJavaDelegate.java b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/delegate/TriggerableJavaDelegate.java new file mode 100644 index 00000000000..feab2b2f53a --- /dev/null +++ b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/delegate/TriggerableJavaDelegate.java @@ -0,0 +1,36 @@ +/* Licensed 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.flowable.engine.impl.delegate; + +import org.flowable.engine.delegate.DelegateExecution; + +/** + * Similar to the {@link TriggerableActivityBehavior} but with a context that allows the implementing class + * to decide if the execution should be left after the trigger or not + * @author Christopher Welsch + */ +public interface TriggerableJavaDelegate { + + void trigger(TriggerableJavaDelegate.Context context); + + interface Context { + + void doNotLeave(); + + Object getSignalData(); + + DelegateExecution getExecution(); + + String getSignalName(); + } +} diff --git a/modules/flowable-engine/src/main/java/org/flowable/engine/impl/delegate/TriggerableJavaDelegateContextImpl.java b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/delegate/TriggerableJavaDelegateContextImpl.java new file mode 100644 index 00000000000..b6b61e57bc9 --- /dev/null +++ b/modules/flowable-engine/src/main/java/org/flowable/engine/impl/delegate/TriggerableJavaDelegateContextImpl.java @@ -0,0 +1,67 @@ +/* Licensed 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.flowable.engine.impl.delegate; + +import org.flowable.engine.delegate.DelegateExecution; + +/** + * @author Christopher Welsch + */ +public class TriggerableJavaDelegateContextImpl implements TriggerableJavaDelegate.Context { + + protected DelegateExecution execution; + protected String signalName; + protected Object signalData; + + protected boolean shouldLeave = true; + + public TriggerableJavaDelegateContextImpl(DelegateExecution execution, String signalName, Object signalData) { + this.execution = execution; + this.signalName = signalName; + this.signalData = signalData; + } + @Override + public DelegateExecution getExecution() { + return execution; + } + + public void setExecution(DelegateExecution execution) { + this.execution = execution; + } + + @Override + public String getSignalName() { + return signalName; + } + + public void setSignalName(String signalName) { + this.signalName = signalName; + } + @Override + public Object getSignalData() { + return signalData; + } + + public void setSignalData(Object signalData) { + this.signalData = signalData; + } + + @Override + public void doNotLeave() { + shouldLeave = false; + } + + public boolean shouldLeave() { + return shouldLeave; + } +} diff --git a/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/LeavingFutureJavaDelegateServiceTask.java b/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/LeavingFutureJavaDelegateServiceTask.java new file mode 100644 index 00000000000..bb2167c5a7a --- /dev/null +++ b/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/LeavingFutureJavaDelegateServiceTask.java @@ -0,0 +1,44 @@ +/* Licensed 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.flowable.engine.test.bpmn.servicetask; + +import java.io.Serializable; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import org.flowable.common.engine.api.async.AsyncTaskInvoker; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.delegate.FutureJavaDelegate; +import org.flowable.engine.delegate.MapBasedFlowableFutureJavaDelegate; +import org.flowable.engine.delegate.ReadOnlyDelegateExecution; +import org.flowable.engine.impl.delegate.TriggerableJavaDelegate; + +public class LeavingFutureJavaDelegateServiceTask + implements TriggerableJavaDelegate, MapBasedFlowableFutureJavaDelegate, Serializable { + + + public static int count = 0; + + @Override + public void trigger(Context context) { + count++; + } + + @Override + public Map execute(ReadOnlyDelegateExecution inputData) { + count++; + return Map.of("SomeKey", "SomeValue"); + } + +} diff --git a/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/LeavingJavaDelegate.java b/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/LeavingJavaDelegate.java new file mode 100644 index 00000000000..d93ed8c900c --- /dev/null +++ b/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/LeavingJavaDelegate.java @@ -0,0 +1,43 @@ +/* Licensed 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.flowable.engine.test.bpmn.servicetask; + +import java.io.Serializable; + +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.delegate.JavaDelegate; +import org.flowable.engine.impl.delegate.TriggerableJavaDelegate; + +public class LeavingJavaDelegate implements JavaDelegate, TriggerableJavaDelegate, Serializable { + + @Override + public void execute(DelegateExecution execution) { + incrementCount(execution); + } + + public void incrementCount(DelegateExecution execution) { + String variableName = "count"; + int count = 0; + if (execution.hasVariable(variableName)) { + count = (int) execution.getVariable(variableName); + } + count++; + execution.setVariable(variableName, count); + } + + @Override + public void trigger(Context context) { + incrementCount(context.getExecution()); + } +} diff --git a/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/NotLeavingFutureJavaDelegateServiceTask.java b/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/NotLeavingFutureJavaDelegateServiceTask.java new file mode 100644 index 00000000000..0103c6f3461 --- /dev/null +++ b/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/NotLeavingFutureJavaDelegateServiceTask.java @@ -0,0 +1,40 @@ +/* Licensed 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.flowable.engine.test.bpmn.servicetask; + +import java.io.Serializable; +import java.util.Map; + +import org.flowable.engine.delegate.MapBasedFlowableFutureJavaDelegate; +import org.flowable.engine.delegate.ReadOnlyDelegateExecution; +import org.flowable.engine.impl.delegate.TriggerableJavaDelegate; + +public class NotLeavingFutureJavaDelegateServiceTask + implements TriggerableJavaDelegate, MapBasedFlowableFutureJavaDelegate, Serializable { + + public static int count = 0; + + @Override + public void trigger(Context context) { + count++; + context.doNotLeave(); + } + + @Override + public Map execute(ReadOnlyDelegateExecution inputData) { + count++; + return Map.of("SomeKey", "SomeValue"); + } + +} diff --git a/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/NotLeavingJavaDelegate.java b/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/NotLeavingJavaDelegate.java new file mode 100644 index 00000000000..b6f9687c47c --- /dev/null +++ b/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/NotLeavingJavaDelegate.java @@ -0,0 +1,44 @@ +/* Licensed 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.flowable.engine.test.bpmn.servicetask; + +import java.io.Serializable; + +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.delegate.JavaDelegate; +import org.flowable.engine.impl.delegate.TriggerableJavaDelegate; + +public class NotLeavingJavaDelegate implements JavaDelegate, TriggerableJavaDelegate, Serializable { + + @Override + public void execute(DelegateExecution execution) { + incrementCount(execution); + } + + public void incrementCount(DelegateExecution execution) { + String variableName = "count"; + int count = 0; + if (execution.hasVariable(variableName)) { + count = (int) execution.getVariable(variableName); + } + count++; + execution.setVariable(variableName, count); + } + + @Override + public void trigger(Context context) { + incrementCount(context.getExecution()); + context.doNotLeave(); + } +} diff --git a/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/ThrowBpmnErrorTriggerableJavaDelegateServiceTask.java b/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/ThrowBpmnErrorTriggerableJavaDelegateServiceTask.java new file mode 100644 index 00000000000..7e3a84445cf --- /dev/null +++ b/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/ThrowBpmnErrorTriggerableJavaDelegateServiceTask.java @@ -0,0 +1,33 @@ +/* Licensed 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.flowable.engine.test.bpmn.servicetask; + +import java.io.Serializable; + +import org.flowable.engine.delegate.BpmnError; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.delegate.JavaDelegate; +import org.flowable.engine.impl.delegate.TriggerableJavaDelegate; + +public class ThrowBpmnErrorTriggerableJavaDelegateServiceTask implements JavaDelegate, TriggerableJavaDelegate, Serializable { + + @Override + public void execute(DelegateExecution execution) { + } + @Override + public void trigger(Context context) { + throw new BpmnError("testErrorCode"); + } + +} diff --git a/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTask.java b/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTask.java new file mode 100644 index 00000000000..fcf700aca52 --- /dev/null +++ b/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTask.java @@ -0,0 +1,55 @@ +/* Licensed 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.flowable.engine.test.bpmn.servicetask; + +import java.io.Serializable; + +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.delegate.JavaDelegate; +import org.flowable.engine.impl.delegate.TriggerableJavaDelegate; + +public class TriggerableJavaDelegateServiceTask implements TriggerableJavaDelegate, JavaDelegate, Serializable { + + protected boolean doNotLeave = false; + + public TriggerableJavaDelegateServiceTask() { + } + + public TriggerableJavaDelegateServiceTask(boolean doNotLeave) { + this.doNotLeave = doNotLeave; + } + + @Override + public void execute(DelegateExecution execution) { + incrementCount(execution); + } + + public void incrementCount(DelegateExecution execution) { + String variableName = "count"; + int count = 0; + if (execution.hasVariable(variableName)) { + count = (int) execution.getVariable(variableName); + } + count++; + execution.setVariable(variableName, count); + } + + @Override + public void trigger(TriggerableJavaDelegate.Context context) { + incrementCount(context.getExecution()); + if (doNotLeave) { + context.doNotLeave(); + } + } +} diff --git a/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.java b/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.java new file mode 100644 index 00000000000..6c704ee0b44 --- /dev/null +++ b/modules/flowable-engine/src/test/java/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.java @@ -0,0 +1,265 @@ +/* Licensed 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.flowable.engine.test.bpmn.servicetask; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.flowable.engine.impl.jobexecutor.AsyncContinuationJobHandler; +import org.flowable.engine.impl.jobexecutor.AsyncTriggerJobHandler; +import org.flowable.engine.impl.test.PluggableFlowableTestCase; +import org.flowable.engine.runtime.Execution; +import org.flowable.engine.test.Deployment; +import org.flowable.job.api.Job; +import org.junit.jupiter.api.Test; + +public class TriggerableJavaDelegateServiceTaskTest extends PluggableFlowableTestCase { + + @Test + @Deployment + public void testClassDelegate() { + String processId = runtimeService.startProcessInstanceByKey("process").getProcessInstanceId(); + + Execution execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("service1").singleResult(); + assertThat(execution).isNotNull(); + int count = (int) runtimeService.getVariable(processId, "count"); + assertThat(count).isEqualTo(1); + + Map processVariables = new HashMap<>(); + processVariables.put("count", ++count); + runtimeService.trigger(execution.getId(), processVariables, null); + + execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("usertask1").singleResult(); + assertThat(execution).isNotNull(); + assertThat(runtimeService.getVariable(processId, "count")).isEqualTo(3); + } + + @Test + @Deployment(resources = "org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testClassDelegateTriggerException.bpmn20.xml") + public void classDelegateTriggerBpmnException() { + String processId = runtimeService.startProcessInstanceByKey("process").getProcessInstanceId(); + Execution execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("service1").singleResult(); + runtimeService.trigger(execution.getId()); + + assertThatBpmnSubProcessActive(processId); + } + + @Test + @Deployment + void classDelegateTriggerExceptionWithoutWaitStateInCatch() { + String processId = runtimeService.startProcessInstanceByKey("process").getProcessInstanceId(); + Execution execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("service1").singleResult(); + runtimeService.trigger(execution.getId()); + + assertThat(runtimeService.createProcessInstanceQuery().processInstanceId(processId).count()).isZero(); + } + + @Test + @Deployment + public void testDelegateExpression() { + Map varMap = new HashMap<>(); + varMap.put("triggerableServiceTask", new TriggerableJavaDelegateServiceTask()); + + String processId = runtimeService.startProcessInstanceByKey("process", varMap).getProcessInstanceId(); + + Execution execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("service1").singleResult(); + assertThat(execution).isNotNull(); + int count = (int) runtimeService.getVariable(processId, "count"); + assertThat(count).isEqualTo(1); + + Map processVariables = new HashMap<>(); + processVariables.put("count", ++count); + runtimeService.trigger(execution.getId(), processVariables, null); + + execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("usertask1").singleResult(); + assertThat(execution).isNotNull(); + assertThat(runtimeService.getVariable(processId, "count")).isEqualTo(3); + } + + @Test + @Deployment(resources = "org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testDelegateExpression.bpmn20.xml") + public void delegateExpressionTriggerBmnError() { + Map varMap = new HashMap<>(); + varMap.put("triggerableServiceTask", new ThrowBpmnErrorTriggerableJavaDelegateServiceTask()); + + String processId = runtimeService.startProcessInstanceByKey("process", varMap).getProcessInstanceId(); + Execution execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("service1").singleResult(); + runtimeService.trigger(execution.getId()); + + assertThatBpmnSubProcessActive(processId); + } + + @Test + @Deployment + public void testAsyncJobs() { + String processId = runtimeService.startProcessInstanceByKey("process").getProcessInstanceId(); + + List jobs = managementService.createJobQuery().processInstanceId(processId).list(); + assertThat(jobs).hasSize(1); + assertThat(jobs.get(0).getJobHandlerType()).isEqualTo(AsyncContinuationJobHandler.TYPE); + + waitForJobExecutorToProcessAllJobs(7000L, 250L); + + Execution execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("service1").singleResult(); + assertThat(execution).isNotNull(); + int count = (int) runtimeService.getVariable(processId, "count"); + assertThat(count).isEqualTo(1); + + Map processVariables = new HashMap<>(); + processVariables.put("count", ++count); + runtimeService.triggerAsync(execution.getId(), processVariables); + + jobs = managementService.createJobQuery().processInstanceId(processId).list(); + assertThat(jobs).hasSize(1); + assertThat(jobs.get(0).getJobHandlerType()).isEqualTo(AsyncTriggerJobHandler.TYPE); + + waitForJobExecutorToProcessAllJobs(7000L, 250L); + + execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("usertask1").singleResult(); + assertThat(execution).isNotNull(); + assertThat(runtimeService.getVariable(processId, "count")).isEqualTo(3); + } + + @Test + @Deployment + public void throwBpmnErrorInTrigger() { + Map varMap = new HashMap<>(); + varMap.put("triggerableServiceTask", new ThrowBpmnErrorTriggerableJavaDelegateServiceTask()); + + String processId = runtimeService.startProcessInstanceByKey("process", varMap).getProcessInstanceId(); + + Execution execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("service1").singleResult(); + assertThat(execution).isNotNull(); + runtimeService.trigger(execution.getId(), Collections.emptyMap(), null); + + execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("taskAfterErrorCatch").singleResult(); + assertThat(execution).isNotNull(); + } + + @Test + @Deployment(resources = "org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testDelegateExpression.bpmn20.xml") + public void testActivityIsNotLeaving() { + Map varMap = new HashMap<>(); + varMap.put("triggerableServiceTask", new TriggerableJavaDelegateServiceTask(true)); + + String processId = runtimeService.startProcessInstanceByKey("process", varMap).getProcessInstanceId(); + + Execution execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("service1").singleResult(); + assertThat(execution).isNotNull(); + runtimeService.trigger(execution.getId(), Collections.emptyMap(), null); + + execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("service1").singleResult(); + assertThat(execution).isNotNull(); + } + + @Test + @Deployment(resources = "org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testDelegateExpression.bpmn20.xml") + public void testActivityIsLeaving() { + Map varMap = new HashMap<>(); + varMap.put("triggerableServiceTask", new TriggerableJavaDelegateServiceTask()); + + String processId = runtimeService.startProcessInstanceByKey("process", varMap).getProcessInstanceId(); + + Execution execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("service1").singleResult(); + assertThat(execution).isNotNull(); + runtimeService.trigger(execution.getId(), Collections.emptyMap(), null); + + execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("usertask1").singleResult(); + assertThat(execution).isNotNull(); + } + + @Test + @Deployment(resources = "org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testJavaDelegateNotLeaving.bpmn20.xml") + public void testClassDelegateIsNotLeaving() { + String processId = runtimeService.startProcessInstanceByKey("process").getProcessInstanceId(); + + Execution execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("service1").singleResult(); + assertThat(execution).isNotNull(); + int count = (int) runtimeService.getVariable(processId, "count"); + assertThat(count).isEqualTo(1); + + Map processVariables = new HashMap<>(); + processVariables.put("count", ++count); + runtimeService.trigger(execution.getId(), processVariables); + + execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("service1").singleResult(); + assertThat(execution).isNotNull(); + count = (int) runtimeService.getVariable(processId, "count"); + assertThat(count).isEqualTo(3); + } + @Test + @Deployment(resources = "org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testLeavingJavaDelegate.bpmn20.xml") + public void testClassDelegateIsLeaving() { + String processId = runtimeService.startProcessInstanceByKey("process").getProcessInstanceId(); + + Execution execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("service1").singleResult(); + assertThat(execution).isNotNull(); + int count = (int) runtimeService.getVariable(processId, "count"); + assertThat(count).isEqualTo(1); + + Map processVariables = new HashMap<>(); + processVariables.put("count", ++count); + runtimeService.trigger(execution.getId(), processVariables); + + execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("usertask1").singleResult(); + assertThat(execution).isNotNull(); + count = (int) runtimeService.getVariable(processId, "count"); + assertThat(count).isEqualTo(3); + + } + + @Test + @Deployment + public void testFutureJavaDelegateNotLeaving() { + NotLeavingFutureJavaDelegateServiceTask.count = 0; + String processId = runtimeService.startProcessInstanceByKey("process").getProcessInstanceId(); + + Execution execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("service1").singleResult(); + assertThat(execution).isNotNull(); + assertThat(NotLeavingFutureJavaDelegateServiceTask.count).isEqualTo(1); + + Map processVariables = new HashMap<>(); + processVariables.put("count", NotLeavingFutureJavaDelegateServiceTask.count++); + runtimeService.trigger(execution.getId(), processVariables); + + execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("service1").singleResult(); + assertThat(execution).isNotNull(); + assertThat(NotLeavingFutureJavaDelegateServiceTask.count).isEqualTo(3); + } + + @Test + @Deployment + public void testFutureJavaDelegateIsLeaving() { + String processId = runtimeService.startProcessInstanceByKey("process").getProcessInstanceId(); + + Execution execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("service1").singleResult(); + assertThat(execution).isNotNull(); + assertThat(LeavingFutureJavaDelegateServiceTask.count).isEqualTo(1); + + Map processVariables = new HashMap<>(); + runtimeService.trigger(execution.getId(), processVariables); + + execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("usertask1").singleResult(); + assertThat(execution).isNotNull(); + assertThat(LeavingFutureJavaDelegateServiceTask.count).isEqualTo(2); + } + + protected void assertThatBpmnSubProcessActive(String processId) { + assertThat(taskService.createTaskQuery().processInstanceId(processId).taskName("Escalated Task").singleResult()).isNotNull(); + } +} diff --git a/modules/flowable-engine/src/test/java/org/flowable/engine/test/logging/ServiceTaskLoggingTest.java b/modules/flowable-engine/src/test/java/org/flowable/engine/test/logging/ServiceTaskLoggingTest.java index c0414609870..272b8c595c0 100644 --- a/modules/flowable-engine/src/test/java/org/flowable/engine/test/logging/ServiceTaskLoggingTest.java +++ b/modules/flowable-engine/src/test/java/org/flowable/engine/test/logging/ServiceTaskLoggingTest.java @@ -35,6 +35,7 @@ import org.flowable.engine.runtime.Execution; import org.flowable.engine.runtime.ProcessInstance; import org.flowable.engine.test.Deployment; +import org.flowable.engine.test.bpmn.servicetask.TriggerableJavaDelegateServiceTask; import org.flowable.engine.test.bpmn.servicetask.TriggerableServiceTask; import org.flowable.job.api.Job; import org.flowable.task.api.Task; @@ -1705,6 +1706,167 @@ void testTriggerableServiceTaskWithDelegateExpression() { } + @Test + @Deployment(resources = "org/flowable/engine/test/bpmn/servicetask/TriggerableServiceTaskTest.testDelegateExpression.bpmn20.xml") + void testTriggerableJavaDelegateServiceTaskWithDelegateExpression() { + FlowableLoggingListener.clear(); + + TriggerableJavaDelegateServiceTask delegate = new TriggerableJavaDelegateServiceTask(); + ProcessInstance processInstance = runtimeService.createProcessInstanceBuilder() + .processDefinitionKey("process") + .transientVariable("triggerableServiceTask", delegate) + .start(); + + String processId = processInstance.getId(); + String processDefinitionId = processInstance.getProcessDefinitionId(); + + List loggingNodes = FlowableLoggingListener.TEST_LOGGING_NODES; + assertThat(loggingNodes) + .extracting(node -> node.path("type").asText("__invalid")) + .containsExactly( + LoggingSessionConstants.TYPE_PROCESS_STARTED, + LoggingSessionConstants.TYPE_ACTIVITY_BEHAVIOR_EXECUTE, + LoggingSessionConstants.TYPE_SEQUENCE_FLOW_TAKE, + LoggingSessionConstants.TYPE_ACTIVITY_BEHAVIOR_EXECUTE, + LoggingSessionConstants.TYPE_SERVICE_TASK_ENTER, + LoggingSessionConstants.TYPE_VARIABLE_CREATE, + LoggingSessionConstants.TYPE_SERVICE_TASK_EXIT, + LoggingSessionConstants.TYPE_COMMAND_CONTEXT_CLOSE + ); + + assertThatJson(loggingNodes.get(0)) + .isEqualTo("{" + + " type: 'processStarted'," + + " message: 'Started process instance with id " + processId + "'," + + " scopeId: '" + processId + "'," + + " subScopeId: '${json-unit.any-string}'," + + " scopeType: 'bpmn'," + + " scopeDefinitionId: '" + processDefinitionId + "'," + + " scopeDefinitionKey: 'process'," + + " scopeDefinitionName: null," + + " __id: '${json-unit.any-string}'," + + " __timeStamp: '${json-unit.any-string}'," + + " __transactionId: '${json-unit.any-string}'," + + " __logNumber: 1" + + "}"); + + assertThatJson(loggingNodes.get(3)) + .isEqualTo("{" + + " type: 'activityBehaviorExecute'," + + " message: 'In ServiceTask, executing ServiceTaskDelegateExpressionActivityBehavior'," + + " scopeId: '" + processId + "'," + + " subScopeId: '${json-unit.any-string}'," + + " scopeType: 'bpmn'," + + " scopeDefinitionId: '" + processDefinitionId + "'," + + " scopeDefinitionKey: 'process'," + + " scopeDefinitionName: null," + + " elementId: 'service1'," + + " elementType: 'ServiceTask'," + + " elementSubType: '${triggerableServiceTask}'," + + " activityBehavior: 'ServiceTaskDelegateExpressionActivityBehavior'," + + " __id: '${json-unit.any-string}'," + + " __timeStamp: '${json-unit.any-string}'," + + " __transactionId: '${json-unit.any-string}'," + + " __logNumber: 4" + + "}"); + + assertThatJson(loggingNodes.get(4)) + .isEqualTo("{" + + " type: 'serviceTaskEnter'," + + " message: 'Executing service task with delegate " + delegate + "'," + + " scopeId: '" + processId + "'," + + " subScopeId: '${json-unit.any-string}'," + + " scopeType: 'bpmn'," + + " scopeDefinitionId: '" + processDefinitionId + "'," + + " scopeDefinitionKey: 'process'," + + " scopeDefinitionName: null," + + " elementId: 'service1'," + + " elementType: 'ServiceTask'," + + " elementSubType: '${triggerableServiceTask}'," + + " __id: '${json-unit.any-string}'," + + " __timeStamp: '${json-unit.any-string}'," + + " __transactionId: '${json-unit.any-string}'," + + " __logNumber: 5" + + "}"); + + assertThatJson(loggingNodes.get(6)) + .isEqualTo("{" + + " type: 'serviceTaskExit'," + + " message: 'Executed service task with delegate " + delegate + "'," + + " scopeId: '" + processId + "'," + + " subScopeId: '${json-unit.any-string}'," + + " scopeType: 'bpmn'," + + " scopeDefinitionId: '" + processDefinitionId + "'," + + " scopeDefinitionKey: 'process'," + + " scopeDefinitionName: null," + + " elementId: 'service1'," + + " elementType: 'ServiceTask'," + + " elementSubType: '${triggerableServiceTask}'," + + " __id: '${json-unit.any-string}'," + + " __timeStamp: '${json-unit.any-string}'," + + " __transactionId: '${json-unit.any-string}'," + + " __logNumber: 7" + + "}"); + + FlowableLoggingListener.clear(); + + Execution execution = runtimeService.createExecutionQuery().processInstanceId(processId).activityId("service1").singleResult(); + runtimeService.trigger(execution.getId(), Collections.emptyMap(), Collections.singletonMap("triggerableServiceTask", delegate)); + + loggingNodes = FlowableLoggingListener.TEST_LOGGING_NODES; + + assertThat(loggingNodes) + .extracting(node -> node.path("type").asText("__invalid")) + .containsExactly( + LoggingSessionConstants.TYPE_SERVICE_TASK_BEFORE_TRIGGER, + LoggingSessionConstants.TYPE_VARIABLE_UPDATE, + LoggingSessionConstants.TYPE_SERVICE_TASK_AFTER_TRIGGER, + LoggingSessionConstants.TYPE_SEQUENCE_FLOW_TAKE, + LoggingSessionConstants.TYPE_ACTIVITY_BEHAVIOR_EXECUTE, + LoggingSessionConstants.TYPE_USER_TASK_CREATE, + LoggingSessionConstants.TYPE_COMMAND_CONTEXT_CLOSE + ); + + assertThatJson(loggingNodes.get(0)) + .isEqualTo("{" + + " type: 'serviceTaskBeforeTrigger'," + + " message: 'Triggering service task with java delegate " + delegate + "'," + + " scopeId: '" + processId + "'," + + " subScopeId: '${json-unit.any-string}'," + + " scopeType: 'bpmn'," + + " scopeDefinitionId: '" + processDefinitionId + "'," + + " scopeDefinitionKey: 'process'," + + " scopeDefinitionName: null," + + " elementId: 'service1'," + + " elementType: 'ServiceTask'," + + " elementSubType: '${triggerableServiceTask}'," + + " __id: '${json-unit.any-string}'," + + " __timeStamp: '${json-unit.any-string}'," + + " __transactionId: '${json-unit.any-string}'," + + " __logNumber: 1" + + "}"); + + assertThatJson(loggingNodes.get(2)) + .isEqualTo("{" + + " type: 'serviceTaskAfterTrigger'," + + " message: 'Triggered service task with java delegate " + delegate + "'," + + " scopeId: '" + processId + "'," + + " subScopeId: '${json-unit.any-string}'," + + " scopeType: 'bpmn'," + + " scopeDefinitionId: '" + processDefinitionId + "'," + + " scopeDefinitionKey: 'process'," + + " scopeDefinitionName: null," + + " elementId: 'service1'," + + " elementType: 'ServiceTask'," + + " elementSubType: '${triggerableServiceTask}'," + + " __id: '${json-unit.any-string}'," + + " __timeStamp: '${json-unit.any-string}'," + + " __transactionId: '${json-unit.any-string}'," + + " __logNumber: 3" + + "}"); + + } + public static class ExceptionServiceTaskDelegate implements JavaDelegate { @Override diff --git a/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.classDelegateTriggerExceptionWithoutWaitStateInCatch.bpmn20.xml b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.classDelegateTriggerExceptionWithoutWaitStateInCatch.bpmn20.xml new file mode 100644 index 00000000000..32a3116b941 --- /dev/null +++ b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.classDelegateTriggerExceptionWithoutWaitStateInCatch.bpmn20.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testAsyncJobs.bpmn20.xml b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testAsyncJobs.bpmn20.xml new file mode 100644 index 00000000000..00e616e03d3 --- /dev/null +++ b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testAsyncJobs.bpmn20.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + diff --git a/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testClassDelegate.bpmn20.xml b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testClassDelegate.bpmn20.xml new file mode 100644 index 00000000000..e02ab171a52 --- /dev/null +++ b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testClassDelegate.bpmn20.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + diff --git a/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testClassDelegateTriggerException.bpmn20.xml b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testClassDelegateTriggerException.bpmn20.xml new file mode 100644 index 00000000000..4d068563681 --- /dev/null +++ b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testClassDelegateTriggerException.bpmn20.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testDelegateExpression.bpmn20.xml b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testDelegateExpression.bpmn20.xml new file mode 100644 index 00000000000..c719dd48726 --- /dev/null +++ b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testDelegateExpression.bpmn20.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testFutureJavaDelegateIsLeaving.bpmn20.xml b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testFutureJavaDelegateIsLeaving.bpmn20.xml new file mode 100644 index 00000000000..c63cbd0398a --- /dev/null +++ b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testFutureJavaDelegateIsLeaving.bpmn20.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + diff --git a/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testFutureJavaDelegateNotLeaving.bpmn20.xml b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testFutureJavaDelegateNotLeaving.bpmn20.xml new file mode 100644 index 00000000000..5378a5a084f --- /dev/null +++ b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testFutureJavaDelegateNotLeaving.bpmn20.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + diff --git a/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testJavaDelegateNotLeaving.bpmn20.xml b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testJavaDelegateNotLeaving.bpmn20.xml new file mode 100644 index 00000000000..124d35dd6fa --- /dev/null +++ b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testJavaDelegateNotLeaving.bpmn20.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + diff --git a/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testLeavingJavaDelegate.bpmn20.xml b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testLeavingJavaDelegate.bpmn20.xml new file mode 100644 index 00000000000..f1f2a4c4853 --- /dev/null +++ b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.testLeavingJavaDelegate.bpmn20.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + diff --git a/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.throwBpmnErrorInTrigger.bpmn20.xml b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.throwBpmnErrorInTrigger.bpmn20.xml new file mode 100644 index 00000000000..47ad2f384d8 --- /dev/null +++ b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableJavaDelegateServiceTaskTest.throwBpmnErrorInTrigger.bpmn20.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableServiceTaskTest.testNoneActivityBehaviorClassDelegate.bpmn20.xml b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableServiceTaskTest.testNoneActivityBehaviorClassDelegate.bpmn20.xml new file mode 100644 index 00000000000..1ad955df35e --- /dev/null +++ b/modules/flowable-engine/src/test/resources/org/flowable/engine/test/bpmn/servicetask/TriggerableServiceTaskTest.testNoneActivityBehaviorClassDelegate.bpmn20.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + +