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

org.bonitasoft.engine.execution.archive.BPMArchiverService Maven / Gradle / Ivy

There is a newer version: 10.2.0
Show 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.archive;

import static org.bonitasoft.engine.classloader.ClassLoaderIdentifier.identifier;

import java.util.List;

import org.bonitasoft.engine.SArchivingException;
import org.bonitasoft.engine.archive.ArchiveInsertRecord;
import org.bonitasoft.engine.archive.ArchiveService;
import org.bonitasoft.engine.builder.BuilderFactory;
import org.bonitasoft.engine.classloader.ClassLoaderService;
import org.bonitasoft.engine.classloader.SClassLoaderException;
import org.bonitasoft.engine.commons.exceptions.SBonitaException;
import org.bonitasoft.engine.core.connector.ConnectorInstanceService;
import org.bonitasoft.engine.core.contract.data.ContractDataService;
import org.bonitasoft.engine.core.document.api.DocumentService;
import org.bonitasoft.engine.core.document.model.SMappedDocument;
import org.bonitasoft.engine.core.process.comment.api.SCommentService;
import org.bonitasoft.engine.core.process.comment.model.SComment;
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.SProcessDefinition;
import org.bonitasoft.engine.core.process.instance.api.ActivityInstanceService;
import org.bonitasoft.engine.core.process.instance.api.ProcessInstanceService;
import org.bonitasoft.engine.core.process.instance.api.RefBusinessDataService;
import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeNotFoundException;
import org.bonitasoft.engine.core.process.instance.api.exceptions.SFlowNodeReadException;
import org.bonitasoft.engine.core.process.instance.api.exceptions.SProcessInstanceModificationException;
import org.bonitasoft.engine.core.process.instance.model.SActivityInstance;
import org.bonitasoft.engine.core.process.instance.model.SAutomaticTaskInstance;
import org.bonitasoft.engine.core.process.instance.model.SCallActivityInstance;
import org.bonitasoft.engine.core.process.instance.model.SConnectorInstance;
import org.bonitasoft.engine.core.process.instance.model.SFlowNodeInstance;
import org.bonitasoft.engine.core.process.instance.model.SGatewayInstance;
import org.bonitasoft.engine.core.process.instance.model.SLoopActivityInstance;
import org.bonitasoft.engine.core.process.instance.model.SManualTaskInstance;
import org.bonitasoft.engine.core.process.instance.model.SMultiInstanceActivityInstance;
import org.bonitasoft.engine.core.process.instance.model.SProcessInstance;
import org.bonitasoft.engine.core.process.instance.model.SReceiveTaskInstance;
import org.bonitasoft.engine.core.process.instance.model.SSendTaskInstance;
import org.bonitasoft.engine.core.process.instance.model.SSubProcessActivityInstance;
import org.bonitasoft.engine.core.process.instance.model.SUserTaskInstance;
import org.bonitasoft.engine.core.process.instance.model.archive.SAFlowNodeInstance;
import org.bonitasoft.engine.core.process.instance.model.archive.SAProcessInstance;
import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAAutomaticTaskInstanceBuilderFactory;
import org.bonitasoft.engine.core.process.instance.model.archive.builder.SACallActivityInstanceBuilderFactory;
import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAGatewayInstanceBuilderFactory;
import org.bonitasoft.engine.core.process.instance.model.archive.builder.SALoopActivityInstanceBuilderFactory;
import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAManualTaskInstanceBuilderFactory;
import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAMultiInstanceActivityInstanceBuilderFactory;
import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAProcessInstanceBuilderFactory;
import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAReceiveTaskInstanceBuilderFactory;
import org.bonitasoft.engine.core.process.instance.model.archive.builder.SASendTaskInstanceBuilderFactory;
import org.bonitasoft.engine.core.process.instance.model.archive.builder.SASubProcessActivityInstanceBuilderFactory;
import org.bonitasoft.engine.core.process.instance.model.archive.builder.SAUserTaskInstanceBuilderFactory;
import org.bonitasoft.engine.core.process.instance.model.business.data.SRefBusinessDataInstance;
import org.bonitasoft.engine.data.instance.api.DataInstanceContainer;
import org.bonitasoft.engine.data.instance.api.DataInstanceService;
import org.bonitasoft.engine.data.instance.exception.SDataInstanceException;
import org.bonitasoft.engine.data.instance.model.SDataInstance;
import org.bonitasoft.engine.dependency.model.ScopeType;
import org.bonitasoft.engine.log.technical.TechnicalLogSeverity;
import org.bonitasoft.engine.log.technical.TechnicalLoggerService;
import org.bonitasoft.engine.persistence.OrderByType;
import org.bonitasoft.engine.persistence.QueryOptions;
import org.bonitasoft.engine.recorder.SRecorderException;

/**
 * @author Elias Ricken de Medeiros
 * @author Baptiste Mesta
 * @author Celine Souchet
 */
public class BPMArchiverService {

    private final ArchiveService archiveService;
    private final ProcessInstanceService processInstanceService;
    private final DocumentService documentService;
    private final TechnicalLoggerService logger;
    private final SCommentService commentService;
    private final ProcessDefinitionService processDefinitionService;
    private final ConnectorInstanceService connectorInstanceService;
    private final ClassLoaderService classLoaderService;
    private final RefBusinessDataService refBusinessDataService;
    private final ContractDataService contractDataService;
    private final DataInstanceService dataInstanceService;
    private final ActivityInstanceService activityInstanceService;

    private final int BATCH_SIZE = 100;

    public BPMArchiverService(ArchiveService archiveService,
            ProcessInstanceService processInstanceService,
            DocumentService documentService,
            TechnicalLoggerService logger,
            SCommentService commentService,
            ProcessDefinitionService processDefinitionService,
            ConnectorInstanceService connectorInstanceService,
            ClassLoaderService classLoaderService,
            RefBusinessDataService refBusinessDataService,
            ContractDataService contractDataService,
            DataInstanceService dataInstanceService,
            ActivityInstanceService activityInstanceService) {
        this.archiveService = archiveService;
        this.processInstanceService = processInstanceService;
        this.documentService = documentService;
        this.logger = logger;
        this.commentService = commentService;
        this.processDefinitionService = processDefinitionService;
        this.connectorInstanceService = connectorInstanceService;
        this.classLoaderService = classLoaderService;
        this.refBusinessDataService = refBusinessDataService;
        this.contractDataService = contractDataService;
        this.dataInstanceService = dataInstanceService;
        this.activityInstanceService = activityInstanceService;
    }

    public void archiveAndDeleteProcessInstance(final SProcessInstance processInstance) throws SArchivingException {

        //set the classloader to this process because we need it e.g. to archive data instance
        ClassLoader processClassLoader;
        try {
            processClassLoader = classLoaderService.getClassLoader(
                    identifier(ScopeType.PROCESS, processInstance.getProcessDefinitionId()));

        } catch (SClassLoaderException e) {
            throw new SArchivingException(e);
        }
        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        Thread.currentThread().setContextClassLoader(processClassLoader);
        try {

            final SAProcessInstance saProcessInstance = buildArchiveProcessInstance(processInstance);

            SProcessDefinition processDefinition;
            try {
                processDefinition = processDefinitionService
                        .getProcessDefinition(processInstance.getProcessDefinitionId());
            } catch (final SBonitaException e) {
                throw new SArchivingException(e);
            }

            final long archiveDate = saProcessInstance.getEndDate();

            // The archive of data instance is not done because it is done on creation + when updating.
            // Archive SComment
            archiveComments(processDefinition, processInstance, archiveDate);

            // archive document mappings
            archiveDocumentMappings(processDefinition, processInstance, archiveDate);

            archiveConnectorInstancesIfAny(processInstance, processDefinition, archiveDate);

            archiveRefBusinessDataInstances(processInstance.getId());

            // Archive
            archiveAndDeleteProcessInstanceObject(processDefinition, processInstance, saProcessInstance, archiveDate);
        } finally {
            Thread.currentThread().setContextClassLoader(contextClassLoader);
        }

    }

    protected void archiveConnectorInstancesIfAny(SProcessInstance processInstance,
            SProcessDefinition processDefinition, long archiveDate) throws SArchivingException {
        if (!processDefinition.getProcessContainer().getConnectors().isEmpty()) {
            archiveConnectors(archiveDate, processInstance.getId(), SConnectorInstance.PROCESS_TYPE);
        }
    }

    protected SAProcessInstance buildArchiveProcessInstance(SProcessInstance processInstance) {
        return BuilderFactory.get(SAProcessInstanceBuilderFactory.class).createNewInstance(processInstance).done();
    }

    private void archiveRefBusinessDataInstances(long processInstanceId) throws SArchivingException {
        try {
            List refBusinessDataInstances;
            int i = 0;
            do {
                refBusinessDataInstances = refBusinessDataService.getRefBusinessDataInstances(processInstanceId, i,
                        i + BATCH_SIZE);
                i += BATCH_SIZE;
                for (final SRefBusinessDataInstance sRefBusinessDataInstance : refBusinessDataInstances) {
                    refBusinessDataService.archiveRefBusinessDataInstance(sRefBusinessDataInstance);
                }
            } while (refBusinessDataInstances.size() == BATCH_SIZE);
        } catch (final SBonitaException e) {
            throw new SArchivingException("Unable to archive RefBusinessDataInstance", e);
        }
    }

    private void archiveConnectors(final long archiveDate,
            final long containerId,
            final String containerType) throws SArchivingException {
        try {
            List connectorInstances;
            int i = 0;
            do {
                connectorInstances = connectorInstanceService.getConnectorInstances(containerId, containerType, i,
                        i + BATCH_SIZE, "id", OrderByType.ASC);
                i += BATCH_SIZE;
                for (final SConnectorInstance sConnectorInstance : connectorInstances) {
                    connectorInstanceService.archiveConnectorInstance(sConnectorInstance, archiveDate);
                }
            } while (connectorInstances.size() == BATCH_SIZE);
        } catch (final SBonitaException e) {
            throw new SArchivingException("Unable to archive the container instance with id " + containerId, e);
        }
    }

    private void archiveAndDeleteProcessInstanceObject(final SProcessDefinition processDefinition,
            final SProcessInstance processInstance,
            final SAProcessInstance saProcessInstance, final long archiveDate)
            throws SArchivingException {
        try {
            final ArchiveInsertRecord insertRecord = new ArchiveInsertRecord(saProcessInstance);
            archiveService.recordInsert(archiveDate, insertRecord);

            if (logger.isLoggable(BPMArchiverService.class, TechnicalLogSeverity.DEBUG)) {
                logger.log(BPMArchiverService.class, TechnicalLogSeverity.DEBUG,
                        "Archiving process instance with id = <" + processInstance.getId() + ">" +
                                " and state " + processInstance.getStateId());
            }
            try {
                processInstanceService.deleteProcessInstance(processInstance.getId());
            } catch (final SBonitaException e) {
                throw new SArchivingException("Unable to delete the process instance during the archiving.", e);
            }
        } catch (final SRecorderException e) {
            setExceptionContext(processDefinition, processInstance, e);
            throw new SArchivingException("Unable to archive the process instance.", e);
        }
    }

    private void archiveDocumentMappings(final SProcessDefinition processDefinition,
            final SProcessInstance processInstance,
            final long archiveDate) throws SArchivingException {
        try {
            List mappedDocuments;
            int startIndex = 0;
            do {
                mappedDocuments = documentService.getDocumentsOfProcessInstance(processInstance.getId(), startIndex,
                        BATCH_SIZE, null, null);
                for (final SMappedDocument mappedDocument : mappedDocuments) {
                    documentService.archive(mappedDocument, archiveDate);
                }
                startIndex += BATCH_SIZE;
            } while (mappedDocuments.size() == BATCH_SIZE);
        } catch (final SBonitaException e) {
            setExceptionContext(processDefinition, processInstance, e);
            throw new SArchivingException("Unable to archive the process instance.", e);
        }
    }

    private void archiveComments(final SProcessDefinition processDefinition, final SProcessInstance processInstance,
            final long archiveDate) throws SArchivingException {
        try {
            List sComments;
            int startIndex = 0;
            do {
                sComments = commentService
                        .getComments(processInstance.getId(),
                                new QueryOptions(startIndex, BATCH_SIZE, SComment.class, "id", OrderByType.ASC));
                for (final SComment sComment : sComments) {
                    commentService.archive(archiveDate, sComment);
                }
                startIndex += BATCH_SIZE;
            } while (!sComments.isEmpty());
        } catch (final SBonitaException e) {
            setExceptionContext(processDefinition, processInstance, e);
            throw new SArchivingException("Unable to archive the process instance comments.", e);
        }
    }

    public void archiveAndDeleteFlowNodeInstance(final SFlowNodeInstance flowNodeInstance,
            final long processDefinitionId) throws SArchivingException {
        try {
            final SProcessDefinition processDefinition = processDefinitionService
                    .getProcessDefinition(processDefinitionId);
            // Remove data instance + data visibility mapping
            archiveAndDeleteFlownodeInstance(flowNodeInstance, processDefinition, System.currentTimeMillis());
        } catch (final SArchivingException e) {
            throw e;
        } catch (final SBonitaException e) {
            throw new SArchivingException(e);
        }

    }

    public void archiveFlowNodeInstance(final SFlowNodeInstance flowNodeInstance) throws SArchivingException {
        archiveFlowNodeInstance(flowNodeInstance, System.currentTimeMillis());
    }

    private void archiveAndDeleteFlownodeInstance(SFlowNodeInstance flowNodeInstance,
            SProcessDefinition processDefinition, long archiveDate) throws SDataInstanceException, SArchivingException,
            SFlowNodeNotFoundException, SFlowNodeReadException, SProcessInstanceModificationException {
        if (flowNodeInstance instanceof SActivityInstance) {
            final SActivityDefinition activityDef = (SActivityDefinition) processDefinition
                    .getProcessContainer().getFlowNode(
                            flowNodeInstance.getFlowNodeDefinitionId());
            // only do search for data instances with there are data definitions. Can be null if it's a manual data add at runtime
            if (activityDef != null && !activityDef.getSDataDefinitions().isEmpty()) {
                /*
                 * Delete data instances defined at activity level:
                 * We do not archive because it's done after update not before update
                 */
                deleteLocalDataInstancesFromActivityInstance(flowNodeInstance);
            }

            if (activityDef != null && !activityDef.getConnectors().isEmpty()) {
                archiveConnectors(archiveDate, flowNodeInstance.getId(),
                        SConnectorInstance.FLOWNODE_TYPE);
            }
        }
        if (flowNodeInstance instanceof SUserTaskInstance) {
            archiveContractData(archiveDate, flowNodeInstance.getId());
        }

        // then archive the flow node instance:
        archiveFlowNodeInstance(flowNodeInstance, archiveDate);

        // Reconnect the persisted object before deleting it:
        final SFlowNodeInstance flowNodeInstance2 = activityInstanceService
                .getFlowNodeInstance(flowNodeInstance.getId());
        processInstanceService.deleteFlowNodeInstance(flowNodeInstance2, processDefinition);
    }

    private void setExceptionContext(final SProcessDefinition processDefinition, final SProcessInstance processInstance,
            final SBonitaException e) {
        e.setProcessInstanceIdOnContext(processInstance.getId());
        e.setRootProcessInstanceIdOnContext(processInstance.getRootProcessInstanceId());
        e.setProcessDefinitionIdOnContext(processInstance.getProcessDefinitionId());
        e.setProcessDefinitionNameOnContext(processDefinition.getName());
        e.setProcessDefinitionVersionOnContext(processDefinition.getVersion());
    }

    private void archiveFlowNodeInstance(final SFlowNodeInstance flowNodeInstance,
            final long archiveDate)
            throws SArchivingException {
        try {
            final SAFlowNodeInstance saFlowNodeInstance = getArchivedObject(flowNodeInstance);
            if (saFlowNodeInstance != null) {
                final ArchiveInsertRecord insertRecord = new ArchiveInsertRecord(saFlowNodeInstance);
                archiveService.recordInsert(archiveDate, insertRecord);
            }
        } catch (final SBonitaException e) {
            throw new SArchivingException(e);
        }
    }

    private SAFlowNodeInstance getArchivedObject(final SFlowNodeInstance flowNodeInstance) {
        SAFlowNodeInstance saFlowNodeInstance = null;
        switch (flowNodeInstance.getType()) {// TODO archive other flow node
            case AUTOMATIC_TASK:
                saFlowNodeInstance = BuilderFactory.get(SAAutomaticTaskInstanceBuilderFactory.class)
                        .createNewAutomaticTaskInstance((SAutomaticTaskInstance) flowNodeInstance).done();
                break;
            case GATEWAY:
                saFlowNodeInstance = BuilderFactory.get(SAGatewayInstanceBuilderFactory.class)
                        .createNewGatewayInstance((SGatewayInstance) flowNodeInstance).done();
                break;
            case MANUAL_TASK:
                saFlowNodeInstance = BuilderFactory.get(SAManualTaskInstanceBuilderFactory.class)
                        .createNewManualTaskInstance((SManualTaskInstance) flowNodeInstance).done();
                break;
            case USER_TASK:
                saFlowNodeInstance = BuilderFactory.get(SAUserTaskInstanceBuilderFactory.class)
                        .createNewUserTaskInstance((SUserTaskInstance) flowNodeInstance)
                        .done();
                break;
            case RECEIVE_TASK:
                saFlowNodeInstance = BuilderFactory.get(SAReceiveTaskInstanceBuilderFactory.class)
                        .createNewReceiveTaskInstance((SReceiveTaskInstance) flowNodeInstance).done();
                break;
            case SEND_TASK:
                saFlowNodeInstance = BuilderFactory.get(SASendTaskInstanceBuilderFactory.class)
                        .createNewSendTaskInstance((SSendTaskInstance) flowNodeInstance)
                        .done();
                break;
            case LOOP_ACTIVITY:
                saFlowNodeInstance = BuilderFactory.get(SALoopActivityInstanceBuilderFactory.class)
                        .createNewLoopActivityInstance((SLoopActivityInstance) flowNodeInstance).done();
                break;
            case CALL_ACTIVITY:
                saFlowNodeInstance = BuilderFactory.get(SACallActivityInstanceBuilderFactory.class)
                        .createNewArchivedCallActivityInstance((SCallActivityInstance) flowNodeInstance).done();
                break;
            case MULTI_INSTANCE_ACTIVITY:
                saFlowNodeInstance = BuilderFactory.get(SAMultiInstanceActivityInstanceBuilderFactory.class)
                        .createNewMultiInstanceActivityInstance((SMultiInstanceActivityInstance) flowNodeInstance)
                        .done();
                break;
            case SUB_PROCESS:
                saFlowNodeInstance = BuilderFactory.get(SASubProcessActivityInstanceBuilderFactory.class)
                        .createNewArchivedSubProcessActivityInstance((SSubProcessActivityInstance) flowNodeInstance)
                        .done();
                break;
            case END_EVENT:
            case START_EVENT:
            case BOUNDARY_EVENT:
            case INTERMEDIATE_CATCH_EVENT:
            case INTERMEDIATE_THROW_EVENT:
            default:
                break;
        }
        return saFlowNodeInstance;
    }

    private void deleteLocalDataInstancesFromActivityInstance(final SFlowNodeInstance flowNodeInstance)
            throws SDataInstanceException {
        List dataInstances;
        do {
            dataInstances = dataInstanceService.getLocalDataInstances(flowNodeInstance.getId(),
                    DataInstanceContainer.ACTIVITY_INSTANCE.toString(), 0, 100);
            for (final SDataInstance sDataInstance : dataInstances) {
                dataInstanceService.deleteDataInstance(sDataInstance);
            }
        } while (dataInstances.size() > 0);
    }

    private void archiveContractData(final long archiveDate,
            final long userTaskId)
            throws SArchivingException {
        try {
            contractDataService.archiveAndDeleteUserTaskData(userTaskId, archiveDate);
        } catch (final SBonitaException e) {
            throw new SArchivingException("Unable to archive contract data of container instance with id " + userTaskId,
                    e);
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy