diff --git a/engine-rest/engine-rest-openapi/src/main/templates/models/org/camunda/bpm/engine/rest/dto/message/CorrelationMessageDto.ftl b/engine-rest/engine-rest-openapi/src/main/templates/models/org/camunda/bpm/engine/rest/dto/message/CorrelationMessageDto.ftl index 53c2fa1e59d..721b60c2f16 100644 --- a/engine-rest/engine-rest-openapi/src/main/templates/models/org/camunda/bpm/engine/rest/dto/message/CorrelationMessageDto.ftl +++ b/engine-rest/engine-rest-openapi/src/main/templates/models/org/camunda/bpm/engine/rest/dto/message/CorrelationMessageDto.ftl @@ -66,8 +66,17 @@ type = "object" additionalProperties = true dto = "VariableValueDto" - desc = "A map of local variables that is injected into the triggered execution or process instance after the - message has been delivered. Each key is a variable name and each value a JSON variable value object + desc = "A map of local variables that is injected into the execution waiting on the message. + Each key is a variable name and each value a JSON variable value object + with the following properties."/> + + <@lib.property + name = "processVariablesToTriggeredScope" + type = "object" + additionalProperties = true + dto = "VariableValueDto" + desc = "A map of variables that is injected into the new scope triggered by message correlation. + Each key is a variable name and each value a JSON variable value object with the following properties."/> <@lib.property diff --git a/engine-rest/engine-rest/src/main/java/org/camunda/bpm/engine/rest/dto/message/CorrelationMessageDto.java b/engine-rest/engine-rest/src/main/java/org/camunda/bpm/engine/rest/dto/message/CorrelationMessageDto.java index 151852fd7b1..7a828fff15e 100644 --- a/engine-rest/engine-rest/src/main/java/org/camunda/bpm/engine/rest/dto/message/CorrelationMessageDto.java +++ b/engine-rest/engine-rest/src/main/java/org/camunda/bpm/engine/rest/dto/message/CorrelationMessageDto.java @@ -28,6 +28,7 @@ public class CorrelationMessageDto { private Map localCorrelationKeys; private Map processVariables; private Map processVariablesLocal; + private Map processVariablesToTriggeredScope; private String tenantId; private boolean withoutTenantId; private String processInstanceId; @@ -84,6 +85,14 @@ public void setProcessVariablesLocal(Map processVariab this.processVariablesLocal = processVariablesLocal; } + public Map getProcessVariablesToTriggeredScope() { + return processVariablesToTriggeredScope; + } + + public void setProcessVariablesToTriggeredScope(Map processVariablesToTriggeredScope) { + this.processVariablesToTriggeredScope = processVariablesToTriggeredScope; + } + public boolean isAll() { return all; } diff --git a/engine-rest/engine-rest/src/main/java/org/camunda/bpm/engine/rest/impl/MessageRestServiceImpl.java b/engine-rest/engine-rest/src/main/java/org/camunda/bpm/engine/rest/impl/MessageRestServiceImpl.java index 455f26910f6..ff088b4e599 100644 --- a/engine-rest/engine-rest/src/main/java/org/camunda/bpm/engine/rest/impl/MessageRestServiceImpl.java +++ b/engine-rest/engine-rest/src/main/java/org/camunda/bpm/engine/rest/impl/MessageRestServiceImpl.java @@ -124,6 +124,7 @@ protected MessageCorrelationBuilder createMessageCorrelationBuilder(CorrelationM Map localCorrelationKeys = VariableValueDto.toMap(messageDto.getLocalCorrelationKeys(), processEngine, objectMapper); Map processVariables = VariableValueDto.toMap(messageDto.getProcessVariables(), processEngine, objectMapper); Map processVariablesLocal = VariableValueDto.toMap(messageDto.getProcessVariablesLocal(), processEngine, objectMapper); + Map processVariablesToTriggeredScope = VariableValueDto.toMap(messageDto.getProcessVariablesToTriggeredScope(), processEngine, objectMapper); MessageCorrelationBuilder builder = runtimeService .createMessageCorrelation(messageDto.getMessageName()); @@ -134,6 +135,9 @@ protected MessageCorrelationBuilder createMessageCorrelationBuilder(CorrelationM if (processVariablesLocal != null) { builder.setVariablesLocal(processVariablesLocal); } + if (processVariablesToTriggeredScope != null) { + builder.setVariablesToTriggeredScope(processVariablesToTriggeredScope); + } if (messageDto.getBusinessKey() != null) { builder.processInstanceBusinessKey(messageDto.getBusinessKey()); } diff --git a/engine-rest/engine-rest/src/test/java/org/camunda/bpm/engine/rest/MessageRestServiceTest.java b/engine-rest/engine-rest/src/test/java/org/camunda/bpm/engine/rest/MessageRestServiceTest.java index 3495f3ef8d2..00f87ed7a5f 100644 --- a/engine-rest/engine-rest/src/test/java/org/camunda/bpm/engine/rest/MessageRestServiceTest.java +++ b/engine-rest/engine-rest/src/test/java/org/camunda/bpm/engine/rest/MessageRestServiceTest.java @@ -102,6 +102,10 @@ public void setupMocks() { when(messageCorrelationBuilderMock.processInstanceVariableEquals(anyString(), any())).thenReturn(messageCorrelationBuilderMock); when(messageCorrelationBuilderMock.setVariables(Mockito.any())).thenReturn(messageCorrelationBuilderMock); when(messageCorrelationBuilderMock.setVariable(anyString(), any())).thenReturn(messageCorrelationBuilderMock); + when(messageCorrelationBuilderMock.setVariablesLocal(Mockito.any())).thenReturn(messageCorrelationBuilderMock); + when(messageCorrelationBuilderMock.setVariableLocal(anyString(), any())).thenReturn(messageCorrelationBuilderMock); + when(messageCorrelationBuilderMock.setVariablesToTriggeredScope(Mockito.any())).thenReturn(messageCorrelationBuilderMock); + when(messageCorrelationBuilderMock.setVariableToTriggeredScope(anyString(), any())).thenReturn(messageCorrelationBuilderMock); executionResult = MockProvider.createMessageCorrelationResult(MessageCorrelationResultType.Execution); procInstanceResult = MockProvider.createMessageCorrelationResult(MessageCorrelationResultType.ProcessDefinition); @@ -122,6 +126,7 @@ public void testFullMessageCorrelation() { String businessKey = "aBusinessKey"; Map variables = VariablesBuilder.create().variable("aKey", "aValue").getVariables(); Map variablesLocal = VariablesBuilder.create().variable("aKeyLocal", "aValueLocal").getVariables(); + Map variablesToTriggeredScope = VariablesBuilder.create().variable("aKeyToTriggeredScope", "aValueToTriggeredScope").getVariables(); Map correlationKeys = VariablesBuilder.create() .variable("aKey", "aValue") @@ -139,6 +144,7 @@ public void testFullMessageCorrelation() { messageParameters.put("localCorrelationKeys", localCorrelationKeys); messageParameters.put("processVariables", variables); messageParameters.put("processVariablesLocal", variablesLocal); + messageParameters.put("processVariablesToTriggeredScope", variablesToTriggeredScope); messageParameters.put("businessKey", businessKey); given().contentType(POST_JSON_CONTENT_TYPE).body(messageParameters) @@ -159,11 +165,14 @@ public void testFullMessageCorrelation() { expectedVariables.put("aKey", "aValue"); Map expectedVariablesLocal = new HashMap<>(); expectedVariablesLocal.put("aKeyLocal", "aValueLocal"); + Map expectedVariablesToTriggeredScope = new HashMap<>(); + expectedVariablesToTriggeredScope.put("aKeyToTriggeredScope", "aValueToTriggeredScope"); verify(runtimeServiceMock).createMessageCorrelation(eq(messageName)); verify(messageCorrelationBuilderMock).processInstanceBusinessKey(eq(businessKey)); verify(messageCorrelationBuilderMock).setVariables(argThat(new EqualsMap(expectedVariables))); verify(messageCorrelationBuilderMock).setVariablesLocal(argThat(new EqualsMap(expectedVariablesLocal))); + verify(messageCorrelationBuilderMock).setVariablesToTriggeredScope(argThat(new EqualsMap(expectedVariablesToTriggeredScope))); for (Entry expectedKey : expectedCorrelationKeys.entrySet()) { String name = expectedKey.getKey(); @@ -178,9 +187,6 @@ public void testFullMessageCorrelation() { } verify(messageCorrelationBuilderMock).correlateWithResult(); - -// verify(runtimeServiceMock).correlateMessage(eq(messageName), eq(businessKey), -// argThat(new EqualsMap(expectedCorrelationKeys)), argThat(new EqualsMap(expectedVariables))); } @Test @@ -266,6 +272,7 @@ public void testFullMessageCorrelationAll() { String businessKey = "aBusinessKey"; Map variables = VariablesBuilder.create().variable("aKey", "aValue").getVariables(); Map variablesLocal = VariablesBuilder.create().variable("aKeyLocal", "aValueLocal").getVariables(); + Map variablesToTriggeredScope = VariablesBuilder.create().variable("aKeyToTriggeredScope", "aValueToTriggeredScope").getVariables(); Map correlationKeys = VariablesBuilder.create() .variable("aKey", "aValue") @@ -283,6 +290,7 @@ public void testFullMessageCorrelationAll() { messageParameters.put("localCorrelationKeys", localCorrelationKeys); messageParameters.put("processVariables", variables); messageParameters.put("processVariablesLocal", variablesLocal); + messageParameters.put("processVariablesToTriggeredScope", variablesToTriggeredScope); messageParameters.put("businessKey", businessKey); messageParameters.put("all", true); @@ -304,11 +312,14 @@ public void testFullMessageCorrelationAll() { expectedVariables.put("aKey", "aValue"); Map expectedVariablesLocal = new HashMap<>(); expectedVariablesLocal.put("aKeyLocal", "aValueLocal"); + Map expectedVariablesToTriggeredScope = new HashMap<>(); + expectedVariablesToTriggeredScope.put("aKeyToTriggeredScope", "aValueToTriggeredScope"); verify(runtimeServiceMock).createMessageCorrelation(eq(messageName)); verify(messageCorrelationBuilderMock).processInstanceBusinessKey(eq(businessKey)); verify(messageCorrelationBuilderMock).setVariables(argThat(new EqualsMap(expectedVariables))); verify(messageCorrelationBuilderMock).setVariablesLocal(argThat(new EqualsMap(expectedVariablesLocal))); + verify(messageCorrelationBuilderMock).setVariablesToTriggeredScope(argThat(new EqualsMap(expectedVariablesToTriggeredScope))); for (Entry expectedKey : expectedCorrelationKeys.entrySet()) { String name = expectedKey.getKey(); @@ -323,7 +334,6 @@ public void testFullMessageCorrelationAll() { } verify(messageCorrelationBuilderMock).correlateAllWithResult(); - } @Test @@ -490,9 +500,6 @@ public void testMessageNameAndBusinessKeyCorrelation() { .then().expect().statusCode(Status.NO_CONTENT.getStatusCode()) .when().post(MESSAGE_URL); -// verify(runtimeServiceMock).correlateMessage(eq(messageName), eq(businessKey), -// argThat(new EqualsMap(null)), argThat(new EqualsMap(null))); - verify(runtimeServiceMock).createMessageCorrelation(eq(messageName)); verify(messageCorrelationBuilderMock).processInstanceBusinessKey(eq(businessKey)); verify(messageCorrelationBuilderMock).correlateWithResult(); @@ -1078,6 +1085,72 @@ public void testFailingDueToUnparseableDateInProcessVariablesLocal() { .when().post(MESSAGE_URL); } + @Test + public void testFailingDueToUnparseableIntegerInProcessVariablesToTriggeredScope() { + String variableKey = "aVariableKey"; + String variableValue = "1abc"; + String variableType = "Integer"; + + Map variableToTriggeredScopeJson = VariablesBuilder.create().variable(variableKey, variableValue, variableType).getVariables(); + + String messageName = "aMessageName"; + + Map messageParameters = new HashMap<>(); + messageParameters.put("messageName", messageName); + messageParameters.put("processVariablesToTriggeredScope", variableToTriggeredScopeJson); + + given().contentType(POST_JSON_CONTENT_TYPE).body(messageParameters) + .then().expect().statusCode(Status.BAD_REQUEST.getStatusCode()) + .body("type", equalTo(InvalidRequestException.class.getSimpleName())) + .body("message", equalTo("Cannot deliver message: " + + ErrorMessageHelper.getExpectedFailingConversionMessage(variableValue, variableType, Integer.class))) + .when().post(MESSAGE_URL); + } + + + @Test + public void testFailingDueToNotSupportedTypeInProcessVariablesToTriggeredScope() { + String variableKey = "aVariableKey"; + String variableValue = "1abc"; + String variableType = "X"; + + Map variableToTriggeredScopeJson = VariablesBuilder.create().variable(variableKey, variableValue, variableType).getVariables(); + + String messageName = "aMessageName"; + + Map messageParameters = new HashMap<>(); + messageParameters.put("messageName", messageName); + messageParameters.put("processVariablesToTriggeredScope", variableToTriggeredScopeJson); + + given().contentType(POST_JSON_CONTENT_TYPE).body(messageParameters) + .then().expect().statusCode(Status.BAD_REQUEST.getStatusCode()) + .body("type", equalTo(InvalidRequestException.class.getSimpleName())) + .body("message", equalTo("Cannot deliver message: Unsupported value type 'X'")) + .when().post(MESSAGE_URL); + } + + @Test + public void testFailingDueToUnparseableDateInProcessVariablesToTriggeredScope() { + String variableKey = "aVariableKey"; + String variableValue = "1abc"; + String variableType = "Date"; + + Map variableToTriggeredScopeJson = VariablesBuilder.create().variable(variableKey, variableValue, variableType).getVariables(); + + String messageName = "aMessageName"; + + Map messageParameters = new HashMap<>(); + messageParameters.put("messageName", messageName); + messageParameters.put("processVariablesToTriggeredScope", variableToTriggeredScopeJson); + + given().contentType(POST_JSON_CONTENT_TYPE).body(messageParameters) + .then().expect().statusCode(Status.BAD_REQUEST.getStatusCode()) + .body("type", equalTo(InvalidRequestException.class.getSimpleName())) + .body("message", equalTo("Cannot deliver message: " + + ErrorMessageHelper.getExpectedFailingConversionMessage(variableValue, variableType, Date.class))) + .when().post(MESSAGE_URL); + } + @Test public void testCorrelateThrowsAuthorizationException() { String messageName = "aMessageName"; diff --git a/engine/src/main/java/org/camunda/bpm/engine/impl/MessageCorrelationBuilderImpl.java b/engine/src/main/java/org/camunda/bpm/engine/impl/MessageCorrelationBuilderImpl.java index b6e183414cc..f5c92bad293 100644 --- a/engine/src/main/java/org/camunda/bpm/engine/impl/MessageCorrelationBuilderImpl.java +++ b/engine/src/main/java/org/camunda/bpm/engine/impl/MessageCorrelationBuilderImpl.java @@ -59,6 +59,7 @@ public class MessageCorrelationBuilderImpl implements MessageCorrelationBuilder protected VariableMap correlationLocalVariables; protected VariableMap payloadProcessInstanceVariables; protected VariableMap payloadProcessInstanceVariablesLocal; + protected VariableMap payloadProcessInstanceVariablesToTriggeredScope; protected String tenantId = null; protected boolean isTenantIdSet = false; @@ -158,6 +159,14 @@ public MessageCorrelationBuilder setVariableLocal(String variableName, Object va return this; } + @Override + public MessageCorrelationBuilder setVariableToTriggeredScope(String variableName, Object variableValue) { + ensureNotNull("variableName", variableName); + ensurePayloadProcessInstanceVariablesToTriggeredScopeInitialized(); + payloadProcessInstanceVariablesToTriggeredScope.put(variableName, variableValue); + return this; + } + public MessageCorrelationBuilder setVariables(Map variables) { if (variables != null) { ensurePayloadProcessInstanceVariablesInitialized(); @@ -175,6 +184,15 @@ public MessageCorrelationBuilder setVariablesLocal(Map variables return this; } + @Override + public MessageCorrelationBuilder setVariablesToTriggeredScope(Map variables) { + if (variables != null) { + ensurePayloadProcessInstanceVariablesToTriggeredScopeInitialized(); + payloadProcessInstanceVariablesToTriggeredScope.putAll(variables); + } + return this; + } + protected void ensurePayloadProcessInstanceVariablesInitialized() { if (payloadProcessInstanceVariables == null) { payloadProcessInstanceVariables = new VariableMapImpl(); @@ -187,6 +205,12 @@ protected void ensurePayloadProcessInstanceVariablesLocalInitialized() { } } + protected void ensurePayloadProcessInstanceVariablesToTriggeredScopeInitialized() { + if (payloadProcessInstanceVariablesToTriggeredScope == null) { + payloadProcessInstanceVariablesToTriggeredScope = new VariableMapImpl(); + } + } + public MessageCorrelationBuilder tenantId(String tenantId) { ensureNotNull( "The tenant-id cannot be null. Use 'withoutTenantId()' if you want to correlate the message to a process definition or an execution which has no tenant-id.", @@ -367,6 +391,10 @@ public VariableMap getPayloadProcessInstanceVariablesLocal() { return payloadProcessInstanceVariablesLocal; } + public VariableMap getPayloadProcessInstanceVariablesToTriggeredScope() { + return payloadProcessInstanceVariablesToTriggeredScope; + } + public boolean isExclusiveCorrelation() { return isExclusiveCorrelation; } diff --git a/engine/src/main/java/org/camunda/bpm/engine/impl/bpmn/behavior/ThrowSignalEventActivityBehavior.java b/engine/src/main/java/org/camunda/bpm/engine/impl/bpmn/behavior/ThrowSignalEventActivityBehavior.java index d557173ca97..36b0689b192 100644 --- a/engine/src/main/java/org/camunda/bpm/engine/impl/bpmn/behavior/ThrowSignalEventActivityBehavior.java +++ b/engine/src/main/java/org/camunda/bpm/engine/impl/bpmn/behavior/ThrowSignalEventActivityBehavior.java @@ -55,7 +55,7 @@ public void execute(ActivityExecution execution) throws Exception { for (EventSubscriptionEntity signalEventSubscription : signalEventSubscriptions) { if (isActiveEventSubscription(signalEventSubscription)) { - signalEventSubscription.eventReceived(variableMap, null, businessKey, signalDefinition.isAsync()); + signalEventSubscription.eventReceived(variableMap, null, null, businessKey, signalDefinition.isAsync()); } } leave(execution); diff --git a/engine/src/main/java/org/camunda/bpm/engine/impl/cmd/AbstractCorrelateMessageCmd.java b/engine/src/main/java/org/camunda/bpm/engine/impl/cmd/AbstractCorrelateMessageCmd.java index f6e1a332535..2c32c8fb566 100644 --- a/engine/src/main/java/org/camunda/bpm/engine/impl/cmd/AbstractCorrelateMessageCmd.java +++ b/engine/src/main/java/org/camunda/bpm/engine/impl/cmd/AbstractCorrelateMessageCmd.java @@ -66,7 +66,12 @@ protected AbstractCorrelateMessageCmd(MessageCorrelationBuilderImpl builder, boo protected void triggerExecution(CommandContext commandContext, CorrelationHandlerResult correlationResult) { String executionId = correlationResult.getExecutionEntity().getId(); - MessageEventReceivedCmd command = new MessageEventReceivedCmd(messageName, executionId, builder.getPayloadProcessInstanceVariables(), builder.getPayloadProcessInstanceVariablesLocal(), builder.isExclusiveCorrelation()); + MessageEventReceivedCmd command = new MessageEventReceivedCmd(messageName, + executionId, + builder.getPayloadProcessInstanceVariables(), + builder.getPayloadProcessInstanceVariablesLocal(), + builder.getPayloadProcessInstanceVariablesToTriggeredScope(), + builder.isExclusiveCorrelation()); command.execute(commandContext); } @@ -132,6 +137,7 @@ protected VariableMap resolveStartVariables() { VariableMap mergedVariables = Variables.createVariables(); mergedVariables.putAll(builder.getPayloadProcessInstanceVariables()); mergedVariables.putAll(builder.getPayloadProcessInstanceVariablesLocal()); + mergedVariables.putAll(builder.getPayloadProcessInstanceVariablesToTriggeredScope()); return mergedVariables; } diff --git a/engine/src/main/java/org/camunda/bpm/engine/impl/cmd/MessageEventReceivedCmd.java b/engine/src/main/java/org/camunda/bpm/engine/impl/cmd/MessageEventReceivedCmd.java index e3d714adec7..26413c0e6f3 100644 --- a/engine/src/main/java/org/camunda/bpm/engine/impl/cmd/MessageEventReceivedCmd.java +++ b/engine/src/main/java/org/camunda/bpm/engine/impl/cmd/MessageEventReceivedCmd.java @@ -43,22 +43,29 @@ public class MessageEventReceivedCmd implements Command, Serializable { protected final String executionId; protected final Map processVariables; protected final Map processVariablesLocal; + protected final Map processVariablesToTriggeredScope; protected final String messageName; protected boolean exclusive = false; public MessageEventReceivedCmd(String messageName, String executionId, Map processVariables) { - this(messageName, executionId, processVariables, null); + this(messageName, executionId, processVariables, null, null); } - public MessageEventReceivedCmd(String messageName, String executionId, Map processVariables, Map processVariablesLocal) { + public MessageEventReceivedCmd(String messageName, String executionId, Map processVariables, + Map processVariablesLocal, + Map processVariablesToTriggeredScope) { this.executionId = executionId; this.messageName = messageName; this.processVariables = processVariables; this.processVariablesLocal = processVariablesLocal; + this.processVariablesToTriggeredScope = processVariablesToTriggeredScope; } - public MessageEventReceivedCmd(String messageName, String executionId, Map processVariables, Map processVariablesLocal, boolean exclusive) { - this(messageName, executionId, processVariables, processVariablesLocal); + public MessageEventReceivedCmd(String messageName, String executionId, Map processVariables, + Map processVariablesLocal, + Map processVariablesToTriggeredScope, + boolean exclusive) { + this(messageName, executionId, processVariables, processVariablesLocal, processVariablesToTriggeredScope); this.exclusive = exclusive; } @@ -88,7 +95,7 @@ public Void execute(CommandContext commandContext) { checker.checkUpdateProcessInstanceById(processInstanceId); } - eventSubscriptionEntity.eventReceived(processVariables, processVariablesLocal, null, false); + eventSubscriptionEntity.eventReceived(processVariables, processVariablesLocal, processVariablesToTriggeredScope, null, false); return null; } diff --git a/engine/src/main/java/org/camunda/bpm/engine/impl/event/CompensationEventHandler.java b/engine/src/main/java/org/camunda/bpm/engine/impl/event/CompensationEventHandler.java index cafcef247de..25ce9b9cb51 100644 --- a/engine/src/main/java/org/camunda/bpm/engine/impl/event/CompensationEventHandler.java +++ b/engine/src/main/java/org/camunda/bpm/engine/impl/event/CompensationEventHandler.java @@ -41,7 +41,7 @@ public String getEventHandlerType() { } @Override - public void handleEvent(EventSubscriptionEntity eventSubscription, Object payload, Object localPayload, String businessKey, CommandContext commandContext) { + public void handleEvent(EventSubscriptionEntity eventSubscription, Object payload, Object localPayload, Object payloadToTriggeredScope, String businessKey, CommandContext commandContext) { eventSubscription.delete(); String configuration = eventSubscription.getConfiguration(); diff --git a/engine/src/main/java/org/camunda/bpm/engine/impl/event/ConditionalEventHandler.java b/engine/src/main/java/org/camunda/bpm/engine/impl/event/ConditionalEventHandler.java index d3b509e2cd1..16fc048cdc9 100644 --- a/engine/src/main/java/org/camunda/bpm/engine/impl/event/ConditionalEventHandler.java +++ b/engine/src/main/java/org/camunda/bpm/engine/impl/event/ConditionalEventHandler.java @@ -36,7 +36,7 @@ public String getEventHandlerType() { } @Override - public void handleEvent(EventSubscriptionEntity eventSubscription, Object payload, Object localPayload, String businessKey, CommandContext commandContext) { + public void handleEvent(EventSubscriptionEntity eventSubscription, Object payload, Object localPayload, Object payloadToTriggeredScope, String businessKey, CommandContext commandContext) { VariableEvent variableEvent; if (payload == null || payload instanceof VariableEvent) { variableEvent = (VariableEvent) payload; diff --git a/engine/src/main/java/org/camunda/bpm/engine/impl/event/EventHandler.java b/engine/src/main/java/org/camunda/bpm/engine/impl/event/EventHandler.java index 15c23387bba..666a98a44ea 100644 --- a/engine/src/main/java/org/camunda/bpm/engine/impl/event/EventHandler.java +++ b/engine/src/main/java/org/camunda/bpm/engine/impl/event/EventHandler.java @@ -26,6 +26,11 @@ public interface EventHandler { public String getEventHandlerType(); - public void handleEvent(EventSubscriptionEntity eventSubscription, Object payload, Object localPayload, String businessKey, CommandContext commandContext); + public void handleEvent(EventSubscriptionEntity eventSubscription, + Object payload, + Object localPayload, + Object payloadToTriggeredScope, + String businessKey, + CommandContext commandContext); } diff --git a/engine/src/main/java/org/camunda/bpm/engine/impl/event/EventHandlerImpl.java b/engine/src/main/java/org/camunda/bpm/engine/impl/event/EventHandlerImpl.java index 9839b1b0a30..f910628e32f 100644 --- a/engine/src/main/java/org/camunda/bpm/engine/impl/event/EventHandlerImpl.java +++ b/engine/src/main/java/org/camunda/bpm/engine/impl/event/EventHandlerImpl.java @@ -18,7 +18,9 @@ import java.util.Map; +import org.camunda.bpm.engine.ActivityTypes; import org.camunda.bpm.engine.impl.bpmn.behavior.EventSubProcessStartEventActivityBehavior; +import org.camunda.bpm.engine.impl.bpmn.helper.BpmnProperties; import org.camunda.bpm.engine.impl.interceptor.CommandContext; import org.camunda.bpm.engine.impl.persistence.entity.EventSubscriptionEntity; import org.camunda.bpm.engine.impl.pvm.process.ActivityImpl; @@ -39,7 +41,11 @@ public EventHandlerImpl(EventType eventType) { this.eventType = eventType; } - public void handleIntermediateEvent(EventSubscriptionEntity eventSubscription, Object payload, Object localPayload, CommandContext commandContext) { + public void handleIntermediateEvent(EventSubscriptionEntity eventSubscription, + Object payload, + Object localPayload, + Object payloadToTriggeredScope, + CommandContext commandContext) { PvmExecutionImpl execution = eventSubscription.getExecution(); ActivityImpl activity = eventSubscription.getActivity(); @@ -55,6 +61,15 @@ public void handleIntermediateEvent(EventSubscriptionEntity eventSubscription, O execution.setVariablesLocal((Map) localPayload); } + if (payloadToTriggeredScope instanceof Map) { + if (ActivityTypes.INTERMEDIATE_EVENT_MESSAGE.equals(activity.getProperty(BpmnProperties.TYPE.getName()))) { + execution.setVariablesLocal((Map) payloadToTriggeredScope); + } else { + execution.getProcessInstance().setPayloadForTriggeredScope((Map) payloadToTriggeredScope); + } + + } + if(activity.equals(execution.getActivity())) { execution.signal("signal", null); } @@ -70,8 +85,13 @@ public void handleIntermediateEvent(EventSubscriptionEntity eventSubscription, O } @Override - public void handleEvent(EventSubscriptionEntity eventSubscription, Object payload, Object localPayload, String businessKey, CommandContext commandContext) { - handleIntermediateEvent(eventSubscription, payload, localPayload, commandContext); + public void handleEvent(EventSubscriptionEntity eventSubscription, + Object payload, + Object localPayload, + Object payloadToTriggeredScope, + String businessKey, + CommandContext commandContext) { + handleIntermediateEvent(eventSubscription, payload, localPayload, payloadToTriggeredScope, commandContext); } @Override diff --git a/engine/src/main/java/org/camunda/bpm/engine/impl/event/SignalEventHandler.java b/engine/src/main/java/org/camunda/bpm/engine/impl/event/SignalEventHandler.java index 2f5efeca689..a58530138e9 100644 --- a/engine/src/main/java/org/camunda/bpm/engine/impl/event/SignalEventHandler.java +++ b/engine/src/main/java/org/camunda/bpm/engine/impl/event/SignalEventHandler.java @@ -59,9 +59,14 @@ protected void handleStartEvent(EventSubscriptionEntity eventSubscription, Map) payload, businessKey, commandContext); diff --git a/engine/src/main/java/org/camunda/bpm/engine/impl/persistence/entity/EventSubscriptionEntity.java b/engine/src/main/java/org/camunda/bpm/engine/impl/persistence/entity/EventSubscriptionEntity.java index 23aa691adc8..183bbd5024b 100644 --- a/engine/src/main/java/org/camunda/bpm/engine/impl/persistence/entity/EventSubscriptionEntity.java +++ b/engine/src/main/java/org/camunda/bpm/engine/impl/persistence/entity/EventSubscriptionEntity.java @@ -84,34 +84,34 @@ public EventSubscriptionEntity(ExecutionEntity executionEntity, EventType eventT // processing ///////////////////////////// public void eventReceived(Object payload, boolean processASync) { - eventReceived(payload, null, null, processASync); + eventReceived(payload, null, null, null, processASync); } - public void eventReceived(Object payload, Object payloadLocal, String businessKey, boolean processASync) { + public void eventReceived(Object payload, Object payloadLocal, Object payloadToTriggeredScope, String businessKey, boolean processASync) { if(processASync) { - scheduleEventAsync(payload, payloadLocal, businessKey); + scheduleEventAsync(payload, payloadLocal, payloadToTriggeredScope, businessKey); } else { - processEventSync(payload, payloadLocal, businessKey); + processEventSync(payload, payloadLocal, payloadToTriggeredScope, businessKey); } } protected void processEventSync(Object payload) { - this.processEventSync(payload, null, null); + this.processEventSync(payload, null, null, null); } - protected void processEventSync(Object payload, Object payloadLocal, String businessKey) { + protected void processEventSync(Object payload, Object payloadLocal, Object payloadToTriggeredScope, String businessKey) { EventHandler eventHandler = Context.getProcessEngineConfiguration().getEventHandler(eventType); ensureNotNull("Could not find eventhandler for event of type '" + eventType + "'", "eventHandler", eventHandler); - eventHandler.handleEvent(this, payload, payloadLocal, businessKey, Context.getCommandContext()); + eventHandler.handleEvent(this, payload, payloadLocal, payloadToTriggeredScope, businessKey, Context.getCommandContext()); } - protected void scheduleEventAsync(Object payload, Object payloadLocal, String businessKey) { + protected void scheduleEventAsync(Object payload, Object payloadLocal, Object payloadToTriggeredScope, String businessKey) { EventSubscriptionJobDeclaration asyncDeclaration = getJobDeclaration(); if (asyncDeclaration == null) { // fallback to sync if we couldn't find a job declaration - processEventSync(payload, payloadLocal, businessKey); + processEventSync(payload, payloadLocal, payloadToTriggeredScope, businessKey); } else { MessageEntity message = asyncDeclaration.createJobInstance(this); @@ -212,7 +212,7 @@ public ActivityImpl getActivity() { public ProcessDefinitionEntity getProcessDefinition() { if (executionId != null) { ExecutionEntity execution = getExecution(); - return (ProcessDefinitionEntity) execution.getProcessDefinition(); + return execution.getProcessDefinition(); } else { // this assumes that start event subscriptions have the process definition id diff --git a/engine/src/main/java/org/camunda/bpm/engine/impl/pvm/runtime/PvmExecutionImpl.java b/engine/src/main/java/org/camunda/bpm/engine/impl/pvm/runtime/PvmExecutionImpl.java index 0d414af37ca..3353b4282ca 100644 --- a/engine/src/main/java/org/camunda/bpm/engine/impl/pvm/runtime/PvmExecutionImpl.java +++ b/engine/src/main/java/org/camunda/bpm/engine/impl/pvm/runtime/PvmExecutionImpl.java @@ -27,6 +27,7 @@ import java.util.List; import java.util.Map; +import org.camunda.bpm.engine.ActivityTypes; import org.camunda.bpm.engine.ProcessEngineException; import org.camunda.bpm.engine.impl.ProcessEngineLogger; import org.camunda.bpm.engine.impl.bpmn.helper.BpmnProperties; @@ -46,7 +47,6 @@ import org.camunda.bpm.engine.impl.incident.IncidentHandler; import org.camunda.bpm.engine.impl.incident.IncidentHandling; import org.camunda.bpm.engine.impl.persistence.entity.DelayedVariableEvent; -import org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity; import org.camunda.bpm.engine.impl.persistence.entity.IncidentEntity; import org.camunda.bpm.engine.impl.pvm.PvmActivity; import org.camunda.bpm.engine.impl.pvm.PvmException; @@ -174,6 +174,8 @@ public abstract class PvmExecutionImpl extends CoreExecution implements protected boolean activityInstanceEndListenersFailed = false; + protected Map payloadForTriggeredScope; + // sequence counter //////////////////////////////////////////////////////// protected long sequenceCounter = 0; @@ -851,11 +853,28 @@ public void executeActivity(PvmActivity activity) { default: setActivity(activityImpl); setActivityInstanceId(null); + setDelayedPayloadToNewScope(activity); performOperation(PvmAtomicOperation.ACTIVITY_START_CREATE_SCOPE); break; } } + /* + * TODO: Move out setDelayedPayloadToNewScope from PvmExecution to PVM Operations + * check https://github.com/camunda/camunda-bpm-platform/issues/3979 + */ + protected void setDelayedPayloadToNewScope(PvmActivity activity) { + String activityType = (String) activity.getProperty(BpmnProperties.TYPE.getName()); + if (ActivityTypes.START_EVENT_MESSAGE.equals(activityType) // Event subprocess message start event + || ActivityTypes.BOUNDARY_MESSAGE.equals(activityType)) { + if (getProcessInstance().getPayloadForTriggeredScope() != null) { + this.setVariablesLocal(getProcessInstance().getPayloadForTriggeredScope()); + // clear the process instance + getProcessInstance().setPayloadForTriggeredScope(null); + } + } + } + /** * Instantiates the given activity stack under this execution. * Sets the variables for the execution responsible to execute the most deeply nested @@ -1926,6 +1945,14 @@ public void setNextActivity(PvmActivity nextActivity) { this.nextActivity = nextActivity; } + public Map getPayloadForTriggeredScope() { + return payloadForTriggeredScope; + } + + public void setPayloadForTriggeredScope(Map payloadForTriggeredScope) { + this.payloadForTriggeredScope = payloadForTriggeredScope; + } + public PvmExecutionImpl getParentScopeExecution(boolean considerSuperExecution) { if (isProcessInstanceExecution()) { if (considerSuperExecution && getSuperExecution() != null) { diff --git a/engine/src/main/java/org/camunda/bpm/engine/impl/pvm/runtime/operation/PvmAtomicOperationCancelActivity.java b/engine/src/main/java/org/camunda/bpm/engine/impl/pvm/runtime/operation/PvmAtomicOperationCancelActivity.java index cfafe97cc2f..78670ac5615 100644 --- a/engine/src/main/java/org/camunda/bpm/engine/impl/pvm/runtime/operation/PvmAtomicOperationCancelActivity.java +++ b/engine/src/main/java/org/camunda/bpm/engine/impl/pvm/runtime/operation/PvmAtomicOperationCancelActivity.java @@ -16,6 +16,9 @@ */ package org.camunda.bpm.engine.impl.pvm.runtime.operation; +import org.camunda.bpm.engine.ActivityTypes; +import org.camunda.bpm.engine.impl.bpmn.helper.BpmnProperties; +import org.camunda.bpm.engine.impl.core.model.CoreModelElement; import org.camunda.bpm.engine.impl.pvm.PvmActivity; import org.camunda.bpm.engine.impl.pvm.process.ActivityStartBehavior; import org.camunda.bpm.engine.impl.pvm.runtime.LegacyBehavior; @@ -59,6 +62,7 @@ public void execute(PvmExecutionImpl execution) { } propagatingExecution.setActivity(cancellingActivity); + setDelayedPayloadToNewScope(propagatingExecution, (CoreModelElement) cancellingActivity); propagatingExecution.setActive(true); propagatingExecution.setEnded(false); activityCancelled(propagatingExecution); @@ -70,4 +74,16 @@ public boolean isAsync(PvmExecutionImpl execution) { return false; } + protected void setDelayedPayloadToNewScope(PvmExecutionImpl execution, CoreModelElement scope) { + String activityType = (String) scope.getProperty(BpmnProperties.TYPE.getName()); + if (ActivityTypes.START_EVENT_MESSAGE.equals(activityType) // Event subprocess message start event + || ActivityTypes.BOUNDARY_MESSAGE.equals(activityType)) { + PvmExecutionImpl processInstance = execution.getProcessInstance(); + if (processInstance.getPayloadForTriggeredScope() != null) { + execution.setVariablesLocal(processInstance.getPayloadForTriggeredScope()); + // clear the process instance + processInstance.setPayloadForTriggeredScope(null); + } + } + } } diff --git a/engine/src/main/java/org/camunda/bpm/engine/impl/pvm/runtime/operation/PvmAtomicOperationCreateConcurrentExecution.java b/engine/src/main/java/org/camunda/bpm/engine/impl/pvm/runtime/operation/PvmAtomicOperationCreateConcurrentExecution.java index fceb36fe7d9..1ea2abfb6b0 100644 --- a/engine/src/main/java/org/camunda/bpm/engine/impl/pvm/runtime/operation/PvmAtomicOperationCreateConcurrentExecution.java +++ b/engine/src/main/java/org/camunda/bpm/engine/impl/pvm/runtime/operation/PvmAtomicOperationCreateConcurrentExecution.java @@ -16,6 +16,9 @@ */ package org.camunda.bpm.engine.impl.pvm.runtime.operation; +import org.camunda.bpm.engine.ActivityTypes; +import org.camunda.bpm.engine.impl.bpmn.helper.BpmnProperties; +import org.camunda.bpm.engine.impl.core.model.CoreModelElement; import org.camunda.bpm.engine.impl.pvm.PvmActivity; import org.camunda.bpm.engine.impl.pvm.runtime.PvmExecutionImpl; @@ -42,6 +45,7 @@ public void execute(PvmExecutionImpl execution) { // set next activity on propagating execution propagatingExecution.setActivity(activityToStart); + setDelayedPayloadToNewScope(propagatingExecution, (CoreModelElement) activityToStart); concurrentExecutionCreated(propagatingExecution); } @@ -51,4 +55,17 @@ public boolean isAsync(PvmExecutionImpl execution) { return false; } + protected void setDelayedPayloadToNewScope(PvmExecutionImpl execution, CoreModelElement scope) { + String activityType = (String) scope.getProperty(BpmnProperties.TYPE.getName()); + if (ActivityTypes.START_EVENT_MESSAGE.equals(activityType) // Event subprocess message start event + || ActivityTypes.BOUNDARY_MESSAGE.equals(activityType)) { + PvmExecutionImpl processInstance = execution.getProcessInstance(); + if (processInstance.getPayloadForTriggeredScope() != null) { + execution.setVariablesLocal(processInstance.getPayloadForTriggeredScope()); + // clear the process instance + processInstance.setPayloadForTriggeredScope(null); + } + } + } + } diff --git a/engine/src/main/java/org/camunda/bpm/engine/runtime/MessageCorrelationBuilder.java b/engine/src/main/java/org/camunda/bpm/engine/runtime/MessageCorrelationBuilder.java index b3a1c042fb3..f36719e9715 100644 --- a/engine/src/main/java/org/camunda/bpm/engine/runtime/MessageCorrelationBuilder.java +++ b/engine/src/main/java/org/camunda/bpm/engine/runtime/MessageCorrelationBuilder.java @@ -126,6 +126,18 @@ public interface MessageCorrelationBuilder { */ MessageCorrelationBuilder setVariableLocal(String variableName, Object variableValue); + /** + *

