Skip to content

Commit 14897a4

Browse files
authored
Dispatch events when updating the business status of a case or process (#4148)
1 parent fd34a3f commit 14897a4

File tree

12 files changed

+440
-7
lines changed

12 files changed

+440
-7
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
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.api.event;
14+
15+
import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent;
16+
17+
/**
18+
* An event representing a CMMN case business status being updated.
19+
*
20+
* @author Matthias Stöckli
21+
*/
22+
public interface FlowableCaseBusinessStatusUpdatedEvent extends FlowableEngineEntityEvent {
23+
24+
/**
25+
* Returns the business status of the case before the update.
26+
*
27+
* @return the old business status
28+
*/
29+
String getOldBusinessStatus();
30+
31+
/**
32+
* Returns the business status of the case after the update.
33+
*
34+
* @return the new business status
35+
*/
36+
String getNewBusinessStatus();
37+
}

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,16 @@
1515
import java.io.Serializable;
1616

1717
import org.flowable.cmmn.api.runtime.CaseInstance;
18+
import org.flowable.cmmn.engine.impl.event.FlowableCmmnEventBuilder;
1819
import org.flowable.cmmn.engine.impl.persistence.entity.CaseInstanceEntity;
1920
import org.flowable.cmmn.engine.impl.persistence.entity.CaseInstanceEntityManager;
2021
import org.flowable.cmmn.engine.impl.util.CommandContextUtil;
2122
import org.flowable.common.engine.api.FlowableIllegalArgumentException;
2223
import org.flowable.common.engine.api.FlowableObjectNotFoundException;
24+
import org.flowable.common.engine.api.delegate.event.FlowableEventDispatcher;
2325
import org.flowable.common.engine.impl.interceptor.Command;
2426
import org.flowable.common.engine.impl.interceptor.CommandContext;
27+
import org.flowable.common.engine.impl.interceptor.EngineConfigurationConstants;
2528

2629
public class SetCaseInstanceBusinessStatusCmd implements Command<Void>, Serializable {
2730

@@ -31,7 +34,7 @@ public class SetCaseInstanceBusinessStatusCmd implements Command<Void>, Serializ
3134
private final String businessStatus;
3235

3336
public SetCaseInstanceBusinessStatusCmd(String caseInstanceId, String businessStatus) {
34-
if (caseInstanceId == null || caseInstanceId.length() < 1) {
37+
if (caseInstanceId == null || caseInstanceId.isEmpty()) {
3538
throw new FlowableIllegalArgumentException("The case instance id is mandatory, but '" + caseInstanceId + "' has not been provided.");
3639
}
3740

@@ -47,8 +50,14 @@ public Void execute(CommandContext commandContext) {
4750
throw new FlowableObjectNotFoundException("No case instance found for id = '" + caseInstanceId + "'.", CaseInstance.class);
4851
}
4952

53+
String oldBusinessStatus = caseInstanceEntity.getBusinessStatus();
5054
caseInstanceEntityManager.updateCaseInstanceBusinessStatus(caseInstanceEntity, businessStatus);
5155

56+
FlowableEventDispatcher eventDispatcher = CommandContextUtil.getEventDispatcher(commandContext);
57+
if (eventDispatcher != null && eventDispatcher.isEnabled()) {
58+
eventDispatcher.dispatchEvent(FlowableCmmnEventBuilder.createCaseBusinessStatusUpdatedEvent(caseInstanceEntity, oldBusinessStatus, businessStatus), EngineConfigurationConstants.KEY_CMMN_ENGINE_CONFIG);
59+
}
60+
5261
return null;
5362
}
5463
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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.event;
14+
15+
import org.flowable.cmmn.api.event.FlowableCaseBusinessStatusUpdatedEvent;
16+
import org.flowable.cmmn.api.runtime.CaseInstance;
17+
import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType;
18+
import org.flowable.common.engine.api.scope.ScopeTypes;
19+
import org.flowable.common.engine.impl.event.FlowableEngineEventImpl;
20+
21+
public class FlowableCaseBusinessStatusUpdatedEventImpl extends FlowableEngineEventImpl implements FlowableCaseBusinessStatusUpdatedEvent {
22+
23+
protected CaseInstance caseInstance;
24+
protected String oldBusinessStatus;
25+
protected String newBusinessStatus;
26+
27+
public FlowableCaseBusinessStatusUpdatedEventImpl(CaseInstance caseInstance, String oldBusinessStatus, String newBusinessStatus) {
28+
super(FlowableEngineEventType.BUSINESS_STATUS_UPDATED, ScopeTypes.CMMN, caseInstance.getId(), null, caseInstance.getCaseDefinitionId());
29+
this.caseInstance = caseInstance;
30+
this.oldBusinessStatus = oldBusinessStatus;
31+
this.newBusinessStatus = newBusinessStatus;
32+
}
33+
34+
@Override
35+
public CaseInstance getEntity() {
36+
return caseInstance;
37+
}
38+
39+
@Override
40+
public String getOldBusinessStatus() {
41+
return oldBusinessStatus;
42+
}
43+
44+
@Override
45+
public String getNewBusinessStatus() {
46+
return newBusinessStatus;
47+
}
48+
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ public static FlowableCaseStageEndedEvent createStageEndedEvent(CaseInstance cas
4545
return new FlowableCaseStageEndedEventImpl(caseInstance, stageInstance, endingState);
4646
}
4747

48+
public static FlowableEntityEvent createCaseBusinessStatusUpdatedEvent(CaseInstance caseInstance, String oldBusinessStatus, String newBusinessStatus) {
49+
return new FlowableCaseBusinessStatusUpdatedEventImpl(caseInstance, oldBusinessStatus, newBusinessStatus);
50+
}
51+
4852
public static FlowableEntityEvent createTaskCreatedEvent(Task task) {
4953
FlowableEntityEventImpl event = new FlowableEntityEventImpl(task, FlowableEngineEventType.TASK_CREATED);
5054

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
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.test.event;
14+
15+
import static org.assertj.core.api.Assertions.assertThat;
16+
17+
import java.util.ArrayList;
18+
import java.util.List;
19+
import java.util.function.Consumer;
20+
21+
import org.flowable.cmmn.api.event.FlowableCaseBusinessStatusUpdatedEvent;
22+
import org.flowable.cmmn.api.runtime.CaseInstance;
23+
import org.flowable.cmmn.engine.test.CmmnDeployment;
24+
import org.flowable.cmmn.test.FlowableCmmnTestCase;
25+
import org.flowable.common.engine.api.delegate.event.AbstractFlowableEventListener;
26+
import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType;
27+
import org.flowable.common.engine.api.delegate.event.FlowableEvent;
28+
import org.flowable.common.engine.api.scope.ScopeTypes;
29+
import org.junit.jupiter.api.AfterEach;
30+
import org.junit.jupiter.api.BeforeEach;
31+
import org.junit.jupiter.api.Test;
32+
33+
/**
34+
* @author Matthias Stöckli
35+
*/
36+
public class CaseBusinessStatusUpdatedEventTest extends FlowableCmmnTestCase {
37+
38+
protected CustomEventListener businessStatusUpdatedEventListener;
39+
40+
@BeforeEach
41+
public void setUp() {
42+
businessStatusUpdatedEventListener = new CustomEventListener();
43+
cmmnEngineConfiguration.getEventDispatcher().addEventListener(businessStatusUpdatedEventListener, FlowableEngineEventType.BUSINESS_STATUS_UPDATED);
44+
}
45+
46+
@AfterEach
47+
public void tearDown() {
48+
if (businessStatusUpdatedEventListener != null) {
49+
cmmnEngineConfiguration.getEventDispatcher().removeEventListener(businessStatusUpdatedEventListener);
50+
}
51+
}
52+
53+
@Test
54+
@CmmnDeployment(resources = "org/flowable/cmmn/test/runtime/oneTaskCase.cmmn")
55+
public void testEmptyBusinessStatusUpdatedFromEmptyEvent() {
56+
List<FlowableEvent> events = new ArrayList<>();
57+
businessStatusUpdatedEventListener.eventConsumer = (flowableEvent) -> {
58+
if (flowableEvent instanceof FlowableCaseBusinessStatusUpdatedEvent caseBusinessStatusUpdatedEvent) {
59+
CaseInstance eventCaseInstance = (CaseInstance) caseBusinessStatusUpdatedEvent.getEntity();
60+
assertThat(caseBusinessStatusUpdatedEvent.getScopeType()).isEqualTo(ScopeTypes.CMMN);
61+
assertThat(caseBusinessStatusUpdatedEvent.getScopeId()).isNotNull().isEqualTo(eventCaseInstance.getId());
62+
assertThat(caseBusinessStatusUpdatedEvent.getOldBusinessStatus()).isEqualTo(null);
63+
assertThat(caseBusinessStatusUpdatedEvent.getNewBusinessStatus()).isEqualTo("newStatus");
64+
events.add(flowableEvent);
65+
}
66+
};
67+
68+
CaseInstance caseInstance = cmmnRuntimeService.createCaseInstanceBuilder()
69+
.caseDefinitionKey("oneTaskCase")
70+
.start();
71+
72+
// Set business status for the first time
73+
cmmnRuntimeService.updateBusinessStatus(caseInstance.getId(), "newStatus");
74+
assertThat(events).hasSize(1);
75+
}
76+
77+
@Test
78+
@CmmnDeployment(resources = "org/flowable/cmmn/test/runtime/oneTaskCase.cmmn")
79+
public void testBusinessStatusUpdatedEvent() {
80+
List<FlowableEvent> events = new ArrayList<>();
81+
businessStatusUpdatedEventListener.eventConsumer = (flowableEvent) -> {
82+
if (flowableEvent instanceof FlowableCaseBusinessStatusUpdatedEvent caseBusinessStatusUpdatedEvent) {
83+
CaseInstance eventCaseInstance = (CaseInstance) caseBusinessStatusUpdatedEvent.getEntity();
84+
assertThat(caseBusinessStatusUpdatedEvent.getScopeType()).isEqualTo(ScopeTypes.CMMN);
85+
assertThat(caseBusinessStatusUpdatedEvent.getScopeId()).isNotNull().isEqualTo(eventCaseInstance.getId());
86+
87+
assertThat(caseBusinessStatusUpdatedEvent.getOldBusinessStatus()).isEqualTo("oldStatus");
88+
assertThat(caseBusinessStatusUpdatedEvent.getNewBusinessStatus()).isEqualTo("newStatus");
89+
events.add(flowableEvent);
90+
}
91+
};
92+
93+
CaseInstance caseInstance = cmmnRuntimeService.createCaseInstanceBuilder()
94+
.caseDefinitionKey("oneTaskCase")
95+
.businessStatus("oldStatus")
96+
.start();
97+
98+
// Update the business status
99+
cmmnRuntimeService.updateBusinessStatus(caseInstance.getId(), "newStatus");
100+
assertThat(events).hasSize(1);
101+
}
102+
103+
public static class CustomEventListener extends AbstractFlowableEventListener {
104+
105+
private Consumer<FlowableEvent> eventConsumer;
106+
107+
@Override
108+
public void onEvent(FlowableEvent event) {
109+
if (eventConsumer != null) {
110+
eventConsumer.accept(event);
111+
}
112+
}
113+
114+
@Override
115+
public boolean isFailOnException() {
116+
return false;
117+
}
118+
}
119+
}

modules/flowable-engine-common-api/src/main/java/org/flowable/common/engine/api/delegate/event/FlowableEngineEventType.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,11 @@ public enum FlowableEngineEventType implements FlowableEventType {
370370
* A change tenant id was executed.
371371
*/
372372
CHANGE_TENANT_ID,
373+
374+
/**
375+
* The business status was updated
376+
*/
377+
BUSINESS_STATUS_UPDATED
373378
;
374379

375380
public static final FlowableEngineEventType[] EMPTY_ARRAY = new FlowableEngineEventType[] {};
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
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.engine.delegate.event;
14+
15+
import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent;
16+
17+
/**
18+
* An event representing a process business status being updated.
19+
*
20+
* @author Matthias Stöckli
21+
*/
22+
public interface FlowableProcessBusinessStatusUpdatedEvent extends FlowableEngineEntityEvent {
23+
24+
/**
25+
* Returns the business status of the process before the update.
26+
*
27+
* @return the old business status
28+
*/
29+
String getOldBusinessStatus();
30+
31+
/**
32+
* Returns the business status of the process after the update.
33+
*
34+
* @return the new business status
35+
*/
36+
String getNewBusinessStatus();
37+
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,10 @@ public static FlowableVariableEvent createVariableEvent(FlowableEngineEventType
416416
return newEvent;
417417
}
418418

419+
public static FlowableEntityEvent createProcessBusinessStatusUpdatedEvent(ExecutionEntity execution, String oldBusinessStatus, String newBusinessStatus) {
420+
return new FlowableProcessBusinessStatusUpdatedEventImpl(execution, oldBusinessStatus, newBusinessStatus);
421+
}
422+
419423
protected static void populateEventWithCurrentContext(FlowableEngineEventImpl event) {
420424
if (event instanceof FlowableEntityEvent) {
421425
Object persistedObject = ((FlowableEntityEvent) event).getEntity();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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.engine.delegate.event.impl;
14+
15+
import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType;
16+
import org.flowable.common.engine.api.scope.ScopeTypes;
17+
import org.flowable.common.engine.impl.event.FlowableEngineEventImpl;
18+
import org.flowable.engine.delegate.event.FlowableProcessBusinessStatusUpdatedEvent;
19+
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
20+
21+
public class FlowableProcessBusinessStatusUpdatedEventImpl extends FlowableEngineEventImpl implements FlowableProcessBusinessStatusUpdatedEvent {
22+
23+
protected ExecutionEntity execution;
24+
protected String oldBusinessStatus;
25+
protected String newBusinessStatus;
26+
27+
public FlowableProcessBusinessStatusUpdatedEventImpl(ExecutionEntity execution, String oldBusinessStatus, String newBusinessStatus) {
28+
super(FlowableEngineEventType.BUSINESS_STATUS_UPDATED, ScopeTypes.BPMN, execution.getProcessInstanceId(), null, execution.getProcessDefinitionId());
29+
this.execution = execution;
30+
this.oldBusinessStatus = oldBusinessStatus;
31+
this.newBusinessStatus = newBusinessStatus;
32+
}
33+
34+
@Override
35+
public ExecutionEntity getEntity() {
36+
return execution;
37+
}
38+
39+
@Override
40+
public String getOldBusinessStatus() {
41+
return oldBusinessStatus;
42+
}
43+
44+
@Override
45+
public String getNewBusinessStatus() {
46+
return newBusinessStatus;
47+
}
48+
}

0 commit comments

Comments
 (0)