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

ee.telekom.workflow.facade.WorkflowEngineFacadeImpl Maven / Gradle / Ivy

package ee.telekom.workflow.facade;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

import ee.telekom.workflow.core.workflowinstance.WorkflowInstanceStatus;
import org.apache.commons.lang.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import ee.telekom.workflow.core.abort.AbortService;
import ee.telekom.workflow.core.common.UnexpectedStatusException;
import ee.telekom.workflow.core.common.WorkflowEngineConfiguration;
import ee.telekom.workflow.core.error.ExecutionError;
import ee.telekom.workflow.core.error.ExecutionErrorDao;
import ee.telekom.workflow.core.lock.LockService;
import ee.telekom.workflow.core.retry.RetryService;
import ee.telekom.workflow.core.workflowinstance.WorkflowInstance;
import ee.telekom.workflow.core.workflowinstance.WorkflowInstanceService;
import ee.telekom.workflow.core.workitem.WorkItem;
import ee.telekom.workflow.core.workitem.WorkItemDao;
import ee.telekom.workflow.executor.GraphEngineFactory;
import ee.telekom.workflow.facade.model.CreateWorkflowInstance;
import ee.telekom.workflow.facade.model.ExecutionErrorState;
import ee.telekom.workflow.facade.model.SearchWorkflowInstances;
import ee.telekom.workflow.facade.model.WorkItemState;
import ee.telekom.workflow.facade.model.WorkflowInstanceFacadeStatus;
import ee.telekom.workflow.facade.model.WorkflowInstanceState;
import ee.telekom.workflow.facade.util.StatusUtil;
import ee.telekom.workflow.facade.workflowinstance.WorkflowInstanceStateDao;
import ee.telekom.workflow.facade.workflowinstance.WorkflowStatusCount;
import ee.telekom.workflow.facade.workitem.WorkItemStateDao;
import ee.telekom.workflow.graph.Graph;
import ee.telekom.workflow.graph.WorkItemStatus;
import ee.telekom.workflow.util.JsonUtil;

/*
 * Note that each workflow instance is associated to a particular cluster and that only each 
 * cluster may manipulate only its own workflow instances. This behaviour is enforced by the 
 * SQL queries in {@link WorkflowInstanceStateDao} and {@link WorkItemStateDao}. All the other
 * services and DAOs do not explicitly test this. (Actually they rely on the fact, that internally
 * the engine never manipulates "foreign" workflow instances.) So, for these services and DAOs we 
 * need to explicitly validate the clusterName field.
 */
@Service
@Transactional
public class WorkflowEngineFacadeImpl implements WorkflowEngineFacade{

    @Autowired
    private WorkflowInstanceService workflowInstanceService;
    @Autowired
    private AbortService abortService;
    @Autowired
    private RetryService retryService;
    @Autowired
    private WorkflowInstanceStateDao workflowInstanceStateDao;

    @Autowired
    private WorkItemDao workItemDao;
    @Autowired
    private WorkItemStateDao workItemStateDao;

    @Autowired
    private ExecutionErrorDao executionErrorDao;

    @Autowired
    private GraphEngineFactory graphEngineFactory;
    @Autowired
    private LockService lockService;
    @Autowired
    private WorkflowEngineConfiguration config;

    @Override
    public void createWorkflowInstance( CreateWorkflowInstance request ){
        WorkflowInstance woin = workflowInstanceService.create( request.getWorkflowName(), request.getWorkflowVersion(), request.getArguments(),
                request.getLabel1(), request.getLabel2() );
        request.setRefNum( woin.getRefNum() );
    }

    @Override
    public void createWorkflowInstances( List requests ){
        for( CreateWorkflowInstance request : requests ){
            createWorkflowInstance( request );
        }
    }

    @Override
    public void abortWorkflowInstance( long woinRefNum ){
        validateClusterNameOfWorkflowInstance( woinRefNum );
        validateCurrentlyNotExecuting( woinRefNum );
        abortService.abort( woinRefNum );
    }

    @Override
    public void suspendWorkflowInstance( long woinRefNum ){
        validateClusterNameOfWorkflowInstance( woinRefNum );
        validateCurrentlyNotExecuting( woinRefNum );
        workflowInstanceService.suspend( woinRefNum );
    }

    @Override
    public void resumeWorkflowInstance( long woinRefNum ){
        validateClusterNameOfWorkflowInstance( woinRefNum );
        workflowInstanceService.resume( woinRefNum );
    }

    @Override
    public void retryWorkflowInstance( long woinRefNum ){
        validateClusterNameOfWorkflowInstance( woinRefNum );
        retryService.retry( woinRefNum );
    }

    @Override
    public void sendSignalToWorkflowInstance( long woinRefNum, String signal, Object argument ){
        String jsonResult = JsonUtil.serialize( argument, true );
        workItemStateDao.updateStatusAndResultByInstanceAndSignal(
                woinRefNum,
                signal,
                WorkItemStatus.EXECUTED,
                WorkItemStatus.NEW,
                jsonResult );
    }

    @Override
    public void sendSignalToWorkItem( long woitRefNum, String signal, Object argument ){
        String jsonResult = JsonUtil.serialize( argument, true );
        workItemStateDao.updateStatusAndResultByWorkItemAndSignal(
                woitRefNum,
                signal,
                WorkItemStatus.EXECUTED,
                WorkItemStatus.NEW,
                jsonResult );
    }

    @Override
    public void sendSignalByLabel1( String label1, String signal, Object argument ){
        String jsonResult = JsonUtil.serialize( argument, true );
        workItemStateDao.updateStatusAndResultByLabel1AndSignal(
                label1,
                signal,
                WorkItemStatus.EXECUTED,
                WorkItemStatus.NEW,
                jsonResult );
    }

    @Override
    public void sendSignalByLabels( String label1, String label2, String signal, Object argument ){
        String jsonResult = JsonUtil.serialize( argument, true );
        workItemStateDao.updateStatusAndResultByLabelsAndSignal(
                label1,
                label2,
                signal,
                WorkItemStatus.EXECUTED,
                WorkItemStatus.NEW,
                jsonResult );
    }

    @Override
    public void skipTimer( long woitRefNum ){
        workItemStateDao.updateDueDate( woitRefNum, new Date(), WorkItemStatus.NEW );
    }

    @Override
    public boolean assignHumanTask( long woitRefNum, String user ){
        return workItemStateDao.updateUserName( woitRefNum, WorkItemStatus.NEW, user );
    }

    @Override
    public void submitTask( long woitRefNum, Object result ){
        validateClusterNameOfWorkItem( woitRefNum );
        String jsonResult = JsonUtil.serialize( result, true );
        WorkItemStatus expectedStatus = WorkItemStatus.NEW;
        boolean updateFailed = !workItemDao.updateStatusAndResult( woitRefNum, WorkItemStatus.EXECUTED, expectedStatus, jsonResult );
        if( updateFailed ){
            throw new UnexpectedStatusException( expectedStatus );
        }
    }

    @Override
    public void submitHumanTask( long woitRefNum, Object result ){
        validateClusterNameOfWorkItem( woitRefNum );
        String jsonResult = JsonUtil.serialize( result, true );
        WorkItemStatus expectedStatus = WorkItemStatus.NEW;
        boolean updateFailed = !workItemDao.updateStatusAndResult( woitRefNum, WorkItemStatus.EXECUTED, expectedStatus, jsonResult );
        if( updateFailed ){
            throw new UnexpectedStatusException( expectedStatus );
        }
    }

    @Override
    public WorkflowInstanceState findWorkflowInstance( long woinRefNum, Boolean isActive ){
        WorkflowInstanceState result;
        if( isActive == null || isActive ){
            result = workflowInstanceStateDao.find( woinRefNum, true );
            if( result == null ){
                result = workflowInstanceStateDao.find( woinRefNum, false );
            }
        }
        else{
            result = workflowInstanceStateDao.find( woinRefNum, false );
        }
        return result;
    }

    @Override
    public List findWorkflowInstancesByLabel1( String label1, boolean activeOnly ){
        return workflowInstanceStateDao.findByLabel1( label1, activeOnly );
    }

    @Override
    public List findWorkflowInstancesByLabels( String label1, String label2, boolean activeOnly ){
        return workflowInstanceStateDao.findByLabels( label1, label2, activeOnly );
    }

    @Override
    public List findWorkflowInstances( SearchWorkflowInstances request ){
        return workflowInstanceStateDao.find( request );
    }

    @Override
    public WorkItemState findWorkItem( long woitRefNum, Boolean isInstanceActive ){
        WorkItemState result;
        if( isInstanceActive == null || isInstanceActive ){
            result = workItemStateDao.find( woitRefNum, true );
            if( result == null ){
                result = workItemStateDao.find( woitRefNum, false );
            }
        }
        else{
            result = workItemStateDao.find( woitRefNum, false );
        }
        return result;
    }

    @Override
    public WorkItemState findActiveWorkItemByTokenId( long woinRefNum, int tokenId ){
        return workItemStateDao.findActive( woinRefNum, tokenId );
    }

    @Override
    public List findWorkItems( long woinRefNum, Boolean isInstanceActive ){
        List result;
        if( isInstanceActive == null || isInstanceActive ){
            result = workItemStateDao.findByWoinRefNum( woinRefNum, true );
            if( result.isEmpty() ){
                result = workItemStateDao.findByWoinRefNum( woinRefNum, false );
            }
        }
        else{
            result = workItemStateDao.findByWoinRefNum( woinRefNum, false );
        }
        return result;
    }

    @Override
    public List findActiveHumanTasksByRole( String role ){
        return workItemStateDao.findActiveByRole( role );
    }

    @Override
    public List findActiveHumanTasksByUser( String user ){
        return workItemStateDao.findActiveByUser( user );
    }

    @Override
    public List findActiveHumanTasksByRoleAndUser( String role, String user ){
        return workItemStateDao.findActiveByRoleAndUser( role, user );
    }

    @Override
    public ExecutionErrorState findExecutionError( long woinRefNum ){
        validateClusterNameOfWorkflowInstance( woinRefNum );
        ExecutionError error = executionErrorDao.findByWoinRefNum( woinRefNum );
        if( error == null ){
            return null;
        }
        else{
            ExecutionErrorState result = new ExecutionErrorState();
            result.setRefNum( error.getRefNum() );
            result.setWoinRefNum( error.getWoinRefNum() );
            result.setWoitRefNum( error.getWoitRefNum() );
            result.setErrorText( error.getErrorText() );
            result.setErrorDetails( error.getErrorDetails() );
            return result;
        }
    }

    @Override
    public Map getNextActiveTimerDueDates( List woinRefNums ){
        return workItemStateDao.findNextActiveTimerDueDates( woinRefNums );
    }

    @Override
    public Set getWorkflowInstancesWithActiveHumanTask( List woinRefNums ){
        return workItemStateDao.findHasActiveHumanTask( woinRefNums );
    }

    @Override
    public Set getDeployedWorkflowNames(){
        Set result = new TreeSet<>( String.CASE_INSENSITIVE_ORDER );
        for( Graph graph : graphEngineFactory.getGraphs() ){
            result.add( graph.getName() );
        }
        return result;
    }

    @Override
    public Set getKnownWorkflowNames(){
        Set result = new TreeSet<>( String.CASE_INSENSITIVE_ORDER );
        result.addAll( getDeployedWorkflowNames() );
        result.addAll( workflowInstanceStateDao.findWorkflowNamesWithInstances() );
        return result;
    }

    @Override
    public Map> getWorkflowStatistics(){
        Map> statistics = createMapWithExistingGraphNames();
        List workflowStatusCounts = workflowInstanceStateDao.findWorklowStatusCount();
        for( WorkflowStatusCount workflowStatusCount : workflowStatusCounts ){
            Map workflowStatistics = statistics.get( workflowStatusCount.getWorkflowName() );
            if( workflowStatistics == null ){
                workflowStatistics = new HashMap<>();
                statistics.put( workflowStatusCount.getWorkflowName(), workflowStatistics );
            }
            WorkflowInstanceFacadeStatus status = StatusUtil.toFacade( workflowStatusCount.getStatus() );
            Integer count = workflowStatistics.get( status );
            if( count == null ){
                count = 0;
            }
            count = count + workflowStatusCount.getCount();
            workflowStatistics.put( status, count );
        }

        return statistics;
    }

    private Map> createMapWithExistingGraphNames(){
        Map> groupedByWorkflowNameAndStatus = new TreeMap<>();
        for( Graph graph : graphEngineFactory.getGraphs() ){
            groupedByWorkflowNameAndStatus.put( graph.getName(), new HashMap() );
        }
        return groupedByWorkflowNameAndStatus;
    }

    @Override
    public boolean isNodeInMasterRole(){
        return lockService.refreshOwnLock();
    }

    // Please see the class level documentation for a reasoning on this method.
    private void validateClusterNameOfWorkflowInstance( long woinRefNum ){
        WorkflowInstance woin = workflowInstanceService.find( woinRefNum );
        String clusterName = config.getClusterName();
        if( !ObjectUtils.equals( clusterName, woin.getClusterName() ) ){
            throw new IllegalArgumentException( "The workflow instance " + woinRefNum + " is not executed by this cluster " + clusterName );
        }
    }

    // Please see the class level documentation for a reasoning on this method.
    private void validateClusterNameOfWorkItem( long woitRefNum ){
        WorkItem woit = workItemDao.findByRefNum( woitRefNum );
        validateClusterNameOfWorkflowInstance( woit.getWoinRefNum() );
    }

    private void validateCurrentlyNotExecuting( long woinRefNum ) {
        WorkflowInstance woin = workflowInstanceService.find( woinRefNum );
        if ( !woin.isLocked() ) {
            return;
        }
        if ( WorkflowInstanceStatus.STARTING.equals( woin.getStatus() )
            || WorkflowInstanceStatus.ABORTING.equals( woin.getStatus() ) ) {
            throw new UnexpectedStatusException( "The workflow instance is currently executing" );
        }
        List woits = workItemDao.findActiveByWoinRefNum( woinRefNum );
        for (WorkItem woit : woits) {
            if ( WorkItemStatus.EXECUTING.equals( woit.getStatus() )
                || WorkItemStatus.COMPLETING.equals( woit.getStatus() ) ) {
                throw new UnexpectedStatusException( "The workflow instance is currently executing" );
            }
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy