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

org.bonitasoft.engine.execution.event.ErrorEventHandlerStrategy Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (C) 2019 Bonitasoft S.A.
 * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble
 * This library is free software; you can redistribute it and/or modify it under the terms
 * of the GNU Lesser General Public License as published by the Free Software Foundation
 * version 2.1 of the License.
 * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Lesser General Public License for more details.
 * You should have received a copy of the GNU Lesser General Public License along with this
 * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301, USA.
 **/
package org.bonitasoft.engine.execution.event;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import org.bonitasoft.engine.builder.BuilderFactory;
import org.bonitasoft.engine.commons.exceptions.SBonitaException;
import org.bonitasoft.engine.core.process.definition.ProcessDefinitionService;
import org.bonitasoft.engine.core.process.definition.model.SActivityDefinition;
import org.bonitasoft.engine.core.process.definition.model.SCallActivityDefinition;
import org.bonitasoft.engine.core.process.definition.model.SFlowElementContainerDefinition;
import org.bonitasoft.engine.core.process.definition.model.SFlowNodeType;
import org.bonitasoft.engine.core.process.definition.model.SProcessDefinition;
import org.bonitasoft.engine.core.process.definition.model.SSubProcessDefinition;
import org.bonitasoft.engine.core.process.definition.model.event.SBoundaryEventDefinition;
import org.bonitasoft.engine.core.process.definition.model.event.SEndEventDefinition;
import org.bonitasoft.engine.core.process.definition.model.event.SEventDefinition;
import org.bonitasoft.engine.core.process.definition.model.event.SStartEventDefinition;
import org.bonitasoft.engine.core.process.definition.model.event.trigger.SCatchErrorEventTriggerDefinition;
import org.bonitasoft.engine.core.process.definition.model.event.trigger.SErrorEventTriggerDefinition;
import org.bonitasoft.engine.core.process.definition.model.event.trigger.SEventTriggerDefinition;
import org.bonitasoft.engine.core.process.instance.api.FlowNodeInstanceService;
import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;
import org.bonitasoft.engine.core.process.instance.api.event.EventInstanceService;
import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventCreationException;
import org.bonitasoft.engine.core.process.instance.api.exceptions.event.trigger.SWaitingEventReadException;
import org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance;
import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;
import org.bonitasoft.engine.core.process.instance.model.SProcessInstance;
import org.bonitasoft.engine.core.process.instance.model.SStateCategory;
import org.bonitasoft.engine.core.process.instance.model.builder.SCallActivityInstanceBuilderFactory;
import org.bonitasoft.engine.core.process.instance.model.builder.SFlowNodeInstanceBuilderFactory;
import org.bonitasoft.engine.core.process.instance.model.builder.event.SBoundaryEventInstanceBuilderFactory;
import org.bonitasoft.engine.core.process.instance.model.builder.event.SEventInstanceBuilderFactory;
import org.bonitasoft.engine.core.process.instance.model.builder.event.SIntermediateCatchEventInstanceBuilderFactory;
import org.bonitasoft.engine.core.process.instance.model.builder.event.SIntermediateThrowEventInstanceBuilderFactory;
import org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingErrorEventBuilder;
import org.bonitasoft.engine.core.process.instance.model.builder.event.handling.SWaitingErrorEventBuilderFactory;
import org.bonitasoft.engine.core.process.instance.model.event.SBoundaryEventInstance;
import org.bonitasoft.engine.core.process.instance.model.event.SCatchEventInstance;
import org.bonitasoft.engine.core.process.instance.model.event.SThrowEventInstance;
import org.bonitasoft.engine.core.process.instance.model.event.handling.SBPMEventType;
import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingErrorEvent;
import org.bonitasoft.engine.core.process.instance.model.event.handling.SWaitingEvent;
import org.bonitasoft.engine.execution.ProcessInstanceInterruptor;
import org.bonitasoft.engine.persistence.FilterOption;
import org.bonitasoft.engine.persistence.OrderByOption;
import org.bonitasoft.engine.persistence.OrderByType;
import org.bonitasoft.engine.persistence.QueryOptions;
import org.bonitasoft.engine.persistence.SBonitaReadException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author Elias Ricken de Medeiros
 * @author Celine Souchet
 * @author Matthieu Chaffotte
 */
