/* 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.flowable.engine.impl.bpmn.behavior;

import java.util.Collection;

import org.flowable.bpmn.model.CallActivity;
import org.flowable.engine.common.api.FlowableException;
import org.flowable.engine.common.impl.util.CollectionUtil;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.history.DeleteReason;
import org.flowable.engine.impl.context.Context;
import org.flowable.engine.impl.interceptor.CommandContext;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.persistence.entity.ExecutionEntityManager;

/**
 * @author Joram Barrez
 */
public class BoundaryEventActivityBehavior extends FlowNodeActivityBehavior {

    private static final long serialVersionUID = 1L;

    protected boolean interrupting;

    public BoundaryEventActivityBehavior() {
    }

    public BoundaryEventActivityBehavior(boolean interrupting) {
        this.interrupting = interrupting;
    }

    @Override
    public void execute(DelegateExecution execution) {
        // Overridden by subclasses
    }

    @Override
    public void trigger(DelegateExecution execution, String triggerName, Object triggerData) {

        ExecutionEntity executionEntity = (ExecutionEntity) execution;

        CommandContext commandContext = Context.getCommandContext();

        if (interrupting) {
            executeInterruptingBehavior(executionEntity, commandContext);
        } else {
            executeNonInterruptingBehavior(executionEntity, commandContext);
        }
    }

    protected void executeInterruptingBehavior(ExecutionEntity executionEntity, CommandContext commandContext) {

        // The destroy scope operation will look for the parent execution and
        // destroy the whole scope, and leave the boundary event using this parent execution.
        //
        // The take outgoing seq flows operation below (the non-interrupting else clause) on the other hand uses the
        // child execution to leave, which keeps the scope alive.
        // Which is what we need here.

        ExecutionEntityManager executionEntityManager = commandContext.getExecutionEntityManager();
        ExecutionEntity attachedRefScopeExecution = executionEntityManager.findById(executionEntity.getParentId());

        ExecutionEntity parentScopeExecution = null;
        ExecutionEntity currentlyExaminedExecution = executionEntityManager.findById(attachedRefScopeExecution.getParentId());
        while (currentlyExaminedExecution != null && parentScopeExecution == null) {
            if (currentlyExaminedExecution.isScope()) {
                parentScopeExecution = currentlyExaminedExecution;
            } else {
                currentlyExaminedExecution = executionEntityManager.findById(currentlyExaminedExecution.getParentId());
            }
        }

        if (parentScopeExecution == null) {
            throw new FlowableException("Programmatic error: no parent scope execution found for boundary event");
        }

        deleteChildExecutions(attachedRefScopeExecution, executionEntity, commandContext);

        // set new parent for boundary event execution
        executionEntity.setParent(parentScopeExecution);

        commandContext.getAgenda().planTakeOutgoingSequenceFlowsOperation(executionEntity, true);
    }

    protected void executeNonInterruptingBehavior(ExecutionEntity executionEntity, CommandContext commandContext) {

        // Non-interrupting: the current execution is given the first parent
        // scope (which isn't its direct parent)
        //
        // Why? Because this execution does NOT have anything to do with
        // the current parent execution (the one where the boundary event is on): when it is deleted or whatever,
        // this does not impact this new execution at all, it is completely independent in that regard.

        // Note: if the parent of the parent does not exists, this becomes a concurrent execution in the process instance!

        ExecutionEntityManager executionEntityManager = commandContext.getExecutionEntityManager();

        ExecutionEntity parentExecutionEntity = executionEntityManager.findById(executionEntity.getParentId());

        ExecutionEntity scopeExecution = null;
        ExecutionEntity currentlyExaminedExecution = executionEntityManager.findById(parentExecutionEntity.getParentId());
        while (currentlyExaminedExecution != null && scopeExecution == null) {
            if (currentlyExaminedExecution.isScope()) {
                scopeExecution = currentlyExaminedExecution;
            } else {
                currentlyExaminedExecution = executionEntityManager.findById(currentlyExaminedExecution.getParentId());
            }
        }

        if (scopeExecution == null) {
            throw new FlowableException("Programmatic error: no parent scope execution found for boundary event");
        }

        ExecutionEntity nonInterruptingExecution = executionEntityManager.createChildExecution(scopeExecution);
        nonInterruptingExecution.setCurrentFlowElement(executionEntity.getCurrentFlowElement());

        commandContext.getAgenda().planTakeOutgoingSequenceFlowsOperation(nonInterruptingExecution, true);
    }

    protected void deleteChildExecutions(ExecutionEntity parentExecution, ExecutionEntity notToDeleteExecution, CommandContext commandContext) {

        // TODO: would be good if this deleteChildExecutions could be removed and the one on the executionEntityManager is used
        // The problem however, is that the 'notToDeleteExecution' is passed here.
        // This could be solved by not reusing an execution, but creating a new

        // Delete all child executions
        ExecutionEntityManager executionEntityManager = commandContext.getExecutionEntityManager();
        Collection<ExecutionEntity> childExecutions = executionEntityManager.findChildExecutionsByParentExecutionId(parentExecution.getId());
        if (CollectionUtil.isNotEmpty(childExecutions)) {
            for (ExecutionEntity childExecution : childExecutions) {
                if (!childExecution.getId().equals(notToDeleteExecution.getId())) {
                    deleteChildExecutions(childExecution, notToDeleteExecution, commandContext);
                }
            }
        }

        String deleteReason = DeleteReason.BOUNDARY_EVENT_INTERRUPTING + " (" + notToDeleteExecution.getCurrentActivityId() + ")";
        if (parentExecution.getCurrentFlowElement() instanceof CallActivity) {
            ExecutionEntity subProcessExecution = executionEntityManager.findSubProcessInstanceBySuperExecutionId(parentExecution.getId());
            if (subProcessExecution != null) {
                executionEntityManager.deleteProcessInstanceExecutionEntity(subProcessExecution.getId(),
                        subProcessExecution.getCurrentActivityId(), deleteReason, true, true);
            }
        }

        executionEntityManager.deleteExecutionAndRelatedData(parentExecution, deleteReason, false);
    }

    public boolean isInterrupting() {
        return interrupting;
    }

    public void setInterrupting(boolean interrupting) {
        this.interrupting = interrupting;
    }

}

@@ -638,53 +638,8 @@
 n;%0A%0A
-import org.flowable.bpmn.model.CallActivity;%0A
 impo
@@ -6933,91 +6933,8 @@
 )%22;%0A
-        if (parentExecution.getCurrentFlowElement() instanceof CallActivity) %7B%0A    
     
@@ -7057,36 +7057,32 @@
 ution.getId());%0A
-    
         if (subP
@@ -7116,28 +7116,24 @@
             
-    
 executionEnt
@@ -7229,20 +7229,16 @@
         
-  
-  
 subProce
@@ -7304,39 +7304,28 @@
 e);%0A        
+%7D
    
- %7D%0A        %7D
 %0A%0A        ex

Remove call activity check, because a sub process instance could be there in other cases as well #364