ee.telekom.workflow.facade.WorkflowEngineFacadeImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of workflow-engine Show documentation
Show all versions of workflow-engine Show documentation
Telekom-workflow-engine core provides the runtime environment for workflow execution together with all the supporting services (clustering, persistence, error handling etc).
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" );
}
}
}
}