public class ErrorEventHandlerStrategy extends CoupleEventHandlerStrategy {

    private static final OperationsWithContext EMPTY = new OperationsWithContext(null, null);

    private final ProcessInstanceService processInstanceService;

    private final FlowNodeInstanceService flowNodeInstanceService;

    private final ProcessDefinitionService processDefinitionService;

    private final EventsHandler eventsHandler;

    private static final Logger LOGGER = LoggerFactory.getLogger(ErrorEventHandlerStrategy.class);

    private ProcessInstanceInterruptor processInstanceInterruptor;

    public ErrorEventHandlerStrategy(final EventInstanceService eventInstanceService,
            final ProcessInstanceService processInstanceService,
            final FlowNodeInstanceService flowNodeInstanceService,
            final ProcessDefinitionService processDefinitionService, final EventsHandler eventsHandler,
            ProcessInstanceInterruptor processInstanceInterruptor) {
        super(eventInstanceService);
        this.processInstanceService = processInstanceService;
        this.flowNodeInstanceService = flowNodeInstanceService;
        this.processDefinitionService = processDefinitionService;
        this.eventsHandler = eventsHandler;
        this.processInstanceInterruptor = processInstanceInterruptor;
    }

    @Override
    public void handleThrowEvent(final SProcessDefinition processDefinition, final SEventDefinition eventDefinition,
            final SThrowEventInstance eventInstance,
            final SEventTriggerDefinition sEventTriggerDefinition) throws SBonitaException {
        LOGGER.debug("Error event is thrown, error code = {} process instance = {}",
                ((SErrorEventTriggerDefinition) sEventTriggerDefinition).getErrorCode(),
                eventInstance.getRootContainerId());
        processInstanceService.setInterruptingEventId(eventInstance.getParentProcessInstanceId(),
                eventInstance.getId());
        processInstanceInterruptor.interruptChildrenOfProcessInstance(eventInstance.getParentContainerId(),
                SStateCategory.ABORTING, eventInstance.getId());
    }

    @Override
    public boolean handlePostThrowEvent(final SProcessDefinition processDefinition,
            final SEndEventDefinition sEventDefinition,
            final SThrowEventInstance sThrowEventInstance, final SEventTriggerDefinition sEventTriggerDefinition,
            final SFlowNodeInstance sFlowNodeInstance)
            throws SBonitaException {
        boolean hasActionToExecute = false;
        final SFlowNodeInstanceBuilderFactory flowNodeKeyProvider = BuilderFactory
                .get(SIntermediateThrowEventInstanceBuilderFactory.class);
        final long parentProcessInstanceId = sThrowEventInstance
                .getLogicalGroup(flowNodeKeyProvider.getParentProcessInstanceIndex());
        final SErrorEventTriggerDefinition errorTrigger = (SErrorEventTriggerDefinition) sEventTriggerDefinition;
        final SWaitingErrorEvent waitingErrorEvent = getWaitingErrorEvent(processDefinition.getProcessContainer(),
                parentProcessInstanceId, errorTrigger,
                sThrowEventInstance, sFlowNodeInstance);

        if (waitingErrorEvent != null) {
            eventsHandler.triggerCatchEvent(waitingErrorEvent, sThrowEventInstance.getId());
            hasActionToExecute = true;
        } else {
            LOGGER.warn(
                    "No catch error event was defined to handle the error code {} defined in the process [name: {}, version: {}], throw event: {}. This throw error event will act as a Terminate Event.",
                    errorTrigger.getErrorCode(), processDefinition.getName(), processDefinition.getVersion(),
                    sEventDefinition == null ? null : sEventDefinition.getName());
        }
        return hasActionToExecute;
    }

    private SWaitingErrorEvent getWaitingErrorEvent(final SFlowElementContainerDefinition container,
            final long parentProcessInstanceId,
            final SErrorEventTriggerDefinition errorTrigger, final SThrowEventInstance eventInstance,
            final SFlowNodeInstance flowNodeInstance)
            throws SBonitaException {
        final SProcessInstance processInstance = processInstanceService.getProcessInstance(parentProcessInstanceId);
        final String errorCode = errorTrigger.getErrorCode();
        SWaitingErrorEvent waitingErrorEvent;

        // check on direct boundary
        waitingErrorEvent = getWaitingErrorEventFromBoundary(eventInstance, errorCode, flowNodeInstance);
        // check on event sub-process
        if (waitingErrorEvent == null) {
            waitingErrorEvent = getWaitingErrorEventSubProcess(container, parentProcessInstanceId, errorCode);
        }
        // check on call activities (recursive)
        if (waitingErrorEvent == null && processInstance.getCallerId() != -1
                && SFlowNodeType.CALL_ACTIVITY.equals(processInstance.getCallerType())) {
            // check on call activities
            waitingErrorEvent = getWaitingErrorEventFromCallActivity(errorTrigger, processInstance, eventInstance,
                    errorCode,
                    flowNodeInstance);
        }
        return waitingErrorEvent;
    }

    protected SWaitingErrorEvent getWaitingErrorEventFromBoundary(final SThrowEventInstance eventInstance,
            final String errorCode, final SFlowNodeInstance flowNodeInstance) throws SBonitaException {
        final SFlowNodeInstanceBuilderFactory flowNodeKeyProvider = BuilderFactory
                .get(SBoundaryEventInstanceBuilderFactory.class);
        // get the parent activity of the boundary
        final long logicalGroup = eventInstance.getLogicalGroup(flowNodeKeyProvider.getParentActivityInstanceIndex());
        if (logicalGroup <= 0) {
            // not in an activity = no boundary
            return null;
        }
        final long processDefinitionId = flowNodeInstance
                .getLogicalGroup(flowNodeKeyProvider.getProcessDefinitionIndex());
        final SProcessDefinition processDefinition = processDefinitionService.getProcessDefinition(processDefinitionId);
        final SActivityDefinition flowNode = (SActivityDefinition) processDefinition.getProcessContainer().getFlowNode(
                flowNodeInstance.getFlowNodeDefinitionId());
        final List boundaryEventDefinitions = flowNode.getBoundaryEventDefinitions();
        SWaitingErrorEvent waitingErrorEvent;
        if (flowNode.getLoopCharacteristics() == null) {
            waitingErrorEvent = getWaitingErrorEventFromBoundary(errorCode, flowNodeInstance, boundaryEventDefinitions);
        } else {
            final long multipleInstanceActivityId = flowNodeInstance
                    .getLogicalGroup(flowNodeKeyProvider.getParentActivityInstanceIndex());
            final SFlowNodeInstance miActivityInstance = flowNodeInstanceService
                    .getFlowNodeInstance(multipleInstanceActivityId);
            waitingErrorEvent = getWaitingErrorEventFromBoundary(errorCode, miActivityInstance,
                    boundaryEventDefinitions);
        }
        return waitingErrorEvent;
    }

    private SWaitingErrorEvent getWaitingErrorEventFromCallActivity(final SErrorEventTriggerDefinition errorTrigger,
            final SProcessInstance processInstance, final SThrowEventInstance eventInstance,
            final String errorCode, final SFlowNodeInstance flowNodeInstance) throws SBonitaException {
        final SFlowNodeInstanceBuilderFactory flowNodeKeyProvider = BuilderFactory
                .get(SCallActivityInstanceBuilderFactory.class);
        final SCallActivityInstance callActivityInstance = (SCallActivityInstance) flowNodeInstanceService
                .getFlowNodeInstance(processInstance.getCallerId());
        final long processDefinitionId = callActivityInstance
                .getLogicalGroup(flowNodeKeyProvider.getProcessDefinitionIndex());
        final SProcessDefinition callActivityContainer = processDefinitionService
                .getProcessDefinition(processDefinitionId);
        final SCallActivityDefinition callActivityDef = (SCallActivityDefinition) callActivityContainer
                .getProcessContainer().getFlowNode(
                        callActivityInstance.getFlowNodeDefinitionId());
        final List boundaryEventDefinitions = callActivityDef.getBoundaryEventDefinitions();
        SWaitingErrorEvent waitingErrorEvent;
        if (callActivityDef.getLoopCharacteristics() != null) {
            final long multipleInstanceActivityId = callActivityInstance
                    .getLogicalGroup(flowNodeKeyProvider.getParentActivityInstanceIndex());
            final SFlowNodeInstance miActivityInstance = flowNodeInstanceService
                    .getFlowNodeInstance(multipleInstanceActivityId);
            waitingErrorEvent = getWaitingErrorEventFromBoundary(errorCode, miActivityInstance,
                    boundaryEventDefinitions);
        } else {
            waitingErrorEvent = getWaitingErrorEventFromBoundary(errorCode, callActivityInstance,
                    boundaryEventDefinitions);
        }
        if (waitingErrorEvent == null) {
            final long callActivityParentProcInstId = callActivityInstance
                    .getLogicalGroup(flowNodeKeyProvider.getParentProcessInstanceIndex());
            waitingErrorEvent = getWaitingErrorEvent(callActivityContainer.getProcessContainer(),
                    callActivityParentProcInstId, errorTrigger, eventInstance,
                    flowNodeInstance);
        }
        return waitingErrorEvent;
    }

    protected SWaitingErrorEvent getWaitingErrorEventFromBoundary(final String errorCode,
            final SFlowNodeInstance flowNodeInstance,
            final List boundaryEventDefinitions) throws SWaitingEventReadException {
        boolean canHandleError;
        String catchingErrorCode = errorCode;
        canHandleError = containsHandler(boundaryEventDefinitions, catchingErrorCode);
        if (!canHandleError) {
            catchingErrorCode = null; // catch all errors
            canHandleError = containsHandler(boundaryEventDefinitions, catchingErrorCode); // check for a handler that is able to catch all error codes
        }
        if (canHandleError) {
            return getEventInstanceService().getBoundaryWaitingErrorEvent(flowNodeInstance.getId(), catchingErrorCode);
        }
        return null;
    }

    private SWaitingErrorEvent getWaitingErrorEventSubProcess(final SFlowElementContainerDefinition container,
            final long parentProcessInstanceId,
            final String errorCode) throws SBonitaReadException, SBPMEventHandlerException {
        String catchingErrorCode = errorCode;
        boolean canHandleError = hasEventSubProcessCatchingError(container, catchingErrorCode);
        if (!canHandleError) {
            // if there is no event sub-process catching that particular error,  we search for an event sub process that catch all kind of error
            catchingErrorCode = null;
            canHandleError = hasEventSubProcessCatchingError(container, catchingErrorCode);
        }
        SWaitingErrorEvent waitingErrorEvent = null;
        if (canHandleError) {
            final SWaitingErrorEventBuilderFactory waitingErrorEventKeyProvider = BuilderFactory
                    .get(SWaitingErrorEventBuilderFactory.class);
            final OrderByOption orderByOption = new OrderByOption(SWaitingEvent.class,
                    waitingErrorEventKeyProvider.getFlowNodeNameKey(), OrderByType.ASC);

            final List filters = new ArrayList<>(3);
            filters.add(new FilterOption(SWaitingErrorEvent.class, waitingErrorEventKeyProvider.getErrorCodeKey(),
                    catchingErrorCode));
            filters.add(new FilterOption(SWaitingErrorEvent.class, waitingErrorEventKeyProvider.getEventTypeKey(),
                    SBPMEventType.EVENT_SUB_PROCESS.name()));
            filters.add(new FilterOption(SWaitingErrorEvent.class,
                    waitingErrorEventKeyProvider.getParentProcessInstanceIdKey(), parentProcessInstanceId));
            final QueryOptions queryOptions = new QueryOptions(0, 2, Collections.singletonList(orderByOption), filters,
                    null);
            final List waitingEvents = getEventInstanceService()
                    .searchWaitingEvents(SWaitingErrorEvent.class, queryOptions);
            if (waitingEvents.size() != 1) {
                final StringBuilder stb = new StringBuilder();
                stb.append("One and only one error start event sub-process was expected for the process instance ");
                stb.append(parentProcessInstanceId);
                stb.append(" and error code ");
                stb.append(catchingErrorCode);
                stb.append(", but ");
                stb.append(waitingEvents.size());
                stb.append(" was found.");
                throw new SBPMEventHandlerException(stb.toString());
            }
            waitingErrorEvent = waitingEvents.get(0);
        }
        return waitingErrorEvent;
    }

    private boolean containsHandler(final List boundaryEventDefinitions,
            final String errorCode) {
        boolean found = false;
        final Iterator iterator = boundaryEventDefinitions.iterator();
        while (iterator.hasNext() && !found) {
            final SBoundaryEventDefinition boundaryEventDefinition = iterator.next();
            final SCatchErrorEventTriggerDefinition currentErrorTrigger = boundaryEventDefinition
                    .getErrorEventTriggerDefinition(errorCode);
            if (currentErrorTrigger != null) {
                found = true;
            }
        }
        return found;
    }

    private boolean hasEventSubProcessCatchingError(final SFlowElementContainerDefinition container,
            final String errorCode) {
        boolean found = false;
        final Iterator iterator = container.getActivities().iterator();
        while (iterator.hasNext() && !found) {
            final SActivityDefinition activity = iterator.next();
            if (SFlowNodeType.SUB_PROCESS.equals(activity.getType())
                    && ((SSubProcessDefinition) activity).isTriggeredByEvent()) {
                final SSubProcessDefinition eventSubProcess = (SSubProcessDefinition) activity;
                final SStartEventDefinition startEventDefinition = eventSubProcess.getSubProcessContainer()
                        .getStartEvents().get(0);
                if (startEventDefinition.getErrorEventTriggerDefinition(errorCode) != null) {
                    found = true;
                }
            }

        }
        return found;
    }

    @Override
    public void handleCatchEvent(final SProcessDefinition processDefinition, final SEventDefinition eventDefinition,
            final SCatchEventInstance eventInstance,
            final SEventTriggerDefinition sEventTriggerDefinition) throws SBonitaException {
        final SWaitingErrorEventBuilderFactory builderFact = BuilderFactory.get(SWaitingErrorEventBuilderFactory.class);
        final SErrorEventTriggerDefinition errorEventTriggerDefinition = (SErrorEventTriggerDefinition) sEventTriggerDefinition;
        final SEventInstanceBuilderFactory eventInstanceKeyProvider = BuilderFactory
                .get(SIntermediateCatchEventInstanceBuilderFactory.class);
        switch (eventDefinition.getType()) {
            case BOUNDARY_EVENT:
                final SBoundaryEventInstance boundary = (SBoundaryEventInstance) eventInstance;
                final long rootProcessInstanceId = eventInstance
                        .getLogicalGroup(eventInstanceKeyProvider.getRootProcessInstanceIndex());
                final long parentProcessInstanceId = eventInstance
                        .getLogicalGroup(eventInstanceKeyProvider.getParentProcessInstanceIndex());
                final SWaitingErrorEventBuilder builder = builderFact.createNewWaitingErrorBoundaryEventInstance(
                        processDefinition.getId(),
                        rootProcessInstanceId, parentProcessInstanceId, eventInstance.getId(),
                        errorEventTriggerDefinition.getErrorCode(),
                        processDefinition.getName(), eventInstance.getFlowNodeDefinitionId(), eventInstance.getName(),
                        boundary.getActivityInstanceId());
                final SWaitingErrorEvent errorEvent = builder.done();
                getEventInstanceService().createWaitingEvent(errorEvent);
                break;
            case INTERMEDIATE_CATCH_EVENT:
            case START_EVENT:
                throw new SWaitingEventCreationException(
                        "Catch error event cannot be put in " + eventDefinition.getType()
                                + ". They must be used as boundary events or start event subprocess.");
            default:
                throw new SWaitingEventCreationException(eventDefinition.getType() + " is not a catch event.");
        }
    }

    @Override
    public OperationsWithContext getOperations(final SWaitingEvent waitingEvent, final Long triggeringElementID) {
        return EMPTY;
    }

    @Override
    public void handleEventSubProcess(final SProcessDefinition processDefinition,
            final SEventDefinition eventDefinition,
            final SEventTriggerDefinition sEventTriggerDefinition, final long subProcessId,
            final SProcessInstance parentProcessInstance)
            throws SBonitaException {
        final SWaitingErrorEventBuilderFactory builderFact = BuilderFactory.get(SWaitingErrorEventBuilderFactory.class);
        final SErrorEventTriggerDefinition trigger = (SErrorEventTriggerDefinition) sEventTriggerDefinition;
        final SWaitingErrorEventBuilder builder = builderFact.createNewWaitingErrorEventSubProcInstance(
                processDefinition.getId(),
                parentProcessInstance.getId(), parentProcessInstance.getRootProcessInstanceId(), trigger.getErrorCode(),
                processDefinition.getName(),
                eventDefinition.getId(), eventDefinition.getName(), subProcessId);

        final SWaitingErrorEvent event = builder.done();
        getEventInstanceService().createWaitingEvent(event);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy