com.ephesoft.dcma.workflows.service.impl.WorkflowServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.ephesoft.dcma.workflows.service.impl.WorkflowServiceImpl.java

Source

/********************************************************************************* 
* Ephesoft is a Intelligent Document Capture and Mailroom Automation program 
* developed by Ephesoft, Inc. Copyright (C) 2015 Ephesoft Inc. 
* 
* This program is free software; you can redistribute it and/or modify it under 
* the terms of the GNU Affero General Public License version 3 as published by the 
* Free Software Foundation with the addition of the following permission added 
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK 
* IN WHICH THE COPYRIGHT IS OWNED BY EPHESOFT, EPHESOFT DISCLAIMS THE WARRANTY 
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS. 
* 
* This program 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 Affero General Public License for more 
* details. 
* 
* You should have received a copy of the GNU Affero General Public License along with 
* this program; if not, see http://www.gnu.org/licenses or write to the Free 
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 
* 02110-1301 USA. 
* 
* You can contact Ephesoft, Inc. headquarters at 111 Academy Way, 
* Irvine, CA 92617, USA. or at email address info@ephesoft.com. 
* 
* The interactive user interfaces in modified source and object code versions 
* of this program must display Appropriate Legal Notices, as required under 
* Section 5 of the GNU Affero General Public License version 3. 
* 
* In accordance with Section 7(b) of the GNU Affero General Public License version 3, 
* these Appropriate Legal Notices must retain the display of the "Ephesoft" logo. 
* If the display of the logo is not reasonably feasible for 
* technical reasons, the Appropriate Legal Notices must display the words 
* "Powered by Ephesoft". 
********************************************************************************/

package com.ephesoft.dcma.workflows.service.impl;

import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.activiti.engine.HistoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.runtime.ProcessInstance;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.ephesoft.dcma.batch.schema.Batch;
import com.ephesoft.dcma.batch.service.BatchSchemaService;
import com.ephesoft.dcma.batch.service.EphesoftContext;
import com.ephesoft.dcma.batch.service.PluginPropertiesService;
import com.ephesoft.dcma.batch.service.WorkflowDetailService;
import com.ephesoft.dcma.core.common.BatchInstanceStatus;
import com.ephesoft.dcma.core.common.DCMABusinessException;
import com.ephesoft.dcma.core.common.constants.CommonConstants;
import com.ephesoft.dcma.core.component.ICommonConstants;
import com.ephesoft.dcma.core.exception.DCMAApplicationException;
import com.ephesoft.dcma.da.dao.BatchInstanceDao;
import com.ephesoft.dcma.da.domain.BatchClassModule;
import com.ephesoft.dcma.da.domain.BatchInstance;
import com.ephesoft.dcma.da.domain.BatchInstanceErrorDetails;
import com.ephesoft.dcma.da.domain.Module;
import com.ephesoft.dcma.da.domain.ServerRegistry;
import com.ephesoft.dcma.da.id.BatchInstanceID;
import com.ephesoft.dcma.da.service.BatchInstanceErrorDetailsService;
import com.ephesoft.dcma.da.service.BatchInstanceService;
import com.ephesoft.dcma.da.service.ServerRegistryService;
import com.ephesoft.dcma.mail.service.MailService;
import com.ephesoft.dcma.util.BackUpFileService;
import com.ephesoft.dcma.util.EphesoftStringUtil;
import com.ephesoft.dcma.util.logger.EphesoftLogger;
import com.ephesoft.dcma.util.logger.EphesoftLoggerFactory;
import com.ephesoft.dcma.workflows.common.WorkflowVariables;
import com.ephesoft.dcma.workflows.constant.WorkflowConstants;
import com.ephesoft.dcma.workflows.service.PickUpService;
import com.ephesoft.dcma.workflows.service.RestartBatchService;
import com.ephesoft.dcma.workflows.service.WorkflowService;
import com.ephesoft.dcma.workflows.service.engine.comparator.HistoricProcessComparator;
import com.ephesoft.dcma.workflows.util.WorkflowUtil;

/**
 * This class presents an implementation of workflow Service with use of a third party engine.
 * 
 * @author Ephesoft
 * 
 *         <b>created on</b> Oct 21, 2014 <br/>
 * @version $LastChangedDate:$ <br/>
 *          $LastChangedRevision:$ <br/>
 */
public class WorkflowServiceImpl implements WorkflowService {

    /**
     * {@link EphesoftLogger} Instance of logger.
     */
    private static final EphesoftLogger LOGGER = EphesoftLoggerFactory.getLogger(WorkflowServiceImpl.class);

    /**
     * {@link RuntimeService} Instance of runtime service.
     */
    @Autowired
    private RuntimeService runtimeService;

    /**
     * {@link BatchInstanceService} Instance of batch instance service.
     */
    @Autowired
    private BatchInstanceService batchInstanceService;

    /**
     * {@link PickUpService} Instance of pick up service.
     */
    @Autowired
    private PickUpService pickupService;

    /**
     * The service for CRUD operation on server_registry table in Ephesoft Database. {@link ServerRegistry}
     */
    @Autowired
    private ServerRegistryService serverRegistryService;

    /**
     * {@link BatchInstanceErrorDetailsService} Instance of batch instance error service.
     */
    @Autowired
    private BatchInstanceErrorDetailsService batchInstanceErrorService;

    /**
     * {@link BatchSchemaService} Instance of batch schema service.
     */
    @Autowired
    private BatchSchemaService batchSchemaService;

    /**
     * {@link String} Instance of subject of error batch mail.
     */
    private String subjectErrorMail;

    /**
     * {@link String}
     */
    private String mailTemplatePath;

    @Autowired
    private RestartBatchService restartService;

    @Autowired
    private BatchInstanceDao batchInstanceDao;

    /**
     * 
     */
    @Autowired
    private ErrorBatchServiceRunnable errorBatchServiceRunnable;

    /**
     * 
     */
    @Autowired
    private HistoryService historyService;

    @Autowired
    private WorkflowDetailService workflowDetailService;

    /**
     * {@link PluginPropertiesService} Instance of plugin properties service.
     */
    @Autowired
    @Qualifier("batchInstancePluginPropertiesService")
    private PluginPropertiesService pluginPropertiesService;

    /**
     * Gets subject error mail.
     * 
     * @return {@link String}
     */
    public String getSubjectErrorMail() {
        return subjectErrorMail;
    }

    /**
     * Sets subject error mail.
     * 
     * @param subject {@link String}
     */
    public void setSubjectErrorMail(final String subjectErrorMail) {
        this.subjectErrorMail = subjectErrorMail;
    }

    /**
     * Gets mail template path.
     * 
     * @return {@link String}
     */
    public String getMailTemplatePath() {
        return mailTemplatePath;
    }

    /**
     * Sets mail template path.
     * 
     * @param mailTemplatePath {@link String}
     */
    public void setMailTemplatePath(String mailTemplatePath) {
        this.mailTemplatePath = mailTemplatePath;
    }

    @Override
    public boolean startWorkflow(BatchInstanceID batchInstanceID, String moduleName) {
        boolean isWorkflowStarted;
        String batchIdentifier = batchInstanceID.getID();
        LOGGER.info("Starting Workflow for batch instance id:", batchIdentifier, " for module name:", moduleName);
        BatchInstance batchInstance = batchInstanceService.getBatchInstanceByIdentifier(batchInstanceID.getID());
        isWorkflowStarted = triggerBatchExecution(batchInstanceID, moduleName, batchIdentifier, batchInstance);
        return isWorkflowStarted;
    }

    @Override
    public void mailOnError(final BatchInstance batchInstance, final Exception exception)
            throws DCMAApplicationException {
        LOGGER.trace("Entering method to call mail service to send mail on batch error.");
        if (null != batchInstance && null != exception) {
            MailService mailService = EphesoftContext.get(MailService.class);
            String activeWorkflowName = getErrorBatchLastWorkflowInExecutionName(batchInstance.getIdentifier());
            mailService.mailOnWorkflowError(batchInstance, exception, subjectErrorMail, mailTemplatePath,
                    activeWorkflowName);
        } else {
            LOGGER.error(
                    "Either or both batchInstance or exception object is null. Error notification mail can't be sent.");
        }
    }

    @Override
    public boolean signalWorkflow(BatchInstance batchInstance) throws DCMAApplicationException {
        boolean isWorkflowStarted;
        String batchIdentifier = batchInstance.getIdentifier();
        LOGGER.info("Signal Workflow for batch instance id:", batchIdentifier);
        final List<Execution> executions = getExecutionsForBatch(batchIdentifier);
        if (CollectionUtils.isNotEmpty(executions)) {
            final String waitingExecutionId = getWaitingExecutionId(executions);
            if (null == waitingExecutionId) {
                LOGGER.info("No active execution found for batch: ", batchIdentifier, ".");
                throw new DCMAApplicationException(EphesoftStringUtil.concatenate("Batch instance: ",
                        batchIdentifier, "as there is no active execution found. Jobs are in inconsistent state."));
            } else {
                isWorkflowStarted = signalExecution(batchIdentifier, waitingExecutionId);
            }
        } else {
            isWorkflowStarted = startWorkflow(batchInstance.getBatchInstanceID(), null);
        }
        return isWorkflowStarted;
    }

    @Override
    public String getWorkflowNameByModuleName(final String moduleName,
            final List<BatchClassModule> batchClassModuleList) {
        String workflowName = null;
        if (!EphesoftStringUtil.isNullOrEmpty(moduleName) && CollectionUtils.isNotEmpty(batchClassModuleList)) {
            for (BatchClassModule batchClassModule : batchClassModuleList) {
                if (null != batchClassModule) {
                    Module module = batchClassModule.getModule();
                    if (null != module && moduleName.equalsIgnoreCase(module.getDescription())) {
                        workflowName = batchClassModule.getWorkflowName();
                        break;
                    }
                }
            }

        }
        return workflowName;
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    @Override
    public void signalWorkflow(String batchId) throws DCMAApplicationException {
        LOGGER.debug("Signal workflow for batch instance id:", batchId);
        BatchInstance batchInstance = batchInstanceService.getBatchInstanceByIdentifier(batchId);
        boolean isValidPauseStateStatus = WorkflowUtil.checkPauseStatus(batchInstance);
        if (isValidPauseStateStatus) {
            reloadBatchAndBackupXML(batchInstance);

            // Move batch to READY state always.
            setPauseStateBatchToReady(batchInstance);
        }
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    @Override
    public void signalWorkflow(String batchInstanceIdentifier, String userName) throws DCMAApplicationException {
        LOGGER.debug("Signal workflow for batch instance id:", batchInstanceIdentifier);
        BatchInstance batchInstance = batchInstanceService.getBatchInstanceByIdentifier(batchInstanceIdentifier);
        boolean isValidPauseStateStatus = WorkflowUtil.checkPauseStatus(batchInstance);
        if (isValidPauseStateStatus) {
            switch (batchInstance.getStatus()) {
            case READY_FOR_REVIEW:
                batchInstance.setReviewUserName(userName);
                break;
            case READY_FOR_VALIDATION:
                batchInstance.setValidationUserName(userName);
                break;
            }
            this.batchInstanceService.updateBatchInstance(batchInstance);
            reloadBatchAndBackupXML(batchInstance);

            // Move batch to READY state always.
            setPauseStateBatchToReady(batchInstance);
        }
    }

    @Override
    public Map<Integer, Integer> getBatchPriorityRunningCount() {
        Map<Integer, Integer> priorityWithRunningBatchCount = null;
        List<BatchInstance> batchInstanceListForAllServer = batchInstanceService.getAllRunningBatches();
        if (CollectionUtils.isNotEmpty(batchInstanceListForAllServer)) {
            priorityWithRunningBatchCount = new HashMap<Integer, Integer>();
            for (BatchInstance batchInstance : batchInstanceListForAllServer) {
                int batchPriority = batchInstance.getPriority();
                Integer executionCount = priorityWithRunningBatchCount.get(batchPriority);
                if (null == executionCount) {
                    priorityWithRunningBatchCount.put(batchPriority, 1);
                } else {
                    executionCount++;
                    priorityWithRunningBatchCount.put(batchPriority, executionCount);
                }
            }
        }
        return priorityWithRunningBatchCount;
    }

    @Override
    public int getRemainingCapacityForPriority(final int priority,
            final Map<Set<Integer>, Integer> priorityServerMaxCapacity,
            final Map<Integer, Integer> serverRunningBatchCount) {
        int capacity = 0;
        if (null == priorityServerMaxCapacity || 0 == priorityServerMaxCapacity.size()) {
            LOGGER.error("Priority range for no server is found.");
        } else {
            LOGGER.info("Getting remaining capacity for all servers for priority: ", priority);
            Set<Set<Integer>> setOfPrioritySets = priorityServerMaxCapacity.keySet();
            Iterator<Set<Integer>> prioritySetIterator = setOfPrioritySets.iterator();
            while (prioritySetIterator.hasNext()) {
                Set<Integer> setOfPriority = (Set<Integer>) prioritySetIterator.next();
                if (CollectionUtils.isNotEmpty(setOfPriority) && setOfPriority.contains(priority)) {
                    capacity = getCapacityForPriority(priority, priorityServerMaxCapacity, serverRunningBatchCount,
                            setOfPriority);
                    break;
                }
            }
        }
        LOGGER.debug("Remaining capacity for priority: ", priority, " is: ", capacity);
        return capacity;
    }

    // @Override
    public int getRemainingCapacityForPriority(final int batchPriority, final int totalCapacity,
            final Map<Integer, Integer> serverRunningBatchCount) {
        int capacity = 0;
        if (null != serverRunningBatchCount) {
            Integer aPriorityRunningBatchCountInteger = serverRunningBatchCount.get(batchPriority);
            int aPriorityRunningBatchCount;
            if (null == aPriorityRunningBatchCountInteger) {
                aPriorityRunningBatchCount = 0;
            } else {
                aPriorityRunningBatchCount = aPriorityRunningBatchCountInteger;
            }
            LOGGER.debug("Total running capacity for a set containing priority: ", batchPriority, " is: ",
                    aPriorityRunningBatchCount);
            capacity = totalCapacity - aPriorityRunningBatchCount;
            LOGGER.debug("Remaining capacity for priority: ", batchPriority, " is: ", capacity);
        }
        return capacity;
    }

    private int getCapacityForPriority(final int priority,
            final Map<Set<Integer>, Integer> priorityServerMaxCapacity,
            final Map<Integer, Integer> serverRunningBatchCount, Set<Integer> setOfPriority) {
        int capacity;
        if (null == priorityServerMaxCapacity || 0 == priorityServerMaxCapacity.size()
                || CollectionUtils.isEmpty(setOfPriority)) {
            capacity = 0;
        } else {
            Iterator<Integer> priorityIterator = setOfPriority.iterator();
            int totalCapacity = priorityServerMaxCapacity.get(setOfPriority);
            int totalPriorityBatchCount = 0;
            if (null != serverRunningBatchCount && !serverRunningBatchCount.isEmpty()) {
                while (priorityIterator.hasNext()) {
                    Integer aPriority = priorityIterator.next();
                    Integer aPriorityRunningBatchCountInteger = serverRunningBatchCount.get(aPriority);
                    int aPriorityRunningBatchCount;
                    if (null == aPriorityRunningBatchCountInteger) {
                        aPriorityRunningBatchCount = 0;
                    } else {
                        aPriorityRunningBatchCount = aPriorityRunningBatchCountInteger;
                    }
                    totalPriorityBatchCount += aPriorityRunningBatchCount;
                }
                LOGGER.debug("Total running capacity for a set containing priority: ", priority, " is: ",
                        totalPriorityBatchCount);
            }
            capacity = totalCapacity - totalPriorityBatchCount;
        }
        return capacity;
    }

    private void reloadBatchAndBackupXML(BatchInstance batchInstance) {
        LOGGER.trace("Entering method reloadBatchAndBackupXML");
        String backupXmlName = ICommonConstants.EMPTY_STRING;
        BatchInstanceStatus status = batchInstance.getStatus();
        if (status == BatchInstanceStatus.READY_FOR_REVIEW) {
            backupXmlName = WorkflowConstants.POST_REVIEW_BACKUP_NAME;
        } else if (status == BatchInstanceStatus.READY_FOR_VALIDATION) {
            backupXmlName = WorkflowConstants.POST_VALIDATION_BACKUP_NAME;
        }
        LOGGER.info("Creating backup XML ", backupXmlName, " for batch instance: ", batchInstance.getIdentifier());
        String batchIdentifier = batchInstance.getIdentifier();
        Batch batch = batchSchemaService.getBatchFromXML(batchIdentifier, true);
        batchSchemaService.updateBatch(batch);
        BackUpFileService.backUpBatchXml(batchInstance.getIdentifier(), backupXmlName);
    }

    @Override
    public void updateBatchInstanceStatusForReviewAndValidation(final BatchInstanceID batchInstanceID,
            final BatchInstanceStatus status) {
        LOGGER.trace("Entering method to update batch instance status");
        String batchInstanceIdentifier = batchInstanceID.getID();
        Batch batch = batchSchemaService.getBatch(batchInstanceIdentifier);
        if (null == batch) {
            LOGGER.info("No in memory batch found for batch instance: ", batchInstanceIdentifier);
            throw new DCMABusinessException(EphesoftStringUtil
                    .concatenate("No In-memory  batch found for batch instance: ", batchInstanceIdentifier));
        }

        // for updating the batch XML and create a copy for the same
        batchSchemaService.updateBatchXML(batch);
        String backupXmlName;
        if (status == BatchInstanceStatus.READY_FOR_REVIEW) {
            backupXmlName = WorkflowConstants.PRE_REVIEW_BACKUP_NAME;
        } else {
            backupXmlName = WorkflowConstants.PRE_VALIDATION_BACKUP_NAME;
        }
        BatchInstance batchInstance = batchInstanceService.getBatchInstanceByIdentifier(batchInstanceIdentifier);
        batchInstance.setStatus(status);
        batchInstance.setExecutingServer(null);
        batchInstance.setServerIP(null);
        BackUpFileService.backUpBatchXml(batch.getBatchInstanceIdentifier(), backupXmlName);
        batchSchemaService.removeBatch(batchInstanceIdentifier);
        pluginPropertiesService.clearCache(batchInstanceIdentifier);
        batchInstanceService.merge(batchInstance);
    }

    @Override
    public void clearCurrentUser(final BatchInstance batchInstance) {
        batchInstance.setCurrentUser(null);
        batchInstanceService.merge(batchInstance);
    }

    /**
     * Signals execution for a batch in a waiting execution state.
     * 
     * @param batchInstanceId {@link String}
     * @param waitingExecutionId {@link String}
     * @return boolean
     */
    private boolean signalExecution(final String batchInstanceId, final String waitingExecutionId) {
        batchInstanceService.updateBatchInstanceStatusByIdentifier(batchInstanceId, BatchInstanceStatus.RUNNING);
        runtimeService.signal(waitingExecutionId);
        return true;
    }

    /**
     * Gets all execution for batch instance.
     * 
     * @param batchInstanceId {@link String}
     * @return {@link List}<{@link Execution}>
     */
    private List<Execution> getExecutionsForBatch(final String batchInstanceId) {
        List<Execution> executions = runtimeService.createExecutionQuery()
                .processVariableValueEquals(WorkflowVariables.KEY, batchInstanceId).list();
        return executions;
    }

    /**
     * Sets Paused batch to READY status.
     * 
     * @param batchInstance {@link BatchInstance}
     */
    private void setPauseStateBatchToReady(final BatchInstance batchInstance) {
        batchInstance.setStatus(BatchInstanceStatus.READY);
        batchInstance.setCurrentUser(null);
        batchInstanceService.updateBatchInstance(batchInstance);
    }

    /**
     * Gets execution id of a batch in a wait state.
     * 
     * @param executions {@link List}<{@link Execution}
     * @return {@link String}
     */
    private String getWaitingExecutionId(final List<Execution> executions) {
        Map<String, String> map = getExecutionHeirarchy(executions);
        String temporaryInstanceId = map.get(null);
        String waitingExecutionId = null;
        while (null != temporaryInstanceId) {
            waitingExecutionId = temporaryInstanceId;
            temporaryInstanceId = map.get(temporaryInstanceId);
        }
        return waitingExecutionId;
    }

    /**
     * Gets hierarchy of executions for a batch instance.
     * 
     * @param executions {@link List}<{@link Execution}>
     * @return {@link Map}<{@link String},{@link String}>
     */
    private Map<String, String> getExecutionHeirarchy(final List<Execution> executions) {
        Map<String, String> map = new HashMap<String, String>(executions.size());
        for (Execution execution : executions) {
            ExecutionEntity executionEntity = (ExecutionEntity) execution;
            String superExecution = executionEntity.getSuperExecutionId();
            String parentExecution = executionEntity.getParentId();
            if (null != superExecution) {
                map.put(superExecution, executionEntity.getId());
            } else if (null != parentExecution) {
                map.put(parentExecution, executionEntity.getId());
            } else {
                map.put(null, executionEntity.getId());
            }
        }
        return map;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public String getActiveModuleName(final String batchInstanceId) {
        LOGGER.debug("Get active module for batch instance id:", batchInstanceId);
        String returnValue = null;
        List<Execution> executions = getExecutionsForBatch(batchInstanceId);

        if (CollectionUtils.isNotEmpty(executions)) {
            // The activity's process definition contains the name of the module in execution.
            for (Execution execution : executions) {
                ProcessInstance processInstance = ((ProcessInstance) execution);
                if (null != processInstance) {
                    String activity = processInstance.getActivityId();
                    if (null != activity && activity.contains(WorkflowConstants.ACTIVITY_ID_IN_PLUGIN)) {
                        String processDefintionId = processInstance.getProcessDefinitionId();
                        int index = processDefintionId.indexOf(ICommonConstants.COLON);
                        if (index > 0) {
                            returnValue = processDefintionId.substring(0, index);
                        }
                        break;
                    }
                }
            }
        }
        LOGGER.debug("Active module workflow for batch instance id: ", batchInstanceId, " is: ", returnValue);
        return returnValue;
    }

    @Override
    public String getActivePluginWorkflow(final String batchInstanceId) {
        LOGGER.debug("Get active plugin workflow for batch instance id:", batchInstanceId);
        String activePluginName = null;
        List<Execution> executions = getExecutionsForBatch(batchInstanceId);
        if (null != executions) {

            // The activity contains the name of the plugin in execution.
            for (Execution execution : executions) {
                if (null != execution) {
                    ProcessInstance processInstance = ((ProcessInstance) execution);
                    if (null != processInstance) {
                        String activityId = processInstance.getActivityId();
                        if (null != activityId) {
                            int index = activityId.indexOf(WorkflowConstants.ACTIVITY_ID_IN_PLUGIN);
                            if (index > 0) {
                                activePluginName = activityId.substring(0, index);
                                break;
                            }
                        }
                    }
                }
            }
        }
        LOGGER.debug("Active plugin workflow for batch instance id:", batchInstanceId, " is:", activePluginName);
        return activePluginName;
    }

    /**
     * Gets last workflow in execution's name for an error batch.
     * 
     * @param batchInstanceIdentifier {@link String}
     * @return {@link String}
     */
    private String getErrorBatchLastWorkflowInExecutionName(final String batchInstanceIdentifier) {
        BatchInstanceErrorDetails batchInstanceErrorDetails = batchInstanceErrorService
                .getBatchInstanceErrorDetailByIdentifier(batchInstanceIdentifier);
        String activeWorkflowName;
        String lastPluginName;
        if (null == batchInstanceErrorDetails) {
            lastPluginName = null;
        } else {
            lastPluginName = batchInstanceErrorDetails.getLastPluginName();
        }
        if (EphesoftStringUtil.isNullOrEmpty(lastPluginName) && null != batchInstanceErrorDetails) {
            activeWorkflowName = batchInstanceErrorDetails.getLastModuleName();
        } else {
            activeWorkflowName = lastPluginName;
        }
        return activeWorkflowName;
    }

    @Override
    public String getErrorBatchLastExecutedModule(String batchInstanceIdentifier) {
        BatchInstanceErrorDetails batchInstanceErrorDetails = batchInstanceErrorService
                .getBatchInstanceErrorDetailByIdentifier(batchInstanceIdentifier);
        String lastModuleName;
        if (null == batchInstanceErrorDetails) {
            lastModuleName = null;
        } else {
            lastModuleName = batchInstanceErrorDetails.getLastModuleName();

        }
        return lastModuleName;
    }

    /**
     * Triggers a batch execution y starting its workflow.
     * 
     * @param batchInstanceID {@link BatchInstanceID}
     * @param moduleName {@link String}
     * @param batchIdentifier {@link String}
     * @param batchInstance {@link BatchInstance}
     * @return boolean
     */
    private boolean triggerBatchExecution(final BatchInstanceID batchInstanceID, final String moduleName,
            final String batchInstanceIdentifier, final BatchInstance batchInstance) {
        LOGGER.trace("Starting workflow for a batch: ", batchInstanceIdentifier);
        boolean isWorkflowStarted;

        // Variables floating through every process instance of a batch.
        Map<String, Object> vars = new HashMap<String, Object>();
        vars.put(WorkflowVariables.BATCH_INSTANCE_ID, batchInstanceID);
        vars.put(WorkflowVariables.RESTART_WORKFLOW, moduleName);
        vars.put(WorkflowVariables.KEY, batchInstanceIdentifier);
        vars.put(WorkflowVariables.IS_MODULE_REMOTE, CommonConstants.NO);
        batchInstanceService.updateBatchInstanceStatusByIdentifier(batchInstanceID.getID(),
                BatchInstanceStatus.RUNNING);
        ProcessInstance processInstance = runtimeService
                .startProcessInstanceByKey(batchInstance.getBatchClass().getName(), batchInstanceIdentifier, vars);
        runtimeService.setProcessInstanceName(processInstance.getId(), batchInstance.getProcessInstanceKey());
        isWorkflowStarted = true;
        LOGGER.info("Started Workflow successfully for batch instance id:", batchInstanceID, " for module name:",
                moduleName);
        return isWorkflowStarted;
    }

    @Override
    public void handleErrorBatch(final BatchInstance batchInstance, final Exception exception,
            String exceptionMessage) {
        setParameters(batchInstance, exception, exceptionMessage);
        Thread thread = new Thread(this.errorBatchServiceRunnable);
        thread.start();
    }

    /**
     * @param batchInstance
     * @param exception
     * @param exceptionMessage
     */
    private void setParameters(final BatchInstance batchInstance, final Exception exception,
            String exceptionMessage) {
        this.errorBatchServiceRunnable.setBatchInstance(batchInstance);
        this.errorBatchServiceRunnable.setException(exception);
        this.errorBatchServiceRunnable.setExceptionMessage(exceptionMessage);
    }

    @Override
    public Map<Set<Integer>, Integer> getAllServerExecutionCapacity() {
        LOGGER.info("Fetching All Server Execution Capacity");
        Map<Set<Integer>, Integer> allServerExecutionCapacityMap = serverRegistryService
                .getPriorityLoadedActiveServers();
        return allServerExecutionCapacityMap;
    }

    private int getAllServerExecutionCapacity(final int priority) {
        int capacity = 0;
        Map<Set<Integer>, Integer> prioritySetCapacityMap = getAllServerExecutionCapacity();
        if (null != prioritySetCapacityMap && !prioritySetCapacityMap.isEmpty()) {
            Set<Set<Integer>> priorityCapacityMapKeys = prioritySetCapacityMap.keySet();
            for (Set<Integer> aPrioritySet : priorityCapacityMapKeys) {
                if (CollectionUtils.isNotEmpty(aPrioritySet) && aPrioritySet.contains(priority)) {
                    LOGGER.debug("A priority set contaning priority: ", priority, "found.");
                    capacity = prioritySetCapacityMap.get(aPrioritySet);
                    break;
                }
            }
        }
        return capacity;
    }

    @Override
    public void createBackupBeforeCleanup(BatchInstanceID batchInstanceID) {
        final BatchInstance batchInstance = batchInstanceDao
                .getBatchInstancesForIdentifier(batchInstanceID.getID());

        // Create module backup XML for the module before cleanup
        if (null != batchInstance && BackUpFileService.isBackupRequired()) {
            String batchInstanceIdentifier = batchInstance.getIdentifier();
            String moduleName = restartService.getBatchLastExecutedModule(batchInstance);
            String workflow = getWorkflowNameByModuleName(moduleName,
                    batchInstance.getBatchClass().getBatchClassModules());
            // batchSchemaService.createBatchXml(batchInstanceIdentifier, workflow);
            batchSchemaService.createBatchXml(batchInstanceIdentifier, "Final");
        }

    }

    @Override
    public String getWorkflowDetails(String batchInstanceIdentifier) {
        String workflowDetail;
        List<HistoricProcessInstance> historicProcessList = getHistoricData(batchInstanceIdentifier);
        String currentModuleWorkflowName = null;
        String lastPluginWorkflowName = null;
        if (CollectionUtils.isEmpty(historicProcessList)) {
            workflowDetail = null;
        } else {
            HistoricProcessInstance lastHistoricProcess = historicProcessList.get(0);
            String lastBusinessKey = lastHistoricProcess.getBusinessKey();
            boolean workflowStarting = isWorkflowStarting(lastBusinessKey, batchInstanceIdentifier);
            boolean workflowFinished = isWorkflowFinished(lastBusinessKey, batchInstanceIdentifier);
            if (workflowStarting) {
                workflowDetail = null;
                LOGGER.debug("Batch instance: ", batchInstanceIdentifier, " has just started.");
            } else if (workflowFinished) {
                workflowDetail = WorkflowConstants.WORKFLOW_UNIT_END_INDICATOR;
                LOGGER.debug("Batch instance: ", batchInstanceIdentifier, " has finished.");
            } else {
                String businessKey;
                for (HistoricProcessInstance historicProcessInstance : historicProcessList) {

                    // searching for first -m and -p entry
                    if (null != historicProcessInstance) {
                        businessKey = historicProcessInstance.getBusinessKey();
                        if (!EphesoftStringUtil.isNullOrEmpty(businessKey)) {

                            // added endtime check to count Ready_for_review and Ready_for_validation halt states as current executing
                            // plugin and not as executed plugins.
                            if (null != historicProcessInstance.getEndTime() && null == lastPluginWorkflowName
                                    && businessKey.endsWith(WorkflowConstants.PLUGIN_BUSINESS_KEY_SIGNATURE)) {
                                lastPluginWorkflowName = getWorkflowNameFromBusinessKey(businessKey,
                                        ICommonConstants.DOT, WorkflowConstants.PLUGIN_BUSINESS_KEY_SIGNATURE);
                            } else if (businessKey.endsWith(WorkflowConstants.MODULE_BUSINESS_KEY_SIGNATURE)) {
                                currentModuleWorkflowName = getWorkflowNameFromBusinessKey(businessKey,
                                        ICommonConstants.DOT, WorkflowConstants.MODULE_BUSINESS_KEY_SIGNATURE);
                                break;
                            }
                        }
                    }
                }
                LOGGER.debug("For batch instance: ", batchInstanceIdentifier, ", Current executing module is: ",
                        currentModuleWorkflowName, " and last executed plugin is: ", lastPluginWorkflowName);
                StringBuilder currentModuleLastPluginBuilder = new StringBuilder();
                if (null != currentModuleWorkflowName) {
                    currentModuleLastPluginBuilder.append(currentModuleWorkflowName);
                }
                if (null != lastPluginWorkflowName) {
                    currentModuleLastPluginBuilder.append(ICommonConstants.COLON);
                    currentModuleLastPluginBuilder.append(lastPluginWorkflowName);
                }
                workflowDetail = currentModuleLastPluginBuilder.toString();
            }
        }
        LOGGER.debug("Workflow Detail for batch instance: ", batchInstanceIdentifier, " is: ", workflowDetail);
        return workflowDetail;
    }

    private String getWorkflowNameFromBusinessKey(String businessKey, String delimeter, String suffix) {
        String workflowName;
        if (EphesoftStringUtil.isNullOrEmpty(businessKey)) {
            workflowName = null;
        } else {
            int index = businessKey.indexOf(delimeter);
            if (-1 == index) {
                workflowName = null;
            } else {
                workflowName = businessKey.substring(index + 1).replace(suffix, ICommonConstants.EMPTY_STRING);
                LOGGER.info("Workflow name for business key: ", businessKey, " is: ", workflowName);
            }
        }
        return workflowName;
    }

    private boolean isWorkflowStarting(final String businessKey, String batchInstanceIdentifier) {
        boolean result;
        if (businessKey.contains(WorkflowConstants.WORKFLOW_STATUS_RUNNING)
                || businessKey.equalsIgnoreCase(batchInstanceIdentifier)) {
            result = true;
        } else {
            result = false;
        }
        return result;
    }

    private boolean isWorkflowFinished(final String businessKey, String batchInstanceIdentifier) {
        boolean result;
        if (businessKey.contains(WorkflowConstants.WORKFLOW_STATUS_FINISHED)) {
            result = true;
        } else {
            result = false;
        }
        return result;
    }

    private List<HistoricProcessInstance> getHistoricData(String batchInstanceIdentifier) {
        List<HistoricProcessInstance> historyProcessList = null;

        // Edited because ordered list from database is sorted on varchar process instance ids, but we need numeric descending sorting.
        if (!EphesoftStringUtil.isNullOrEmpty(batchInstanceIdentifier)) {
            historyProcessList = historyService.createHistoricProcessInstanceQuery()
                    .variableValueEquals(WorkflowVariables.KEY, batchInstanceIdentifier).list();
        }
        if (CollectionUtils.isNotEmpty(historyProcessList)) {
            Comparator<HistoricProcessInstance> reverseComparator = Collections
                    .reverseOrder(new HistoricProcessComparator());
            Collections.sort(historyProcessList, reverseComparator);
        }
        return historyProcessList;
    }

    @Override
    public Map<Integer, List<Integer>> getBatchPriorityCapacityMap() {
        // MultiKeyMap batchPriorityCapacityMap = MultiKeyMap.decorate(new LinkedMap(ICommonConstants.MAX_BATCH_INSTANCE_PRIORITY));
        Map<Integer, List<Integer>> batchPriorityCapacityMap = new HashMap<Integer, List<Integer>>(
                ICommonConstants.MAX_BATCH_INSTANCE_PRIORITY);
        for (int index = 0; index < ICommonConstants.MAX_BATCH_INSTANCE_PRIORITY; index++) {
            batchPriorityCapacityMap.put(index, null);
        }
        return batchPriorityCapacityMap;
    }
}