Skip to content

Commit af24d6c

Browse files
committed
Delegate deployment-deletion start event restore to behavior.deploy()
DeploymentProcessDefinitionDeletionManagerImpl now iterates the previous process definition's top-level start events and calls behavior.deploy() instead of dispatching by instanceof. The deploy context gains an isRestoringPreviousVersion() flag so MessageStartEventActivityBehavior can skip its duplicate-subscription guard — the just-deleted PD's row is still in the in-session cache because the bulk delete is queued, not flushed.
1 parent 5241d77 commit af24d6c

4 files changed

Lines changed: 84 additions & 130 deletions

File tree

modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/behavior/MessageStartEventActivityBehavior.java

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,22 @@ public void deploy(ProcessLevelStartEventDeployContext context) {
4646
ProcessDefinitionEntity processDefinition = context.getProcessDefinition();
4747

4848
String messageName = EventDefinitionExpressionUtil.determineMessageName(commandContext, messageEventDefinition, processDefinition);
49-
List<EventSubscriptionEntity> subscriptionsForSameMessageName = eventSubscriptionService
50-
.findEventSubscriptionsByName(MessageEventHandler.EVENT_HANDLER_TYPE, messageName, processDefinition.getTenantId());
5149

52-
for (EventSubscriptionEntity eventSubscriptionEntity : subscriptionsForSameMessageName) {
53-
// throw exception only if there's already a subscription as start event
54-
if (eventSubscriptionEntity.getProcessInstanceId() == null || eventSubscriptionEntity.getProcessInstanceId().isEmpty()) {
55-
// the event subscription has no instance-id, so it's a message start event
56-
throw new FlowableException("Cannot deploy process definition '" + processDefinition.getResourceName()
57-
+ "': there already is a message event subscription for the message with name '" + messageName + "'. For " + eventSubscriptionEntity);
50+
// Skip the duplicate check when restoring a previous version's start events after the latest
51+
// deployment was deleted: the just-deleted process definition's subscription is still in the in-session entity
52+
// cache (the bulk delete in deleteEventSubscriptionsForProcessDefinition is queued, not flushed)
53+
// and would trip a false-positive conflict.
54+
if (!context.isRestoringPreviousVersion()) {
55+
List<EventSubscriptionEntity> subscriptionsForSameMessageName = eventSubscriptionService
56+
.findEventSubscriptionsByName(MessageEventHandler.EVENT_HANDLER_TYPE, messageName, processDefinition.getTenantId());
57+
58+
for (EventSubscriptionEntity eventSubscriptionEntity : subscriptionsForSameMessageName) {
59+
// throw exception only if there's already a subscription as start event
60+
if (eventSubscriptionEntity.getProcessInstanceId() == null || eventSubscriptionEntity.getProcessInstanceId().isEmpty()) {
61+
// the event subscription has no instance-id, so it's a message start event
62+
throw new FlowableException("Cannot deploy process definition '" + processDefinition.getResourceName()
63+
+ "': there already is a message event subscription for the message with name '" + messageName + "'. For " + eventSubscriptionEntity);
64+
}
5865
}
5966
}
6067

modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/behavior/ProcessLevelStartEventDeployContext.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,25 @@ public class ProcessLevelStartEventDeployContext {
3232
protected final StartEvent startEvent;
3333
protected final ProcessEngineConfigurationImpl processEngineConfiguration;
3434
protected final CommandContext commandContext;
35+
protected final boolean restoringPreviousVersion;
3536

3637
public ProcessLevelStartEventDeployContext(ProcessDefinitionEntity processDefinition,
3738
Process process, BpmnModel bpmnModel, StartEvent startEvent,
3839
ProcessEngineConfigurationImpl processEngineConfiguration, CommandContext commandContext) {
40+
this(processDefinition, process, bpmnModel, startEvent, processEngineConfiguration, commandContext, false);
41+
}
42+
43+
public ProcessLevelStartEventDeployContext(ProcessDefinitionEntity processDefinition,
44+
Process process, BpmnModel bpmnModel, StartEvent startEvent,
45+
ProcessEngineConfigurationImpl processEngineConfiguration, CommandContext commandContext,
46+
boolean restoringPreviousVersion) {
3947
this.processDefinition = processDefinition;
4048
this.process = process;
4149
this.bpmnModel = bpmnModel;
4250
this.startEvent = startEvent;
4351
this.processEngineConfiguration = processEngineConfiguration;
4452
this.commandContext = commandContext;
53+
this.restoringPreviousVersion = restoringPreviousVersion;
4554
}
4655

4756
public ProcessDefinitionEntity getProcessDefinition() {
@@ -72,4 +81,14 @@ public EventSubscriptionService getEventSubscriptionService() {
7281
return processEngineConfiguration.getEventSubscriptionServiceConfiguration().getEventSubscriptionService();
7382
}
7483

84+
/**
85+
* {@code true} when this deploy is restoring a previous (earlier-version) process definition's start
86+
* events because the latest version's deployment was just deleted. Behaviors should skip duplicate-
87+
* subscription validation in this mode — the just-deleted process definition's subscriptions are still in the in-
88+
* session entity cache (the bulk delete hasn't been flushed yet) so a re-insert would otherwise
89+
* trip a false-positive conflict. The fresh-deployment path leaves this {@code false}.
90+
*/
91+
public boolean isRestoringPreviousVersion() {
92+
return restoringPreviousVersion;
93+
}
7594
}

modules/flowable-engine/src/main/java/org/flowable/engine/impl/repository/DeploymentProcessDefinitionDeletionManagerImpl.java

Lines changed: 22 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -14,41 +14,25 @@
1414

1515
import java.util.List;
1616

17-
import org.apache.commons.lang3.StringUtils;
18-
import org.flowable.bpmn.constants.BpmnXMLConstants;
1917
import org.flowable.bpmn.model.BpmnModel;
20-
import org.flowable.bpmn.model.EventDefinition;
21-
import org.flowable.bpmn.model.EventRegistryEventDefinition;
22-
import org.flowable.bpmn.model.Message;
23-
import org.flowable.bpmn.model.MessageEventDefinition;
24-
import org.flowable.bpmn.model.SignalEventDefinition;
18+
import org.flowable.bpmn.model.FlowElement;
2519
import org.flowable.bpmn.model.StartEvent;
26-
import org.flowable.bpmn.model.TimerEventDefinition;
2720
import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType;
2821
import org.flowable.common.engine.api.delegate.event.FlowableEventDispatcher;
29-
import org.flowable.common.engine.api.scope.ScopeTypes;
3022
import org.flowable.common.engine.impl.interceptor.CommandContext;
3123
import org.flowable.common.engine.impl.util.CollectionUtil;
3224
import org.flowable.engine.ProcessEngineConfiguration;
3325
import org.flowable.engine.delegate.event.impl.FlowableEventBuilder;
3426
import org.flowable.engine.impl.ProcessDefinitionQueryImpl;
27+
import org.flowable.engine.impl.bpmn.behavior.ProcessLevelStartEventActivityBehavior;
28+
import org.flowable.engine.impl.bpmn.behavior.ProcessLevelStartEventDeployContext;
3529
import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
3630
import org.flowable.engine.impl.context.Context;
37-
import org.flowable.engine.impl.event.EventDefinitionExpressionUtil;
38-
import org.flowable.engine.impl.jobexecutor.TimerEventHandler;
3931
import org.flowable.engine.impl.jobexecutor.TimerStartEventJobHandler;
4032
import org.flowable.engine.impl.persistence.entity.ProcessDefinitionEntity;
4133
import org.flowable.engine.impl.persistence.entity.ProcessDefinitionEntityManager;
42-
import org.flowable.engine.impl.util.CorrelationUtil;
43-
import org.flowable.engine.impl.util.CountingEntityUtil;
4434
import org.flowable.engine.impl.util.ProcessDefinitionUtil;
45-
import org.flowable.engine.impl.util.TimerUtil;
4635
import org.flowable.engine.repository.ProcessDefinition;
47-
import org.flowable.eventsubscription.api.EventSubscription;
48-
import org.flowable.eventsubscription.api.EventSubscriptionBuilder;
49-
import org.flowable.eventsubscription.service.EventSubscriptionService;
50-
import org.flowable.eventsubscription.service.impl.persistence.entity.MessageEventSubscriptionEntity;
51-
import org.flowable.eventsubscription.service.impl.persistence.entity.SignalEventSubscriptionEntity;
5236
import org.flowable.job.service.TimerJobService;
5337
import org.flowable.job.service.impl.persistence.entity.TimerJobEntity;
5438

@@ -71,10 +55,8 @@ public void deleteDefinitionForDeployment(ProcessDefinition processDefinition, S
7155

7256
removeTimerStartJobs(processDefinition);
7357

74-
// If previous process definition version has a timer/signal/message start event, it must be added
75-
// Only if the currently deleted process definition is the latest version,
76-
// we fall back to the previous timer/signal/message start event
77-
58+
// If the deleted process definition was the latest version, restore the previous version's
59+
// timer / message / signal / event-registry start events.
7860
restorePreviousStartEventsIfNeeded(processDefinition);
7961
}
8062

@@ -109,110 +91,31 @@ protected void removeTimerStartJobs(ProcessDefinition processDefinition) {
10991

11092
protected void restorePreviousStartEventsIfNeeded(ProcessDefinition processDefinition) {
11193
ProcessDefinitionEntity latestProcessDefinition = findLatestProcessDefinition(processDefinition);
112-
if (latestProcessDefinition != null && processDefinition.getId().equals(latestProcessDefinition.getId())) {
113-
114-
// Try to find a previous version (it could be some versions are missing due to deletions)
115-
ProcessDefinition previousProcessDefinition = findNewLatestProcessDefinitionAfterRemovalOf(processDefinition);
116-
if (previousProcessDefinition != null) {
117-
118-
BpmnModel bpmnModel = ProcessDefinitionUtil.getBpmnModel(previousProcessDefinition.getId());
119-
org.flowable.bpmn.model.Process previousProcess = ProcessDefinitionUtil.getProcess(previousProcessDefinition.getId());
120-
if (CollectionUtil.isNotEmpty(previousProcess.getFlowElements())) {
121-
122-
List<StartEvent> startEvents = previousProcess.findFlowElementsOfType(StartEvent.class);
123-
124-
if (CollectionUtil.isNotEmpty(startEvents)) {
125-
for (StartEvent startEvent : startEvents) {
126-
127-
if (CollectionUtil.isNotEmpty(startEvent.getEventDefinitions())) {
128-
EventDefinition eventDefinition = startEvent.getEventDefinitions().get(0);
129-
if (eventDefinition instanceof TimerEventDefinition) {
130-
restoreTimerStartEvent(previousProcessDefinition, startEvent, eventDefinition);
131-
} else if (eventDefinition instanceof SignalEventDefinition) {
132-
restoreSignalStartEvent(previousProcessDefinition, bpmnModel, startEvent, eventDefinition);
133-
} else if (eventDefinition instanceof MessageEventDefinition) {
134-
restoreMessageStartEvent(previousProcessDefinition, bpmnModel, startEvent, eventDefinition);
135-
} else if (eventDefinition instanceof EventRegistryEventDefinition eventRegistryEventDefinition
136-
&& StringUtils.isNotEmpty(eventRegistryEventDefinition.getEventDefinitionKey())) {
137-
restoreEventRegistryStartEvent(previousProcessDefinition, bpmnModel, startEvent, eventRegistryEventDefinition.getEventDefinitionKey());
138-
}
139-
}
140-
141-
}
142-
}
143-
144-
}
145-
146-
}
94+
if (latestProcessDefinition == null || !processDefinition.getId().equals(latestProcessDefinition.getId())) {
95+
return;
14796
}
148-
}
14997

150-
protected void restoreTimerStartEvent(ProcessDefinition previousProcessDefinition, StartEvent startEvent, EventDefinition eventDefinition) {
151-
TimerEventDefinition timerEventDefinition = (TimerEventDefinition) eventDefinition;
152-
153-
TimerJobEntity timerJob = TimerUtil.createTimerEntityForTimerEventDefinition(timerEventDefinition, startEvent,
154-
false, previousProcessDefinition, TimerStartEventJobHandler.TYPE, TimerEventHandler.createConfiguration(startEvent.getId(),
155-
timerEventDefinition.getEndDate(), timerEventDefinition.getCalendarName()));
156-
157-
engineConfiguration.getJobServiceConfiguration().getTimerJobService().scheduleTimerJob(timerJob);
158-
}
159-
160-
protected void restoreSignalStartEvent(ProcessDefinition previousProcessDefinition, BpmnModel bpmnModel, StartEvent startEvent, EventDefinition eventDefinition) {
161-
CommandContext commandContext = Context.getCommandContext();
162-
SignalEventDefinition signalEventDefinition = (SignalEventDefinition) eventDefinition;
163-
SignalEventSubscriptionEntity subscriptionEntity = engineConfiguration.getEventSubscriptionServiceConfiguration().getEventSubscriptionService().createSignalEventSubscription();
164-
165-
String eventName = EventDefinitionExpressionUtil.determineSignalName(commandContext, signalEventDefinition, bpmnModel, previousProcessDefinition);
166-
subscriptionEntity.setEventName(eventName);
167-
subscriptionEntity.setActivityId(startEvent.getId());
168-
subscriptionEntity.setProcessDefinitionId(previousProcessDefinition.getId());
169-
if (previousProcessDefinition.getTenantId() != null) {
170-
subscriptionEntity.setTenantId(previousProcessDefinition.getTenantId());
171-
}
172-
173-
engineConfiguration.getEventSubscriptionServiceConfiguration().getEventSubscriptionService().insertEventSubscription(subscriptionEntity);
174-
CountingEntityUtil.handleInsertEventSubscriptionEntityCount(subscriptionEntity);
175-
}
176-
177-
protected void restoreMessageStartEvent(ProcessDefinition previousProcessDefinition, BpmnModel bpmnModel, StartEvent startEvent, EventDefinition eventDefinition) {
178-
MessageEventDefinition messageEventDefinition = (MessageEventDefinition) eventDefinition;
179-
if (bpmnModel.containsMessageId(messageEventDefinition.getMessageRef())) {
180-
Message message = bpmnModel.getMessage(messageEventDefinition.getMessageRef());
181-
messageEventDefinition.setMessageRef(message.getName());
98+
// Try to find a previous version (it could be some versions are missing due to deletions)
99+
ProcessDefinition previousProcessDefinition = findNewLatestProcessDefinitionAfterRemovalOf(processDefinition);
100+
if (previousProcessDefinition == null) {
101+
return;
182102
}
183103

184-
CommandContext commandContext = Context.getCommandContext();
185-
MessageEventSubscriptionEntity newSubscription = engineConfiguration.getEventSubscriptionServiceConfiguration().getEventSubscriptionService().createMessageEventSubscription();
186-
String messageName = EventDefinitionExpressionUtil.determineMessageName(commandContext, messageEventDefinition, previousProcessDefinition);
187-
newSubscription.setEventName(messageName);
188-
newSubscription.setActivityId(startEvent.getId());
189-
newSubscription.setConfiguration(previousProcessDefinition.getId());
190-
newSubscription.setProcessDefinitionId(previousProcessDefinition.getId());
191-
192-
if (previousProcessDefinition.getTenantId() != null) {
193-
newSubscription.setTenantId(previousProcessDefinition.getTenantId());
104+
org.flowable.bpmn.model.Process previousProcess = ProcessDefinitionUtil.getProcess(previousProcessDefinition.getId());
105+
if (CollectionUtil.isEmpty(previousProcess.getFlowElements())) {
106+
return;
194107
}
195108

196-
engineConfiguration.getEventSubscriptionServiceConfiguration().getEventSubscriptionService().insertEventSubscription(newSubscription);
197-
CountingEntityUtil.handleInsertEventSubscriptionEntityCount(newSubscription);
198-
}
199-
200-
protected void restoreEventRegistryStartEvent(ProcessDefinition previousProcessDefinition, BpmnModel bpmnModel, StartEvent startEvent, String eventDefinitionKey) {
109+
BpmnModel bpmnModel = ProcessDefinitionUtil.getBpmnModel(previousProcessDefinition.getId());
201110
CommandContext commandContext = Context.getCommandContext();
202-
EventSubscriptionService eventSubscriptionService = engineConfiguration.getEventSubscriptionServiceConfiguration().getEventSubscriptionService();
203-
EventSubscriptionBuilder eventSubscriptionBuilder = eventSubscriptionService.createEventSubscriptionBuilder()
204-
.eventType(eventDefinitionKey)
205-
.activityId(startEvent.getId())
206-
.processDefinitionId(previousProcessDefinition.getId())
207-
.scopeType(ScopeTypes.BPMN)
208-
.configuration(CorrelationUtil.getCorrelationKey(BpmnXMLConstants.ELEMENT_EVENT_CORRELATION_PARAMETER, commandContext, startEvent, null));
209-
210-
if (previousProcessDefinition.getTenantId() != null) {
211-
eventSubscriptionBuilder.tenantId(previousProcessDefinition.getTenantId());
111+
ProcessDefinitionEntity previousProcessDefinitionEntity = (ProcessDefinitionEntity) previousProcessDefinition;
112+
for (FlowElement flowElement : previousProcess.getFlowElements()) {
113+
if (flowElement instanceof StartEvent startEvent
114+
&& startEvent.getBehavior() instanceof ProcessLevelStartEventActivityBehavior behavior) {
115+
behavior.deploy(new ProcessLevelStartEventDeployContext(previousProcessDefinitionEntity,
116+
previousProcess, bpmnModel, startEvent, engineConfiguration, commandContext, true));
117+
}
212118
}
213-
214-
EventSubscription eventSubscription = eventSubscriptionBuilder.create();
215-
CountingEntityUtil.handleInsertEventSubscriptionEntityCount(eventSubscription);
216119
}
217120

218121
protected ProcessDefinitionEntity findLatestProcessDefinition(ProcessDefinition processDefinition) {

0 commit comments

Comments
 (0)