Class Interpreter

java.lang.Object
com.scivicslab.pojoactor.workflow.Interpreter

public class Interpreter extends Object
Workflow interpreter that executes matrix-based workflow definitions.

The Interpreter reads workflow definitions in YAML or JSON format and executes them by invoking actions on registered actors. Workflows are represented as a matrix of states and actions, where each row defines state transitions and the actions to execute during those transitions.

This class maintains the current execution state, including the current row and state, and coordinates with an IIActorSystem to invoke actor methods dynamically.

Author:
devteam@scivics-lab.com
  • Field Details

    • logger

      protected Logger logger
    • code

      protected MatrixCode code
    • currentVertexIndex

      protected int currentVertexIndex
    • currentState

      protected String currentState
    • system

      protected IIActorSystem system
    • selfActorRef

      protected IIActorRef<?> selfActorRef
      Reference to the actor executing this interpreter (for Unix-style path resolution). When this interpreter is running inside an actor (e.g., Node extends Interpreter), this field holds a reference to that actor, enabling relative path resolution like "." (self), ".." (parent), "../sibling" (sibling actors), etc.
    • workflowBaseDir

      protected String workflowBaseDir
      Base directory for resolving relative workflow file paths. When set, runWorkflow will look for files relative to this directory.
  • Constructor Details

    • Interpreter

      public Interpreter()
  • Method Details

    • action

      public ActionResult action()
      Executes the actions in the current row of the workflow matrix.

      For each action in the current row, this method retrieves the corresponding actor(s) from the system and invokes the specified action by name with the provided arguments.

      Supports Unix-style actor path resolution when selfActorRef is set:

      • . or this - self (the current interpreter's actor)
      • .. - parent actor
      • ./* - all children
      • ../* - all siblings
      • ../sibling - specific sibling by name
      • ../web* - siblings matching wildcard pattern

      If selfActorRef is not set, falls back to absolute actor name lookup.

      Supports both legacy argument (String) and new arguments (List/Map) formats. If arguments is present, it is converted to JSON array format before passing to the actor.

      Returns:
      an ActionResult indicating success or failure
    • getCode

      public MatrixCode getCode()
      Returns the currently loaded workflow code.
      Returns:
      the MatrixCode representing the workflow
    • setCode

      public void setCode(MatrixCode code)
      Sets the workflow code directly (for testing purposes).
      Parameters:
      code - the workflow code to set
    • readYaml

      public void readYaml(InputStream yamlInput)
      Reads and parses a workflow definition from a YAML input stream.
      Parameters:
      yamlInput - the YAML input stream containing the workflow definition
    • readYaml

      public void readYaml(Path yamlPath) throws IOException
      Reads and parses a workflow definition from a YAML file.
      Parameters:
      yamlPath - the path to the YAML file containing the workflow definition
      Throws:
      IOException - if the file cannot be read
    • readYaml

      public void readYaml(Path yamlPath, Path overlayDir) throws IOException
      Reads and parses a workflow definition from a YAML file with overlay applied.

      This method applies the overlay configuration from the specified directory to the base workflow before loading. The overlay can modify vertices, add new steps, substitute variables, and apply name transformations.

      Example usage:

       interpreter.readYaml(
           Path.of("workflows/base/main-workflow.yaml"),
           Path.of("workflows/overlays/production")
       );
       
      Parameters:
      yamlPath - the path to the base YAML workflow file
      overlayDir - the directory containing overlay-conf.yaml
      Throws:
      IOException - if files cannot be read
      Since:
      2.9.0
    • readJson

      public void readJson(InputStream jsonInput) throws IOException
      Reads and parses a workflow definition from a JSON input stream.
      Parameters:
      jsonInput - the JSON input stream containing the workflow definition
      Throws:
      IOException - if an I/O error occurs during parsing
    • readXml

      public void readXml(InputStream xmlInput) throws IOException, ParserConfigurationException, SAXException
      Reads and parses a workflow definition from an XML input stream.

      The XML format follows this structure:

      
       <workflow name="workflow-name">
         <steps>
           <transition from="state1" to="state2">
             <action actor="actorName" method="methodName">argument</action>
           </transition>
         </steps>
       </workflow>
       
      Parameters:
      xmlInput - the XML input stream containing the workflow definition
      Throws:
      IOException - if an I/O error occurs during parsing
      ParserConfigurationException - if the XML parser cannot be configured
      SAXException - if the XML is malformed
    • execCode

      public ActionResult execCode()
      Executes the loaded workflow code.

      This method implements finite automaton semantics:

      1. Find a step whose from-state matches the current state
      2. Execute the actions in that step
      3. If any action returns false, skip to the next step and retry
      4. If all actions succeed, transition to the to-state

      This enables conditional branching: multiple steps can have the same from-state, and the first one whose actions all succeed will be taken.

      Returns:
      an ActionResult indicating success or failure
    • runUntilEnd

      public ActionResult runUntilEnd()
      Executes the workflow until reaching the "end" state.

      This method provides a self-contained way to run workflows that have a defined termination state. The workflow author defines transitions to the "end" state in the YAML file, and this method handles the execution loop automatically.

      The method repeatedly calls execCode() until:

      • The current state becomes "end" (success)
      • An action returns failure (error)
      • No matching state transition is found (error)
      • Maximum iterations exceeded (error - prevents infinite loops)

      Usage Example:

      
       // YAML workflow with "end" state:
       // steps:
       //   - states: ["0", "1"]
       //     actions:
       //       - actor: worker
       //         method: process
       //   - states: ["1", "end"]
       //     actions:
       //       - actor: worker
       //         method: finish
      
       Interpreter interpreter = new Interpreter.Builder()
           .loggerName("workflow")
           .team(system)
           .build();
      
       interpreter.readYaml(workflowStream);
       ActionResult result = interpreter.runUntilEnd();
      
       if (result.isSuccess()) {
           System.out.println("Workflow completed successfully");
       } else {
           System.out.println("Workflow failed: " + result.getResult());
       }
       
      Returns:
      an ActionResult with success=true if "end" state reached, or success=false with error message if workflow failed
      Since:
      2.8.0
      See Also:
    • runWorkflow

      public ActionResult runWorkflow(String workflowFile)
      Loads and runs a workflow file to completion.

      This is a convenience method that combines loading a workflow and running it until the "end" state is reached. The workflow file is reloaded each time this method is called, ensuring fresh state.

      The method:

      1. Resets the interpreter state
      2. Loads the workflow from classpath or file system
      3. Runs until "end" state is reached
      Parameters:
      workflowFile - the workflow file path (YAML or JSON)
      Returns:
      ActionResult with success=true if completed, false otherwise
      Since:
      2.8.0
    • runWorkflow

      public ActionResult runWorkflow(String workflowFile, int maxIterations)
      Loads and runs a workflow file to completion with custom iteration limit.
      Parameters:
      workflowFile - the workflow file path (YAML or JSON)
      maxIterations - maximum number of state transitions allowed
      Returns:
      ActionResult with success=true if completed, false otherwise
      Since:
      2.8.0
    • runUntilEnd

      public ActionResult runUntilEnd(int maxIterations)
      Executes the workflow until reaching the "end" state with a custom iteration limit.

      This method runs the workflow with the following termination conditions:

      • Success: current state becomes "end" (automaton accepted)
      • Failure: an action fails and no alternative row matches the current state
      • Failure: maxIterations exceeded without reaching "end" (automaton did not accept)
      Parameters:
      maxIterations - maximum number of state transitions allowed
      Returns:
      an ActionResult with success=true if "end" state reached, or success=false with error message if workflow failed or iterations exceeded
      Since:
      2.8.0
      See Also:
    • hasCodeLoaded

      public boolean hasCodeLoaded()
      Checks if workflow code is loaded.
      Returns:
      true if code is loaded and has at least one step
    • matchesCurrentState

      public boolean matchesCurrentState(Vertex vertex)
      Checks if a vertex's from-state matches the current state.

      A vertex matches if it has at least 2 states (from and to) and the from-state pattern (index 0) matches the interpreter's current state.

      Supported state patterns:

      • Exact match: "1" - matches only state "1"
      • Wildcard: "*" - matches any state
      • Negation: "!end" - matches any state except "end"
      • OR condition: "1|2|3" - matches "1", "2", or "3"
      • Numeric comparison: ">=1", "<=5", ">1", "<5"
      Parameters:
      vertex - the vertex to check
      Returns:
      true if the vertex's from-state pattern matches current state
    • matchesStatePattern

      protected boolean matchesStatePattern(String pattern, String state)
      Checks if a state pattern matches a given state value.

      Supported patterns:

      • Exact match: "1" matches only "1"
      • Wildcard: "*" matches any state
      • Negation: "!end" matches anything except "end"
      • OR condition: "1|2|3" matches "1", "2", or "3"
      • Numeric comparison: ">=1", "<5" etc.
      • JEXL expression: "jexl:state >= 5 && state < 10"
      Parameters:
      pattern - the state pattern (may contain wildcards, operators, etc.)
      state - the actual state value to match against
      Returns:
      true if the pattern matches the state
    • getToState

      public String getToState(Vertex vertex)
      Gets the to-state from a vertex.
      Parameters:
      vertex - the vertex containing the state transition
      Returns:
      the to-state (second element), or null if not present
    • transitionTo

      public void transitionTo(String toState)
      Transitions to a new state and finds the next matching row.

      This method:

      1. Updates the current state to the specified to-state
      2. Searches for the first step whose from-state matches the new current state
      3. Updates currentVertexIndex to point to that step
      Parameters:
      toState - the state to transition to
    • findNextMatchingVertex

      protected void findNextMatchingVertex()
      Finds the first step whose from-state pattern matches the current state.

      Searches from the beginning of the steps list and updates currentVertexIndex to the index of the first matching step. Supports state patterns including wildcards, negations, and numeric comparisons.

    • getCurrentState

      public String getCurrentState()
      Gets the current state of the interpreter.
      Returns:
      the current state string
    • getCurrentVertexIndex

      public int getCurrentVertexIndex()
      Gets the current step index.
      Returns:
      the current step index
    • reset

      public void reset()
      Resets the interpreter to its initial state.

      This method resets the execution state of the interpreter, allowing it to be reused for executing a new workflow without creating a new instance. The following state is reset:

      • Current row index is reset to 0
      • Current state is reset to "0" (initial state)
      • Loaded workflow code is cleared

      The following state is preserved:

      • Logger instance
      • Actor system reference

      This method is primarily used by ReusableSubWorkflowCaller to reuse a single Interpreter instance across multiple sub-workflow calls, reducing object allocation overhead.

      Usage Example:

      
       Interpreter interpreter = new Interpreter.Builder()
           .loggerName("workflow")
           .team(system)
           .build();
      
       // First workflow execution
       interpreter.readYaml(workflow1);
       while (true) {
           ActionResult result = interpreter.execCode();
           if (result.getResult().contains("end")) break;
       }
      
       // Reset and reuse for second workflow
       interpreter.reset();
       interpreter.readYaml(workflow2);
       while (true) {
           ActionResult result = interpreter.execCode();
           if (result.getResult().contains("end")) break;
       }
       
      Since:
      2.5.0
      See Also:
    • onEnterVertex

      protected void onEnterVertex(Vertex vertex)
      Hook method called when entering a vertex during workflow execution.

      This method is called just before executing the actions of a matching vertex. Subclasses can override this method to provide custom behavior such as logging, visualization, or debugging output.

      The default implementation does nothing.

      Parameters:
      vertex - the vertex being entered
      Since:
      2.9.0
    • setSelfActorRef

      public void setSelfActorRef(IIActorRef<?> actorRef)
      Sets the reference to the actor executing this interpreter.

      This method is called by IIActorRef implementations to establish the link between the interpreter and its actor wrapper, enabling Unix-style path resolution within workflows.

      Parameters:
      actorRef - the actor reference wrapping this interpreter
    • setWorkflowBaseDir

      public void setWorkflowBaseDir(String baseDir)
      Sets the base directory for resolving relative workflow file paths.
      Parameters:
      baseDir - the directory path
    • getWorkflowBaseDir

      public String getWorkflowBaseDir()
      Gets the base directory for resolving relative workflow file paths.
      Returns:
      the directory path, or null if not set
    • generateChildName

      protected String generateChildName(String workflowFile)
      Generates a unique child actor name for subworkflow execution.

      The name format is: subwf-{workflowName}-{timestamp}-{random}

      Example: subwf-user-validation-1735812345678-04821

      The combination of timestamp and random number ensures collision-free naming even under parallel invocation or nested subworkflows.

      Parameters:
      workflowFile - the workflow file name (e.g., "user-validation.yaml")
      Returns:
      a unique child actor name
      Since:
      2.9.0
    • call

      public ActionResult call(String workflowFile)
      Calls a subworkflow by creating a child interpreter, executing it, and removing it.

      This method implements the 4-step subworkflow pattern:

      1. createChild — Create a child InterpreterIIAR and register it
      2. loadWorkflow — Load the YAML workflow into the child interpreter
      3. runUntilEnd — Execute the subworkflow until "end" state
      4. removeChild — Remove the child actor (always executed via finally)

      The child interpreter shares the same IIActorSystem as the parent, so all registered actors are accessible from the subworkflow.

      Usage in YAML:

      
       - states: ["0", "1"]
         actions:
           - actor: this
             method: call
             arguments: ["sub-workflow.yaml"]
       
      Parameters:
      workflowFile - the workflow file name to execute
      Returns:
      ActionResult indicating success or failure
      Since:
      2.9.0
    • loadWorkflowFromClasspath

      protected InputStream loadWorkflowFromClasspath(String workflowFile)
      Loads a workflow from the classpath.

      Searches for the workflow in the following locations:

      1. /workflows/{workflowFile}
      2. /{workflowFile}
      Parameters:
      workflowFile - the workflow file name
      Returns:
      InputStream for the workflow, or null if not found
    • removeChildActor

      protected void removeChildActor(String childName)
      Removes a child actor from the system and parent-child relationship.
      Parameters:
      childName - the name of the child actor to remove
    • apply

      public ActionResult apply(String actionDefinition)
      Applies an action to existing child actors (with wildcard support).

      Unlike call(String) which creates and deletes child actors, this method operates on existing child actors without removing them.

      Supports wildcard patterns for actor names:

      • * — all child actors
      • Species-* — child actors starting with "Species-"
      • *-worker — child actors ending with "-worker"
      • node-*-primary — child actors matching the pattern

      Usage in YAML:

      
       - actor: this
         method: apply
         arguments:
           - actor: "Species-*"
             method: mutate
             arguments: [0.05, 0.02, 0.5]
       
      Parameters:
      actionDefinition - JSON string containing actor, method, and arguments
      Returns:
      ActionResult indicating success or failure
      Since:
      2.9.0
    • findMatchingActors

      protected List<IIActorRef<?>> findMatchingActors(String pattern)
      Finds all actors in the system matching a wildcard pattern.

      This method searches all registered actors in the IIActorSystem, not just child actors of the current interpreter.

      Parameters:
      pattern - the pattern (exact name or wildcard like "Species-*")
      Returns:
      list of matching actors
      Since:
      2.9.0
    • findMatchingChildActors

      protected List<IIActorRef<?>> findMatchingChildActors(String pattern)
      Finds child actors matching a wildcard pattern.
      Parameters:
      pattern - the pattern (exact name or wildcard like "Species-*")
      Returns:
      list of matching child actors
    • addToAccumulator

      public ActionResult addToAccumulator(String type, String data)
      Adds a result to the accumulator actor in the system.

      This is a convenience method for child workflows to report results back to an accumulator. It looks up the accumulator actor by name (default: "accumulator") and calls its "add" action.

      Usage in YAML:

      
       - states: ["0", "1"]
         actions:
           - actor: this
             method: addToAccumulator
             arguments:
               type: "cpu"
               data: "Intel Xeon E5-2680"
       
      Parameters:
      type - the type of result (e.g., "cpu", "memory")
      data - the result data
      Returns:
      ActionResult indicating success or failure
      Since:
      2.8.0
    • addToAccumulator

      public ActionResult addToAccumulator(String accumulatorName, String type, String data)
      Adds a result to a named accumulator actor in the system.
      Parameters:
      accumulatorName - the name of the accumulator actor
      type - the type of result (e.g., "cpu", "memory")
      data - the result data
      Returns:
      ActionResult indicating success or failure
      Since:
      2.8.0