Package org.camunda.bpm.engine.impl.cmmn.execution

Source Code of org.camunda.bpm.engine.impl.cmmn.execution.CmmnExecution

/* 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.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.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.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;

import org.camunda.bpm.engine.ProcessEngineException;
import org.camunda.bpm.engine.delegate.CaseVariableListener;
import org.camunda.bpm.engine.delegate.Expression;
import org.camunda.bpm.engine.delegate.TaskListener;
import org.camunda.bpm.engine.delegate.VariableListener;
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.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.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;

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

  private static final long serialVersionUID = 1L;

  protected transient CmmnCaseDefinition caseDefinition;

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

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

  /** the activity which is to be started next */
  protected transient CmmnActivity nextActivity;

  protected boolean required;

  protected int previousState;

  protected int currentState = NEW.getStateCode();

  protected List<String> satisfiedSentries;

  protected Queue<VariableEvent> variableEventsQueue;

  protected transient TaskEntity task;

  public CmmnExecution() {
  }

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

  public abstract List<? extends CmmnExecution> getCaseExecutions();

  protected abstract List<? extends CmmnExecution> 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 = TaskEntity.createAndInsert(this);

    task.setCaseExecution(this);
    setTask(task);

    taskDecorator.decorate(task, this);

    Context.getCommandContext()
      .getHistoricTaskInstanceManager()
      .createHistoricTask(task);

    // All properties set, now firing 'create' event
    task.fireEvent(TaskListener.EVENTNAME_CREATE);

    return task;
  }

  // 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<CmmnSentryDeclaration> 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<CmmnOnPartDeclaration> onPartDeclarations = sentryDeclaration.getOnParts();

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

      }
    }
  }

  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();

    CmmnExecution sourceCaseExecution = findCaseExecution(sourceActivityId);
    sentryPart.setSourceCaseExecution(sourceCaseExecution);

    // 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 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<String> affectedSentries = collectAffectedSentries(child, transition);

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

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

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

    // the following steps are a workaround, because setVariable()
    // does not check nor fire a sentry!!!

    // Step 5: get all not affected sentries to avoid that a
    // sentry will be checked twice;
    // notAffectedSentries = getSentries().keySet() / affectedSentries
    Map<String, List<CmmnSentryPart>> sentries = getSentries();
    List<String> notAffectedSentries = new ArrayList<String>();
    for (String sentryId : sentries.keySet()) {
      // but only those ones which has an ifPart defined
      if (!affectedSentries.contains(sentryId) && containsIfPart(sentryId)) {
        notAffectedSentries.add(sentryId);
      }
    }

    // Step 6: check each not affected sentry whether it is satisfied
    satisfiedSentries = getSatisfiedSentries(notAffectedSentries);

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

  protected List<String> collectAffectedSentries(CmmnExecution child, String transition) {
    List<? extends CmmnSentryPart> sentryParts = getCaseSentryParts();

    List<String> affectedSentries = new ArrayList<String>();

    for (CmmnSentryPart sentryPart : sentryParts) {

      // check the ids not the references itself to avoid a select!
      String sourceCaseExecutionId = sentryPart.getSourceCaseExecutionId();
      if (child.getId().equals(sourceCaseExecutionId)) {

        String standardEvent = sentryPart.getStandardEvent();

        if (transition.equals(standardEvent)) {

          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);
            }
          }
        }
      }
    }

    return affectedSentries;
  }

  protected void forceUpdateOnCaseSentryPart(List<String> sentryIds) {
    for (String sentryId : sentryIds) {
      List<? extends CmmnSentryPart> 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();
        }
      }
    }
  }

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

    if (sentryIds != null) {

      for (String sentryId : sentryIds) {

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

    return result;
  }

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

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

      // returns a copy of the list of child case executions!
      List<? extends CmmnExecution> children = getCaseExecutions();

      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 checkAndFireExitCriteria(List<String> satisfiedSentries) {
    if (!isNew() && !isCompleted() && !isTerminated()) {
      CmmnActivity activity = getActivity();
      ensureNotNull(PvmException.class, "Case execution '"+getId()+"': has no current activity.", "activity", activity);

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

        if (satisfiedSentries.contains(sentryDeclaration.getId())) {
          fireExitCriteria();
          break;
        }
      }
    }
  }

  protected void checkAndFireEntryCriteria(List<String> satisfiedSentries) {
    if (isAvailable()) {
      // 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<CmmnSentryDeclaration> entryCriteria = activity.getEntryCriteria();
      for (CmmnSentryDeclaration sentryDeclaration : entryCriteria) {

        if (satisfiedSentries.contains(sentryDeclaration.getId())) {
          fireEntryCriteria();
          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<? extends CmmnSentryPart> getCaseSentryParts();

  protected abstract List<? extends CmmnSentryPart> findSentry(String sentryId);

  protected abstract Map<String, List<CmmnSentryPart>> getSentries();

  public boolean isSentrySatisfied(String sentryId) {
    List<? extends CmmnSentryPart> sentryParts = findSentry(sentryId);

    // if part will be evaluated in the end
    CmmnSentryPart ifPart = null;

    for (CmmnSentryPart sentryPart : sentryParts) {

      if (PLAN_ITEM_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) {

      CmmnActivity activity = 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 containsIfPart(String sentryId) {
    List<? extends CmmnSentryPart> sentries = findSentry(sentryId);

    for (CmmnSentryPart part : sentries) {
      if (IF_PART.equals(part.getType())) {
        return true;
      }
    }

    return false;
  }

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

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

  // 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;
  }

  // next activity /////////////////////////////////////////////////////////////

  public CmmnActivity getNextActivity() {
    return nextActivity;
  }

  public void setNextActivity(CmmnActivity nextActivity) {
    this.nextActivity = nextActivity;
  }

  // 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<String, Object> variables) {
    if(variables != null) {
      setVariables(variables);
    }

    performOperation(CASE_INSTANCE_CREATE);
  }

  public List<CmmnExecution> createChildExecutions(List<CmmnActivity> activities) {
    List<CmmnExecution> children = new ArrayList<CmmnExecution>();

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

    return children;
  }


  public void triggerChildExecutionsLifecycle(List<CmmnExecution> 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 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<String, List<VariableListener<?>>> 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<VariableEvent> 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<VariableEvent> 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<String, List<VariableListener<?>>> listenersByActivity =
          sourceExecution.getActivity().getVariableListeners(delegateVariable.getEventName(), includeCustomerListeners);

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

        if (currentExecution.getActivityId() != null) {
          List<VariableListener<?>> 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 new ProcessEngineException("Variable listener invocation failed: "+e.getMessage(), e);
              }
            }
          }
        }

        currentExecution = currentExecution.getParent();
      }

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

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

    return variableEventsQueue;
  }

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

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

  protected String getToStringIdentity() {
    return id;
  }

}
TOP

Related Classes of org.camunda.bpm.engine.impl.cmmn.execution.CmmnExecution

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.