Working with Flows in Code

This page will discuss working with flows in the Flux Java API. For more general information on flows, see Workflows.

Let’s look at an example that starts with a Timer Trigger and flows into a Java Action. To start, you need to define a flow from the Timer Trigger to the Java Action.

This code creates a flow from the Timer Trigger into the Java Action. After the Timer Trigger fires, control flows into the Java Action.

TimerTrigger timerTrigger = workflow.makeTimerTrigger("My Timer Trigger");
...
JavaAction javaAction = workflow.makeJavaAction("My Java Action");
...
timerTrigger.addFlow(javaAction);

Let’s take this example a bit further. Suppose you wish to define a workflow that executes a Java Action every five seconds, indefinitely. The above workflow will execute your Java Action only once. To allow your Java Action to execute again after another five seconds, you must add a flow from the Java Action back up to the Timer Trigger.

javaAction.addFlow(timerTrigger);

In this manner, your Java Action will fire every five seconds, indefinitely.

Continuing with this example, suppose you want this Java Action to fire for a total of ten firings. To add in this restriction, you need to set a count on the Timer Trigger.

timerTrigger.setCount(10);

After the Timer Trigger fires ten times, control will not flow into the Java Action. Instead, it branches down a specialexpiration flow. This expiration flow allows you to perform different actions. Or, in the simplest case, your workflow can terminate.

Here is a complete example. This example may seem a bit long, but it’s basic. Just paste it into your text editor, compile, and run it.

import flux.ActionListener;
import flux.Engine;
import flux.EngineHelper;
import flux.Factory;
import flux.FlowChart;
import flux.JavaAction;
import flux.KeyFlowContext;
import flux.TimerTrigger;

public class CompleteExample {

    public static void main(String[] args) throws Exception {

        Factory fluxFactory = Factory.makeInstance();
        FlowChart workflow = EngineHelper.makeFlowChart("My Workflow");
        TimerTrigger trigger = workflow.makeTimerTrigger("My Trigger");
        trigger.setTimeExpression("+1s");
        trigger.setCount(10);
        JavaAction action = workflow.makeJavaAction("Normal Action");
        action.setListener(MyListener.class);
        action.setKey(new MyKey("My Data"));
        trigger.addFlow(action);
        action.addFlow(trigger);

//    The above code creates a workflow that fires a Java Action every
//    five seconds for a total of ten firings.
//    Now create an expiration flow to an expiration action.
        JavaAction expired = workflow.makeJavaAction("Expired Action");
        expired.setListener(AnotherListener.class);
        expired.setKey(new AnotherKey("More Data"));

//    Finally, after the Timer Trigger has fired ten times,
//    flow into a different part of the workflow. You are free
//    to add more actions and triggers after the "expired" action
//    executes.
        trigger.setExpirationFlow(expired);

//    Now we have defined our workflow. To see this workflow fire,
//    create an engine, start it, and then add this workflow
//    to the engine.
        Engine scheduler = fluxFactory.makeEngine();
        scheduler.start();
        scheduler.put(workflow);

//    Give the example a chance to run.
        Thread.sleep(15000);

//    Finally, shut down the scheduler and exit.
        scheduler.dispose();
    }

    //  A listener that executes Java code when called.
    public static class MyListener implements ActionListener {

        public Object actionFired(KeyFlowContext flowContext) throws Exception {
            System.out.println("MyListener has fired.");
            System.out.println("Your code goes here.");
            return null;
        }
    }

    //  Another listener that executes Java code when called.
    public static class AnotherListener implements ActionListener {

        public Object actionFired(KeyFlowContext flowContext) throws Exception {
            System.out.println("AnotherListener has fired.");
            System.out.println("Your code goes here.");
            return null;
        }
    }

    //  Holds data.
    public static class MyKey {

        //    The actual data.
        public String myData;

        //    The default constructor.
        public MyKey() { } // constructor

        //    Initializes the data object.
        public MyKey(String keyData) {
            this.myData = keyData;
        }
    }

    //  Holds more data.
    public static class AnotherKey {

        //    The actual data.
        public String moreData;

        //    The default constructor.
        public AnotherKey() { } // constructor

        //    Initializes the data object.
        public AnotherKey(String keyData) {
            this.moreData = keyData;
        }
    }
}

This last example, while a little more complex, shows the expressive power of Flux workflow. You can create workflows that truly model the complexity of your requirements. The above workflow executes a Java Action 10 times by creating a loop in the workflow. After the tenth execution, flow then branches down a new path, where new actions and triggers can be executed and fired.

Flow Conditions in Code

When building your workflow, you must be aware of the kinds of variables that triggers and actions can return. With this knowledge, you can create a conditional expression that references particular public fields in a persistent variable that was returned by a previous action or trigger. The following code illustrates this capability.

timerTrigger.addFlow(javaAction, "result.istrue");

Control flows from the Timer Trigger to the Java Action only if the condition result.istrue is satisfied, otherwise, that flow is not followed. You can add any number of such conditional flows leading from one action or trigger to another.

In general, you do not need to hard code the string result. Instead, you can use a method from the EngineHelperhelper object.

timerTrigger.addFlow(javaAction, EngineHelper.useLastResult("istrue"));

These two conditional flows are equivalent.

These conditional expressions can test fields that are of any basic type, including strings, booleans, integers, floats, and so on. A string expression is satisfied if it contains the word true. Case is ignored. A simple boolean expression is satisfied if it contains the value true. Similarly, the numerical expressions are satisfied if they contain a non-zero value.

In the above examples, it is not known whether result.istrue is a string, a boolean, or a numerical type. It does not matter. The conditional expression is evaluated regardless of its underlying type.