package ee.telekom.workflow.graph.core;
import java.util.Collection;
import java.util.Date;
import java.util.Map;
import ee.telekom.workflow.graph.BeanResolver;
import ee.telekom.workflow.graph.Environment;
import ee.telekom.workflow.graph.Graph;
import ee.telekom.workflow.graph.GraphEngine;
import ee.telekom.workflow.graph.GraphInstance;
import ee.telekom.workflow.graph.GraphInstanceEventListener;
import ee.telekom.workflow.graph.GraphRepository;
import ee.telekom.workflow.graph.GraphWorkItem;
import ee.telekom.workflow.graph.GraphWorkItemEventListener;
import ee.telekom.workflow.graph.NewGraphInstanceCreator;
import ee.telekom.workflow.graph.Node;
import ee.telekom.workflow.graph.NodeEventListener;
import ee.telekom.workflow.graph.Token;
import ee.telekom.workflow.graph.Transition;
import ee.telekom.workflow.graph.WorkItemStatus;
import ee.telekom.workflow.graph.WorkflowException;
public class GraphEngineImpl implements GraphEngine{
private GraphRepository repository = new GraphRepositoryImpl();
private BeanResolver beanResolver;
private NewGraphInstanceCreator newGraphInstanceCreator;
private EventNotifier notifier = new EventNotifier();
@Override
public GraphInstanceImpl start( String graphName ){
return start( graphName, null, null, null );
}
@Override
public GraphInstanceImpl start( String graphName, Integer version ){
return start( graphName, null, null );
}
@Override
public GraphInstanceImpl start( String graphName, Integer version, Environment initialEnvironment ){
return start( graphName, version, initialEnvironment, null );
}
@Override
public GraphInstanceImpl start( String graphName, Integer version, Environment initialEnvironment, Long externalId ){
if( graphName == null ){
throw new WorkflowException( "Can not start workflow for a null graph name" );
}
Graph graph = getRepository().getGraph( graphName, version );
if( graph == null ){
throw new WorkflowException( "No graph found with name '" + graphName + "'" );
}
return start( graph, initialEnvironment, externalId );
}
@Override
public GraphInstanceImpl start( Graph graph ){
return start( graph, null );
}
@Override
public GraphInstanceImpl start( Graph graph, Environment initialEnvironment ){
return start( graph, initialEnvironment, null );
}
@Override
public GraphInstanceImpl start( Graph graph, Environment initialEnvironment, Long externalId ){
if( graph == null ){
throw new WorkflowException( "Can not workflow for a null graph" );
}
GraphInstanceImpl instance = new GraphInstanceImpl( graph );
instance.setExternalId( externalId );
if( initialEnvironment != null ){
instance.getEnvironment().importEnvironment( initialEnvironment );
}
notifier.fireCreated( instance );
start( instance );
return instance;
}
private void start( GraphInstance instance ){
Node node = instance.getGraph().getStartNode();
if( node != null ){
Token mainToken = addToken( instance, node, null );
instance.addToExecutionQueue( mainToken );
}
instance.addToHistory( "start" );
execute( instance );
notifier.fireStarted( instance );
if( instance.isCompleted() ){
notifier.fireCompleted( instance );
}
}
@Override
public void abort( GraphInstance instance ){
notifier.fireAborting( instance );
instance.addToHistory( "abort" );
for( Token token : instance.getActiveTokens() ){
// preventing double-cancelling
if( token.isActive() ){
token.getNode().cancel( this, token );
token.markInactive();
}
}
instance.addToHistory( "aborted" );
notifier.fireAborted( instance );
}
@Override
public void complete( GraphWorkItem workItem ){
Token token = workItem.getToken();
GraphInstance instance = token.getInstance();
instance.addToHistory( "continue:" + token.getId() );
instance.addToExecutionQueue( token );
complete( token, workItem.getResult() );
execute( instance );
if( instance.isCompleted() ){
notifier.fireCompleted( instance );
}
}
@Override
public GraphRepository getRepository(){
return repository;
}
public void setRepository( GraphRepository repository ){
this.repository = repository;
}
@Override
public BeanResolver getBeanResolver(){
if( beanResolver == null ){
throw new WorkflowException( "This engine does not provide a BeanResolver." );
}
return beanResolver;
}
public void setBeanResolver( BeanResolver beanResolver ){
this.beanResolver = beanResolver;
}
@Override
public NewGraphInstanceCreator getNewGraphInstanceCreator(){
if( beanResolver == null ){
throw new WorkflowException( "This engine does not provide a NewGraphInstanceCreator." );
}
return newGraphInstanceCreator;
}
public void setNewGraphInstanceCreator( NewGraphInstanceCreator newGraphInstanceCreator ){
this.newGraphInstanceCreator = newGraphInstanceCreator;
}
@Override
public void registerInstanceEventListener( GraphInstanceEventListener listener ){
notifier.registerInstanceEventListener( listener );
}
@Override
public void registerWorkItemEventListener( GraphWorkItemEventListener listener ){
notifier.registerWorkItemEventListener( listener );
}
@Override
public void registerNodeEventListener( NodeEventListener listener ){
notifier.registerNodeEventListener( listener );
}
private void execute( GraphInstance instance ){
Token token = instance.getFirstFromExecutionQueue();
while( token != null ){
boolean execute = true;
while( execute ){
if( token.isActive() ){
Node node = token.getNode();
notifier.fireEntering( token, node );
instance.addToHistory( token.getId() + ":" + node.getId() + "!" );
node.execute( this, token );
execute = node != token.getNode();
}
else{
execute = false;
}
}
instance.removeFirstFromExecutionQueue();
token = instance.getFirstFromExecutionQueue();
}
if( instance.isCompleted() ){
instance.addToHistory( "executed" );
}
else{
instance.addToHistory( "waitstate" );
}
}
@Override
public TokenImpl addToken( GraphInstance instance, Node node, Token parent ){
TokenImpl token = new TokenImpl();
token.setId( instance.nextTokenId() );
token.setNode( node );
token.setInstance( instance );
token.setParent( parent );
token.setActive( true );
instance.addToken( token );
return token;
}
@Override
public GraphWorkItem addSignalItem( GraphInstance instance, Token token, String signal ){
GraphWorkItem wi = GraphWorkItemImpl.createSignalItem( token, signal );
instance.addWorkItem( wi );
notifier.fireCreated( wi );
return wi;
}
@Override
public GraphWorkItem addTimerItem( GraphInstance instance, Token token, Date dueDate ){
GraphWorkItem wi = GraphWorkItemImpl.createTimerItem( token, dueDate );
instance.addWorkItem( wi );
notifier.fireCreated( wi );
return wi;
}
@Override
public GraphWorkItem addTaskItem( GraphInstance instance, Token token, String bean, String method, Object[] arguments ){
GraphWorkItem wi = GraphWorkItemImpl.createTaskItem( token, bean, method, arguments );
instance.addWorkItem( wi );
notifier.fireCreated( wi );
return wi;
}
@Override
public GraphWorkItem addHumanTaskItem( GraphInstance instance, Token token, String role, String user, Map<String, Object> arguments ){
GraphWorkItem wi = GraphWorkItemImpl.createHumanTaskItem( token, role, user, arguments );
instance.addWorkItem( wi );
notifier.fireCreated( wi );
return wi;
}
@Override
public void cancelWorkItem( Token token ){
GraphInstance instance = token.getInstance();
GraphWorkItem wi = instance.getActiveWorkItem( token );
if( wi != null ){
wi.setStatus( WorkItemStatus.CANCELLED );
notifier.fireCancelled( wi );
}
}
@Override
public void complete( Token token, Object result ){
complete( token, result, Transition.DEFAULT_TRANSITION_NAME );
}
@Override
public void complete( Token token, Object result, String transitionName ){
GraphInstance instance = token.getInstance();
Graph graph = instance.getGraph();
Node node = token.getNode();
// completing work item
GraphWorkItem wi = instance.getActiveWorkItem( token );
if( wi != null ){
wi.setStatus( WorkItemStatus.COMPLETED );
notifier.fireCompleted( wi );
}
// storing result in environment
node.store( instance.getEnvironment(), result );
// finding next node
Transition transition = graph.getOutputTransitions( node, transitionName );
Node nextNode = transition == null ? null : transition.getEndNode();
if( nextNode != null ){
instance.addToHistory( token.getId() + ":" + node.getId() + "=>" + nextNode.getId() );
token.setNode( nextNode );
if( token != instance.getFirstFromExecutionQueue() ){
instance.addToExecutionQueue( token );
}
}
else if( graph.getOutputTransitions( node ).isEmpty() ){
// Node is implicit end node
terminate( token );
}
notifier.fireLeft( token, node );
}
@Override
public void terminate( Token token ){
token.markInactive();
Token parent = token.getParent();
if( parent != null ){
Collection<Token> siblings = token.getInstance().getActiveChildTokens( parent );
if( siblings.isEmpty() ){
terminate( parent );
}
}
}
@Override
public void cancel( Token token ){
token.getNode().cancel( this, token );
token.markInactive();
}
}