package ee.telekom.workflow.core.workflowinstance;
import java.lang.invoke.MethodHandles;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import ee.telekom.workflow.core.common.UnexpectedStatusException;
import ee.telekom.workflow.core.common.WorkflowEngineConfiguration;
import ee.telekom.workflow.core.error.ExecutionErrorService;
import ee.telekom.workflow.executor.marshall.Marshaller;
@Service
@Transactional
public class WorkflowInstanceServiceImpl implements WorkflowInstanceService{
private static final Logger log = LoggerFactory.getLogger( MethodHandles.lookup().lookupClass() );
@Autowired
private WorkflowInstanceDao dao;
@Autowired
private ExecutionErrorService executionErrorService;
@Autowired
private WorkflowEngineConfiguration config;
@Override
public WorkflowInstance create( String workflowName, Integer workflowVersion, Map<String, Object> arguments, String label1, String label2 ){
WorkflowInstance woin = new WorkflowInstance();
woin.setWorkflowName( workflowName );
woin.setWorkflowVersion( workflowVersion );
woin.setAttributes( Marshaller.serializeAttributes( arguments ) );
woin.setLabel1( StringUtils.trimToNull( label1 ) );
woin.setLabel2( StringUtils.trimToNull( label2 ) );
woin.setClusterName( config.getClusterName() );
woin.setLocked( false );
woin.setStatus( WorkflowInstanceStatus.NEW );
dao.create( woin );
log.info( "Created workflow instance {}", woin.getRefNum() );
return woin;
}
@Override
public WorkflowInstance find( long refNum ){
return dao.findByRefNum( refNum );
}
@Override
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void markStarting( long refNum ) throws UnexpectedStatusException{
updateStatus( refNum, WorkflowInstanceStatus.STARTING, WorkflowInstanceStatus.NEW );
}
@Override
public void markExecuting( long refNum ) throws UnexpectedStatusException{
updateStatus( refNum, WorkflowInstanceStatus.EXECUTING, WorkflowInstanceStatus.STARTING );
}
@Override
public void markExecuted( long refNum ) throws UnexpectedStatusException{
updateStatus( refNum, WorkflowInstanceStatus.EXECUTED, WorkflowInstanceStatus.EXECUTING );
}
@Override
public void markAbort( long refNum ) throws UnexpectedStatusException{
Collection<WorkflowInstanceStatus> expectedStatuses = Arrays.asList(
WorkflowInstanceStatus.NEW,
WorkflowInstanceStatus.STARTING,
WorkflowInstanceStatus.STARTING_ERROR,
WorkflowInstanceStatus.EXECUTING,
WorkflowInstanceStatus.EXECUTING_ERROR,
WorkflowInstanceStatus.SUSPENDED );
updateStatus( refNum, WorkflowInstanceStatus.ABORT, expectedStatuses );
}
@Override
public void markAborting( long refNum ) throws UnexpectedStatusException{
updateStatus( refNum, WorkflowInstanceStatus.ABORTING, WorkflowInstanceStatus.ABORT );
}
@Override
public void markAborted( long refNum ) throws UnexpectedStatusException{
updateStatus( refNum, WorkflowInstanceStatus.ABORTED, WorkflowInstanceStatus.ABORTING );
}
@Override
public void assertIsExecuting( long refNum ) throws UnexpectedStatusException{
WorkflowInstanceStatus status = dao.findStatusByRefNum( refNum );
if ( !WorkflowInstanceStatus.EXECUTING.equals( status ) ){
throw new UnexpectedStatusException( WorkflowInstanceStatus.EXECUTING );
}
}
@Override
public void rewindAfterError( long refNum ) throws UnexpectedStatusException{
WorkflowInstance woin = find( refNum );
if( WorkflowInstanceStatus.STARTING_ERROR.equals( woin.getStatus() ) ){
markNewAfterStartingError( refNum );
}
else if( WorkflowInstanceStatus.ABORTING_ERROR.equals( woin.getStatus() ) ){
markAbortAfterAbortingError( refNum );
}
else if( WorkflowInstanceStatus.EXECUTING_ERROR.equals( woin.getStatus() ) ){
markExecutingAfterExecutingError( refNum );
}
else{
throw new UnexpectedStatusException( Arrays.asList(
WorkflowInstanceStatus.STARTING_ERROR,
WorkflowInstanceStatus.ABORTING_ERROR,
WorkflowInstanceStatus.EXECUTING_ERROR ) );
}
}
private void markNewAfterStartingError( long refNum ) throws UnexpectedStatusException{
updateStatus( refNum, WorkflowInstanceStatus.NEW, WorkflowInstanceStatus.STARTING_ERROR );
}
private void markExecutingAfterExecutingError( long refNum ) throws UnexpectedStatusException{
updateStatus( refNum, WorkflowInstanceStatus.EXECUTING, WorkflowInstanceStatus.EXECUTING_ERROR );
}
private void markAbortAfterAbortingError( long refNum ) throws UnexpectedStatusException{
updateStatus( refNum, WorkflowInstanceStatus.ABORT, WorkflowInstanceStatus.ABORTING_ERROR );
}
@Override
public void suspend( long refNum ) throws UnexpectedStatusException{
updateStatus( refNum, WorkflowInstanceStatus.SUSPENDED, WorkflowInstanceStatus.EXECUTING );
}
@Override
public void resume( long refNum ) throws UnexpectedStatusException{
updateStatus( refNum, WorkflowInstanceStatus.EXECUTING, WorkflowInstanceStatus.SUSPENDED );
}
@Override
public void handleStartingError( long woinRefNum, Exception exception ) throws UnexpectedStatusException{
executionErrorService.handleError( woinRefNum, null, exception );
Collection<WorkflowInstanceStatus> expectedStatuses = Arrays.asList(
WorkflowInstanceStatus.NEW,
WorkflowInstanceStatus.STARTING,
WorkflowInstanceStatus.EXECUTING,
WorkflowInstanceStatus.EXECUTED );
updateStatus( woinRefNum, WorkflowInstanceStatus.STARTING_ERROR, expectedStatuses );
}
@Override
public void handleAbortingError( long woinRefNum, Exception exception ) throws UnexpectedStatusException{
executionErrorService.handleError( woinRefNum, null, exception );
Collection<WorkflowInstanceStatus> expectedStatuses = Arrays.asList(
WorkflowInstanceStatus.ABORT,
WorkflowInstanceStatus.ABORTING,
WorkflowInstanceStatus.ABORTED );
updateStatus( woinRefNum, WorkflowInstanceStatus.ABORTING_ERROR, expectedStatuses );
}
@Override
public void handleCompleteError( long woinRefNum, Long woitRefNum, Exception exception ) throws UnexpectedStatusException{
executionErrorService.handleError( woinRefNum, woitRefNum, exception );
updateStatus( woinRefNum, WorkflowInstanceStatus.EXECUTING_ERROR, WorkflowInstanceStatus.EXECUTING );
}
@Override
public void lock( List<Long> refNums ){
dao.updateLock( refNums, true );
}
@Override
public void unlock( long refNum ){
dao.updateLockAndNodeName( refNum, false, null );
}
@Override
public void updateNodeName( long refNum, String nodeName ){
dao.updateNodeName( refNum, nodeName );
}
@Override
public void updateState( long refNum, String state ){
WorkflowInstanceStatus expectedStatus = WorkflowInstanceStatus.ABORTING;
boolean updateFailed = !dao.updateState( refNum, state, expectedStatus );
if( updateFailed ){
throw new UnexpectedStatusException( expectedStatus );
}
}
@Override
public void updateHistory( Long refNum, String history ){
WorkflowInstanceStatus expectedStatus = WorkflowInstanceStatus.ABORTING;
boolean updateFailed = !dao.updateHistory( refNum, history, expectedStatus );
if( updateFailed ){
throw new UnexpectedStatusException( expectedStatus );
}
}
@Override
public void recoverNotAssigned( String clusterName ){
int count = dao.recoverNotAssigned( clusterName );
log.info( "Recovered {} locked workflow instances not assigned to a node name for cluster {}", count, clusterName );
}
@Override
public void recoverNew( String nodeName ){
int count = dao.recover( nodeName, WorkflowInstanceStatus.NEW, WorkflowInstanceStatus.NEW );
log.info( "Recovered {} new workflow instances for node {}", count, nodeName );
}
@Override
public void recoverStarting( String nodeName ){
int count = dao.recover( nodeName, WorkflowInstanceStatus.STARTING, WorkflowInstanceStatus.NEW );
log.info( "Recovered {} starting workflow instances for node {}", count, nodeName );
}
@Override
public void recoverExecuting( String nodeName ){
int count = dao.recover( nodeName, WorkflowInstanceStatus.EXECUTING, WorkflowInstanceStatus.EXECUTING );
log.info( "Recovered {} executing workflow instances for node {}", count, nodeName );
}
@Override
public void recoverAbort( String nodeName ){
int count = dao.recover( nodeName, WorkflowInstanceStatus.ABORT, WorkflowInstanceStatus.ABORT );
log.info( "Recovered {} aborting workflow instances for node {}", count, nodeName );
}
@Override
public void recoverAborting( String nodeName ){
int count = dao.recover( nodeName, WorkflowInstanceStatus.ABORTING, WorkflowInstanceStatus.ABORT );
log.info( "Recovered {} abort workflow instances for node {}", count, nodeName );
}
private void updateStatus( long refNum, WorkflowInstanceStatus newStatus,
WorkflowInstanceStatus expectedStatus ) throws UnexpectedStatusException{
updateStatus( refNum, newStatus, Collections.singleton( expectedStatus ) );
}
private void updateStatus( long refNum, WorkflowInstanceStatus newStatus,
Collection<WorkflowInstanceStatus> expectedStatuses ) throws UnexpectedStatusException{
boolean updateFailed = !dao.updateStatus( refNum, newStatus, expectedStatuses );
if( updateFailed ){
throw new UnexpectedStatusException( expectedStatuses );
}
else{
log.info( "Updated the status of workflow instance {} to {}", refNum, newStatus );
}
}
}