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

brainslug.flow.execution.token.TokenFlowExecutor Maven / Gradle / Ivy

The newest version!
package brainslug.flow.execution.token;

import brainslug.flow.definition.DefinitionStore;
import brainslug.flow.definition.Identifier;
import brainslug.flow.context.*;
import brainslug.flow.execution.*;
import brainslug.flow.execution.async.AsyncTriggerScheduler;
import brainslug.flow.execution.async.AsyncTriggerStore;
import brainslug.flow.execution.expression.ExpressionEvaluator;
import brainslug.flow.execution.instance.InstanceSelector;
import brainslug.flow.execution.instance.InstanceStore;
import brainslug.flow.execution.node.*;
import brainslug.flow.execution.node.task.CallDefinitionExecutor;
import brainslug.flow.execution.node.task.ScriptExecutor;
import brainslug.flow.execution.property.store.PropertyStore;
import brainslug.flow.execution.instance.FlowInstance;
import brainslug.flow.execution.instance.FlowInstanceProperty;
import brainslug.flow.listener.EventType;
import brainslug.flow.listener.ListenerManager;
import brainslug.flow.node.*;
import brainslug.util.Option;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;

public class TokenFlowExecutor implements FlowExecutor {

  private final ExecutionContextFactory executionContextFactory;
  private TokenOperations tokenOperations;

  private Logger log = LoggerFactory.getLogger(TokenFlowExecutor.class);

  protected TokenStore tokenStore;
  protected InstanceStore instanceStore;
  protected DefinitionStore definitionStore;
  protected PropertyStore propertyStore;
  protected ListenerManager listenerManager;
  protected Registry registry;
  protected ExpressionEvaluator expressionEvaluator;
  protected AsyncTriggerStore asyncTriggerStore;
  protected AsyncTriggerScheduler asyncTriggerScheduler;
  protected CallDefinitionExecutor callDefinitionExecutor;
  protected ScriptExecutor scriptExecutor;

  Map, FlowNodeExecutor> nodeExecutors = new HashMap, FlowNodeExecutor>();


  public TokenFlowExecutor(TokenStore tokenStore,
                           InstanceStore instanceStore,
                           DefinitionStore definitionStore,
                           PropertyStore propertyStore,
                           ListenerManager listenerManager,
                           Registry registry,
                           ExpressionEvaluator expressionEvaluator,
                           AsyncTriggerStore asyncTriggerStore,
                           AsyncTriggerScheduler asyncTriggerScheduler,
                           CallDefinitionExecutor callDefinitionExecutor,
                           ScriptExecutor scriptExecutor) {
    this.tokenStore = tokenStore;
    this.instanceStore = instanceStore;
    this.definitionStore = definitionStore;
    this.propertyStore = propertyStore;
    this.listenerManager = listenerManager;
    this.registry = registry;
    this.expressionEvaluator = expressionEvaluator;
    this.asyncTriggerStore = asyncTriggerStore;
    this.asyncTriggerScheduler = asyncTriggerScheduler;
    this.callDefinitionExecutor = callDefinitionExecutor;
    this.scriptExecutor = scriptExecutor;

    this.tokenOperations = new TokenOperations(tokenStore);
    this.executionContextFactory = new ExecutionContextFactory(propertyStore, registry);

    addNodeExecutorMappings();
  }

  protected void addNodeExecutorMappings() {
    nodeExecutors.put(EventDefinition.class, new EventNodeExecutor(asyncTriggerStore, expressionEvaluator));
    nodeExecutors.put(ParallelDefinition.class, new DefaultNodeExecutor());
    nodeExecutors.put(MergeDefinition.class, new DefaultNodeExecutor());
    nodeExecutors.put(ChoiceDefinition.class, new ChoiceNodeExecutor(expressionEvaluator));
    nodeExecutors.put(JoinDefinition.class, new JoinNodeExecutor());
    nodeExecutors.put(TaskDefinition.class, createTaskNodeExecutor());
  }

  protected TaskNodeExecutor createTaskNodeExecutor() {
    return new TaskNodeExecutor(definitionStore, expressionEvaluator, callDefinitionExecutor, asyncTriggerScheduler, scriptExecutor);
  }

  FlowNodeDefinition getNode(Identifier definitionId, Identifier nodeId) {
    FlowNodeDefinition node = definitionStore.findById(definitionId).getNode(nodeId);
    if (node == null) {
      throw new IllegalArgumentException(String.format("node for does not exist %s", nodeId));
    }
    return node;
  }

  protected  FlowNodeExecutor getNodeExecutor(T nodeDefinition) {
    FlowNodeExecutor nodeExecutor = nodeExecutors.get(nodeDefinition.getClass());
    if (nodeExecutor == null) {
      throw new IllegalArgumentException(String.format("no executor found for node definition %s", nodeDefinition));
    }
    return nodeExecutor;
  }

  @Override
  public FlowInstance startFlow(TriggerContext trigger) {
    FlowNodeDefinition startNode = getStartNodeDefinition(trigger.getDefinitionId(), trigger.getNodeId());

    FlowInstance instance = instanceStore.createInstance(trigger.getDefinitionId());
    tokenStore.addToken(instance.getIdentifier(), startNode.getId(), Option.empty(), false);

    storeProperties(trigger, instance.getIdentifier());

    trigger(new Trigger()
            .nodeId(startNode.getId())
            .definitionId(trigger.getDefinitionId())
            .instanceId(instance.getIdentifier())
            .properties(trigger.getProperties()));

    // get a fresh copy of the instance after triggering
    return instanceStore.findInstance(new InstanceSelector().withInstanceId(instance.getIdentifier())).get();
  }

  protected void storeProperties(TriggerContext trigger, Identifier instanceId) {
    for (FlowInstanceProperty property : trigger.getProperties().values()) {
      if (!property.isTransient()) {
        propertyStore.setProperty(instanceId, property);
      }
    }
  }

  protected FlowNodeDefinition getStartNodeDefinition(Identifier definitionId, Identifier nodeId) {
    return getNode(definitionId, nodeId);
  }

  @Override
  public void trigger(final TriggerContext trigger) {
    log.debug("triggering {}", trigger);

    if (trigger.getInstanceId() == null) {
      throw new IllegalArgumentException("instance not specified for trigger");
    }

    final FlowNodeDefinition node = getNode(trigger.getDefinitionId(), trigger.getNodeId());

    FlowInstance flowInstance = instanceStore
            .findInstance(new InstanceSelector().withInstanceId(trigger.getInstanceId()))
            .get();

    final FlowNodeExecutor nodeExecutor = getNodeExecutor(node);

    final ExecutionContext executionContext = executionContextFactory.createExecutionContext(flowInstance, trigger);

    FlowNodeExecutionResult executionResult = executeWithListeners(trigger, new Callable() {
      @Override
      public FlowNodeExecutionResult call() throws Exception {
        FlowNodeExecutionResult executionResult = nodeExecutor.execute(node, executionContext);
        storeProperties(trigger, trigger.getInstanceId());
        return executionResult;
      }
    });

    triggerNextOnSuccess(trigger, node, executionResult);
  }

  private void triggerNextOnSuccess(TriggerContext trigger, FlowNodeDefinition node, FlowNodeExecutionResult executionResult) {
    if (!executionResult.isFailed()) {
      removeTokens(trigger, executionResult);
      triggerNext(trigger, node, executionResult);
    } else {
      Exception cause = executionResult.getException().orElse(new RuntimeException());
      throw new FlowNodeExecutionException(cause).setTriggerContext(trigger);
    }
  }

   T executeWithListeners(TriggerContext trigger, Callable callable) {
    try {
      listenerManager.notifyListeners(EventType.BEFORE_EXECUTION, trigger);
      T result = callable.call();
      listenerManager.notifyListeners(EventType.AFTER_EXECUTION, trigger);
      return result;
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  private void removeTokens(TriggerContext trigger, FlowNodeExecutionResult executionResult) {
    for (FlowNodeExecutionResult.TokenRemoval removedToken : executionResult.getRemovedTokens()) {
      tokenOperations.removeTokens(trigger.getInstanceId(), removedToken.getNodeId(), removedToken.getSourceId(), removedToken.getQuantity());
    }
  }

  protected void triggerNext(TriggerContext trigger, FlowNodeDefinition node, FlowNodeExecutionResult flowNodeExecutionResult) {
    for (FlowNodeDefinition nextNode : flowNodeExecutionResult.getNextNodes()) {
      addToken(trigger, node, nextNode);
      trigger(createTriggerContextForNextNode(trigger, nextNode));
    }
  }

  protected Trigger createTriggerContextForNextNode(TriggerContext event, FlowNodeDefinition nextNode) {
    return new Trigger()
          .nodeId(nextNode.getId())
          .definitionId(event.getDefinitionId())
          .instanceId(event.getInstanceId())
          .properties(event.getProperties());
  }

  protected void addToken(TriggerContext trigger, FlowNodeDefinition node, FlowNodeDefinition nextNode) {
    tokenStore.addToken(trigger.getInstanceId(), nextNode.getId(), Option.of(node.getId()), isFinalToken(nextNode));
  }

  private boolean isFinalToken(FlowNodeDefinition nextNode) {
    return nextNode.getOutgoing().isEmpty();
  }

  public void setTokenStore(TokenStore tokenStore) {
    this.tokenStore = tokenStore;
  }

  public void setTokenOperations(TokenOperations tokenOperations) {
    this.tokenOperations = tokenOperations;
  }

  public void setDefinitionStore(DefinitionStore definitionStore) {
    this.definitionStore = definitionStore;
  }

  public void setPropertyStore(PropertyStore propertyStore) {
    this.propertyStore = propertyStore;
  }

  public void setListenerManager(ListenerManager listenerManager) {
    this.listenerManager = listenerManager;
  }

  public void setRegistry(Registry registry) {
    this.registry = registry;
  }

  public void setExpressionEvaluator(ExpressionEvaluator expressionEvaluator) {
    this.expressionEvaluator = expressionEvaluator;
  }

  public void setAsyncTriggerStore(AsyncTriggerStore asyncTriggerStore) {
    this.asyncTriggerStore = asyncTriggerStore;
  }

  public void setAsyncTriggerScheduler(AsyncTriggerScheduler asyncTriggerScheduler) {
    this.asyncTriggerScheduler = asyncTriggerScheduler;
  }

  public void setCallDefinitionExecutor(CallDefinitionExecutor callDefinitionExecutor) {
    this.callDefinitionExecutor = callDefinitionExecutor;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy