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

org.activiti.engine.impl.bpmn.helper.ErrorPropagation Maven / Gradle / Ivy

The newest version!
/* 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.helper;

import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.*;
import org.activiti.engine.ActivitiException;
import org.activiti.engine.delegate.BpmnError;
import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.event.ActivitiEventType;
import org.activiti.engine.delegate.event.impl.ActivitiEventBuilder;
import org.activiti.engine.impl.context.Context;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.persistence.entity.ExecutionEntityManager;
import org.activiti.engine.impl.util.CollectionUtil;
import org.activiti.engine.impl.util.ProcessDefinitionUtil;
import org.activiti.engine.impl.util.ReflectUtil;
import org.apache.commons.lang3.StringUtils;

import java.util.*;

/**
 * This class is responsible for finding and executing error handlers for BPMN Errors.
 *
 * Possible error handlers include Error Intermediate Events and Error Event Sub-Processes.
 *


 */
public class ErrorPropagation {

  public static void propagateError(BpmnError error, DelegateExecution execution) {
    propagateError(error.getErrorCode(), execution);
  }

  public static void propagateError(String errorCode, DelegateExecution execution) {
    Map> eventMap = findCatchingEventsForProcess(execution.getProcessDefinitionId(), errorCode);
    if (eventMap.size() > 0) {
      executeCatch(eventMap, execution, errorCode);
    } else if (!execution.getProcessInstanceId().equals(execution.getRootProcessInstanceId())) { // Call activity

      ExecutionEntityManager executionEntityManager = Context.getCommandContext().getExecutionEntityManager();
      ExecutionEntity processInstanceExecution = executionEntityManager.findById(execution.getProcessInstanceId());
      if (processInstanceExecution != null) {

        ExecutionEntity parentExecution = processInstanceExecution.getSuperExecution();

        Set toDeleteProcessInstanceIds = new HashSet();
        toDeleteProcessInstanceIds.add(execution.getProcessInstanceId());

        while (parentExecution != null && eventMap.size() == 0) {
          eventMap = findCatchingEventsForProcess(parentExecution.getProcessDefinitionId(), errorCode);
          if (eventMap.size() > 0) {

            for (String processInstanceId : toDeleteProcessInstanceIds) {
              ExecutionEntity processInstanceEntity = executionEntityManager.findById(processInstanceId);

              // Delete
              executionEntityManager.deleteProcessInstanceExecutionEntity(processInstanceEntity.getId(),
                  execution.getCurrentFlowElement() != null ? execution.getCurrentFlowElement().getId() : null,
                  "ERROR_EVENT " + errorCode,
                  false, false);

              // Event
              if (Context.getProcessEngineConfiguration() != null && Context.getProcessEngineConfiguration().getEventDispatcher().isEnabled()) {
                Context.getProcessEngineConfiguration().getEventDispatcher()
                    .dispatchEvent(ActivitiEventBuilder.createEntityEvent(ActivitiEventType.PROCESS_COMPLETED_WITH_ERROR_END_EVENT, processInstanceEntity));
              }
            }
            executeCatch(eventMap, parentExecution, errorCode);

          } else {
            toDeleteProcessInstanceIds.add(parentExecution.getProcessInstanceId());
            ExecutionEntity superExecution = parentExecution.getSuperExecution();
            if (superExecution != null) {
              parentExecution = superExecution;
            } else if (!parentExecution.getId().equals(parentExecution.getRootProcessInstanceId())) { // stop at the root
                parentExecution = parentExecution.getProcessInstance();
            } else {
              parentExecution = null;
            }
          }
        }

      }

    }

    if (eventMap.size() == 0) {
      throw new BpmnError(errorCode, "No catching boundary event found for error with errorCode '" + errorCode + "', neither in same process nor in parent process");
    }
  }

  protected static void executeCatch(Map> eventMap, DelegateExecution delegateExecution, String errorId) {
    Event matchingEvent = null;
    ExecutionEntity currentExecution = (ExecutionEntity) delegateExecution;
    ExecutionEntity parentExecution = null;

    if (eventMap.containsKey(currentExecution.getActivityId())) {
      matchingEvent = eventMap.get(currentExecution.getActivityId()).get(0);

      // Check for multi instance
      if (currentExecution.getParentId() != null && currentExecution.getParent().isMultiInstanceRoot()) {
        parentExecution = currentExecution.getParent();
      } else {
        parentExecution = currentExecution;
      }

    } else {
      parentExecution = currentExecution.getParent();

      // Traverse parents until one is found that is a scope and matches the activity the boundary event is defined on
      while (matchingEvent == null && parentExecution != null) {
        FlowElementsContainer currentContainer = null;
        if (parentExecution.getCurrentFlowElement() instanceof FlowElementsContainer) {
          currentContainer = (FlowElementsContainer) parentExecution.getCurrentFlowElement();
        } else if (parentExecution.getId().equals(parentExecution.getProcessInstanceId())) {
          currentContainer = ProcessDefinitionUtil.getProcess(parentExecution.getProcessDefinitionId());
        }

        for (String refId : eventMap.keySet()) {
          List events = eventMap.get(refId);
          if (CollectionUtil.isNotEmpty(events) && events.get(0) instanceof StartEvent) {
            if (currentContainer.getFlowElement(refId) != null) {
              matchingEvent = events.get(0);
            }
          }
        }

        if (matchingEvent == null) {
          if (eventMap.containsKey(parentExecution.getActivityId())) {
            matchingEvent = eventMap.get(parentExecution.getActivityId()).get(0);

            // Check for multi instance
            if (parentExecution.getParentId() != null && parentExecution.getParent().isMultiInstanceRoot()) {
              parentExecution = parentExecution.getParent();
            }

          } else if (StringUtils.isNotEmpty(parentExecution.getParentId())) {
            parentExecution = parentExecution.getParent();
          } else {
            parentExecution = null;
          }
        }
      }
    }

    if (matchingEvent != null && parentExecution != null) {
      executeEventHandler(matchingEvent, parentExecution, currentExecution, errorId);
    } else {
      throw new ActivitiException("No matching parent execution for error code " + errorId + " found");
    }
  }

  protected static void executeEventHandler(Event event, ExecutionEntity parentExecution, ExecutionEntity currentExecution, String errorId) {
    if (Context.getProcessEngineConfiguration() != null && Context.getProcessEngineConfiguration().getEventDispatcher().isEnabled()) {
      BpmnModel bpmnModel = ProcessDefinitionUtil.getBpmnModel(parentExecution.getProcessDefinitionId());
      if (bpmnModel != null) {

        String errorCode = bpmnModel.getErrors().get(errorId);
        if (errorCode == null) {
          errorCode = errorId;
        }

        Context.getProcessEngineConfiguration().getEventDispatcher().dispatchEvent(
          ActivitiEventBuilder.createErrorEvent(ActivitiEventType.ACTIVITY_ERROR_RECEIVED, event.getId(), errorId, errorCode, parentExecution.getId(),
              parentExecution.getProcessInstanceId(), parentExecution.getProcessDefinitionId()));
      }
    }

    if (event instanceof StartEvent) {
      ExecutionEntityManager executionEntityManager = Context.getCommandContext().getExecutionEntityManager();

      if (!currentExecution.getParentId().equals(parentExecution.getId())) {
        Context.getAgenda().planDestroyScopeOperation(currentExecution);
      } else {
        executionEntityManager.deleteExecutionAndRelatedData(currentExecution, null, false);
      }

      ExecutionEntity eventSubProcessExecution = executionEntityManager.createChildExecution(parentExecution);
      eventSubProcessExecution.setCurrentFlowElement(event);
      Context.getAgenda().planContinueProcessOperation(eventSubProcessExecution);

    } else {
      ExecutionEntity boundaryExecution = null;
      List childExecutions = parentExecution.getExecutions();
      for (ExecutionEntity childExecution : childExecutions) {
        if (childExecution.getActivityId().equals(event.getId())) {
          boundaryExecution = childExecution;
        }
      }

      Context.getAgenda().planTriggerExecutionOperation(boundaryExecution);
    }
  }

  protected static Map> findCatchingEventsForProcess(String processDefinitionId, String errorCode) {
    Map> eventMap = new HashMap>();
    Process process = ProcessDefinitionUtil.getProcess(processDefinitionId);
    BpmnModel bpmnModel = ProcessDefinitionUtil.getBpmnModel(processDefinitionId);

    String compareErrorCode = retrieveErrorCode(bpmnModel, errorCode);

    List subProcesses = process.findFlowElementsOfType(EventSubProcess.class, true);
    for (EventSubProcess eventSubProcess : subProcesses) {
      for (FlowElement flowElement : eventSubProcess.getFlowElements()) {
        if (flowElement instanceof StartEvent) {
          StartEvent startEvent = (StartEvent) flowElement;
          if (CollectionUtil.isNotEmpty(startEvent.getEventDefinitions()) && startEvent.getEventDefinitions().get(0) instanceof ErrorEventDefinition) {
            ErrorEventDefinition errorEventDef = (ErrorEventDefinition) startEvent.getEventDefinitions().get(0);
            String eventErrorCode = retrieveErrorCode(bpmnModel, errorEventDef.getErrorCode());

            if (eventErrorCode == null || compareErrorCode == null || eventErrorCode.equals(compareErrorCode)) {
              List startEvents = new ArrayList();
              startEvents.add(startEvent);
              eventMap.put(eventSubProcess.getId(), startEvents);
            }
          }
        }
      }
    }

    List boundaryEvents = process.findFlowElementsOfType(BoundaryEvent.class, true);
    for (BoundaryEvent boundaryEvent : boundaryEvents) {
      if (boundaryEvent.getAttachedToRefId() != null && CollectionUtil.isNotEmpty(boundaryEvent.getEventDefinitions()) && boundaryEvent.getEventDefinitions().get(0) instanceof ErrorEventDefinition) {

        ErrorEventDefinition errorEventDef = (ErrorEventDefinition) boundaryEvent.getEventDefinitions().get(0);
        String eventErrorCode = retrieveErrorCode(bpmnModel, errorEventDef.getErrorCode());

        if (eventErrorCode == null || compareErrorCode == null || eventErrorCode.equals(compareErrorCode)) {
          List elementBoundaryEvents = null;
          if (!eventMap.containsKey(boundaryEvent.getAttachedToRefId())) {
            elementBoundaryEvents = new ArrayList();
            eventMap.put(boundaryEvent.getAttachedToRefId(), elementBoundaryEvents);
          } else {
            elementBoundaryEvents = eventMap.get(boundaryEvent.getAttachedToRefId());
          }
          elementBoundaryEvents.add(boundaryEvent);
        }
      }
    }
    return eventMap;
  }

  public static boolean mapException(Exception e, ExecutionEntity execution, List exceptionMap) {
    String errorCode = findMatchingExceptionMapping(e, exceptionMap);
    if (errorCode != null) {
      propagateError(errorCode, execution);
      return true;
    } else {
      ExecutionEntity callActivityExecution = null;
      ExecutionEntity parentExecution = execution.getParent();
      while (parentExecution != null && callActivityExecution == null) {
        if (parentExecution.getId().equals(parentExecution.getProcessInstanceId())) {
          if (parentExecution.getSuperExecution() != null) {
            callActivityExecution = parentExecution.getSuperExecution();
          } else {
            parentExecution = null;
          }
        } else {
          parentExecution = parentExecution.getParent();
        }
      }

      if (callActivityExecution != null) {
        CallActivity callActivity = (CallActivity) callActivityExecution.getCurrentFlowElement();
        if (CollectionUtil.isNotEmpty(callActivity.getMapExceptions())) {
          errorCode = findMatchingExceptionMapping(e, callActivity.getMapExceptions());
          if (errorCode != null) {
            propagateError(errorCode, callActivityExecution);
            return true;
          }
        }
      }

      return false;
    }
  }

  protected static String findMatchingExceptionMapping(Exception e, List exceptionMap) {
    String defaultExceptionMapping = null;

    for (MapExceptionEntry me : exceptionMap) {
      String exceptionClass = me.getClassName();
      String errorCode = me.getErrorCode();

      // save the first mapping with no exception class as default map
      if (StringUtils.isNotEmpty(errorCode) && StringUtils.isEmpty(exceptionClass) && defaultExceptionMapping == null) {
        defaultExceptionMapping = errorCode;
        continue;
      }

      // ignore if error code or class are not defined
      if (StringUtils.isEmpty(errorCode) || StringUtils.isEmpty(exceptionClass)) {
        continue;
      }

      if (e.getClass().getName().equals(exceptionClass)) {
        return errorCode;
      }
      if (me.isAndChildren()) {
        Class exceptionClassClass = ReflectUtil.loadClass(exceptionClass);
        if (exceptionClassClass.isAssignableFrom(e.getClass())) {
          return errorCode;
        }
      }
    }

    return defaultExceptionMapping;
  }

  protected static String retrieveErrorCode(BpmnModel bpmnModel, String errorCode) {
    String finalErrorCode = null;
    if (errorCode != null && bpmnModel.containsErrorRef(errorCode)) {
      finalErrorCode = bpmnModel.getErrors().get(errorCode);
    } else {
      finalErrorCode = errorCode;
    }
    return finalErrorCode;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy