Skip to content

Commit

Permalink
TerminateEndEvent - extending activiti events. (cherry picked from co…
Browse files Browse the repository at this point in the history
…mmit 307f8ea)
  • Loading branch information
martin-grofcik committed Mar 12, 2015
1 parent 715113f commit 33f893b
Show file tree
Hide file tree
Showing 5 changed files with 202 additions and 48 deletions.
Expand Up @@ -12,8 +12,10 @@
*/
package org.activiti.engine.impl.bpmn.behavior;

import org.activiti.engine.delegate.event.impl.ActivitiEventBuilder;
import org.activiti.engine.impl.bpmn.helper.ScopeUtil;
import org.activiti.engine.impl.pvm.PvmActivity;
import org.activiti.engine.impl.context.Context;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.pvm.delegate.ActivityExecution;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.impl.pvm.runtime.InterpretableExecution;
Expand All @@ -24,43 +26,61 @@
public class TerminateEndEventActivityBehavior extends FlowNodeActivityBehavior {

public void execute(ActivityExecution execution) throws Exception {

PvmActivity terminateEndEventActivity = execution.getActivity();
ActivityImpl terminateEndEventActivity = (ActivityImpl) execution.getActivity();
ActivityExecution scopeExecution = ScopeUtil.findScopeExecution(execution);


// send cancelled event
sendCancelledEvent( execution, terminateEndEventActivity, scopeExecution);

// destroy the scope
scopeExecution.destroyScope("terminate end event fired");

// set the scope execution to the terminate end event and make it end here.
// (the history should reflect that the execution ended here and we want an 'end time' for the
// historic activity instance.)
((InterpretableExecution)scopeExecution).setActivity((ActivityImpl) terminateEndEventActivity);
((InterpretableExecution)scopeExecution).setActivity(terminateEndEventActivity);
// end the scope execution
scopeExecution.end();
}


// If we use this implementation, we run into trouble in the DbSqlSession, see ACT-1382

// public void execute(ActivityExecution execution) throws Exception {
//
// PvmActivity terminateEndEventActivity = execution.getActivity();
//
// ActivityExecution scopeExecution = ScopeUtil.findScopeExecution(execution);
//
// // first end the current execution normally
// execution.end();
//
// // if this does not end the scope execution, interrupt it and destroy it.
// if (!scopeExecution.isEnded()) {
// // destroy the scope execution (this interrupts all child executions / sub process instances)
// scopeExecution.destroyScope("terminate end event fired");
//
// // set the scope execution to the terminate end event make
// // (the history should reflect that the execution ended here).
// ((InterpretableExecution)scopeExecution).setActivity((ActivityImpl) terminateEndEventActivity);
// // end the scope execution
// scopeExecution.end();
// }
// }

private void sendCancelledEvent(ActivityExecution execution, ActivityImpl terminateEndEventActivity, ActivityExecution scopeExecution) {
if (Context.getProcessEngineConfiguration().getEventDispatcher().isEnabled()) {
Context.getProcessEngineConfiguration().getEventDispatcher().dispatchEvent(
ActivitiEventBuilder.createCancelledEvent(execution.getId(), execution.getProcessInstanceId(),
execution.getProcessDefinitionId(), terminateEndEventActivity));
}
dispatchExecutionCancelled(scopeExecution, terminateEndEventActivity);
}

private void dispatchExecutionCancelled(ActivityExecution execution, ActivityImpl causeActivity) {
// subprocesses
for (ActivityExecution subExecution : execution.getExecutions()) {
dispatchExecutionCancelled(subExecution, causeActivity);
}

// call activities
ExecutionEntity subProcessInstance = Context.getCommandContext().getExecutionEntityManager().findSubProcessInstanceBySuperExecutionId(execution.getId());
if (subProcessInstance != null) {
dispatchExecutionCancelled(subProcessInstance, causeActivity);
}

// activity with message/signal boundary events
ActivityImpl activity = (ActivityImpl) execution.getActivity();
if (activity != null && activity.getActivityBehavior() != null && activity != causeActivity) {
dispatchActivityCancelled(execution, activity, causeActivity);
}
}

private void dispatchActivityCancelled(ActivityExecution execution, ActivityImpl activity, ActivityImpl causeActivity) {
Context.getProcessEngineConfiguration().getEventDispatcher().dispatchEvent(
ActivitiEventBuilder.createActivityCancelledEvent(activity.getId(),
(String) activity.getProperties().get("name"),
execution.getId(),
execution.getProcessInstanceId(), execution.getProcessDefinitionId(),
(String) activity.getProperties().get("type"),
activity.getActivityBehavior().getClass().getCanonicalName(),
causeActivity)
);
}

}
Expand Up @@ -144,5 +144,5 @@ public interface ActivityExecution extends DelegateExecution {
* Performs destroy scope behavior: all child executions and sub-process instances and other related
* resources are removed. The execution itself can continue execution.
*/
void destroyScope(String string);
void destroyScope(String reason);
}
Expand Up @@ -201,7 +201,8 @@ public void remove() {
}
}
}


@Override
public void destroyScope(String reason) {

log.debug("performing destroy scope behavior for execution {}", this);
Expand Down
Expand Up @@ -12,21 +12,24 @@
*/
package org.activiti.engine.test.api.event;

import java.util.ArrayList;
import java.util.List;

import org.activiti.engine.delegate.event.ActivitiActivityCancelledEvent;
import org.activiti.engine.delegate.event.ActivitiCancelledEvent;
import org.activiti.engine.delegate.event.ActivitiEntityEvent;
import org.activiti.engine.delegate.event.ActivitiEvent;
import org.activiti.engine.delegate.event.ActivitiEventListener;
import org.activiti.engine.delegate.event.ActivitiEventType;
import org.activiti.engine.delegate.event.*;
import org.activiti.engine.delegate.event.impl.ActivitiActivityCancelledEventImpl;
import org.activiti.engine.delegate.event.impl.ActivitiProcessCancelledEventImpl;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.impl.test.PluggableActivitiTestCase;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.activiti.engine.test.Deployment;

import java.util.ArrayList;
import java.util.List;

import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertNotEquals;

/**
* Test case for all {@link ActivitiEvent}s related to process instances.
*
Expand Down Expand Up @@ -257,30 +260,29 @@ public void testProcessInstanceCancelledEvents_cancell() throws Exception {
assertEquals("The execution instance has to be the same as in deleteProcessInstance method call", processInstance.getId(), activityCancelledEvent.getExecutionId());
assertEquals("The cause has to be the same as in deleteProcessInstance method call", "delete_test", activityCancelledEvent.getCause());


listener.clearEventsReceived();
}

@Deployment(resources = {"org/activiti/engine/test/api/runtime/nestedSubProcess.bpmn20.xml",
"org/activiti/engine/test/api/runtime/subProcess.bpmn20.xml"})
public void testProcessInstanceCancelledEvents_cancelProcessHierarchy() throws Exception {
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("nestedSimpleSubProcess");
ProcessInstance subProcess = runtimeService.createProcessInstanceQuery().superProcessInstanceId(processInstance.getId()).singleResult();
assertNotNull(processInstance);
listener.clearEventsReceived();

runtimeService.deleteProcessInstance(processInstance.getId(), "delete_test");

List<ActivitiEvent> processCancelledEvents = listener.filterEvents(ActivitiEventType.PROCESS_CANCELLED);
assertEquals("ActivitiEventType.PROCESS_CANCELLED was expected 1 time.", 1, processCancelledEvents.size());
ActivitiCancelledEvent processCancelledEvent = (ActivitiCancelledEvent) processCancelledEvents.get(0);
assertTrue("The cause has to be the same as deleteProcessInstance method call", ActivitiCancelledEvent.class.isAssignableFrom(processCancelledEvent.getClass()));
assertEquals("The process instance has to be the same as in deleteProcessInstance method call", processInstance.getId(), processCancelledEvent.getProcessInstanceId());
assertEquals("The execution instance has to be the same as in deleteProcessInstance method call", processInstance.getId(), processCancelledEvent.getExecutionId());
assertEquals("The cause has to be the same as in deleteProcessInstance method call", "delete_test", processCancelledEvent.getCause());

assertEquals("No task can be active for deleted process.", 0, this.taskService.createTaskQuery().processInstanceId(processInstance.getId()).count());

List<ActivitiEvent> taskCancelledEvents = listener.filterEvents(ActivitiEventType.ACTIVITY_CANCELLED);
assertEquals("ActivitiEventType.ACTIVITY_CANCELLED was expected 1 time.", 1, taskCancelledEvents.size());
ActivitiActivityCancelledEvent activityCancelledEvent = (ActivitiActivityCancelledEvent) taskCancelledEvents.get(0);
Expand All @@ -289,7 +291,8 @@ public void testProcessInstanceCancelledEvents_cancelProcessHierarchy() throws E
assertEquals("The process instance has to point to the subprocess", subProcess.getId(), activityCancelledEvent.getProcessInstanceId());
assertEquals("The execution instance has to point to the subprocess", subProcess.getId(), activityCancelledEvent.getExecutionId());
assertEquals("The cause has to be the same as in deleteProcessInstance method call", "delete_test", activityCancelledEvent.getCause());



listener.clearEventsReceived();
}

Expand All @@ -308,6 +311,100 @@ public void testProcessInstanceCancelledEvents_complete() throws Exception {

}

@Deployment(resources = {"org/activiti/engine/test/api/runtime/oneTaskProcess.bpmn20.xml"})
public void testProcessInstanceTerminatedEvents_complete() throws Exception {
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("oneTaskProcess");
assertNotNull(processInstance);

Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult();
taskService.complete(task.getId());

List<ActivitiEvent> processTerminatedEvents = listener.filterEvents(ActivitiEventType.PROCESS_CANCELLED);
assertEquals("There should be no ActivitiEventType.PROCESS_TERMINATED event after process complete.", 0, processTerminatedEvents.size());
}

@Deployment(resources="org/activiti/engine/test/bpmn/event/end/TerminateEndEventTest.testProcessTerminate.bpmn")
public void testProcessInstanceTerminatedEvents() throws Exception {
ProcessInstance pi = runtimeService.startProcessInstanceByKey("terminateEndEventExample");

long executionEntities = runtimeService.createExecutionQuery().processInstanceId(pi.getId()).count();
assertEquals(3, executionEntities);

Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).taskDefinitionKey("preTerminateTask").singleResult();
taskService.complete(task.getId());

List<ActivitiEvent> processTerminatedEvents = listener.filterEvents(ActivitiEventType.PROCESS_CANCELLED);
assertEquals("There should be exactly one ActivitiEventType.PROCESS_CANCELLED event after the task complete.", 1, processTerminatedEvents.size());
ActivitiProcessCancelledEventImpl activitiEvent = (ActivitiProcessCancelledEventImpl) processTerminatedEvents.get(0);
assertThat(activitiEvent.getProcessInstanceId(), is(pi.getProcessInstanceId()));
assertThat(((ActivityImpl) activitiEvent.getCause()).getId(), is("EndEvent_2"));

List<ActivitiEvent> activityTerminatedEvents = listener.filterEvents(ActivitiEventType.ACTIVITY_CANCELLED);
assertThat("There should be exactly two ActivitiEventType.ACTIVITY_CANCELLED event after the task complete.", activityTerminatedEvents.size(), is(2));
ActivitiActivityCancelledEventImpl activityEvent = (ActivitiActivityCancelledEventImpl) activityTerminatedEvents.get(0);
assertThat("The user task must be terminated", activityEvent.getActivityId(), is("preNormalTerminateTask"));
assertThat("The cause must be terminate end event", ((ActivityImpl) activityEvent.getCause()).getId(), is("EndEvent_2"));
activityEvent = (ActivitiActivityCancelledEventImpl) activityTerminatedEvents.get(1);
assertThat("The gateway must be terminated", activityEvent.getActivityId(), is("ParallelGateway_1"));
assertThat("The cause must be terminate end event", ((ActivityImpl) activityEvent.getCause()).getId(), is("EndEvent_2"));
}

@Deployment(resources = {
"org/activiti/engine/test/bpmn/event/end/TerminateEndEventTest.testTerminateInCallActivity.bpmn",
"org/activiti/engine/test/bpmn/event/end/TerminateEndEventTest.subProcessTerminate.bpmn"
})
public void testProcessInstanceTerminatedEvents_callActivity() throws Exception {
ProcessInstance pi = runtimeService.startProcessInstanceByKey("terminateEndEventExample");

// should terminate the called process and continue the parent
long executionEntities = runtimeService.createExecutionQuery().count();
assertEquals(1, executionEntities);

Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).taskDefinitionKey("preNormalEnd").singleResult();
taskService.complete(task.getId());

assertProcessEnded(pi.getId());
List<ActivitiEvent> processTerminatedEvents = listener.filterEvents(ActivitiEventType.PROCESS_CANCELLED);
assertEquals("There should be exactly one ActivitiEventType.PROCESS_TERMINATED event after the task complete.", 1, processTerminatedEvents.size());
ActivitiProcessCancelledEventImpl processCancelledEvent = (ActivitiProcessCancelledEventImpl) processTerminatedEvents.get(0);
assertNotEquals(pi.getProcessInstanceId(), processCancelledEvent.getProcessInstanceId());
assertThat(processCancelledEvent.getProcessDefinitionId(), containsString("terminateEndEventSubprocessExample"));

List<ActivitiEvent> activityTerminatedEvents = listener.filterEvents(ActivitiEventType.ACTIVITY_CANCELLED);
assertThat("There is no ActivitiEventType.ACTIVITY_CANCELLED event after the task complete.", activityTerminatedEvents.isEmpty(), is(true));
}

@Deployment(resources = {
"org/activiti/engine/test/bpmn/event/end/TerminateEndEventTest.testTerminateInParentProcess.bpmn",
"org/activiti/engine/test/api/runtime/oneTaskProcess.bpmn20.xml"
})
public void testProcessInstanceTerminatedEvents_terminateInParentProcess() throws Exception {
ProcessInstance pi = runtimeService.startProcessInstanceByKey("terminateParentProcess");

// should terminate the called process and continue the parent
Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).taskDefinitionKey("preTerminateEnd").singleResult();
taskService.complete(task.getId());

assertProcessEnded(pi.getId());
List<ActivitiEvent> processTerminatedEvents = listener.filterEvents(ActivitiEventType.PROCESS_CANCELLED);
assertEquals("There should be exactly one ActivitiEventType.PROCESS_TERMINATED event after the task complete.", 1, processTerminatedEvents.size());
ActivitiProcessCancelledEventImpl processCancelledEvent = (ActivitiProcessCancelledEventImpl) processTerminatedEvents.get(0);
assertThat(processCancelledEvent.getProcessInstanceId(), is(pi.getProcessInstanceId()));
assertThat(processCancelledEvent.getProcessDefinitionId(), containsString("terminateParentProcess"));

List<ActivitiEvent> activityTerminatedEvents = listener.filterEvents(ActivitiEventType.ACTIVITY_CANCELLED);
assertThat("3 activities must be cancelled.", activityTerminatedEvents.size(), is(3));
ActivitiActivityCancelledEventImpl activityEvent = (ActivitiActivityCancelledEventImpl) activityTerminatedEvents.get(0);
assertThat("The user task must be terminated in the called sub process.", activityEvent.getActivityId(), is("theTask"));
assertThat("The cause must be terminate end event", ((ActivityImpl) activityEvent.getCause()).getId(), is("EndEvent_3"));
activityEvent = (ActivitiActivityCancelledEventImpl) activityTerminatedEvents.get(1);
assertThat("The call activity must be terminated", activityEvent.getActivityId(), is("CallActivity_1"));
assertThat("The cause must be terminate end event", ((ActivityImpl) activityEvent.getCause()).getId(), is("EndEvent_3"));
activityEvent = (ActivitiActivityCancelledEventImpl) activityTerminatedEvents.get(2);
assertThat("The gateway must be terminated", activityEvent.getActivityId(), is("ParallelGateway_1"));
assertThat("The cause must be terminate end event", ((ActivityImpl) activityEvent.getCause()).getId(), is("EndEvent_3"));
}

@Override
protected void initializeServices() {
super.initializeServices();
Expand Down
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL"
xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd" id="Definitions_1"
targetNamespace="http://activiti.org/bpmn">
<bpmn2:process id="terminateParentProcess">
<bpmn2:startEvent id="start">
<bpmn2:outgoing>SequenceFlow_2</bpmn2:outgoing>
</bpmn2:startEvent>
<bpmn2:parallelGateway id="ParallelGateway_1">
<bpmn2:incoming>SequenceFlow_2</bpmn2:incoming>
<bpmn2:outgoing>SequenceFlow_4</bpmn2:outgoing>
<bpmn2:outgoing>SequenceFlow_6</bpmn2:outgoing>
</bpmn2:parallelGateway>
<bpmn2:sequenceFlow id="SequenceFlow_2" sourceRef="start" targetRef="ParallelGateway_1"/>
<bpmn2:userTask id="preTerminateEnd" name="check before end">
<bpmn2:incoming>SequenceFlow_4</bpmn2:incoming>
<bpmn2:outgoing>SequenceFlow_1</bpmn2:outgoing>
</bpmn2:userTask>
<bpmn2:sequenceFlow id="SequenceFlow_4" sourceRef="ParallelGateway_1" targetRef="preTerminateEnd"/>
<bpmn2:endEvent id="EndEvent_1">
<bpmn2:incoming>SequenceFlow_7</bpmn2:incoming>
</bpmn2:endEvent>
<bpmn2:callActivity id="CallActivity_1" name="Call Activity" calledElement="oneTaskProcess">
<bpmn2:incoming>SequenceFlow_6</bpmn2:incoming>
<bpmn2:outgoing>SequenceFlow_7</bpmn2:outgoing>
</bpmn2:callActivity>
<bpmn2:sequenceFlow id="SequenceFlow_6" sourceRef="ParallelGateway_1" targetRef="CallActivity_1"/>
<bpmn2:sequenceFlow id="SequenceFlow_7" name="" sourceRef="CallActivity_1" targetRef="EndEvent_1"/>
<bpmn2:endEvent id="EndEvent_3">
<bpmn2:incoming>SequenceFlow_1</bpmn2:incoming>
<bpmn2:terminateEventDefinition id="TerminateEventDefinition_1"/>
</bpmn2:endEvent>
<bpmn2:sequenceFlow id="SequenceFlow_1" sourceRef="preTerminateEnd" targetRef="EndEvent_3"/>
</bpmn2:process>
</bpmn2:definitions>

0 comments on commit 33f893b

Please sign in to comment.