Skip to content

Commit aa0477d

Browse files
Adding support for suspending a timer event listener (#4179)
* Adding support for suspending a timer event listener * Update after review * Implemented delete logic for suspended timer jobs
1 parent c71fa26 commit aa0477d

27 files changed

Lines changed: 682 additions & 11 deletions

modules/flowable-cmmn-api/src/main/java/org/flowable/cmmn/api/runtime/PlanItemInstanceTransitionBuilder.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,16 @@ public interface PlanItemInstanceTransitionBuilder {
113113
* Starts a plan item instance, this typically will executes it associated behavior.
114114
*/
115115
void start();
116+
117+
/**
118+
* Suspend a plan item instance.
119+
*/
120+
void suspend();
121+
122+
/**
123+
* Sets the plan item instance to available state.
124+
*/
125+
void resume();
116126

117127
/**
118128
* Manually terminates a plan item instance.

modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/agenda/CmmnEngineAgenda.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,15 @@ public interface CmmnEngineAgenda extends Agenda {
7474

7575
void planExitPlanItemInstanceOperation(PlanItemInstanceEntity planItemInstanceEntity, String exitCriterionId, String exitType, String exitEventType);
7676

77+
void planSuspendPlanItemInstanceOperation(PlanItemInstanceEntity planItemInstanceEntity);
78+
7779
void planTerminatePlanItemInstanceOperation(PlanItemInstanceEntity planItemInstanceEntity, String exitType, String exitEventType);
7880

7981
void planTriggerPlanItemInstanceOperation(PlanItemInstanceEntity planItemInstanceEntity);
8082

8183
void planChangePlanItemInstanceToAvailableOperation(PlanItemInstanceEntity planItemInstanceEntity);
84+
85+
void planResumePlanItemInstanceOperation(PlanItemInstanceEntity planItemInstanceEntity);
8286

8387
void planCompleteCaseInstanceOperation(CaseInstanceEntity caseInstanceEntity);
8488

modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/agenda/DefaultCmmnEngineAgenda.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@
4141
import org.flowable.cmmn.engine.impl.agenda.operation.ReactivateCaseInstanceOperation;
4242
import org.flowable.cmmn.engine.impl.agenda.operation.ReactivatePlanItemInstanceOperation;
4343
import org.flowable.cmmn.engine.impl.agenda.operation.ReactivatePlanModelInstanceOperation;
44+
import org.flowable.cmmn.engine.impl.agenda.operation.ResumePlanItemInstanceOperation;
4445
import org.flowable.cmmn.engine.impl.agenda.operation.StartPlanItemInstanceOperation;
46+
import org.flowable.cmmn.engine.impl.agenda.operation.SuspendPlanItemInstanceOperation;
4547
import org.flowable.cmmn.engine.impl.agenda.operation.TerminateCaseInstanceOperation;
4648
import org.flowable.cmmn.engine.impl.agenda.operation.TerminatePlanItemInstanceOperation;
4749
import org.flowable.cmmn.engine.impl.agenda.operation.TriggerPlanItemInstanceOperation;
@@ -253,6 +255,11 @@ public void planOccurPlanItemInstanceOperation(PlanItemInstanceEntity planItemIn
253255
public void planExitPlanItemInstanceOperation(PlanItemInstanceEntity planItemInstanceEntity, String exitCriterionId, String exitType, String exitEventType) {
254256
addOperation(new ExitPlanItemInstanceOperation(commandContext, planItemInstanceEntity, exitCriterionId, exitType, exitEventType));
255257
}
258+
259+
@Override
260+
public void planSuspendPlanItemInstanceOperation(PlanItemInstanceEntity planItemInstanceEntity) {
261+
addOperation(new SuspendPlanItemInstanceOperation(commandContext, planItemInstanceEntity));
262+
}
256263

257264
@Override
258265
public void planTerminatePlanItemInstanceOperation(PlanItemInstanceEntity planItemInstanceEntity, String exitType, String exitEventType) {
@@ -263,6 +270,11 @@ public void planTerminatePlanItemInstanceOperation(PlanItemInstanceEntity planIt
263270
public void planChangePlanItemInstanceToAvailableOperation(PlanItemInstanceEntity planItemInstanceEntity) {
264271
addOperation(new ChangePlanItemInstanceToAvailableOperation(commandContext, planItemInstanceEntity));
265272
}
273+
274+
@Override
275+
public void planResumePlanItemInstanceOperation(PlanItemInstanceEntity planItemInstanceEntity) {
276+
addOperation(new ResumePlanItemInstanceOperation(commandContext, planItemInstanceEntity));
277+
}
266278

267279
@Override
268280
public void planTriggerPlanItemInstanceOperation(PlanItemInstanceEntity planItemInstanceEntity) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/* Licensed under the Apache License, Version 2.0 (the "License");
2+
* you may not use this file except in compliance with the License.
3+
* You may obtain a copy of the License at
4+
*
5+
* http://www.apache.org/licenses/LICENSE-2.0
6+
*
7+
* Unless required by applicable law or agreed to in writing, software
8+
* distributed under the License is distributed on an "AS IS" BASIS,
9+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
* See the License for the specific language governing permissions and
11+
* limitations under the License.
12+
*/
13+
package org.flowable.cmmn.engine.impl.agenda.operation;
14+
15+
import java.util.List;
16+
17+
import org.flowable.cmmn.api.runtime.PlanItemInstanceState;
18+
import org.flowable.cmmn.engine.CmmnEngineConfiguration;
19+
import org.flowable.cmmn.engine.impl.persistence.entity.PlanItemInstanceEntity;
20+
import org.flowable.cmmn.engine.impl.util.CommandContextUtil;
21+
import org.flowable.cmmn.model.PlanItemDefinition;
22+
import org.flowable.cmmn.model.PlanItemTransition;
23+
import org.flowable.cmmn.model.TimerEventListener;
24+
import org.flowable.common.engine.api.FlowableIllegalStateException;
25+
import org.flowable.common.engine.impl.interceptor.CommandContext;
26+
import org.flowable.job.service.impl.persistence.entity.SuspendedJobEntity;
27+
28+
/**
29+
* @author Tijs Rademakers
30+
*/
31+
public class ResumePlanItemInstanceOperation extends AbstractChangePlanItemInstanceStateOperation {
32+
33+
public ResumePlanItemInstanceOperation(CommandContext commandContext, PlanItemInstanceEntity planItemInstanceEntity) {
34+
super(commandContext, planItemInstanceEntity);
35+
}
36+
37+
@Override
38+
public String getLifeCycleTransition() {
39+
return PlanItemTransition.SUSPEND;
40+
}
41+
42+
@Override
43+
public String getNewState() {
44+
return PlanItemInstanceState.AVAILABLE;
45+
}
46+
47+
@Override
48+
protected void internalExecute() {
49+
planItemInstanceEntity.setLastAvailableTime(getCurrentTime(commandContext));
50+
51+
PlanItemDefinition planItemDefinition = planItemInstanceEntity.getPlanItem().getPlanItemDefinition();
52+
if (planItemDefinition instanceof TimerEventListener) {
53+
CmmnEngineConfiguration cmmnEngineConfiguration = CommandContextUtil.getCmmnEngineConfiguration(commandContext);
54+
List<SuspendedJobEntity> suspendedJobs = cmmnEngineConfiguration.getJobServiceConfiguration().getSuspendedJobEntityManager().findJobsBySubScopeId(planItemInstanceEntity.getId());
55+
if (suspendedJobs != null && !suspendedJobs.isEmpty()) {
56+
cmmnEngineConfiguration.getJobServiceConfiguration().getJobService().activateSuspendedJob(suspendedJobs.get(0));
57+
}
58+
}
59+
60+
CommandContextUtil.getCmmnHistoryManager(commandContext).recordPlanItemInstanceAvailable(planItemInstanceEntity);
61+
}
62+
63+
@Override
64+
public boolean isStateNotChanged(String oldState, String newState) {
65+
if (oldState != null && !PlanItemInstanceState.SUSPENDED.equals(oldState)) {
66+
throw new FlowableIllegalStateException("plan item instance can only be resumed if the state is suspended");
67+
}
68+
69+
return oldState != null && oldState.equals(newState) && abortOperationIfNewStateEqualsOldState();
70+
}
71+
72+
@Override
73+
public boolean abortOperationIfNewStateEqualsOldState() {
74+
return true;
75+
}
76+
77+
@Override
78+
public String getOperationName() {
79+
return null; // Default one is ok.
80+
}
81+
82+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/* Licensed under the Apache License, Version 2.0 (the "License");
2+
* you may not use this file except in compliance with the License.
3+
* You may obtain a copy of the License at
4+
*
5+
* http://www.apache.org/licenses/LICENSE-2.0
6+
*
7+
* Unless required by applicable law or agreed to in writing, software
8+
* distributed under the License is distributed on an "AS IS" BASIS,
9+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
* See the License for the specific language governing permissions and
11+
* limitations under the License.
12+
*/
13+
package org.flowable.cmmn.engine.impl.agenda.operation;
14+
15+
import java.util.List;
16+
17+
import org.flowable.cmmn.api.runtime.PlanItemInstanceState;
18+
import org.flowable.cmmn.engine.CmmnEngineConfiguration;
19+
import org.flowable.cmmn.engine.impl.persistence.entity.PlanItemInstanceEntity;
20+
import org.flowable.cmmn.engine.impl.util.CommandContextUtil;
21+
import org.flowable.cmmn.model.PlanItemDefinition;
22+
import org.flowable.cmmn.model.PlanItemTransition;
23+
import org.flowable.cmmn.model.TimerEventListener;
24+
import org.flowable.common.engine.api.FlowableIllegalStateException;
25+
import org.flowable.common.engine.impl.interceptor.CommandContext;
26+
import org.flowable.job.service.impl.persistence.entity.TimerJobEntity;
27+
28+
public class SuspendPlanItemInstanceOperation extends AbstractChangePlanItemInstanceStateOperation {
29+
30+
public SuspendPlanItemInstanceOperation(CommandContext commandContext, PlanItemInstanceEntity planItemInstanceEntity) {
31+
super(commandContext, planItemInstanceEntity);
32+
}
33+
34+
@Override
35+
public String getNewState() {
36+
return PlanItemInstanceState.SUSPENDED;
37+
}
38+
39+
@Override
40+
public String getLifeCycleTransition() {
41+
return PlanItemTransition.SUSPEND;
42+
}
43+
44+
@Override
45+
protected void internalExecute() {
46+
planItemInstanceEntity.setLastSuspendedTime(getCurrentTime(commandContext));
47+
48+
PlanItemDefinition planItemDefinition = planItemInstanceEntity.getPlanItem().getPlanItemDefinition();
49+
if (planItemDefinition instanceof TimerEventListener) {
50+
CmmnEngineConfiguration cmmnEngineConfiguration = CommandContextUtil.getCmmnEngineConfiguration(commandContext);
51+
List<TimerJobEntity> timerJobs = cmmnEngineConfiguration.getJobServiceConfiguration().getTimerJobEntityManager().findJobsByScopeIdAndSubScopeId(
52+
planItemInstanceEntity.getCaseInstanceId(), planItemInstanceEntity.getId());
53+
if (timerJobs != null && !timerJobs.isEmpty()) {
54+
cmmnEngineConfiguration.getJobServiceConfiguration().getJobService().moveJobToSuspendedJob(timerJobs.get(0));
55+
}
56+
}
57+
58+
CommandContextUtil.getCmmnHistoryManager(commandContext).recordPlanItemInstanceSuspended(planItemInstanceEntity);
59+
}
60+
61+
@Override
62+
public boolean isStateNotChanged(String oldState, String newState) {
63+
if (oldState != null && oldState.equals(newState)) {
64+
throw new FlowableIllegalStateException("plan item instance is already suspended");
65+
}
66+
67+
return false;
68+
}
69+
70+
@Override
71+
public boolean abortOperationIfNewStateEqualsOldState() {
72+
return true;
73+
}
74+
75+
@Override
76+
public String getOperationName() {
77+
return "[Suspend plan item]";
78+
}
79+
}

modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/behavior/impl/TimerEventListenerActivityBehaviour.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import org.apache.commons.lang3.StringUtils;
2323
import org.flowable.cmmn.api.delegate.DelegatePlanItemInstance;
24+
import org.flowable.cmmn.api.runtime.PlanItemInstanceState;
2425
import org.flowable.cmmn.engine.CmmnEngineConfiguration;
2526
import org.flowable.cmmn.engine.impl.behavior.CmmnActivityBehavior;
2627
import org.flowable.cmmn.engine.impl.behavior.CoreCmmnActivityBehavior;
@@ -45,6 +46,8 @@
4546
import org.flowable.common.engine.impl.util.DateUtil;
4647
import org.flowable.job.service.JobServiceConfiguration;
4748
import org.flowable.job.service.impl.persistence.entity.JobEntity;
49+
import org.flowable.job.service.impl.persistence.entity.SuspendedJobEntity;
50+
import org.flowable.job.service.impl.persistence.entity.SuspendedJobEntityManager;
4851
import org.flowable.job.service.impl.persistence.entity.TimerJobEntity;
4952
import org.flowable.job.service.impl.persistence.entity.TimerJobEntityManager;
5053
import org.joda.time.DateTime;
@@ -73,7 +76,12 @@ public void onStateTransition(CommandContext commandContext, DelegatePlanItemIns
7376
|| PlanItemTransition.TERMINATE.equals(transition)
7477
|| PlanItemTransition.EXIT.equals(transition)) {
7578

76-
removeTimerJob(commandContext, (PlanItemInstanceEntity) planItemInstance);
79+
if (PlanItemInstanceState.SUSPENDED.equals(planItemInstance.getState())) {
80+
removeSuspendedJob(commandContext, (PlanItemInstanceEntity) planItemInstance);
81+
82+
} else {
83+
removeTimerJob(commandContext, (PlanItemInstanceEntity) planItemInstance);
84+
}
7785
}
7886
}
7987

@@ -214,11 +222,20 @@ protected void scheduleTimerJob(CommandContext commandContext, PlanItemInstanceE
214222
protected void removeTimerJob(CommandContext commandContext, PlanItemInstanceEntity planItemInstanceEntity) {
215223
TimerJobEntityManager timerJobEntityManager = CommandContextUtil.getCmmnEngineConfiguration(commandContext).getJobServiceConfiguration().getTimerJobEntityManager();
216224
List<TimerJobEntity> timerJobsEntities = timerJobEntityManager
217-
.findJobsByScopeIdAndSubScopeId(planItemInstanceEntity.getCaseInstanceId(), planItemInstanceEntity.getId());
225+
.findJobsByScopeIdAndSubScopeId(planItemInstanceEntity.getCaseInstanceId(), planItemInstanceEntity.getId());
218226
for (TimerJobEntity timerJobEntity : timerJobsEntities) {
219227
timerJobEntityManager.delete(timerJobEntity);
220228
}
221229
}
230+
231+
protected void removeSuspendedJob(CommandContext commandContext, PlanItemInstanceEntity planItemInstanceEntity) {
232+
SuspendedJobEntityManager suspendedJobEntityManager = CommandContextUtil.getCmmnEngineConfiguration(commandContext).getJobServiceConfiguration().getSuspendedJobEntityManager();
233+
List<SuspendedJobEntity> suspendedJobsEntities = suspendedJobEntityManager
234+
.findJobsBySubScopeId(planItemInstanceEntity.getId());
235+
for (SuspendedJobEntity suspendedJobEntity : suspendedJobsEntities) {
236+
suspendedJobEntityManager.delete(suspendedJobEntity);
237+
}
238+
}
222239

223240
protected Object resolveTimerExpression(CommandContext commandContext, PlanItemInstanceEntity planItemInstanceEntity) {
224241
ExpressionManager expressionManager = CommandContextUtil.getExpressionManager(commandContext);

modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/cmd/ActivateTaskCmd.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,11 @@
1313
package org.flowable.cmmn.engine.impl.cmd;
1414

1515
import java.util.Date;
16+
import java.util.List;
1617

18+
import org.flowable.cmmn.api.runtime.PlanItemInstanceState;
1719
import org.flowable.cmmn.engine.CmmnEngineConfiguration;
20+
import org.flowable.cmmn.engine.impl.persistence.entity.PlanItemInstanceEntity;
1821
import org.flowable.cmmn.engine.impl.util.CommandContextUtil;
1922
import org.flowable.common.engine.api.FlowableException;
2023
import org.flowable.common.engine.api.FlowableIllegalArgumentException;
@@ -72,6 +75,14 @@ public Void execute(CommandContext commandContext) {
7275
}
7376
task.setSuspensionState(SuspensionState.ACTIVE.getStateCode());
7477

78+
List<PlanItemInstanceEntity> planItemInstances = cmmnEngineConfiguration.getPlanItemInstanceEntityManager().findByReferenceId(task.getId());
79+
80+
if (planItemInstances != null && !planItemInstances.isEmpty()) {
81+
planItemInstances.get(0).setState(PlanItemInstanceState.ACTIVE);
82+
83+
cmmnEngineConfiguration.getCmmnHistoryManager().recordPlanItemInstanceUpdated(planItemInstances.get(0));
84+
}
85+
7586
HistoricTaskService historicTaskService = cmmnEngineConfiguration.getTaskServiceConfiguration().getHistoricTaskService();
7687
historicTaskService.recordTaskInfoChange(task, updateTime, cmmnEngineConfiguration);
7788

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/* Licensed under the Apache License, Version 2.0 (the "License");
2+
* you may not use this file except in compliance with the License.
3+
* You may obtain a copy of the License at
4+
*
5+
* http://www.apache.org/licenses/LICENSE-2.0
6+
*
7+
* Unless required by applicable law or agreed to in writing, software
8+
* distributed under the License is distributed on an "AS IS" BASIS,
9+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
* See the License for the specific language governing permissions and
11+
* limitations under the License.
12+
*/
13+
package org.flowable.cmmn.engine.impl.cmd;
14+
15+
import org.flowable.cmmn.engine.impl.persistence.entity.PlanItemInstanceEntity;
16+
import org.flowable.cmmn.engine.impl.util.CommandContextUtil;
17+
import org.flowable.common.engine.impl.interceptor.CommandContext;
18+
19+
public class ResumePlanItemInstanceCmd extends AbstractNeedsPlanItemInstanceCmd {
20+
21+
public ResumePlanItemInstanceCmd(String planItemInstanceId) {
22+
super(planItemInstanceId);
23+
}
24+
25+
@Override
26+
protected void internalExecute(CommandContext commandContext, PlanItemInstanceEntity planItemInstanceEntity) {
27+
CommandContextUtil.getAgenda(commandContext).planResumePlanItemInstanceOperation(planItemInstanceEntity);
28+
}
29+
30+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/* Licensed under the Apache License, Version 2.0 (the "License");
2+
* you may not use this file except in compliance with the License.
3+
* You may obtain a copy of the License at
4+
*
5+
* http://www.apache.org/licenses/LICENSE-2.0
6+
*
7+
* Unless required by applicable law or agreed to in writing, software
8+
* distributed under the License is distributed on an "AS IS" BASIS,
9+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
* See the License for the specific language governing permissions and
11+
* limitations under the License.
12+
*/
13+
package org.flowable.cmmn.engine.impl.cmd;
14+
15+
import org.flowable.cmmn.engine.impl.persistence.entity.PlanItemInstanceEntity;
16+
import org.flowable.cmmn.engine.impl.util.CommandContextUtil;
17+
import org.flowable.common.engine.impl.interceptor.CommandContext;
18+
19+
public class SuspendPlanItemInstanceCmd extends AbstractNeedsPlanItemInstanceCmd {
20+
21+
public SuspendPlanItemInstanceCmd(String planItemInstanceId) {
22+
super(planItemInstanceId);
23+
}
24+
25+
@Override
26+
protected void internalExecute(CommandContext commandContext, PlanItemInstanceEntity planItemInstanceEntity) {
27+
CommandContextUtil.getAgenda(commandContext).planSuspendPlanItemInstanceOperation(planItemInstanceEntity);
28+
}
29+
30+
}

0 commit comments

Comments
 (0)