Pass a variable to the new scope triggered by message correlation. Use this method for passing the + * message's payload.

+ * + *

Invoking this method multiple times allows passing multiple variables.

+ * + * @param variableName the name of the variable to set + * @param variableValue the value of the variable to set + * @return the builder + */ + MessageCorrelationBuilder setVariableToTriggeredScope(String variableName, Object variableValue); + /** *

Pass a map of variables to the execution waiting on the message. Use this method * for passing the message's payload

@@ -144,6 +156,15 @@ public interface MessageCorrelationBuilder { */ MessageCorrelationBuilder setVariablesLocal(Map variables); + /** + *

Pass a map of variables to the new scope triggered by message correlation. Use this method + * for passing the message's payload.

+ * + * @param variables the map of variables + * @return the builder + */ + MessageCorrelationBuilder setVariablesToTriggeredScope(Map variables); + /** * Specify a tenant to deliver the message to. The message can only be * received on executions or process definitions which belongs to the given diff --git a/engine/src/test/java/org/camunda/bpm/engine/test/api/runtime/MessageCorrelationTest.java b/engine/src/test/java/org/camunda/bpm/engine/test/api/runtime/MessageCorrelationTest.java index c800d3ec7bb..4e8ad400472 100644 --- a/engine/src/test/java/org/camunda/bpm/engine/test/api/runtime/MessageCorrelationTest.java +++ b/engine/src/test/java/org/camunda/bpm/engine/test/api/runtime/MessageCorrelationTest.java @@ -15,7 +15,7 @@ * limitations under the License. */ package org.camunda.bpm.engine.test.api.runtime; - +import static org.camunda.bpm.engine.test.api.runtime.migration.ModifiableBpmnModelInstance.modify; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.Assert.assertEquals; @@ -35,6 +35,7 @@ import java.util.Map; import org.camunda.bpm.engine.BadUserRequestException; +import org.camunda.bpm.engine.HistoryService; import org.camunda.bpm.engine.MismatchingMessageCorrelationException; import org.camunda.bpm.engine.ProcessEngineConfiguration; import org.camunda.bpm.engine.ProcessEngineException; @@ -44,11 +45,14 @@ import org.camunda.bpm.engine.delegate.DelegateExecution; import org.camunda.bpm.engine.delegate.JavaDelegate; import org.camunda.bpm.engine.exception.NullValueException; +import org.camunda.bpm.engine.history.HistoricActivityInstance; +import org.camunda.bpm.engine.history.HistoricVariableInstance; import org.camunda.bpm.engine.impl.digest._apacheCommonsCodec.Base64; import org.camunda.bpm.engine.impl.persistence.entity.ExecutionEntity; import org.camunda.bpm.engine.impl.util.StringUtil; import org.camunda.bpm.engine.repository.ProcessDefinition; import org.camunda.bpm.engine.runtime.Execution; +import org.camunda.bpm.engine.runtime.Job; import org.camunda.bpm.engine.runtime.MessageCorrelationResult; import org.camunda.bpm.engine.runtime.MessageCorrelationResultType; import org.camunda.bpm.engine.runtime.MessageCorrelationResultWithVariables; @@ -72,7 +76,6 @@ import org.junit.Assert; import org.junit.Before; import org.junit.ClassRule; -import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.rules.RuleChain; @@ -1778,7 +1781,6 @@ public void testCorrelateAllWithResultTwoTimesInSameTransaction() { } @Test - @Ignore("CAM-10198") public void testMessageStartEventCorrelationWithLocalVariables() { // given BpmnModelInstance model = Bpmn.createExecutableProcess("Process_1") @@ -2160,6 +2162,418 @@ public void testFailStartMessageOnlyFlagWithCorrelationVariables() { } } + @Test + public void shouldCorrelateNonInterruptingWithVariablesInNewScope() { + // given + BpmnModelInstance model = createModelWithBoundaryEvent(false, false); + + testRule.deploy(model); + + Map variables = new HashMap<>(); + variables.put("processInstanceVar", "processInstanceVarValue"); + engineRule.getRuntimeService().startProcessInstanceByKey("Process_1", variables); + + Map messagePayload = new HashMap<>(); + String outpuValue = "outputValue"; + String variableName = "testVar"; + messagePayload.put(variableName, outpuValue); + + // when + runtimeService + .createMessageCorrelation("1") + .setVariablesToTriggeredScope(messagePayload) + .correlate(); + + // then the scope is "afterMessage" activity + Execution activityInstance = runtimeService.createExecutionQuery().activityId("afterMessage").singleResult(); + assertThat(activityInstance).isNotNull(); + VariableInstance variable = runtimeService + .createVariableInstanceQuery() + .variableName(variableName) + .variableScopeIdIn(activityInstance.getId()) + .singleResult(); + assertThat(variable).isNotNull(); + assertThat(variable.getValue()).isEqualTo(outpuValue); + } + + @Test + public void shouldCorrelateInterruptingWithVariablesInNewScope() { + // given + BpmnModelInstance model = createModelWithBoundaryEvent(true, false); + + testRule.deploy(model); + + Map variables = new HashMap<>(); + variables.put("processInstanceVar", "processInstanceVarValue"); + ProcessInstance processInstance = engineRule.getRuntimeService().startProcessInstanceByKey("Process_1", variables); + + Map messagePayload = new HashMap<>(); + String outpuValue = "outputValue"; + String variableName = "testVar"; + messagePayload.put(variableName, outpuValue); + + // when + runtimeService + .createMessageCorrelation("1") + .setVariablesToTriggeredScope(messagePayload) + .correlate(); + + // then the scope is the PI + VariableInstance variable = runtimeService + .createVariableInstanceQuery() + .variableName(variableName) + .variableScopeIdIn(processInstance.getId()) + .singleResult(); + assertThat(variable).isNotNull(); + assertThat(variable.getValue()).isEqualTo(outpuValue); + } + + @Test + public void shouldCorrelateEventSubprocessNonInterruptingWithVariablesInNewScope() { + // given + BpmnModelInstance targetModel = createModelWithEventSubprocess(false, false); + testRule.deploy(targetModel); + Map variables = new HashMap<>(); + variables.put("processInstanceVar", "processInstanceVarValue"); + engineRule.getRuntimeService().startProcessInstanceByKey("Process_1", variables); + + String outpuValue = "outputValue"; + String variableName = "testVar"; + + // when + runtimeService + .createMessageCorrelation("1") + .setVariableToTriggeredScope(variableName, outpuValue) + .correlate(); + + // then the scope is "afterMessage" activity + Execution activityInstance = runtimeService.createExecutionQuery().activityId("afterMessage").singleResult(); + assertThat(activityInstance).isNotNull(); + VariableInstance variable = runtimeService + .createVariableInstanceQuery() + .variableName(variableName) + .variableScopeIdIn(activityInstance.getId()) + .singleResult(); + assertThat(variable).isNotNull(); + assertThat(variable.getValue()).isEqualTo(outpuValue); + } + + @Test + public void shouldCorrelateEventSubprocessInterruptingWithVariablesInNewScope() { + // given + BpmnModelInstance targetModel = createModelWithEventSubprocess(true, false); + testRule.deploy(targetModel); + Map variables = new HashMap<>(); + variables.put("processInstanceVar", "processInstanceVarValue"); + engineRule.getRuntimeService().startProcessInstanceByKey("Process_1", variables); + + String outpuValue = "outputValue"; + String variableName = "testVar"; + + // when + runtimeService + .createMessageCorrelation("1") + .setVariableToTriggeredScope(variableName, outpuValue) + .correlate(); + + // then the scope is "afterMessage" activity + Execution activityInstance = runtimeService.createExecutionQuery().activityId("afterMessage").singleResult(); + assertThat(activityInstance).isNotNull(); + VariableInstance variable = runtimeService + .createVariableInstanceQuery() + .variableName(variableName) + .variableScopeIdIn(activityInstance.getId()) + .singleResult(); + assertThat(variable).isNotNull(); + assertThat(variable.getValue()).isEqualTo(outpuValue); + } + + @Test + public void shouldCorrelateStartWithVariablesInNewScope() { + // given + BpmnModelInstance model = Bpmn.createExecutableProcess("Process_1") + .startEvent() + .message("1") + .userTask("afterMessage") + .endEvent() + .done(); + + testRule.deploy(model); + + Map variables = new HashMap<>(); + variables.put("processInstanceVar", "processInstanceVarValue"); + + Map messagePayload = new HashMap<>(); + String outpuValue = "outputValue"; + String variableName = "testVar"; + messagePayload.put(variableName, outpuValue); + + // when + MessageCorrelationResult result = runtimeService + .createMessageCorrelation("1") + .setVariablesToTriggeredScope(messagePayload) + .correlateWithResult(); + + // then the scope is the PI + VariableInstance variable = runtimeService + .createVariableInstanceQuery() + .variableScopeIdIn(result.getProcessInstance().getId()) + .variableName(variableName) + .singleResult(); + assertThat(variable).isNotNull(); + assertThat(variable.getValue()).isEqualTo(outpuValue); + } + + @Test + @RequiredHistoryLevel(ProcessEngineConfiguration.HISTORY_FULL) + public void shouldCorrelateIntermediateCatchMessageWithVariablesInNewScope() { + // given + BpmnModelInstance model = Bpmn.createExecutableProcess("Process_1") + .startEvent() + .intermediateCatchEvent("Message_1") + .message("1") + .userTask("afterMessage") + .endEvent() + .done(); + + testRule.deploy(model); + + Map variables = new HashMap<>(); + variables.put("processInstanceVar", "processInstanceVarValue"); + engineRule.getRuntimeService().startProcessInstanceByKey("Process_1", variables); + + Map messagePayload = new HashMap<>(); + String outpuValue = "outputValue"; + String variableName = "testVar"; + messagePayload.put(variableName, outpuValue); + + // when + runtimeService + .createMessageCorrelation("1") + .setVariablesToTriggeredScope(messagePayload) + .correlate(); + + // the scope was "Message" activity + HistoryService historyService = engineRule.getHistoryService(); + HistoricVariableInstance historicVariable = historyService.createHistoricVariableInstanceQuery() + .variableName(variableName) + .singleResult(); + assertThat(historicVariable).isNotNull(); + HistoricActivityInstance historicActivity = historyService.createHistoricActivityInstanceQuery() + .activityId("Message_1").singleResult(); + assertThat(historicActivity).isNotNull(); + assertThat(historicVariable.getActivityInstanceId()).isEqualTo(historicActivity.getId()); + + } + + @Test + public void shouldFailCorrelateWithNullVariableNameInNewScope() { + // given + BpmnModelInstance targetModel = createModelWithBoundaryEvent(true, false); + testRule.deploy(targetModel); + Map variables = new HashMap<>(); + variables.put("processInstanceVar", "processInstanceVarValue"); + engineRule.getRuntimeService().startProcessInstanceByKey("Process_1", variables); + + String outpuValue = "outputValue"; + + // when/then + assertThatThrownBy(() -> runtimeService + .createMessageCorrelation("1") + .setVariableToTriggeredScope(null, outpuValue) + .correlate()) + .isInstanceOf(NullValueException.class) + .hasMessageContaining("variableName"); + } + + @Test + public void shouldCorrelateAsyncNonInterruptingWithVariablesInNewScope() { + // given + BpmnModelInstance model = createModelWithBoundaryEvent(false, true); + + testRule.deploy(model); + + Map variables = new HashMap<>(); + variables.put("processInstanceVar", "processInstanceVarValue"); + engineRule.getRuntimeService().startProcessInstanceByKey("Process_1", variables); + + Map messagePayload = new HashMap<>(); + String outpuValue = "outputValue"; + String variableName = "testVar"; + messagePayload.put(variableName, outpuValue); + + + // when + runtimeService + .createMessageCorrelation("1") + .setVariablesToTriggeredScope(messagePayload) + .correlate(); + Job asyncJob = engineRule.getManagementService().createJobQuery().list().get(0); + assertNotNull(asyncJob); + engineRule.getManagementService().executeJob(asyncJob.getId()); + + // then the scope is "afterMessage" activity + Execution activityInstance = runtimeService.createExecutionQuery().activityId("afterMessage").singleResult(); + assertThat(activityInstance).isNotNull(); + VariableInstance variable = runtimeService + .createVariableInstanceQuery() + .variableName(variableName) + .variableScopeIdIn(activityInstance.getId()) + .singleResult(); + assertThat(variable).isNotNull(); + assertThat(variable.getValue()).isEqualTo(outpuValue); + } + + @Test + public void shouldCorrelateAsyncInterruptingWithVariablesInNewScope() { + // given + BpmnModelInstance model = createModelWithBoundaryEvent(true, true); + + testRule.deploy(model); + + Map variables = new HashMap<>(); + variables.put("processInstanceVar", "processInstanceVarValue"); + ProcessInstance processInstance = engineRule.getRuntimeService().startProcessInstanceByKey("Process_1", variables); + + Map messagePayload = new HashMap<>(); + String outpuValue = "outputValue"; + String variableName = "testVar"; + messagePayload.put(variableName, outpuValue); + + // when + runtimeService + .createMessageCorrelation("1") + .setVariablesToTriggeredScope(messagePayload) + .correlate(); + Job asyncJob = engineRule.getManagementService().createJobQuery().singleResult(); + assertNotNull(asyncJob); + engineRule.getManagementService().executeJob(asyncJob.getId()); + + // then the scope is the PI + VariableInstance variable = runtimeService + .createVariableInstanceQuery() + .variableName(variableName) + .variableScopeIdIn(processInstance.getId()) + .singleResult(); + assertThat(variable).isNotNull(); + assertThat(variable.getValue()).isEqualTo(outpuValue); + } + + @Test + public void shouldCorrelateEventSubprocessAsyncNonInterruptingWithVariablesInNewScope() { + // given + BpmnModelInstance targetModel = createModelWithEventSubprocess(false, true); + testRule.deploy(targetModel); + Map variables = new HashMap<>(); + variables.put("processInstanceVar", "processInstanceVarValue"); + engineRule.getRuntimeService().startProcessInstanceByKey("Process_1", variables); + + String outpuValue = "outputValue"; + String variableName = "testVar"; + + // when + runtimeService + .createMessageCorrelation("1") + .setVariableToTriggeredScope(variableName, outpuValue) + .correlate(); + Job asyncJob = engineRule.getManagementService().createJobQuery().singleResult(); + assertNotNull(asyncJob); + engineRule.getManagementService().executeJob(asyncJob.getId()); + + // then the scope is "afterMessage" activity + Execution activityInstance = runtimeService.createExecutionQuery().activityId("afterMessage").singleResult(); + assertThat(activityInstance).isNotNull(); + VariableInstance variable = runtimeService + .createVariableInstanceQuery() + .variableName(variableName) + .variableScopeIdIn(activityInstance.getId()) + .singleResult(); + assertThat(variable).isNotNull(); + assertThat(variable.getValue()).isEqualTo(outpuValue); + } + + @Test + public void shouldCorrelateEventSubprocessAsyncInterruptingWithVariablesInNewScope() { + // given + BpmnModelInstance targetModel = createModelWithEventSubprocess(true, true); + testRule.deploy(targetModel); + Map variables = new HashMap<>(); + variables.put("processInstanceVar", "processInstanceVarValue"); + engineRule.getRuntimeService().startProcessInstanceByKey("Process_1", variables); + + String outpuValue = "outputValue"; + String variableName = "testVar"; + + // when + runtimeService + .createMessageCorrelation("1") + .setVariableToTriggeredScope(variableName, outpuValue) + .correlate(); + Job asyncJob = engineRule.getManagementService().createJobQuery().singleResult(); + assertNotNull(asyncJob); + engineRule.getManagementService().executeJob(asyncJob.getId()); + + // then the scope is "afterMessage" activity + Execution activityInstance = runtimeService.createExecutionQuery().activityId("afterMessage").singleResult(); + assertThat(activityInstance).isNotNull(); + VariableInstance variable = runtimeService + .createVariableInstanceQuery() + .variableName(variableName) + .variableScopeIdIn(activityInstance.getId()) + .singleResult(); + assertThat(variable).isNotNull(); + assertThat(variable.getValue()).isEqualTo(outpuValue); + } + + @Test + @RequiredHistoryLevel(ProcessEngineConfiguration.HISTORY_FULL) + public void shouldCorrelateAsyncIntermediateCatchMessageWithVariablesInNewScope() { + // given + BpmnModelInstance model = Bpmn.createExecutableProcess("Process_1") + .startEvent() + .intermediateCatchEvent("Message_1") + .camundaAsyncBefore(true) + .message("1") + .userTask("afterMessage") + .endEvent() + .done(); + + testRule.deploy(model); + + Map variables = new HashMap<>(); + variables.put("processInstanceVar", "processInstanceVarValue"); + engineRule.getRuntimeService().startProcessInstanceByKey("Process_1", variables); + + Map messagePayload = new HashMap<>(); + String outpuValue = "outputValue"; + String variableName = "testVar"; + messagePayload.put(variableName, outpuValue); + + Job asyncJob = engineRule.getManagementService().createJobQuery().singleResult(); + assertNotNull(asyncJob); + engineRule.getManagementService().executeJob(asyncJob.getId()); + + // when + runtimeService + .createMessageCorrelation("1") + .setVariablesToTriggeredScope(messagePayload) + .correlate(); + + // the scope was "Message" activity + HistoryService historyService = engineRule.getHistoryService(); + HistoricVariableInstance historicVariable = historyService.createHistoricVariableInstanceQuery() + .variableName(variableName) + .singleResult(); + assertThat(historicVariable).isNotNull(); + HistoricActivityInstance historicActivity = historyService.createHistoricActivityInstanceQuery() + .activityId("Message_1").singleResult(); + assertThat(historicActivity).isNotNull(); + assertThat(historicVariable.getActivityInstanceId()).isEqualTo(historicActivity.getId()); + + } + + // helpers ------------------------------------------------------------------ + protected void deployTwoVersionsWithStartMessageEvent() { testRule.deploy(Bpmn.createExecutableProcess("process") .startEvent() @@ -2189,6 +2603,56 @@ protected void assertTwoInstancesAreStarted(ProcessDefinition firstProcessDefini .isEqualTo(1); } + protected BpmnModelInstance createModelWithEventSubprocess(boolean isInterrupting, boolean isAsync) { + BpmnModelInstance targetModel = modify(Bpmn.createExecutableProcess("Process_1") + .startEvent() + .subProcess("Subprocess_1") + .embeddedSubProcess() + .startEvent() + .userTask() + .endEvent() + .subProcessDone() + .endEvent() + .done()) + .addSubProcessTo("Subprocess_1") + .triggerByEvent() + .embeddedSubProcess() + .startEvent("Message_1") + .camundaAsyncBefore(isAsync) + .interrupting(isInterrupting) + .message("1") + .exclusiveGateway("Gateway_1") + .condition("Condition_1", "${testVar == 'outputValue'}") + .userTask("afterMessage") + .endEvent("happyEnd") + .moveToLastGateway() + .condition("Condition_2", "${testVar != 'outputValue'}") + .userTask("wrongOutcome") + .endEvent("unhappyEnd") + .done(); + return targetModel; + } + + protected BpmnModelInstance createModelWithBoundaryEvent(boolean isInterrupting, boolean isAsync) { + return Bpmn.createExecutableProcess("Process_1") + .startEvent() + .userTask("UserTask_1") + .boundaryEvent("Message_1") + .camundaAsyncBefore(isAsync) + .cancelActivity(isInterrupting) + .message("1") + .exclusiveGateway("Gateway_1") + .condition("Condition_1", "${testVar == 'outputValue'}") + .userTask("afterMessage") + .endEvent("happyEnd") + .moveToLastGateway() + .condition("Condition_2", "${testVar != 'outputValue'}") + .userTask("wrongOutcome") + .endEvent("unhappyEnd") + .done(); + } + + public static class ChangeVariableDelegate implements JavaDelegate { @Override public void execute(DelegateExecution execution) throws Exception { diff --git a/engine/src/test/java/org/camunda/bpm/engine/test/bpmn/event/signal/SignalEventConcurrencyTest.java b/engine/src/test/java/org/camunda/bpm/engine/test/bpmn/event/signal/SignalEventConcurrencyTest.java index e97b91ab9e3..09e362bac20 100644 --- a/engine/src/test/java/org/camunda/bpm/engine/test/bpmn/event/signal/SignalEventConcurrencyTest.java +++ b/engine/src/test/java/org/camunda/bpm/engine/test/bpmn/event/signal/SignalEventConcurrencyTest.java @@ -103,7 +103,7 @@ public Object execute(final CommandContext commandContext) { sendSignalCommand.getMonitor().sync(); return invocation.callRealMethod(); - }).when(evSpy).handleEvent(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()); + }).when(evSpy).handleEvent(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()); // send the signal in a separate thread & wait until it reaches our breakpoint (sync()) in the SignalEventHandler ThreadControl signalThread = executeControllableCommand(sendSignalCommand);