All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.camunda.bpm.engine.impl.cmmn.execution.CmmnExecution Maven / Gradle / Ivy

There is a newer version: 7.22.0-alpha1
Show newest version
/*
 * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH
 * under one or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information regarding copyright
 * ownership. Camunda licenses this file to you under the Apache License,
 * Version 2.0; 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.camunda.bpm.engine.impl.cmmn.execution;

import static org.camunda.bpm.engine.impl.cmmn.execution.CaseExecutionState.ACTIVE;
import static org.camunda.bpm.engine.impl.cmmn.execution.CaseExecutionState.AVAILABLE;
import static org.camunda.bpm.engine.impl.cmmn.execution.CaseExecutionState.CLOSED;
import static org.camunda.bpm.engine.impl.cmmn.execution.CaseExecutionState.COMPLETED;
import static org.camunda.bpm.engine.impl.cmmn.execution.CaseExecutionState.DISABLED;
import static org.camunda.bpm.engine.impl.cmmn.execution.CaseExecutionState.ENABLED;
import static org.camunda.bpm.engine.impl.cmmn.execution.CaseExecutionState.FAILED;
import static org.camunda.bpm.engine.impl.cmmn.execution.CaseExecutionState.NEW;
import static org.camunda.bpm.engine.impl.cmmn.execution.CaseExecutionState.SUSPENDED;
import static org.camunda.bpm.engine.impl.cmmn.execution.CaseExecutionState.SUSPENDING_ON_PARENT_SUSPENSION;
import static org.camunda.bpm.engine.impl.cmmn.execution.CaseExecutionState.SUSPENDING_ON_SUSPENSION;
import static org.camunda.bpm.engine.impl.cmmn.execution.CaseExecutionState.TERMINATED;
import static org.camunda.bpm.engine.impl.cmmn.execution.CaseExecutionState.TERMINATING_ON_EXIT;
import static org.camunda.bpm.engine.impl.cmmn.execution.CaseExecutionState.TERMINATING_ON_PARENT_TERMINATION;
import static org.camunda.bpm.engine.impl.cmmn.execution.CaseExecutionState.TERMINATING_ON_TERMINATION;
import static org.camunda.bpm.engine.impl.cmmn.model.CmmnSentryDeclaration.IF_PART;
import static org.camunda.bpm.engine.impl.cmmn.model.CmmnSentryDeclaration.PLAN_ITEM_ON_PART;
import static org.camunda.bpm.engine.impl.cmmn.model.CmmnSentryDeclaration.VARIABLE_ON_PART;
import static org.camunda.bpm.engine.impl.cmmn.operation.CmmnAtomicOperation.CASE_EXECUTION_COMPLETE;
import static org.camunda.bpm.engine.impl.cmmn.operation.CmmnAtomicOperation.CASE_EXECUTION_CREATE;
import static org.camunda.bpm.engine.impl.cmmn.operation.CmmnAtomicOperation.CASE_EXECUTION_DELETE_CASCADE;
import static org.camunda.bpm.engine.impl.cmmn.operation.CmmnAtomicOperation.CASE_EXECUTION_DISABLE;
import static org.camunda.bpm.engine.impl.cmmn.operation.CmmnAtomicOperation.CASE_EXECUTION_ENABLE;
import static org.camunda.bpm.engine.impl.cmmn.operation.CmmnAtomicOperation.CASE_EXECUTION_EXIT;
import static org.camunda.bpm.engine.impl.cmmn.operation.CmmnAtomicOperation.CASE_EXECUTION_FIRE_ENTRY_CRITERIA;
import static org.camunda.bpm.engine.impl.cmmn.operation.CmmnAtomicOperation.CASE_EXECUTION_FIRE_EXIT_CRITERIA;
import static org.camunda.bpm.engine.impl.cmmn.operation.CmmnAtomicOperation.CASE_EXECUTION_MANUAL_COMPLETE;
import static org.camunda.bpm.engine.impl.cmmn.operation.CmmnAtomicOperation.CASE_EXECUTION_MANUAL_START;
import static org.camunda.bpm.engine.impl.cmmn.operation.CmmnAtomicOperation.CASE_EXECUTION_OCCUR;
import static org.camunda.bpm.engine.impl.cmmn.operation.CmmnAtomicOperation.CASE_EXECUTION_PARENT_RESUME;
import static org.camunda.bpm.engine.impl.cmmn.operation.CmmnAtomicOperation.CASE_EXECUTION_PARENT_SUSPEND;
import static org.camunda.bpm.engine.impl.cmmn.operation.CmmnAtomicOperation.CASE_EXECUTION_PARENT_TERMINATE;
import static org.camunda.bpm.engine.impl.cmmn.operation.CmmnAtomicOperation.CASE_EXECUTION_RESUME;
import static org.camunda.bpm.engine.impl.cmmn.operation.CmmnAtomicOperation.CASE_EXECUTION_RE_ACTIVATE;
import static org.camunda.bpm.engine.impl.cmmn.operation.CmmnAtomicOperation.CASE_EXECUTION_RE_ENABLE;
import static org.camunda.bpm.engine.impl.cmmn.operation.CmmnAtomicOperation.CASE_EXECUTION_START;
import static org.camunda.bpm.engine.impl.cmmn.operation.CmmnAtomicOperation.CASE_EXECUTION_SUSPEND;
import static org.camunda.bpm.engine.impl.cmmn.operation.CmmnAtomicOperation.CASE_EXECUTION_SUSPENDING_ON_PARENT_SUSPENSION;
import static org.camunda.bpm.engine.impl.cmmn.operation.CmmnAtomicOperation.CASE_EXECUTION_SUSPENDING_ON_SUSPENSION;
import static org.camunda.bpm.engine.impl.cmmn.operation.CmmnAtomicOperation.CASE_EXECUTION_TERMINATE;
import static org.camunda.bpm.engine.impl.cmmn.operation.CmmnAtomicOperation.CASE_EXECUTION_TERMINATING_ON_EXIT;
import static org.camunda.bpm.engine.impl.cmmn.operation.CmmnAtomicOperation.CASE_EXECUTION_TERMINATING_ON_PARENT_TERMINATION;
import static org.camunda.bpm.engine.impl.cmmn.operation.CmmnAtomicOperation.CASE_EXECUTION_TERMINATING_ON_TERMINATION;
import static org.camunda.bpm.engine.impl.cmmn.operation.CmmnAtomicOperation.CASE_INSTANCE_CLOSE;
import static org.camunda.bpm.engine.impl.cmmn.operation.CmmnAtomicOperation.CASE_INSTANCE_CREATE;
import static org.camunda.bpm.engine.impl.util.EnsureUtil.ensureInstanceOf;
import static org.camunda.bpm.engine.impl.util.EnsureUtil.ensureNotNull;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;

import org.camunda.bpm.engine.delegate.CaseVariableListener;
import org.camunda.bpm.engine.delegate.Expression;
import org.camunda.bpm.engine.delegate.VariableListener;
import org.camunda.bpm.engine.impl.ProcessEngineLogger;
import org.camunda.bpm.engine.impl.cmmn.behavior.CmmnBehaviorLogger;
import org.camunda.bpm.engine.impl.cmmn.entity.runtime.CaseExecutionEntity;
import org.camunda.bpm.engine.impl.cmmn.entity.runtime.CaseSentryPartEntity;
import org.camunda.bpm.engine.impl.cmmn.model.CmmnActivity;
import org.camunda.bpm.engine.impl.cmmn.model.CmmnCaseDefinition;
import org.camunda.bpm.engine.impl.cmmn.model.CmmnIfPartDeclaration;
import org.camunda.bpm.engine.impl.cmmn.model.CmmnOnPartDeclaration;
import org.camunda.bpm.engine.impl.cmmn.model.CmmnSentryDeclaration;
import org.camunda.bpm.engine.impl.cmmn.model.CmmnVariableOnPartDeclaration;
import org.camunda.bpm.engine.impl.cmmn.operation.CmmnAtomicOperation;
import org.camunda.bpm.engine.impl.context.Context;
import org.camunda.bpm.engine.impl.core.instance.CoreExecution;
import org.camunda.bpm.engine.impl.core.variable.event.VariableEvent;
import org.camunda.bpm.engine.impl.core.variable.scope.AbstractVariableScope;
import org.camunda.bpm.engine.impl.persistence.entity.TaskEntity;
import org.camunda.bpm.engine.impl.persistence.entity.TaskEntity.TaskState;
import org.camunda.bpm.engine.impl.pvm.PvmException;
import org.camunda.bpm.engine.impl.pvm.PvmProcessDefinition;
import org.camunda.bpm.engine.impl.pvm.runtime.PvmExecutionImpl;
import org.camunda.bpm.engine.impl.task.TaskDecorator;
import org.camunda.bpm.engine.impl.variable.listener.CaseVariableListenerInvocation;
import org.camunda.bpm.engine.impl.variable.listener.DelegateCaseVariableInstanceImpl;
import org.camunda.bpm.engine.task.Task;
import org.camunda.bpm.engine.variable.value.TypedValue;

/**
 * @author Roman Smirnov
 *
 */
public abstract class CmmnExecution extends CoreExecution implements CmmnCaseInstance {

  protected static final CmmnBehaviorLogger LOG = ProcessEngineLogger.CMNN_BEHAVIOR_LOGGER;

  private static final long serialVersionUID = 1L;

  protected transient CmmnCaseDefinition caseDefinition;

  // current position //////////////////////////////////////

  /** current activity */
  protected transient CmmnActivity activity;

  protected boolean required = false;

  protected int previousState;

  protected int currentState = NEW.getStateCode();

  protected Queue variableEventsQueue;

  protected transient TaskEntity task;

  /**
   * This property will be used if this
   * {@link CmmnExecution} is in state {@link CaseExecutionState#NEW}
   * to note that an entry criterion is satisfied.
   */
  protected boolean entryCriterionSatisfied = false;

  public CmmnExecution() {
  }

  // plan items ///////////////////////////////////////////////////////////////

  public abstract List getCaseExecutions();

  protected abstract List getCaseExecutionsInternal();

  public CmmnExecution findCaseExecution(String activityId) {
    if ((getActivity()!=null) && (getActivity().getId().equals(activityId))) {
     return this;
   }
   for (CmmnExecution nestedExecution : getCaseExecutions()) {
     CmmnExecution result = nestedExecution.findCaseExecution(activityId);
     if (result != null) {
       return result;
     }
   }
   return null;
  }

  // task /////////////////////////////////////////////////////////////////////

  public TaskEntity getTask() {
    return this.task;
  }

  public void setTask(Task task) {
    this.task = (TaskEntity) task;
  }

  public TaskEntity createTask(TaskDecorator taskDecorator) {
    TaskEntity task = new TaskEntity((CaseExecutionEntity) this);
    task.insert();

    setTask(task);
    taskDecorator.decorate(task, this);

    // task decoration is part of the initialization of the task,
    // so we transition to CREATED only afterwards
    task.transitionTo(TaskState.STATE_CREATED);

    return task;
  }

  // super execution  ////////////////////////////////////////////////////////

  public abstract PvmExecutionImpl getSuperExecution();

  public abstract void setSuperExecution(PvmExecutionImpl superExecution);

  // sub process instance ////////////////////////////////////////////////////

  public abstract PvmExecutionImpl getSubProcessInstance();

  public abstract void setSubProcessInstance(PvmExecutionImpl subProcessInstance);

  public abstract PvmExecutionImpl createSubProcessInstance(PvmProcessDefinition processDefinition);

  public abstract PvmExecutionImpl createSubProcessInstance(PvmProcessDefinition processDefinition, String businessKey);

  public abstract PvmExecutionImpl createSubProcessInstance(PvmProcessDefinition processDefinition, String businessKey, String caseInstanceId);

  // sub-/super- case instance ////////////////////////////////////////////////////

  public abstract CmmnExecution getSubCaseInstance();

  public abstract void setSubCaseInstance(CmmnExecution subCaseInstance);

  public abstract CmmnExecution createSubCaseInstance(CmmnCaseDefinition caseDefinition);

  public abstract CmmnExecution createSubCaseInstance(CmmnCaseDefinition caseDefinition, String businessKey);

  public abstract CmmnExecution getSuperCaseExecution();

  public abstract void setSuperCaseExecution(CmmnExecution superCaseExecution);

  // sentry //////////////////////////////////////////////////////////////////

  // sentry: (1) create and initialize sentry parts

  protected abstract CmmnSentryPart newSentryPart();

  protected abstract void addSentryPart(CmmnSentryPart sentryPart);

  public void createSentryParts() {
    CmmnActivity activity = getActivity();
    ensureNotNull("Case execution '"+id+"': has no current activity", "activity", activity);

    List sentries = activity.getSentries();

    if (sentries != null && !sentries.isEmpty()) {

      for (CmmnSentryDeclaration sentryDeclaration : sentries) {

        CmmnIfPartDeclaration ifPartDeclaration = sentryDeclaration.getIfPart();
        if (ifPartDeclaration != null) {
          CmmnSentryPart ifPart = createIfPart(sentryDeclaration, ifPartDeclaration);
          addSentryPart(ifPart);
        }

        List onPartDeclarations = sentryDeclaration.getOnParts();

        for (CmmnOnPartDeclaration onPartDeclaration : onPartDeclarations) {
          CmmnSentryPart onPart = createOnPart(sentryDeclaration, onPartDeclaration);
          addSentryPart(onPart);
        }

        List variableOnPartDeclarations = sentryDeclaration.getVariableOnParts();
        for(CmmnVariableOnPartDeclaration variableOnPartDeclaration: variableOnPartDeclarations) {
          CmmnSentryPart variableOnPart = createVariableOnPart(sentryDeclaration, variableOnPartDeclaration);
          addSentryPart(variableOnPart);
        }

      }
    }
  }

  protected CmmnSentryPart createOnPart(CmmnSentryDeclaration sentryDeclaration, CmmnOnPartDeclaration onPartDeclaration) {
    CmmnSentryPart sentryPart = createSentryPart(sentryDeclaration, PLAN_ITEM_ON_PART);

    // set the standard event
    String standardEvent = onPartDeclaration.getStandardEvent();
    sentryPart.setStandardEvent(standardEvent);

    // set source case execution
    CmmnActivity source = onPartDeclaration.getSource();
    ensureNotNull("The source of sentry '"+sentryDeclaration.getId()+"' is null.", "source", source);

    String sourceActivityId = source.getId();
    sentryPart.setSource(sourceActivityId);

    // TODO: handle also sentryRef!!! (currently not implemented on purpose)

    return sentryPart;
  }

  protected CmmnSentryPart createIfPart(CmmnSentryDeclaration sentryDeclaration, CmmnIfPartDeclaration ifPartDeclaration) {
    return createSentryPart(sentryDeclaration, IF_PART);
  }

  protected CmmnSentryPart createVariableOnPart(CmmnSentryDeclaration sentryDeclaration, CmmnVariableOnPartDeclaration variableOnPartDeclaration) {
    CmmnSentryPart sentryPart = createSentryPart(sentryDeclaration, VARIABLE_ON_PART);

    // set the variable event
    String variableEvent = variableOnPartDeclaration.getVariableEvent();
    sentryPart.setVariableEvent(variableEvent);

    // set the variable name
    String variableName = variableOnPartDeclaration.getVariableName();
    sentryPart.setVariableName(variableName);

    return sentryPart;
  }

