/* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.activiti.engine.impl.bpmn.behavior;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.activiti.bpmn.model.EndEvent;
import org.activiti.bpmn.model.EventDefinition;
import org.activiti.bpmn.model.TerminateEventDefinition;
import org.activiti.engine.delegate.event.ActivitiEventType;
import org.activiti.engine.delegate.event.impl.ActivitiEventBuilder;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.impl.HistoricActivityInstanceQueryImpl;
import org.activiti.engine.impl.bpmn.helper.ScopeUtil;
import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.activiti.engine.impl.context.Context;
import org.activiti.engine.impl.history.HistoryLevel;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.persistence.entity.HistoricActivityInstanceEntity;
import org.activiti.engine.impl.pvm.delegate.ActivityExecution;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.impl.pvm.runtime.InterpretableExecution;
/**
* @author Martin Grofcik
* @author Tijs Rademakers
* @author Joram Barrez
*/
public class TerminateEndEventActivityBehavior extends FlowNodeActivityBehavior {
private static final long serialVersionUID = 1L;
protected EndEvent endEvent;
protected boolean terminateAll;
public TerminateEndEventActivityBehavior(EndEvent endEvent) {
this.endEvent = endEvent.clone();
// Terminate all attribute
if (endEvent.getEventDefinitions() != null) {
for (EventDefinition eventDefinition : endEvent.getEventDefinitions()) {
if (eventDefinition instanceof TerminateEventDefinition) {
TerminateEventDefinition terminateEventDefinition = (TerminateEventDefinition) eventDefinition;
if (terminateEventDefinition.isTerminateAll()) {
this.terminateAll = true;
break;
}
}
}
}
}
public void execute(ActivityExecution execution) throws Exception {
ActivityImpl terminateEndEventActivity = (ActivityImpl) execution.getActivity();
if (terminateAll) {
ActivityExecution processInstanceExecution = findRootProcessInstanceExecution((ExecutionEntity) execution);
terminateProcessInstanceExecution(execution, terminateEndEventActivity, processInstanceExecution);
} else {
ActivityExecution scopeExecution = ScopeUtil.findScopeExecution(execution);
if (scopeExecution != null) {
terminateExecution(execution, terminateEndEventActivity, scopeExecution);
}
}
}
/**
* Finds the parent execution that is a process instance.
* For a callactivity, this will be the process instance representing the called process instance
* and NOT the root process instance!
*/
protected ActivityExecution findProcessInstanceExecution(ActivityExecution execution) {
ActivityExecution currentExecution = execution;
while (currentExecution.getParent() != null) {
currentExecution = currentExecution.getParent();
}
return currentExecution;
}
protected ActivityExecution findRootProcessInstanceExecution(ExecutionEntity execution) {
ExecutionEntity currentExecution = execution;
while (currentExecution.getParentId() != null || currentExecution.getSuperExecutionId() != null) {
ExecutionEntity parentExecution = currentExecution.getParent();
if (parentExecution != null) {
currentExecution = parentExecution;
} else if (currentExecution.getSuperExecutionId() != null) {
currentExecution = currentExecution.getSuperExecution();
}
}
return currentExecution;
}
protected void terminateExecution(ActivityExecution execution, ActivityImpl terminateEndEventActivity, ActivityExecution scopeExecution) {
// 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(terminateEndEventActivity);
// end the scope execution
scopeExecution.end();
// Scope execution can already have been ended (for example when multiple seq flow arrive in the same terminate end event)
// in that case, we need to make sure the activity instance is ended
if (scopeExecution.isEnded()) {
Context.getCommandContext().getHistoryManager().recordActivityEnd((ExecutionEntity) execution);
}
}
protected void terminateProcessInstanceExecution(ActivityExecution execution, ActivityImpl terminateEndEventActivity, ActivityExecution processInstanceExecution) {
sendCancelledEvent( execution, terminateEndEventActivity, processInstanceExecution);
deleteProcessInstance((ExecutionEntity) processInstanceExecution, execution, "terminate end event (" + terminateEndEventActivity.getId() + ")");
}
protected void deleteProcessInstance(ExecutionEntity processInstanceExecution, ActivityExecution execution, String deleteReason) {
List<ExecutionEntity> orderedExecutions = orderExecutionsRootToLeaf(processInstanceExecution);
Collections.reverse(orderedExecutions);
endAllHistoricActivities(processInstanceExecution.getId());
for (ExecutionEntity executionToDelete : orderedExecutions) {
executionToDelete.setDeleteReason(deleteReason);
executionToDelete.setEnded(true);
executionToDelete.setActive(false);
executionToDelete.setDeleteRoot(true);
executionToDelete.remove();
}
Context.getCommandContext().getHistoryManager().recordProcessInstanceEnd(processInstanceExecution.getId(), deleteReason, execution.getActivity().getId());
}
protected List<ExecutionEntity> orderExecutionsRootToLeaf(ExecutionEntity execution) {
// Find root process instance
ExecutionEntity rootExecution = execution;
while (rootExecution.getParent() != null || rootExecution.getSuperExecution() != null) {
rootExecution = rootExecution.getParent() != null ? rootExecution.getParent() : rootExecution.getSuperExecution();
}
return orderExecutionsRootToLeaf(rootExecution, new ArrayList<ExecutionEntity>());
}
protected List<ExecutionEntity> orderExecutionsRootToLeaf(ExecutionEntity rootExecution, List<ExecutionEntity> orderedExecutions) {
orderedExecutions.add(rootExecution);
// Children
if (rootExecution.getExecutions() != null && rootExecution.getExecutions().size() > 0) {
for (ExecutionEntity childExecution : rootExecution.getExecutions()) {
orderExecutionsRootToLeaf(childExecution, orderedExecutions);
}
}
// Called process instances (subprocess)
if (rootExecution.getSubProcessInstance() != null) {
orderExecutionsRootToLeaf(rootExecution.getSubProcessInstance(), orderedExecutions);
}
return orderedExecutions;
}
protected void endAllHistoricActivities(String processInstanceId) {
if (!Context.getProcessEngineConfiguration().getHistoryLevel().isAtLeast(HistoryLevel.ACTIVITY)) {
return;
}
Map<String, HistoricActivityInstanceEntity> historicActivityInstancMap = new HashMap<String, HistoricActivityInstanceEntity>();
List<HistoricActivityInstance> historicActivityInstances = new HistoricActivityInstanceQueryImpl(Context.getCommandContext())
.processInstanceId(processInstanceId)
.unfinished()
.list();
for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
historicActivityInstancMap.put(historicActivityInstance.getId(), (HistoricActivityInstanceEntity) historicActivityInstance);
}
// Cached version overwites entity
List<HistoricActivityInstanceEntity> cachedHistoricActivityInstances = Context.getCommandContext().getDbSqlSession()
.findInCache(HistoricActivityInstanceEntity.class);
for (HistoricActivityInstanceEntity cachedHistoricActivityInstance : cachedHistoricActivityInstances) {
if (processInstanceId.equals(cachedHistoricActivityInstance.getProcessInstanceId())
&& (cachedHistoricActivityInstance.getEndTime() == null)) {
historicActivityInstancMap.put(cachedHistoricActivityInstance.getId(), cachedHistoricActivityInstance);
}
}
for (HistoricActivityInstanceEntity historicActivityInstance : historicActivityInstancMap.values()) {
historicActivityInstance.markEnded(null);
// Fire event
ProcessEngineConfigurationImpl config = Context.getProcessEngineConfiguration();
if (config != null && config.getEventDispatcher().isEnabled()) {
config.getEventDispatcher().dispatchEvent(
ActivitiEventBuilder.createEntityEvent(ActivitiEventType.HISTORIC_ACTIVITY_INSTANCE_ENDED, historicActivityInstance));
}
}
}
protected 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);
}
}
protected 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)
);
}
public EndEvent getEndEvent() {
return this.endEvent;
}
}