From f872ecdbbc9f4cffee3ac47927279bec127e5632 Mon Sep 17 00:00:00 2001 From: Tijs Rademakers Date: Thu, 31 Aug 2023 08:41:18 +0200 Subject: [PATCH] Fix issue with event subscription triggering a plan item instance in unavailable state --- .../CmmnEventRegistryEventConsumer.java | 47 ++++++++++----- .../CaseWithEventRegistryTest.java | 60 +++++++++++++++++++ .../ProcessWithEventRegistryTest.java | 1 + .../caseWithEventListener.cmmn | 23 +++++++ 4 files changed, 116 insertions(+), 15 deletions(-) create mode 100644 modules/flowable-event-registry-integration-test/src/test/resources/org/flowable/eventregistry/integrationtest/caseWithEventListener.cmmn diff --git a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/eventregistry/CmmnEventRegistryEventConsumer.java b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/eventregistry/CmmnEventRegistryEventConsumer.java index 6cb710d57b3..3ce17ce7dd1 100644 --- a/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/eventregistry/CmmnEventRegistryEventConsumer.java +++ b/modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/eventregistry/CmmnEventRegistryEventConsumer.java @@ -21,10 +21,14 @@ import org.flowable.cmmn.api.repository.CaseDefinition; import org.flowable.cmmn.api.runtime.CaseInstanceBuilder; import org.flowable.cmmn.api.runtime.CaseInstanceQuery; +import org.flowable.cmmn.api.runtime.PlanItemInstanceState; import org.flowable.cmmn.converter.CmmnXmlConstants; import org.flowable.cmmn.engine.CmmnEngineConfiguration; +import org.flowable.cmmn.engine.impl.persistence.entity.PlanItemInstanceEntity; import org.flowable.cmmn.model.CmmnModel; +import org.flowable.cmmn.model.EventListener; import org.flowable.cmmn.model.ExtensionElement; +import org.flowable.cmmn.model.PlanItem; import org.flowable.common.engine.api.constant.ReferenceTypes; import org.flowable.common.engine.api.scope.ScopeTypes; import org.flowable.common.engine.impl.lock.LockManager; @@ -76,23 +80,37 @@ protected EventRegistryProcessingInfo eventReceived(EventInstance eventInstance) for (EventSubscription eventSubscription : eventSubscriptions) { EventConsumerInfo eventConsumerInfo = new EventConsumerInfo(eventSubscription.getId(), eventSubscription.getSubScopeId(), eventSubscription.getScopeDefinitionId(), ScopeTypes.CMMN); - handleEventSubscription(cmmnRuntimeService, eventSubscription, eventInstance, correlationKeys, eventConsumerInfo); - eventRegistryProcessingInfo.addEventConsumerInfo(eventConsumerInfo); + boolean eventSubscriptionHandled = handleEventSubscription(cmmnRuntimeService, eventSubscription, eventInstance, correlationKeys, eventConsumerInfo); + + if (eventSubscriptionHandled) { + eventRegistryProcessingInfo.addEventConsumerInfo(eventConsumerInfo); + } } return eventRegistryProcessingInfo; } - protected void handleEventSubscription(CmmnRuntimeService cmmnRuntimeService, EventSubscription eventSubscription, + protected boolean handleEventSubscription(CmmnRuntimeService cmmnRuntimeService, EventSubscription eventSubscription, EventInstance eventInstance, Collection correlationKeys, EventConsumerInfo eventConsumerInfo) { if (eventSubscription.getSubScopeId() != null) { // When a subscope id is set, this means that a plan item instance is waiting for the event - cmmnRuntimeService.createPlanItemInstanceTransitionBuilder(eventSubscription.getSubScopeId()) - .transientVariable(EventConstants.EVENT_INSTANCE, eventInstance) - .trigger(); + PlanItemInstanceEntity planItemInstanceEntity = (PlanItemInstanceEntity) cmmnRuntimeService.createPlanItemInstanceQuery().planItemInstanceId(eventSubscription.getSubScopeId()).singleResult(); + CmmnModel cmmnModel = cmmnEngineConfiguration.getCmmnRepositoryService().getCmmnModel(planItemInstanceEntity.getCaseDefinitionId()); + PlanItem planItem = cmmnModel.findPlanItemByPlanItemDefinitionId(planItemInstanceEntity.getPlanItemDefinitionId()); + if (PlanItemInstanceState.ACTIVE.equals(planItemInstanceEntity.getState()) + || (planItem != null && planItem.getPlanItemDefinition() instanceof EventListener + && PlanItemInstanceState.AVAILABLE.equals(planItemInstanceEntity.getState()))) { + + cmmnRuntimeService.createPlanItemInstanceTransitionBuilder(eventSubscription.getSubScopeId()) + .transientVariable(EventConstants.EVENT_INSTANCE, eventInstance) + .trigger(); + + } else { + return false; + } } else if (eventSubscription.getScopeDefinitionId() != null && eventSubscription.getScopeId() == null @@ -122,7 +140,7 @@ protected void handleEventSubscription(CmmnRuntimeService cmmnRuntimeService, Ev // Returning, no new instance should be started eventConsumerInfo.setHasExistingInstancesForUniqueCorrelation(true); LOGGER.debug("Event received to start a new case instance, but a unique instance already exists."); - return; + return true; } else if (cmmnEngineConfiguration.isEventRegistryUniqueCaseInstanceCheckWithLock()) { @@ -161,11 +179,11 @@ protected void handleEventSubscription(CmmnRuntimeService cmmnRuntimeService, Ev // Returning, no new instance should be started eventConsumerInfo.setHasExistingInstancesForUniqueCorrelation(true); LOGGER.debug("Event received to start a new case instance, but a unique instance already exists."); - return; + return true; } startCaseInstance(caseInstanceBuilder, correlationKeyWithAllParameters.getValue(), ReferenceTypes.EVENT_CASE); - return; + return true; } finally { lockManager.releaseAndDeleteLock(); @@ -173,24 +191,23 @@ protected void handleEventSubscription(CmmnRuntimeService cmmnRuntimeService, Ev } } else { - LOGGER.info( - "Lock for {} was not acquired. This means that another event has already acquired that lock and will start a new case instance. Ignoring this one.", - countLockName); - return; + LOGGER.info("Lock for {} was not acquired. This means that another event has already acquired that lock and will start a new case instance. Ignoring this one.", countLockName); + return true; } } else { startCaseInstance(caseInstanceBuilder, correlationKeyWithAllParameters.getValue(), ReferenceTypes.EVENT_CASE); - return; + return true; } } } startCaseInstance(caseInstanceBuilder, null, null); - } + + return true; } protected long countCaseInstances(CmmnRuntimeService cmmnRuntimeService, EventInstance eventInstance, diff --git a/modules/flowable-event-registry-integration-test/src/test/java/org/flowable/eventregistry/integrationtest/CaseWithEventRegistryTest.java b/modules/flowable-event-registry-integration-test/src/test/java/org/flowable/eventregistry/integrationtest/CaseWithEventRegistryTest.java index ca055b016c8..5d9235077e3 100644 --- a/modules/flowable-event-registry-integration-test/src/test/java/org/flowable/eventregistry/integrationtest/CaseWithEventRegistryTest.java +++ b/modules/flowable-event-registry-integration-test/src/test/java/org/flowable/eventregistry/integrationtest/CaseWithEventRegistryTest.java @@ -31,7 +31,12 @@ import org.flowable.common.engine.impl.interceptor.EngineConfigurationConstants; import org.flowable.eventregistry.api.EventDeployment; import org.flowable.eventregistry.api.EventRegistry; +import org.flowable.eventregistry.api.EventRegistryEvent; +import org.flowable.eventregistry.api.EventRegistryNonMatchingEventConsumer; +import org.flowable.eventregistry.api.EventRegistryProcessingInfo; import org.flowable.eventregistry.api.EventRepositoryService; +import org.flowable.eventregistry.api.runtime.EventInstance; +import org.flowable.eventregistry.api.runtime.EventPayloadInstance; import org.flowable.eventregistry.impl.EventRegistryEngineConfiguration; import org.flowable.eventregistry.model.InboundChannelModel; import org.flowable.task.api.Task; @@ -151,6 +156,43 @@ public void testStartCaseWithEventDirectlyOnChannel() { } } + @Test + @CmmnDeployment(resources = { "org/flowable/eventregistry/integrationtest/caseWithEventListener.cmmn", + "org/flowable/eventregistry/integrationtest/one.event", + "org/flowable/eventregistry/integrationtest/one.channel"}) + public void testStartCaseWithEventInUnavailableState() throws Exception { + try { + CaseInstance caseInstance = cmmnRuntimeService.createCaseInstanceBuilder().caseDefinitionKey("testEvent") + .start(); + + assertThat(cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance.getId()).count()).isEqualTo(0); + + TestNonMatchingEventConsumer nonMatchingEventConsumer = new TestNonMatchingEventConsumer(); + getEventRegistryEngineConfiguration().setNonMatchingEventConsumer(nonMatchingEventConsumer); + + InboundChannelModel channelModel = (InboundChannelModel) getEventRepositoryService().getChannelModelByKey("one"); + ObjectNode eventNode = getEventRegistryEngineConfiguration().getObjectMapper().createObjectNode(); + eventNode.put("payload1", "fozzie"); + eventNode.put("payload2", 456); + getEventRegistry().eventReceived(channelModel, eventNode.toString()); + + assertThat(cmmnTaskService.createTaskQuery().caseInstanceId(caseInstance.getId()).count()).isEqualTo(0); + + assertThat(nonMatchingEventConsumer.getNonMatchingEvent()).isNotNull(); + EventInstance eventInstance = (EventInstance) nonMatchingEventConsumer.getNonMatchingEvent().getEventObject(); + EventPayloadInstance payloadInstance = eventInstance.getPayloadInstances().iterator().next(); + assertThat(payloadInstance.getDefinitionName()).isEqualTo("payload1"); + assertThat(payloadInstance.getValue()).isEqualTo("fozzie"); + + } finally { + getEventRegistryEngineConfiguration().setNonMatchingEventConsumer(null); + List eventDeployments = getEventRepositoryService().createDeploymentQuery().list(); + for (EventDeployment eventDeployment : eventDeployments) { + getEventRepositoryService().deleteDeployment(eventDeployment.getId()); + } + } + } + @Test @CmmnDeployment(resources = { "org/flowable/eventregistry/integrationtest/testSendEventTask.cmmn", "org/flowable/eventregistry/integrationtest/one.event", @@ -307,4 +349,22 @@ protected EventRegistry getEventRegistry() { protected EventRegistryEngineConfiguration getEventRegistryEngineConfiguration() { return (EventRegistryEngineConfiguration) cmmnEngine.getCmmnEngineConfiguration().getEngineConfigurations().get(EngineConfigurationConstants.KEY_EVENT_REGISTRY_CONFIG); } + + protected class TestNonMatchingEventConsumer implements EventRegistryNonMatchingEventConsumer { + + protected EventRegistryEvent nonMatchingEvent; + + @Override + public void handleNonMatchingEvent(EventRegistryEvent event, EventRegistryProcessingInfo eventRegistryProcessingInfo) { + nonMatchingEvent = event; + } + + public EventRegistryEvent getNonMatchingEvent() { + return nonMatchingEvent; + } + + public void setNonMatchingEvent(EventRegistryEvent nonMatchingEvent) { + this.nonMatchingEvent = nonMatchingEvent; + } + } } diff --git a/modules/flowable-event-registry-integration-test/src/test/java/org/flowable/eventregistry/integrationtest/ProcessWithEventRegistryTest.java b/modules/flowable-event-registry-integration-test/src/test/java/org/flowable/eventregistry/integrationtest/ProcessWithEventRegistryTest.java index 9aa0c04acb5..db28eab9a15 100644 --- a/modules/flowable-event-registry-integration-test/src/test/java/org/flowable/eventregistry/integrationtest/ProcessWithEventRegistryTest.java +++ b/modules/flowable-event-registry-integration-test/src/test/java/org/flowable/eventregistry/integrationtest/ProcessWithEventRegistryTest.java @@ -254,6 +254,7 @@ public void testHeaderCorrelationEvent() { assertThat(runtimeService.getVariable(anotherProcessInstance.getId(), "value2")).isEqualTo(456); } finally { + getEventRegistryEngineConfiguration().setNonMatchingEventConsumer(null); List eventDeployments = getEventRepositoryService().createDeploymentQuery().list(); for (EventDeployment eventDeployment : eventDeployments) { getEventRepositoryService().deleteDeployment(eventDeployment.getId()); diff --git a/modules/flowable-event-registry-integration-test/src/test/resources/org/flowable/eventregistry/integrationtest/caseWithEventListener.cmmn b/modules/flowable-event-registry-integration-test/src/test/resources/org/flowable/eventregistry/integrationtest/caseWithEventListener.cmmn new file mode 100644 index 00000000000..d76275706bf --- /dev/null +++ b/modules/flowable-event-registry-integration-test/src/test/resources/org/flowable/eventregistry/integrationtest/caseWithEventListener.cmmn @@ -0,0 +1,23 @@ + + + + + + + + + + + occur + + + + + + + + + + + \ No newline at end of file