  protected CmmnSentryPart createSentryPart(CmmnSentryDeclaration sentryDeclaration, String type) {
    CmmnSentryPart newSentryPart = newSentryPart();

    // set the type
    newSentryPart.setType(type);

    // set the case instance and case execution
    newSentryPart.setCaseInstance(getCaseInstance());
    newSentryPart.setCaseExecution(this);

    // set sentry id
    String sentryId = sentryDeclaration.getId();
    newSentryPart.setSentryId(sentryId);

    return newSentryPart;
  }

  // sentry: (2) handle transitions

  public void handleChildTransition(CmmnExecution child, String transition) {
    // Step 1: collect all affected sentries
    List affectedSentries = collectAffectedSentries(child, transition);

    // Step 2: fire force update on all case sentry part
    // contained by a affected sentry to provoke an
    // OptimisticLockingException
    forceUpdateOnSentries(affectedSentries);

    // Step 3: check each affected sentry whether it is satisfied.
    // the returned list contains all satisfied sentries
    List satisfiedSentries = getSatisfiedSentries(affectedSentries);

    // Step 4: reset sentries -> satisfied == false
    resetSentries(satisfiedSentries);

    // Step 5: fire satisfied sentries
    fireSentries(satisfiedSentries);

  }

  public void fireIfOnlySentryParts() {
    // the following steps are a workaround, because setVariable()
    // does not check nor fire a sentry!!!
    Set affectedSentries = new HashSet();
    List sentryParts = collectSentryParts(getSentries());
    for (CmmnSentryPart sentryPart : sentryParts) {
      if (isNotSatisfiedIfPartOnly(sentryPart)) {
        affectedSentries.add(sentryPart.getSentryId());
      }
    }

    // Step 7: check each not affected sentry whether it is satisfied
    List satisfiedSentries = getSatisfiedSentries(new ArrayList(affectedSentries));

    // Step 8: reset sentries -> satisfied == false
    resetSentries(satisfiedSentries);

    // Step 9: fire satisfied sentries
    fireSentries(satisfiedSentries);
  }

  public void handleVariableTransition(String variableName, String transition) {
    Map> sentries = collectAllSentries();

    List sentryParts = collectSentryParts(sentries);

    List affectedSentries = collectAffectedSentriesWithVariableOnParts(variableName, transition, sentryParts);

    List affectedSentryParts = getAffectedSentryParts(sentries,affectedSentries);
    forceUpdateOnCaseSentryParts(affectedSentryParts);

    List allSentries = new ArrayList(sentries.keySet());

    List satisfiedSentries = getSatisfiedSentriesInExecutionTree(allSentries, sentries);

    List satisfiedSentryParts = getAffectedSentryParts(sentries, satisfiedSentries);
    resetSentryParts(satisfiedSentryParts);

    fireSentries(satisfiedSentries);

  }

  protected List collectAffectedSentries(CmmnExecution child, String transition) {
    List sentryParts = getCaseSentryParts();

    List affectedSentries = new ArrayList();

    for (CmmnSentryPart sentryPart : sentryParts) {

      // necessary for backward compatibility
      String sourceCaseExecutionId = sentryPart.getSourceCaseExecutionId();
      String sourceRef = sentryPart.getSource();
      if (child.getActivityId().equals(sourceRef) || child.getId().equals(sourceCaseExecutionId)) {

        String standardEvent = sentryPart.getStandardEvent();
        if (transition.equals(standardEvent)) {
          addIdIfNotSatisfied(affectedSentries, sentryPart);
        }
      }
    }

    return affectedSentries;
  }

  protected boolean isNotSatisfiedIfPartOnly(CmmnSentryPart sentryPart) {
    return IF_PART.equals(sentryPart.getType())
        && getSentries().get(sentryPart.getSentryId()).size() == 1
        && !sentryPart.isSatisfied();
  }

  protected void addIdIfNotSatisfied(List affectedSentries, CmmnSentryPart sentryPart) {
    if (!sentryPart.isSatisfied()) {
      // if it is not already satisfied, then set the
      // current case sentry part to satisfied (=true).
      String sentryId = sentryPart.getSentryId();
      sentryPart.setSatisfied(true);

      // collect the id of affected sentry.
      if (!affectedSentries.contains(sentryId)) {
        affectedSentries.add(sentryId);
      }
    }
  }

  protected List collectAffectedSentriesWithVariableOnParts(String variableName, String variableEvent, List sentryParts) {

    List affectedSentries = new ArrayList();

    for (CmmnSentryPart sentryPart : sentryParts) {

      String sentryVariableName = sentryPart.getVariableName();
      String sentryVariableEvent = sentryPart.getVariableEvent();
      CmmnExecution execution = sentryPart.getCaseExecution();
      if (VARIABLE_ON_PART.equals(sentryPart.getType()) && sentryVariableName.equals(variableName)
        && sentryVariableEvent.equals(variableEvent)
        && !hasVariableWithSameNameInParent(execution, sentryVariableName)) {

        addIdIfNotSatisfied(affectedSentries, sentryPart);
      }
    }

    return affectedSentries;
  }

  protected boolean hasVariableWithSameNameInParent(CmmnExecution execution, String variableName) {
    while(execution != null) {
      if (execution.getId().equals(getId())) {
        return false;
      }
      TypedValue variableTypedValue = execution.getVariableLocalTyped(variableName);
      if (variableTypedValue != null) {
        return true;
      }
      execution = execution.getParent();
    }
    return false;
  }

  protected Map> collectAllSentries() {
    Map> sentries = new HashMap>();
    List caseExecutions = getCaseExecutions();
    for(CmmnExecution caseExecution: caseExecutions) {
      sentries.putAll(caseExecution.collectAllSentries());
    }
    sentries.putAll(getSentries());
    return sentries;
  }

  protected List getAffectedSentryParts(Map> allSentries, List affectedSentries) {
    List affectedSentryParts = new ArrayList();
    for(String affectedSentryId: affectedSentries) {
      affectedSentryParts.addAll(allSentries.get(affectedSentryId));
    }
    return affectedSentryParts;
  }

  protected List collectSentryParts(Map> sentries) {
    List sentryParts = new ArrayList();
    for(String sentryId: sentries.keySet()) {
      sentryParts.addAll(sentries.get(sentryId));
    }
    return sentryParts;
  }

  protected void forceUpdateOnCaseSentryParts(List sentryParts) {
    // set for each case sentry part forceUpdate flag to true to provoke
    // an OptimisticLockingException if different case sentry parts of the
    // same sentry has been satisfied concurrently.
    for (CmmnSentryPart sentryPart : sentryParts) {
      if (sentryPart instanceof CaseSentryPartEntity) {
        CaseSentryPartEntity sentryPartEntity = (CaseSentryPartEntity) sentryPart;
        sentryPartEntity.forceUpdate();
      }
    }
  }

  /**
   * Checks for each given sentry id whether the corresponding
   * sentry is satisfied.
   */
  protected List getSatisfiedSentries(List sentryIds) {
    List result = new ArrayList();

    if (sentryIds != null) {

      for (String sentryId : sentryIds) {

        if (isSentrySatisfied(sentryId)) {
          result.add(sentryId);
        }
      }
    }

    return result;
  }

  /**
   * Checks for each given sentry id in the execution tree whether the corresponding
   * sentry is satisfied.
   */
  protected List getSatisfiedSentriesInExecutionTree(List sentryIds, Map> allSentries) {
    List result = new ArrayList();

    if (sentryIds != null) {

      for (String sentryId : sentryIds) {
        List sentryParts = allSentries.get(sentryId);
        if (isSentryPartsSatisfied(sentryId, sentryParts)) {
          result.add(sentryId);
        }
      }
    }

    return result;
  }

  protected void forceUpdateOnSentries(List sentryIds) {
    for (String sentryId : sentryIds) {
      List sentryParts = findSentry(sentryId);
      // set for each case sentry part forceUpdate flag to true to provoke
      // an OptimisticLockingException if different case sentry parts of the
      // same sentry has been satisfied concurrently.
      for (CmmnSentryPart sentryPart : sentryParts) {
        if (sentryPart instanceof CaseSentryPartEntity) {
          CaseSentryPartEntity sentryPartEntity = (CaseSentryPartEntity) sentryPart;
          sentryPartEntity.forceUpdate();
        }
      }
    }
  }

  protected void resetSentries(List sentries) {
    for (String sentry : sentries) {
      List parts = getSentries().get(sentry);
      for (CmmnSentryPart part : parts) {
        part.setSatisfied(false);
      }
    }
  }

  protected void resetSentryParts(List parts) {
    for (CmmnSentryPart part : parts) {
      part.setSatisfied(false);
    }
  }

  protected void fireSentries(List satisfiedSentries) {
    if (satisfiedSentries != null && !satisfiedSentries.isEmpty()) {
      // if there are satisfied sentries, trigger the associated
      // case executions

      // 1. propagate to all child case executions ///////////////////////////////////////////

      // collect the execution tree.
      ArrayList children = new ArrayList();
      collectCaseExecutionsInExecutionTree(children);

      for (CmmnExecution currentChild : children) {

        // check and fire first exitCriteria
        currentChild.checkAndFireExitCriteria(satisfiedSentries);

        // then trigger entryCriteria
        currentChild.checkAndFireEntryCriteria(satisfiedSentries);
      }

      // 2. check exit criteria of the case instance //////////////////////////////////////////

      if (isCaseInstanceExecution() && isActive()) {
        checkAndFireExitCriteria(satisfiedSentries);
      }

    }
  }

  protected void collectCaseExecutionsInExecutionTree(List children) {
    for(CmmnExecution child: getCaseExecutions()) {
      child.collectCaseExecutionsInExecutionTree(children);
    }
    children.addAll(getCaseExecutions());
  }

  protected void checkAndFireExitCriteria(List satisfiedSentries) {
    if (isActive()) {
      CmmnActivity activity = getActivity();
      ensureNotNull(PvmException.class, "Case execution '"+getId()+"': has no current activity.", "activity", activity);

      // trigger first exitCriteria
      List exitCriteria = activity.getExitCriteria();
      for (CmmnSentryDeclaration sentryDeclaration : exitCriteria) {

        if (sentryDeclaration != null && satisfiedSentries.contains(sentryDeclaration.getId())) {
          fireExitCriteria();
          break;
        }
      }
    }
  }

  protected void checkAndFireEntryCriteria(List satisfiedSentries) {
    if (isAvailable() || isNew()) {
      // do that only, when this child case execution
      // is available

      CmmnActivity activity = getActivity();
      ensureNotNull(PvmException.class, "Case execution '"+getId()+"': has no current activity.", "activity", activity);

      List criteria = activity.getEntryCriteria();
      for (CmmnSentryDeclaration sentryDeclaration : criteria) {
        if (sentryDeclaration != null && satisfiedSentries.contains(sentryDeclaration.getId())) {
          if (isAvailable()) {
            fireEntryCriteria();
          }
          else {
            entryCriterionSatisfied = true;
          }
          break;
        }
      }
    }
  }

  public void fireExitCriteria() {
    performOperation(CASE_EXECUTION_FIRE_EXIT_CRITERIA);
  }

  public void fireEntryCriteria() {
    performOperation(CASE_EXECUTION_FIRE_ENTRY_CRITERIA);
  }

  // sentry: (3) helper

  public abstract List getCaseSentryParts();

  protected abstract List findSentry(String sentryId);

  protected abstract Map> getSentries();

  public boolean isSentrySatisfied(String sentryId) {
    List sentryParts = findSentry(sentryId);
    return isSentryPartsSatisfied(sentryId, sentryParts);

  }

  protected boolean isSentryPartsSatisfied(String sentryId, List sentryParts) {
    // if part will be evaluated in the end
    CmmnSentryPart ifPart = null;

    if (sentryParts != null && !sentryParts.isEmpty()) {
      for (CmmnSentryPart sentryPart : sentryParts) {

        if (PLAN_ITEM_ON_PART.equals(sentryPart.getType())) {

          if (!sentryPart.isSatisfied()) {
            return false;
          }

        } else if (VARIABLE_ON_PART.equals(sentryPart.getType())) {
          if (!sentryPart.isSatisfied()) {
            return false;
          }
        } else { /* IF_PART.equals(sentryPart.getType) == true */

          ifPart = sentryPart;

          // once the ifPart has been satisfied the whole sentry is satisfied
          if (ifPart.isSatisfied()) {
            return true;
          }

        }

      }
    }

    if (ifPart != null) {

      CmmnExecution execution = ifPart.getCaseExecution();
      ensureNotNull("Case execution of sentry '"+ifPart.getSentryId() +"': is null", execution);

      CmmnActivity activity = ifPart.getCaseExecution().getActivity();
      ensureNotNull("Case execution '"+id+"': has no current activity", "activity", activity);

      CmmnSentryDeclaration sentryDeclaration = activity.getSentry(sentryId);
      ensureNotNull("Case execution '"+id+"': has no declaration for sentry '"+sentryId+"'", "sentryDeclaration", sentryDeclaration);

      CmmnIfPartDeclaration ifPartDeclaration = sentryDeclaration.getIfPart();
      ensureNotNull("Sentry declaration '"+sentryId+"' has no definied ifPart, but there should be one defined for case execution '"+id+"'.", "ifPartDeclaration", ifPartDeclaration);

      Expression condition = ifPartDeclaration.getCondition();
      ensureNotNull("A condition was expected for ifPart of Sentry declaration '"+sentryId+"' for case execution '"+id+"'.", "condition", condition);

      Object result = condition.getValue(this);
      ensureInstanceOf("condition expression returns non-Boolean", "result", result, Boolean.class);

      Boolean booleanResult = (Boolean) result;
      ifPart.setSatisfied(booleanResult);
      return booleanResult;

    }

    // if all onParts are satisfied and there is no
    // ifPart then the whole sentry is satisfied.
    return true;
  }

  protected boolean containsIfPartAndExecutionActive(String sentryId, Map> sentries) {
    List sentryParts = sentries.get(sentryId);

    for (CmmnSentryPart part : sentryParts) {
      CmmnExecution caseExecution = part.getCaseExecution();
      if (IF_PART.equals(part.getType()) && caseExecution != null
          && caseExecution.isActive()) {
        return true;
      }
    }

    return false;
  }

  public boolean isEntryCriterionSatisfied() {
    return entryCriterionSatisfied;
  }

  // business key ////////////////////////////////////////////////////////////

  public String getCaseBusinessKey() {
    return getCaseInstance().getBusinessKey();
  }

  @Override
  public String getBusinessKey() {
    if (this.isCaseInstanceExecution()) {
      return businessKey;
    }
    else return getCaseBusinessKey();
  }

  // case definition ///////////////////////////////////////////////////////

  public CmmnCaseDefinition getCaseDefinition() {
    return caseDefinition;
  }

  public void setCaseDefinition(CmmnCaseDefinition caseDefinition) {
    this.caseDefinition = caseDefinition;
  }

  // case instance /////////////////////////////////////////////////////////

  /** ensures initialization and returns the process instance. */
  public abstract CmmnExecution getCaseInstance();

  public abstract void setCaseInstance(CmmnExecution caseInstance);

  public boolean isCaseInstanceExecution() {
    return getParent() == null;
  }

  // case instance id /////////////////////////////////////////////////////////

  /** ensures initialization and returns the process instance. */
  public String getCaseInstanceId() {
    return getCaseInstance().getId();
  }

  // parent ///////////////////////////////////////////////////////////////////

  /** ensures initialization and returns the parent */
  public abstract CmmnExecution getParent();

  public abstract void setParent(CmmnExecution parent);

  // activity /////////////////////////////////////////////////////////////////

  /** ensures initialization and returns the activity */
  public CmmnActivity getActivity() {
    return activity;
  }

  public void setActivity(CmmnActivity activity) {
    this.activity = activity;
  }

  // variables ////////////////////////////////////////////

  @Override
  public String getVariableScopeKey() {
    return "caseExecution";
  }

  public AbstractVariableScope getParentVariableScope() {
    return getParent();
  }

  //delete/remove /////////////////////////////////////////////////////

  public void deleteCascade() {
   performOperation(CASE_EXECUTION_DELETE_CASCADE);
  }

  public void remove() {
   CmmnExecution parent = getParent();
   if (parent!=null) {
     parent.getCaseExecutionsInternal().remove(this);
   }
  }

  // required //////////////////////////////////////////////////

  public boolean isRequired() {
    return required;
  }

  public void setRequired(boolean required) {
    this.required = required;
  }

  // state /////////////////////////////////////////////////////

  public CaseExecutionState getCurrentState() {
    return CaseExecutionState.CASE_EXECUTION_STATES.get(getState());
  }

  public void setCurrentState(CaseExecutionState currentState) {
    if (!isSuspending() && !isTerminating()) {
      // do not reset the previous state, if this case execution
      // is currently terminating or suspending. otherwise the
      // "real" previous state is lost.
      previousState = this.currentState;
    }
    this.currentState = currentState.getStateCode();
  }

  public int getState() {
    return currentState;
  }

  public void setState(int state) {
    this.currentState = state;
  }

  public boolean isNew() {
    return currentState == NEW.getStateCode();
  }

  public boolean isAvailable() {
    return currentState == AVAILABLE.getStateCode();
  }

  public boolean isEnabled() {
    return currentState == ENABLED.getStateCode();
  }

  public boolean isDisabled() {
    return currentState == DISABLED.getStateCode();
  }

  public boolean isActive() {
    return currentState == ACTIVE.getStateCode();
  }

  public boolean isCompleted() {
    return currentState == COMPLETED.getStateCode();
  }

  public boolean isSuspended() {
    return currentState == SUSPENDED.getStateCode();
  }

  public boolean isSuspending() {
    return currentState == SUSPENDING_ON_SUSPENSION.getStateCode()
        || currentState == SUSPENDING_ON_PARENT_SUSPENSION.getStateCode();
  }

  public boolean isTerminated() {
    return currentState == TERMINATED.getStateCode();
  }

  public boolean isTerminating() {
    return currentState == TERMINATING_ON_TERMINATION.getStateCode()
        || currentState == TERMINATING_ON_PARENT_TERMINATION.getStateCode()
        || currentState == TERMINATING_ON_EXIT.getStateCode();
  }

  public boolean isFailed() {
    return currentState == FAILED.getStateCode();
  }

  public boolean isClosed() {
    return currentState == CLOSED.getStateCode();
  }

  // previous state /////////////////////////////////////////////

  public CaseExecutionState getPreviousState() {
    return CaseExecutionState.CASE_EXECUTION_STATES.get(getPrevious());
  }

  public int getPrevious() {
    return previousState;
  }

  public void setPrevious(int previous) {
    this.previousState = previous;
  }

  // state transition ///////////////////////////////////////////

  public void create() {
    create(null);
  }

  public void create(Map variables) {
    if(variables != null) {
      setVariables(variables);
    }

    performOperation(CASE_INSTANCE_CREATE);
  }

  public List createChildExecutions(List activities) {
    List children = new ArrayList();

    // first create new child case executions
    for (CmmnActivity currentActivity : activities) {
      CmmnExecution child = createCaseExecution(currentActivity);
      children.add(child);
    }

    return children;
  }


  public void triggerChildExecutionsLifecycle(List children) {
    // then notify create listener for each created
    // child case execution
    for (CmmnExecution child : children) {

      if (isActive()) {
        if (child.isNew()) {
          child.performOperation(CASE_EXECUTION_CREATE);
        }
      } else {
        // if this case execution is not active anymore,
        // then stop notifying create listener and executing
        // of each child case execution
        break;
      }
    }
  }

  protected abstract CmmnExecution createCaseExecution(CmmnActivity activity);

  protected abstract CmmnExecution newCaseExecution();

  public void enable() {
    performOperation(CASE_EXECUTION_ENABLE);
  }

  public void disable() {
    performOperation(CASE_EXECUTION_DISABLE);
  }

  public void reenable() {
    performOperation(CASE_EXECUTION_RE_ENABLE);
  }

  public void manualStart() {
    performOperation(CASE_EXECUTION_MANUAL_START);
  }

  public void start() {
    performOperation(CASE_EXECUTION_START);
  }

  public void complete() {
    performOperation(CASE_EXECUTION_COMPLETE);
  }

  public void manualComplete() {
    performOperation(CASE_EXECUTION_MANUAL_COMPLETE);
  }

  public void occur() {
    performOperation(CASE_EXECUTION_OCCUR);
  }

  public void terminate() {
    performOperation(CASE_EXECUTION_TERMINATING_ON_TERMINATION);
  }

  public void performTerminate() {
    performOperation(CASE_EXECUTION_TERMINATE);
  }

  public void parentTerminate() {
    performOperation(CASE_EXECUTION_TERMINATING_ON_PARENT_TERMINATION);
  }

  public void performParentTerminate() {
    performOperation(CASE_EXECUTION_PARENT_TERMINATE);
  }

  public void exit() {
    performOperation(CASE_EXECUTION_TERMINATING_ON_EXIT);
  }

  public void parentComplete() {
    performOperation(CmmnAtomicOperation.CASE_EXECUTION_PARENT_COMPLETE);
  }

  public void performExit() {
    performOperation(CASE_EXECUTION_EXIT);
  }

  public void suspend() {
    performOperation(CASE_EXECUTION_SUSPENDING_ON_SUSPENSION);
  }

  public void performSuspension() {
    performOperation(CASE_EXECUTION_SUSPEND);
  }

  public void parentSuspend() {
    performOperation(CASE_EXECUTION_SUSPENDING_ON_PARENT_SUSPENSION);
  }

  public void performParentSuspension() {
    performOperation(CASE_EXECUTION_PARENT_SUSPEND);
  }

  public void resume() {
    performOperation(CASE_EXECUTION_RESUME);
  }

  public void parentResume() {
    performOperation(CASE_EXECUTION_PARENT_RESUME);
  }

  public void reactivate() {
    performOperation(CASE_EXECUTION_RE_ACTIVATE);
  }

  public void close() {
    performOperation(CASE_INSTANCE_CLOSE);
  }

  // variable listeners
  public void dispatchEvent(VariableEvent variableEvent) {
    boolean invokeCustomListeners =
        Context
          .getProcessEngineConfiguration()
          .isInvokeCustomVariableListeners();

    Map>> listeners = getActivity()
        .getVariableListeners(variableEvent.getEventName(), invokeCustomListeners);

    // only attempt to invoke listeners if there are any (as this involves resolving the upwards execution hierarchy)
    if (!listeners.isEmpty()) {
      getCaseInstance().queueVariableEvent(variableEvent, invokeCustomListeners);
    }
  }

  protected void queueVariableEvent(VariableEvent variableEvent, boolean includeCustomerListeners) {

    Queue variableEventsQueue = getVariableEventQueue();

    variableEventsQueue.add(variableEvent);

    // if this is the first event added, trigger listener invocation
    if (variableEventsQueue.size() == 1) {
      invokeVariableListeners(includeCustomerListeners);
    }
  }

  protected void invokeVariableListeners(boolean includeCustomerListeners) {
    Queue variableEventsQueue = getVariableEventQueue();

    while (!variableEventsQueue.isEmpty()) {
      // do not remove the event yet, as otherwise new events will immediately be dispatched
      VariableEvent nextEvent = variableEventsQueue.peek();

      CmmnExecution sourceExecution = (CmmnExecution) nextEvent.getSourceScope();

      DelegateCaseVariableInstanceImpl delegateVariable =
          DelegateCaseVariableInstanceImpl.fromVariableInstance(nextEvent.getVariableInstance());
      delegateVariable.setEventName(nextEvent.getEventName());
      delegateVariable.setSourceExecution(sourceExecution);

      Map>> listenersByActivity =
          sourceExecution.getActivity().getVariableListeners(delegateVariable.getEventName(), includeCustomerListeners);

      CmmnExecution currentExecution = sourceExecution;
      while (currentExecution != null) {

        if (currentExecution.getActivityId() != null) {
          List> listeners = listenersByActivity.get(currentExecution.getActivityId());

          if (listeners != null) {
            delegateVariable.setScopeExecution(currentExecution);

            for (VariableListener listener : listeners) {
              try {
                CaseVariableListener caseVariableListener = (CaseVariableListener) listener;
                CaseVariableListenerInvocation invocation = new CaseVariableListenerInvocation(caseVariableListener, delegateVariable, currentExecution);
                Context.getProcessEngineConfiguration()
                  .getDelegateInterceptor()
                  .handleInvocation(invocation);
              } catch (Exception e) {
                throw LOG.invokeVariableListenerException(e);
              }
            }
          }
        }

        currentExecution = currentExecution.getParent();
      }

      // finally remove the event from the queue
      variableEventsQueue.remove();
    }
  }

  protected Queue getVariableEventQueue() {
    if (variableEventsQueue == null) {
      variableEventsQueue = new LinkedList();
    }

    return variableEventsQueue;
  }

  // toString() //////////////////////////////////////  ///////////

  public String toString() {
    if (isCaseInstanceExecution()) {
      return "CaseInstance["+getToStringIdentity()+"]";
    } else {
      return "CmmnExecution["+getToStringIdentity() + "]";
    }
  }

  protected String getToStringIdentity() {
    return id;
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy