Child pages
  • Custom Triggers and Actions
Skip to end of metadata
Go to start of metadata

Designing custom triggers and actions requires experience coding with Java. This documentation assumes that you are comfortable writing and working with Java code.

Custom triggers and actions provide amazing flexibility and strength to your Flux application, allowing for seamless integration into every aspect of your application and environment.

A custom action is an action that you create, invoking your custom code and accepting any custom parameters you require, that can be used in the drag-and-drop interface of the Flux Designer. This allows you to expose API-level functionality to the end users who visually design your workflows; your users don't need any knowledge of Java coding to use the custom triggers and action.

To see a complete, working example that demonstrates how to create and use a custom action, with parameters exposed to your end users, see <Flux Home>/examples/software_developers/custom_action.

For an example of a custom trigger, see <Flux Home>/examples/software_developers/custom_synchronous_trigger.

Custom Triggers

You can create a custom trigger by following these steps:

  1. Create an interface that is used to initialize your trigger. This interface must extend flux.Trigger and must contain getter and setter methods for each trigger property that you want to expose to end users.
  2. Create a custom variable class to persist the trigger's data to the database (data including internal state data that must be saved between pollings, or the trigger properties that are shown to end users). The variable class must implement java.io.Serializable and must follow the rules for Persistence.
  3. Create an implementation class that extends fluximpl.TriggerImpl and implements your interface. The implementation class must follow the naming convention "<Trigger Interface Name>Impl". For instance, if your interface is named "MyTrigger", the implementation must be named "MyTriggerImpl". In this implementation class, make sure the following methods are defined:
    1. Getter and setter methods defined in step 1. Make sure the getter and setter methods store and retrieve data changes by calling the get() and put() methods of the variable manager retrieved by calling getVariableManager()
    2. One constructor that calls super(FlowChartImpl, String), and another that calls super(FlowChartImpl, "<My Trigger Name>"), where "<My Trigger Name>" is the default name you want this trigger to use (that is, the name that will appear when a user initially adds the trigger to a workflow).
    3. getHiddenVariables() — this method must return a java.util.Set that contains the names of any persistent variables you use in this trigger.
    4. verify() — this method should check if all of the required properties have been set on the trigger. If not, throw an EngineException from this method.
    5. execute() — the method that Flux calls to see if the trigger is ready to fire. Your code in this method should check to see whether your required event has occurred, and if not, throw a flux.dev.NotTriggeredException. You can also throw your own exception here, which will be handled using the error handling mechanism you've defined. Finally, you can return data, which will then become accessible through the flow context using the standard "RESULT" variable name for trigger and action results, and can be used in a conditional flow or retrieved by the next action in the workflow.
    6. getNextPollingDate() — this method must return a java.util.Date that specifies the next time the engine should poll your trigger (the next time the engine will call the trigger's execute() method). For optimal performance, your trigger should wait as long as your requirements allow before polling again.

Custom Actions

You can create a custom trigger by following these steps:

  1. Create an interface that is used to initialize your action. This interface must extend flux.Action and must contain getter and setter methods for each action property that you want to expose to end users.
  2. Create a custom variable class to persist the action's data to the database (data including internal state data that must be saved between executions, or the action properties that are shown to end users). The variable class must implement java.io.Serializable and must follow the rules for Persistence.
  3. Create an implementation class that extends fluximpl.ActionImpl and implements your interface. The implementation class must follow the naming convention "<Action Interface Name>Impl". For instance, if your interface is named "MyAction", the implementation must be named "MyActionImpl". In this implementation class, make sure the following methods are defined:
    1. Getter and setter methods defined in step 1. Make sure the getter and setter methods store and retrieve data changes by calling the get() and put() methods of the variable manager retrieved by calling getVariableManager()
    2. One constructor that calls super(FlowChartImpl, String), and another that calls super(FlowChartImpl, "<My Action Name>"), where "<My Action Name>" is the default name you want this action to use (that is, the name that will appear when a user initially adds the action to a workflow).
    3. getHiddenVariables() — this method must return a java.util.Set that contains the names of any persistent variables you use in this action.
    4. verify() — this method should check if all of the required properties have been set on the action. If not, throw an EngineException from this method.
    5. execute() — the method that Flux calls when the action runs in a workflow. You can also throw an own exception here, which will be handled using the error handling mechanism you've defined. You can also return data, which will then become accessible through the flow context using the standard "RESULT" variable name for trigger and action results, and can be used in a conditional flow or retrieved by the next action in the workflow.
    6. getNextPollingDate() — this method must return a java.util.Date that specifies the next time the engine should poll your trigger (the next time the engine will call the trigger's execute() method). For optimal performance, your trigger should wait as long as your requirements allow before polling again.

The code below demonstrates a simple "Hello World!" custom action.

public interface CustomAction extends flux.Action {
//  Normally, getter() and setter() methods would go here, but
//  this action has no properties to get or set.
}
import flux.EngineException;
import flux.FlowContext;
import flux.file.FileActionException;
import fluximpl.ActionImpl;
import fluximpl.FlowChartImpl;
import java.util.Set;

public class CustomActionImpl extends ActionImpl implements CustomAction {

//  This constructor creates the action with a default name.
  public CustomActionImpl() {
    super(new FlowChartImpl(), "Custom Action");
  }

//  This constructor is required too.
  public CustomActionImpl(FlowChartImpl fc, String name) {
    super(fc, name);
  }

//  No persistent variables, so nothing to return.
  public Set getHiddenVariableNames() {
    return null;
  }

//  This action has no data that needs verified.
  public void verify() throws EngineException, FileActionException {
  }

//  Code that will be executed when this action runs.
  public Object execute(FlowContext flowContext) throws Exception {
    System.out.println("Hello World!");

//  Always return for the the result. Represented as the
//  flow context variable RESULT in the workflow.
    return new Boolean(true);
  }

}

Adapter Factories

When you create custom triggers and actions, you must make them available for inclusion in your workflows by creating adapter factories. An adapter factory is a factory that makes new instances of your custom triggers and actions.

To create an adapter factory, first create a class that implements flux.dev.AdapterFactory. Your custom factory must implement the init(FlowChartImpl) method of AdapterFactory, as in the following code:

import flux.dev.AdapterFactory;
import fluximpl.FlowChartImpl;

public class MyCustomFactory implements AdapterFactory {

  private FlowChartImpl workflow;

  public void init(FlowChartImpl workflow) {
    this.workflow = workflow;
  }

}

Make sure that the variable workflow is saved in a field. This variable is used to attach your custom triggers or actions to a specific workflow when they are created.

You will also need to create additional methods that make new instances of your custom triggers and actions. These methods must take a String argument that specifies the name of the trigger or action instance, and call the appropriate constructor on your custom class. For example:

public MyCustomAction makeCustomAction (String name) {
  return new MyCustomActionImpl(flowChart, name);
}

By passing in a reference to the workflow, you bind the custom trigger or action to that workflow.

Once you have created your adapter factory, you will need to make it available to the engine. to do so, create a file called factories.properties. In this file, give your adapter factory a short name that links it to your custom factory class, like so:

MyFactoryShortName=examples.custom_action.FileFactory

You can give your factory any short name you like, as long as it does not conflict with any of the built-in factory names. These are:

  • file
  • gui
  • jee
  • jmx
  • messaging
  • transform
  • web_services
  • xml

You can also view all of the built-in adapter factories by calling the flux.EngineHelper.getActionCatalog() method. This method returns a CatalogNode object representing the root node of the tree of actions. Any node in this tree that has at least one child represents an action factory.

Transient Variables

Any variable you return from your trigger or action's execute() method is assumed to be persistent and will be saved to the database. If you need to return a non-persistent variable instead, call flux.FlowContext.returnTransient() immediately before you return your variable from the execute() method.

By calling the returnTransient() method just before returning your variable, you ensure that the variable will not be stored to the database. Instead, it will be removed from the workflow at the next available transaction break.

Creating your BeanInfo

The BeanInfo class provides information to the Flux user interface on how to render the properties required for the custom action.

import fluximpl.ActionImplBeanInfo;
import java.beans.BeanDescriptor;
/**
 * A BeanInfo for this custom trigger. A BeanInfo provides additional
 * meta-data regarding a JavaBean.
 *
 * @author @copyright@
 */
public class FileTriggerImplBeanInfo extends ActionImplBeanInfo {
  /**
   * This BeanInfo needs a BeanDescriptor.
   */
  public FileTriggerImplBeanInfo() {
    super(new BeanDescriptor(FileTriggerImpl.class), "Custom Trigger", ActionImplBeanInfo.FILE);
  } // constructor
  @Override
  protected void configurePropertyDescriptors() throws Exception {
    super.configurePropertyDescriptors();
    Class className = FileTriggerImpl.class;
    addPropertyDescriptor("fileName", "File Name", className, "getFileName", "setFileName").setValue("required", true);
  }
} // class FileTriggerImplBeanInfo

Using Custom Triggers and Actions

To use your custom triggers and actions in Java code, you must include the following on your engine's class path:

  • All of your custom classes, including interfaces, implementation classes, variable classes, and adapter factories.
  • The factories.properties files for your custom adapter factories.

You can access your custom adapter factory in code by calling FlowChart.makeFactory(), like so:

FlowChart workflow = EngineHelper.makeFlowChart();
MyCustomFactory myCustomFactory = workflow.makeFactory("MyFactoryShortName");

This method returns an instance of your adapter factory, which you can then use to make your custom triggers and actions.

A JavaBean BeanInfo class can be created for custom actions by following the standard JavaBean conventions and extending SimpleBeanInfo:

http://docs.oracle.com/javase/6/docs/api/java/beans/SimpleBeanInfo.html

In the getPropertyDescriptors() method of your BeanInfo class, call super.getPropertyDescriptors() to retrieve the base property descriptors. Return them, along with your custom action’s property descriptors, from your getPropertyDescriptors() method.

The Designer uses these property descriptors to display and edit your custom action’s properties. The Designer also uses your BeanInfo to display other information, such as your custom action’s icons.

Any property that contains an attribute named "transient" with its value set to Boolean.TRUE is not saved to XML.

In the Flux Designer

Once your custom trigger or action is created, just package it into a JAR file, make sure the factories.properties file (noted in the previous example) is available at the root level of the JAR, then place the JAR onto the class path of your Flux engine and Operations Console and restart both the engine and console. After the restart, your new action will appear in the Designer, ready for use.

Adding Custom Actions and Triggers to Flux Cockpit

Adding custom actions and triggers requires modifying some files in Cockpit. All file names are case sensitive - do exactly as instructed. Assuming that you have created a custom action in the above steps, and named it 'My Custom Action', and it has the fields of Type, Key, and ID.

 1) Open the file EditorUI.js in FLUX_HOME/webapp/flux/javascript/mx

Locate the line containing the text  json.type = action

Add the following below that line:

if (action == "My Custom Action") {
    (json["Type (Data,Photo,Both)"]) = "Data";
    (json["ID"]) = '448';
    (json["Key"]) = 'Data';
}

Save and close the file.

 2) Open the file Actions.js  in FLUX_HOME/webapp/flux/javascript/mx

Locate the line containing the text (including quotes):  "Agent Pool":

Just ABOVE that line paste the following:

"Type (Data,Photo,Both)": {"title": "Download Type", "propertyOrder": 3, "type": "string","enum": ["Data","Photo","Both"],
"options": {"input_width": "250px"}},
"ID": {"title": "My ID", "propertyOrder": 4, "type": "string","format": "number",
"options": {"input_width": "250px"}},
"Key": {"title": "My Key", "propertyOrder": 5, "type": "string",
"options": {"input_width": "250px"}},

Save and close the file.

3) Open Sidebar.js in FLUX_HOME/webapp/flux/javascript/mx

Locate the line starting with the text and characters  ['Process Action

Change the line to start ['MyCustomAction','ProcessAction

Change the next line to start as follows

  ['My Custom Action','Process Action

Save and close the file.

 4) Open ffctomx.js in FLUX_HOME/webapp/flux/styles

Locate the following:

<xsl:when test="@type ='Console Action'">
               <xsl:text>ConsoleAction</xsl:text>
</xsl:when>

Place directly above it the following lines:

<xsl:when test="@type ='My Custom Action'">
    <xsl:text>MyCustomAction</xsl:text>
</xsl:when>

Save and close the file. 

 4) Create and copy two images specific to your custom action in the directory FLUX_HOME/webapp/flux/stencils/clipart

These images are 32 by 32 pixel png files. One file is named MyCustomAction.png, and the other is MyCustomAction.pngx. The pngx is a version of the png where black areas are made into gray, and is only used in the Status view of Cockpit to show those actions that have already executed. The following images are provided as an example.

MyCustomAction.png  and  MyCustomAction.pngx  (which will not render here because of the unrecognized pngx file extension).

 

 

 5) Clear your browser cache and sign into Cockpit.

 

  • No labels

2 Comments

  1. Anonymous

    Does flux 8.0 have a standalone designer? How would we configure the properties within custom actions? – Navin

    1. Hi Navin, thank you for the question! Flux 8.0 doesn't have a standalone designer, but custom triggers and actions are fully supported in the Web-based Designer. As long as your custom action is available on the engine's class path, it will appear and be editable in the Designer just like the built-in Flux action library!