Running a Process Flow

In this section:

The execution component of iWay Service Manager (iSM) is the process flow. Process flows are run in response to input routing decisions or at other times as required. The mechanism described in this section allows applications to run process flows. It is first necessary to prepare the way with a dictionary and a worker, and then to provide a compiled process flow in internal form. These methods generally assume that the process flows have been built by iWay Designer and published to the registry.

As a best practice it is recommended that applications use only the existing components, and avoid use of these methods unless no other options are available.


Top of page

x
Understanding the PFlowExecutor API

The PFlowExecutor API is designed to simplify the process of running a process flow from an iSM component or a standalone Java program.

Since a process flow requires an XDWorker to execute, the PFlowExecutor constructor takes an XDWorker instance as an argument. Runtime components of the server, like services and preparsers, generally have access to an XDWorker instance and can pass this. However, in some cases, like console pages, functions, and standalone programs, no worker is available and it is the responsibility of the caller to create one. Normally, this will be an XDMTLclWorker, as shown in the example below.

Methods

XDPFlowState runProcessByName(String flowName, XDDocument docIn, boolean cache, boolean commit, boolean overrideTimeout) throws XDException

Given the name of an available system flow and an input document, execute the flow. Returns an XDPFlowState object that describes execution status.

Parameter

Type

Description

flowName

String

Name of the flow to execute. The named flow must be defined at the system level for the worker that will execute the flow. When using a local worker, make sure that the flow is available in the dictionary used to create the local master.

docIn

XDDocument

Input to the flow. This may be either an XML or flat XDDocument.

cache

boolean

Workers are normally related to instances of XDMaster, which can maintain a cache of reusable process flow objects. If this argument is set as true, the PFlowExecutor will attempt to retrieve the flow from the master's cache and will cache the flow after execution. When the caller expects to reuse a worker or to use workers related to a common instance of XDMaster, this option can greatly improve performance.

commit

boolean

Determines whether the worker should commit or rollback the local transaction after flow execution is completed. This is the equivalent of the local transaction setting in a configured channel.

overrideTimeout

boolean

If true, a flow with no timeout set will be assigned a 10 second timeout.

XDPFlowState runProcess(String flowName, XDNode flowNode, XDDocument docIn, boolean commit, boolean cache, boolean overrideTimeout) throws XDException

Given either a pflowloc node from an XDictionary or the root of a compiled flow, execute the flow with the input document provided. Returns an XDPFlowState object that describes execution status.

Parameter

Type

Description

flowName

String

Name of the flow to execute. The named flow must be defined at the system level for the worker that will execute the flow. When using a local worker, take care that the flow is available in the dictionary used to create the local master. When flowNode is supplied, this parameter is not required unless cache is true.

flowNode

XDNode

This can be either a pflowloc node as used in the system defines area of the runtime dictionary or the root of a compiled flow (see compileGUIFlow method below).

docIn

XDDocument

Input to the flow. This may be either an XML or flat XDDocument.

cache

boolean

Workers are normally related to instances of XDMaster, which can maintain a cache of reusable process flow objects. If this argument is set as true, the PFlowExecutor will attempt to retrieve the flow from the master's cache and will cache the flow after execution. When the caller expects to reuse a worker or to use workers related to a common instance of XDMaster, this option can greatly improve performance. When cache is true, be sure to supply flowName.

commit

boolean

Determines whether the worker should commit or rollback the local transaction after flow execution is completed. This is the equivalent of the local transaction setting in a configured channel.

overrideTimeout

boolean

If true, a flow with no timeout set will be assigned a 10 second timeout.

XDNode compileGUIFlow(String guiFlow, String configId) throws XDException

Given the raw output of iWay Designer, compile this into an executable form. Returns the root of the compiled flow. This is supplied as a convenience for running tests from design tools.

Parameter

Type

Description

guiFlow

String

String containing a process flow as output from iWay Designer.

configId

String

The name of the iWay Service Manager configuration where the compiler should check for dependencies when compiling the GUI flow.

List<XDDocument> getOutDocs()

After flow execution, retrieve output documents from the flow.

String getReturnState()

For convenience, after flow execution, return the execution status as a string.

void reset()

If using the PFlowExecutor more than once, call reset() after each flow execution to clear the results of the previous flow.


Top of page

x
Understanding the XDPFlowState Object

These functions return a XDPflowState object. This object is used internally by the process flow interpreter, which uses it to manage transitions. You can determine if the flow succeeded by examining the type of the state object. The method is:

int getType()

Returns a code as listed and described in the following table:

Value

Name

Purpose

0

XDPFlowState.NORMAL

The process flow was successful.

1

XDPFlowState.COMMIT

The process flow was successful/reserved for future use.

2

XDPFlowState.RETRY

The process flow ended with retry.

3

XDPFlowState.FAIL

The process flow ended with failure.

Success is indicated by a normal return. Any other return indicates a failure. The transactional operation (commit or rollback as appropriate, if configured in the call) will have taken place before the return from the return from the flow execution.


Top of page

x
Example #1: RunPFlowSampleAgent

This service demonstrates the use of PFlowExecutor when an instance of XDWorker is available.

package com.ibi.agents;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.ibi.common.IXLogger;
import com.ibi.config.ConfigurationException;
import com.ibi.edaqm.XDAgent;
import com.ibi.edaqm.XDDocument;
import com.ibi.edaqm.XDException;
import com.ibi.edaqm.XDNode;
import com.ibi.edaqm.XDUtil;
import com.ibi.pflow.PFlowExecutor;
import com.ibi.pflow.XDPFlowState;
/**
 * 
 * Attempts to execute the specified process flow using the PFlowExecutor API.
 * If the flow outputs more than one document, additional
 * documents will be output as siblings to the first.
 * 
 */
public final class RunPFlowSampleAgent extends XDAgent
{
  public RunPFlowSampleAgent()
  {
    super();
  }
  public String getDesc()
  {
    return "Attempts to execute the specified process flow using the PFlowExecutor API.";
  }
  public String getLabel()
  {
    return "Sample PFlow Agent";
  }
  private static String[] flowNameParm = { "flowname", "string", "Name of the process flow,
          defined at the system level, to execute", "",	"yes", "", "Process Flow" };
  private static String[][] parmsMeta = { flowNameParm, };
  String[][] parmsEnums = { null, };
  private HashMap parmMap; // parms are stored here
  public void init(String[] parms) throws XDException
  {
    // merge input parameters, account for required parms, and make final parameter map
    parmMap = initParms(parms, parmsMeta);
  }
  /**
   * Provides metadata to the Adapter Designer as to which flow edges are expected to be followed.
   * If you plan to return fixed names, return them in the array of this method. These names will
   * appear in the designer as standard returns.
   *
   */
  public String[] getOPEdges()
  {
    String[] r = {
     "success",
     XDAgent.EX_FAIL_NOTFOUND,
     XDAgent.EX_FAIL_OPERATION,
    };
  		return r;
  }
  public String execute(XDDocument docIn, XDDocument docOut) throws XDException
  {
    // evaluate parameters since flowName might be an iFL expression
    HashMap tMap = (HashMap) parmMap.clone();
    XDUtil.evaluate(tMap, docIn, this.getSRM(), worker.getLogger());
    String flowName = (String) tMap.get(flowNameParm[0]);
    // is the requested flow available?
    XDNode pflowlocNode = master.getDictionary().findSystemPFlowNode(flowName);
    if (pflowlocNode == null)
    {
      docIn.moveTo(docOut);
      return EX_FAIL_NOTFOUND;
    }
    // construct a pflow executor with my worker
    PFlowExecutor executor = new PFlowExecutor(worker);
    // run the flow, using the master's cache.
    XDPFlowState pfState = executor.runProcess(flowName, pflowlocNode, docIn, true, true, false);
    List<XDDocument> outDocs = executor.getOutDocs();
    if (outDocs != null && outDocs.size() > 0)
    {
      // first output doc is agent's docOut
      outDocs.get(0).moveTo(docOut);
      // subsequent are siblings
      for (int i = 1; i < outDocs.size(); i++)
      {
        docOut.addSibling(outDocs.get(i));
      }
    }
    else
    {
      logger.debug("process flow did not output any documents.  Copying input to output.");
      docIn.moveTo(docOut);
    }
    if (pfState.getType() != XDPFlowState.NORMAL)
    {
      return EX_FAIL_OPERATION;
    }
    else
    {
    return EX_SUCCESS;
    }
  }
  /*
   * standard methods for handling agent metadata
   */
  public int getIPCount()
  {
    return parmsMeta.length;
  }
  public String[] getIPGroups()
  {
    return setupIPGroups(parmsMeta); // service routine in extended class
  }
  public String getIPName(int index)
  {
    return rtnIPName(index, parmsMeta); // service routine in extended class
  }
  public String getIPDisplayName(int index)
  {
    if (index < 0 || index >= parmsMeta.length)
    {
      return "";
    }
    return parmsMeta[index][6];
  }
  public String getIPDesc(int index)
  {
    return rtnIPDesc(index, parmsMeta); // service routine in extended class
  }
  public boolean getIPReqd(int index)
  {
    if ((index < 0) || (index >= parmsMeta.length))
    {
      return false;
    }
    return (parmsMeta[index][4]).equalsIgnoreCase("yes");
  }
  public String[] getIPEnum(int i)
  {
    if ((i < 0) || (i >= parmsMeta.length))
    {
      return null;
    }
    return parmsEnums[i];
  }
  public int getIPType(int index)
  {
    return rtnIPType(index, parmsMeta); // service routine in extended class
  }
  public String getIPDefaultValue(int index)
  {
    if ((index < 0) || (index >= parmsMeta.length))
    {
      return null;
    }
    return parmsMeta[index][5];
  }
}

Top of page

x
Example #2: RunPFlowSample

This standalone Java program runs a process flow by creating a local worker based on an existing iWay Service Manager configuration.

package com.ibi.samples;
import java.util.HashMap;
import java.util.List;
import com.ibi.edaqm.XD;
import com.ibi.edaqm.XDDocument;
import com.ibi.edaqm.XDException;
import com.ibi.edaqm.XDMTLclMaster;
import com.ibi.edaqm.XDNode;
import com.ibi.edaqm.XDWorker;
import com.ibi.pflow.PFlowExecutor;
import com.ibi.pflow.XDPFlowState;
/*
 * Agent to run a pflow by name as a stand-alone command. Note that
 * this is linked with the supplied lib from the server install.
*/
public class RunPFlowSample
{
 private String dict;
 private String configName;// name of the configuration
 private String flowName;  // name of the flow
 private String input;     // the input to process
 private boolean isXML;    // is theinput document XML?
 private List<XDDocument> outDocs;
 public RunPFlowSample(String dict, String configName,
                              String flowName, String input,
                              boolean isXML)
 {
  this.dict = dict;
  this.configName = configName;
  this.flowName = flowName;
  this.input = input;
  this.isXML = isXML;
 }
 public int runFlow()
 {
 	XDWorker worker = null;
  // set up a local worker, based on the supplied dictionary and configuration
  try
  {
   worker = getLocalWorker(dict, configName);
  }
  catch (XDException xde)
  {
   logger.error.println("Error: Unable to construct worker from dictionary file");
   return -1;
  }
  // verify requested flow exists -- this uses an internal accessor
  XDNode pflowlocNode = worker.getMaster().getDictionary().findSystemPFlowNode(flowName);
  if (pflowlocNode == null)
  {
   logger.error.println("Error: Unable to find requested pflow in dictionary");
   return -1;
  }
  // set up XDDocument, flat or XML, for input
  XDDocument inDoc = new XDDocument(worker);
  if (isXML)
  {
   try
   {
    inDoc.toXML(input);
   }
   catch (Exception e)
   {
    logger.error.println("Error: Unable to parse input as XML");
    return -1;
   }
  }
  else
  {
   inDoc.addLine(input);
  }
  // run the flow
  PFlowExecutor executor = new PFlowExecutor(worker);
  XDPFlowState pfState = null;
  try
  {
   pfState = executor.runProcess(flowName, pflowlocNode, inDoc, true, false, false);
  }
  catch (XDException xde)
  {
   logger.error.println("Error: Unhandled exception running process flow.");
   return -1;
  }
  // set aside the flow's output
  outDocs = executor.getOutDocs();
  return pfState.getType();
 }
 public List<XDDocument> getOutDocs()
 {
  return outDocs;
 }
 /**
  * This method establishes a local worker for execution. The
  * assumption is that the dictionay to use changes frequently.
  * If the same dictionary is used many times, caching the
  * MTLocal master is suggested.
 */
 private XDWorker getLocalWorker(String dictionary, String configName) throws XDException
 {
  HashMap<String, String> h = new HashMap<String, String>();
  h.put("dictionary", dictionary);
  h.put("name", "ExecProcess");
  h.put("configurationName", configName);
  XDMTLclMaster master = null;
  try
  {
   master = new XDMTLclMaster(h, null);
  }
  catch (Exception e)
  {
   throw new XDException(e);
  }
  return master.getWorker();
 }
 public static void main(String[] args)
 {
  if (args.length != 5)
  {
   logger.debug.println("Usage: RunPFlowSample <path to dictionary file> <config name>
      <flow name> <path to flow input> <true/false, is input XML?>");
   System.exit(-1);
  }
  String dictPath = args[0];
  String dict = null;
  try
  {
   dict = XD.getFileContents(dictPath);
  }
  catch(XDException xde)
  {
   logger.error.println("Error: Unable to load dictionary from " + dictPath);
   System.exit(-1);
  }
  String configName = args[1];
  String flowName = args[2];
  String inputPath = args[3];
  String input = null;
  try
  {
   input = XD.getFileContents(inputPath);
  }
  catch(XDException xde)
  {
   logger.error.println("Error: Unable to load input from " + inputPath);
   System.exit(-1);
  }
  boolean isXML = "true".equals(args[4]);
  RunPFlowSample me = new RunPFlowSample(dict, configName, flowName, input, isXML);
  int status = me.runFlow();
  if (status < 0)
  {
   logger.error.println("Process terminated with abnormal state: " + status);
  }
  List<XDDocument> outDocs = me.getOutDocs();
  if (outDocs != null)
  {
   for (XDDocument doc : outDocs)
   {
     logger.debug.println("Output Document from source: " + doc.getSourceName());
     logger.debug.println(doc.flatten());
   }
  }
  System.exit(status);
 }
}

iWay Software