Skip to content

Commit 00c47a1

Browse files
added definition variable container to support EL Resolvers to use the definition if no VariableScope is available (during deployment)
1 parent 18db156 commit 00c47a1

13 files changed

Lines changed: 635 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Copyright 2026, Flowable Licences AG.
3+
* This license is based on the software license agreement and terms and conditions in effect between the parties
4+
* at the time of purchase of the Flowable software product.
5+
* Your agreement to these terms and conditions is required to install or use the Flowable software product and/or this file.
6+
* Flowable is a trademark of Flowable AG registered in several countries.
7+
*/
8+
package org.flowable.common.engine.api.definition;
9+
10+
import java.util.Set;
11+
12+
import org.flowable.common.engine.api.variable.VariableContainer;
13+
14+
/**
15+
* A {@link VariableContainer} that carries definition context (definitionId, deploymentId, scopeType, tenantId)
16+
* for expression evaluation when no execution is active (e.g. during deployment).
17+
* This allows EL resolvers to look up the parent deployment and resolve definition-scoped expressions.
18+
*
19+
* @author Christopher Welsch
20+
*/
21+
public class DefinitionVariableContainer implements VariableContainer {
22+
23+
protected String definitionId;
24+
protected String deploymentId;
25+
protected String scopeType;
26+
protected String tenantId;
27+
28+
public DefinitionVariableContainer() {
29+
}
30+
31+
public DefinitionVariableContainer(String definitionId, String deploymentId, String scopeType, String tenantId) {
32+
this.definitionId = definitionId;
33+
this.deploymentId = deploymentId;
34+
this.scopeType = scopeType;
35+
this.tenantId = tenantId;
36+
}
37+
38+
public String getDefinitionId() {
39+
return definitionId;
40+
}
41+
42+
public String getDeploymentId() {
43+
return deploymentId;
44+
}
45+
46+
public String getScopeType() {
47+
return scopeType;
48+
}
49+
50+
@Override
51+
public String getTenantId() {
52+
return tenantId;
53+
}
54+
55+
@Override
56+
public boolean hasVariable(String variableName) {
57+
return false;
58+
}
59+
60+
@Override
61+
public Object getVariable(String variableName) {
62+
return null;
63+
}
64+
65+
@Override
66+
public void setVariable(String variableName, Object variableValue) {
67+
68+
}
69+
70+
@Override
71+
public void setTransientVariable(String variableName, Object variableValue) {
72+
73+
}
74+
75+
@Override
76+
public Set<String> getVariableNames() {
77+
return Set.of();
78+
}
79+
}

modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/deployer/EventSubscriptionManager.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import org.flowable.eventsubscription.service.impl.persistence.entity.EventSubscriptionEntity;
4646
import org.flowable.eventsubscription.service.impl.persistence.entity.MessageEventSubscriptionEntity;
4747
import org.flowable.eventsubscription.service.impl.persistence.entity.SignalEventSubscriptionEntity;
48+
import org.flowable.common.engine.api.definition.DefinitionVariableContainer;
4849

4950
/**
5051
* Manages event subscriptions for newly-deployed process definitions and their previous versions.
@@ -173,7 +174,10 @@ protected void insertSignalEvent(SignalEventDefinition signalEventDefinition, St
173174
EventSubscriptionService eventSubscriptionService = processEngineConfiguration.getEventSubscriptionServiceConfiguration().getEventSubscriptionService();
174175
SignalEventSubscriptionEntity subscriptionEntity = eventSubscriptionService.createSignalEventSubscription();
175176

176-
String signalName = EventDefinitionExpressionUtil.determineSignalName(commandContext, signalEventDefinition, bpmnModel,null);
177+
DefinitionVariableContainer definitionVariableContainer = new DefinitionVariableContainer(processDefinition.getId(), processDefinition.getDeploymentId(),
178+
ScopeTypes.BPMN, processDefinition.getTenantId());
179+
180+
String signalName = EventDefinitionExpressionUtil.determineSignalName(commandContext, signalEventDefinition, bpmnModel, definitionVariableContainer);
177181
subscriptionEntity.setEventName(signalName);
178182

179183
subscriptionEntity.setActivityId(startEvent.getId());
@@ -191,8 +195,12 @@ protected void insertMessageEvent(MessageEventDefinition messageEventDefinition,
191195

192196
ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(commandContext);
193197
EventSubscriptionService eventSubscriptionService = processEngineConfiguration.getEventSubscriptionServiceConfiguration().getEventSubscriptionService();
198+
199+
DefinitionVariableContainer definitionVariableContainer = new DefinitionVariableContainer(processDefinition.getId(), processDefinition.getDeploymentId(),
200+
ScopeTypes.BPMN, processDefinition.getTenantId());
201+
194202
// look for subscriptions for the same name in db:
195-
String messageName = EventDefinitionExpressionUtil.determineMessageName(commandContext, messageEventDefinition, null);
203+
String messageName = EventDefinitionExpressionUtil.determineMessageName(commandContext, messageEventDefinition, definitionVariableContainer);
196204
List<EventSubscriptionEntity> subscriptionsForSameMessageName = eventSubscriptionService
197205
.findEventSubscriptionsByName(MessageEventHandler.EVENT_HANDLER_TYPE, messageName, processDefinition.getTenantId());
198206

modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/deployer/TimerManager.java

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.flowable.bpmn.model.Process;
2121
import org.flowable.bpmn.model.StartEvent;
2222
import org.flowable.bpmn.model.TimerEventDefinition;
23+
import org.flowable.common.engine.api.scope.ScopeTypes;
2324
import org.flowable.common.engine.impl.context.Context;
2425
import org.flowable.common.engine.impl.util.CollectionUtil;
2526
import org.flowable.engine.ProcessEngineConfiguration;
@@ -32,6 +33,7 @@
3233
import org.flowable.job.service.TimerJobService;
3334
import org.flowable.job.service.impl.cmd.CancelJobsCmd;
3435
import org.flowable.job.service.impl.persistence.entity.TimerJobEntity;
36+
import org.flowable.common.engine.api.definition.DefinitionVariableContainer;
3537

3638
/**
3739
* Manages timers for newly-deployed process definitions and their previous versions.
@@ -73,18 +75,21 @@ protected List<TimerJobEntity> getTimerDeclarations(ProcessDefinitionEntity proc
7375
if (CollectionUtil.isNotEmpty(startEvent.getEventDefinitions())) {
7476
EventDefinition eventDefinition = startEvent.getEventDefinitions().get(0);
7577
if (eventDefinition instanceof TimerEventDefinition timerEventDefinition) {
78+
79+
DefinitionVariableContainer definitionVariableContainer = new DefinitionVariableContainer(processDefinition.getId(),
80+
processDefinition.getDeploymentId(),
81+
ScopeTypes.BPMN, processDefinition.getTenantId());
82+
7683
TimerJobEntity timerJob = TimerUtil.createTimerEntityForTimerEventDefinition(timerEventDefinition, startEvent,
77-
false, null, TimerStartEventJobHandler.TYPE, TimerEventHandler.createConfiguration(startEvent.getId(),
84+
false, definitionVariableContainer, TimerStartEventJobHandler.TYPE, TimerEventHandler.createConfiguration(startEvent.getId(),
7885
timerEventDefinition.getEndDate(), timerEventDefinition.getCalendarName()));
7986

80-
if (timerJob != null) {
81-
timerJob.setProcessDefinitionId(processDefinition.getId());
87+
timerJob.setProcessDefinitionId(processDefinition.getId());
8288

83-
if (processDefinition.getTenantId() != null) {
84-
timerJob.setTenantId(processDefinition.getTenantId());
85-
}
86-
timers.add(timerJob);
89+
if (processDefinition.getTenantId() != null) {
90+
timerJob.setTenantId(processDefinition.getTenantId());
8791
}
92+
timers.add(timerJob);
8893

8994
}
9095
}

modules/flowable-engine/src/main/java/org/flowable/engine/impl/dynamic/AbstractDynamicStateManager.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
import org.flowable.bpmn.model.UserTask;
5656
import org.flowable.bpmn.model.ValuedDataObject;
5757
import org.flowable.common.engine.api.FlowableException;
58+
import org.flowable.common.engine.api.definition.DefinitionVariableContainer;
5859
import org.flowable.common.engine.api.delegate.Expression;
5960
import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType;
6061
import org.flowable.common.engine.api.delegate.event.FlowableEventDispatcher;
@@ -1585,7 +1586,9 @@ protected void processCreatedEventSubProcess(EventSubProcess eventSubProcess, Ex
15851586
messageExecution.setEventScope(true);
15861587
messageExecution.setActive(false);
15871588

1588-
String messageName = EventDefinitionExpressionUtil.determineMessageName(commandContext, messageEventDefinition, null);
1589+
DefinitionVariableContainer definitionVariableContainer = new DefinitionVariableContainer(messageExecution.getProcessDefinitionId(),
1590+
eventSubProcessExecution.getDeploymentId(), ScopeTypes.BPMN, messageExecution.getTenantId());
1591+
String messageName = EventDefinitionExpressionUtil.determineMessageName(commandContext, messageEventDefinition, definitionVariableContainer);
15891592
EventSubscriptionEntity messageSubscription = (EventSubscriptionEntity) eventSubscriptionService.createEventSubscriptionBuilder()
15901593
.eventType(MessageEventSubscriptionEntity.EVENT_TYPE)
15911594
.eventName(messageName)
@@ -1618,7 +1621,9 @@ protected void processCreatedEventSubProcess(EventSubProcess eventSubProcess, Ex
16181621
signalExecution.setEventScope(true);
16191622
signalExecution.setActive(false);
16201623

1621-
String eventName = EventDefinitionExpressionUtil.determineSignalName(commandContext, signalEventDefinition, bpmnModel, null);
1624+
DefinitionVariableContainer signalDefinitionVariableContainer = new DefinitionVariableContainer(signalExecution.getProcessDefinitionId(),
1625+
eventSubProcessExecution.getDeploymentId(), ScopeTypes.BPMN, signalExecution.getTenantId());
1626+
String eventName = EventDefinitionExpressionUtil.determineSignalName(commandContext, signalEventDefinition, bpmnModel, signalDefinitionVariableContainer);
16221627

16231628
EventSubscriptionEntity signalSubscription = (EventSubscriptionEntity) eventSubscriptionService.createEventSubscriptionBuilder()
16241629
.eventType(SignalEventSubscriptionEntity.EVENT_TYPE)

modules/flowable-engine/src/main/java/org/flowable/engine/impl/event/EventDefinitionExpressionUtil.java

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.flowable.common.engine.impl.interceptor.CommandContext;
2222
import org.flowable.engine.delegate.DelegateExecution;
2323
import org.flowable.engine.impl.util.CommandContextUtil;
24+
import org.flowable.common.engine.api.variable.VariableContainer;
2425
import org.flowable.variable.service.impl.el.NoExecutionVariableScope;
2526

2627
/**
@@ -57,6 +58,34 @@ public static String determineSignalName(CommandContext commandContext, SignalEv
5758
return signalName;
5859
}
5960

61+
/**
62+
* Determines signal name from definition or expression
63+
*/
64+
public static String determineSignalName(CommandContext commandContext, SignalEventDefinition signalEventDefinition, BpmnModel bpmnModel,
65+
VariableContainer definitionVariableContainer) {
66+
String signalName = null;
67+
68+
if (StringUtils.isNotEmpty(signalEventDefinition.getSignalRef())) {
69+
Signal signal = bpmnModel.getSignal(signalEventDefinition.getSignalRef());
70+
if (signal != null) {
71+
signalName = signal.getName();
72+
} else {
73+
signalName = signalEventDefinition.getSignalRef();
74+
}
75+
76+
} else {
77+
signalName = signalEventDefinition.getSignalExpression();
78+
79+
}
80+
81+
if (StringUtils.isNotEmpty(signalName)) {
82+
Expression expression = CommandContextUtil.getProcessEngineConfiguration(commandContext).getExpressionManager().createExpression(signalName);
83+
return expression.getValue(definitionVariableContainer).toString();
84+
}
85+
86+
return signalName;
87+
}
88+
6089
/**
6190
* Determines the event name of the {@link org.flowable.bpmn.model.MessageEventDefinition} that is passed:
6291
* - if a message ref is set, it has precedence
@@ -83,4 +112,26 @@ public static String determineMessageName(CommandContext commandContext, Message
83112
return messageName;
84113
}
85114

115+
/**
116+
* Determines message name from definition or expression
117+
*/
118+
public static String determineMessageName(CommandContext commandContext, MessageEventDefinition messageEventDefinition,
119+
VariableContainer definitionVariableContainer) {
120+
String messageName = null;
121+
122+
if (StringUtils.isNotEmpty(messageEventDefinition.getMessageRef())) {
123+
return messageEventDefinition.getMessageRef();
124+
125+
} else {
126+
messageName = messageEventDefinition.getMessageExpression();
127+
128+
}
129+
130+
if (StringUtils.isNotEmpty(messageName)) {
131+
Expression expression = CommandContextUtil.getProcessEngineConfiguration(commandContext).getExpressionManager().createExpression(messageName);
132+
return expression.getValue(definitionVariableContainer).toString();
133+
}
134+
135+
return messageName;
136+
}
86137
}

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

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import org.flowable.eventsubscription.service.impl.persistence.entity.SignalEventSubscriptionEntity;
5151
import org.flowable.job.service.TimerJobService;
5252
import org.flowable.job.service.impl.persistence.entity.TimerJobEntity;
53+
import org.flowable.common.engine.api.definition.DefinitionVariableContainer;
5354

5455
/**
5556
* @author Filip Hrisafov
@@ -154,13 +155,17 @@ protected void restorePreviousStartEventsIfNeeded(ProcessDefinition processDefin
154155

155156
protected void restoreTimerStartEvent(ProcessDefinition previousProcessDefinition, StartEvent startEvent, EventDefinition eventDefinition) {
156157
TimerEventDefinition timerEventDefinition = (TimerEventDefinition) eventDefinition;
158+
159+
DefinitionVariableContainer definitionVariableContainer = new DefinitionVariableContainer(previousProcessDefinition.getId(),
160+
previousProcessDefinition.getDeploymentId(), ScopeTypes.BPMN, previousProcessDefinition.getTenantId());
161+
157162
TimerJobEntity timer = TimerUtil.createTimerEntityForTimerEventDefinition((TimerEventDefinition) eventDefinition, startEvent,
158-
false, null, TimerStartEventJobHandler.TYPE, TimerEventHandler.createConfiguration(startEvent.getId(),
163+
false, definitionVariableContainer, TimerStartEventJobHandler.TYPE, TimerEventHandler.createConfiguration(startEvent.getId(),
159164
timerEventDefinition.getEndDate(), timerEventDefinition.getCalendarName()));
160165

161166
if (timer != null) {
162167
TimerJobEntity timerJob = TimerUtil.createTimerEntityForTimerEventDefinition(timerEventDefinition, startEvent,
163-
false, null, TimerStartEventJobHandler.TYPE, TimerEventHandler.createConfiguration(startEvent.getId(),
168+
false, definitionVariableContainer, TimerStartEventJobHandler.TYPE, TimerEventHandler.createConfiguration(startEvent.getId(),
164169
timerEventDefinition.getEndDate(), timerEventDefinition.getCalendarName()));
165170

166171
timerJob.setProcessDefinitionId(previousProcessDefinition.getId());
@@ -178,7 +183,9 @@ protected void restoreSignalStartEvent(ProcessDefinition previousProcessDefiniti
178183
SignalEventDefinition signalEventDefinition = (SignalEventDefinition) eventDefinition;
179184
SignalEventSubscriptionEntity subscriptionEntity = engineConfiguration.getEventSubscriptionServiceConfiguration().getEventSubscriptionService().createSignalEventSubscription();
180185

181-
String eventName = EventDefinitionExpressionUtil.determineSignalName(commandContext, signalEventDefinition, bpmnModel, null);
186+
DefinitionVariableContainer definitionVariableContainer = new DefinitionVariableContainer(previousProcessDefinition.getId(),
187+
previousProcessDefinition.getDeploymentId(), ScopeTypes.BPMN, previousProcessDefinition.getTenantId());
188+
String eventName = EventDefinitionExpressionUtil.determineSignalName(commandContext, signalEventDefinition, bpmnModel, definitionVariableContainer);
182189
subscriptionEntity.setEventName(eventName);
183190
subscriptionEntity.setActivityId(startEvent.getId());
184191
subscriptionEntity.setProcessDefinitionId(previousProcessDefinition.getId());
@@ -199,7 +206,9 @@ protected void restoreMessageStartEvent(ProcessDefinition previousProcessDefinit
199206

200207
CommandContext commandContext = Context.getCommandContext();
201208
MessageEventSubscriptionEntity newSubscription = engineConfiguration.getEventSubscriptionServiceConfiguration().getEventSubscriptionService().createMessageEventSubscription();
202-
String messageName = EventDefinitionExpressionUtil.determineMessageName(commandContext, messageEventDefinition, null);
209+
DefinitionVariableContainer definitionVariableContainer = new DefinitionVariableContainer(previousProcessDefinition.getId(),
210+
previousProcessDefinition.getDeploymentId(), ScopeTypes.BPMN, previousProcessDefinition.getTenantId());
211+
String messageName = EventDefinitionExpressionUtil.determineMessageName(commandContext, messageEventDefinition, definitionVariableContainer);
203212
newSubscription.setEventName(messageName);
204213
newSubscription.setActivityId(startEvent.getId());
205214
newSubscription.setConfiguration(previousProcessDefinition.getId());

modules/flowable-engine/src/main/java/org/flowable/engine/impl/util/ProcessInstanceHelper.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
import org.flowable.eventsubscription.service.impl.persistence.entity.SignalEventSubscriptionEntity;
6565
import org.flowable.identitylink.api.IdentityLinkType;
6666
import org.flowable.job.service.impl.persistence.entity.TimerJobEntity;
67+
import org.flowable.common.engine.api.definition.DefinitionVariableContainer;
6768

6869
import tools.jackson.databind.node.ObjectNode;
6970

@@ -157,7 +158,10 @@ public ProcessInstance createAndStartProcessInstanceByMessage(ProcessDefinition
157158
if (CollectionUtil.isNotEmpty(startEvent.getEventDefinitions()) && startEvent.getEventDefinitions()
158159
.get(0) instanceof MessageEventDefinition messageEventDefinition) {
159160

160-
String actualMessageName = EventDefinitionExpressionUtil.determineMessageName(commandContext, messageEventDefinition, null);
161+
DefinitionVariableContainer definitionVariableContainer = new DefinitionVariableContainer(processDefinition.getId(), processDefinition.getDeploymentId(),
162+
ScopeTypes.BPMN, processDefinition.getTenantId());
163+
164+
String actualMessageName = EventDefinitionExpressionUtil.determineMessageName(commandContext, messageEventDefinition, definitionVariableContainer);
161165
if (Objects.equals(actualMessageName, messageName)) {
162166
initialFlowElement = flowElement;
163167
break;
@@ -461,7 +465,9 @@ protected void handleSignalEventSubscription(EventDefinition eventDefinition, St
461465
signalExecution.setEventScope(true);
462466
signalExecution.setActive(false);
463467

464-
String eventName = EventDefinitionExpressionUtil.determineSignalName(commandContext, signalEventDefinition, bpmnModel, null);
468+
DefinitionVariableContainer definitionVariableContainer = new DefinitionVariableContainer(parentExecution.getProcessDefinitionId(),
469+
parentExecution.getDeploymentId(), ScopeTypes.BPMN, parentExecution.getTenantId());
470+
String eventName = EventDefinitionExpressionUtil.determineSignalName(commandContext, signalEventDefinition, bpmnModel, definitionVariableContainer);
465471

466472
EventSubscriptionEntity eventSubscription = (EventSubscriptionEntity) processEngineConfiguration.getEventSubscriptionServiceConfiguration()
467473
.getEventSubscriptionService().createEventSubscriptionBuilder()

0 commit comments

Comments
 (0)