Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Minor corrections to text

Table of Contents

Scope

  • Use Case:
    • A Consider the situation where a number of orders that have been added to a job chain - either from a file order source watching incoming files, from Ad Hoc or from an external source such as JOC that adds ad hoc orders or from permanent orders. These orders - should be forced into serialized processing then be processed serially in a predefined sort sorting order.
  • Solution Outline:
    • A single job is added to at the top beginning of a job chain and that will:
      • suspend all incoming orders and check completeness until a predefined idle timeout is has beens reached and no more orders are expected,
      • sort the orders in alphabetical sequence of according to the order id ID and move them to the next job node in the job chain.
  • References

...

  • Download sort_orders.zip
  • Extract the archive to any folder within the ./config/live folder of your JobScheduler installation.
  • The archive will extract extracts the files to a folder folder named sort_orders. 
  • You can store the sample files to a in any folder as you like, the solution does not make use of specific folder names or job names.

Pattern

Flowchart
job_chain [label="Job Chain\ntriggered by File Orders\nor by Ad Hoc Orders",fillcolor="orange"]
job_sorter [label="Job Order Sorter",fillcolor="lightskyblue"]
job_next_job [label="Next Job", fillcolor="lightskyblue"]

sorter_orders_completed [shape=diamond,label="list of orders completed?",fillcolor="white"]
order_suspend [label="Suspend Order",fillcolor="white"]
order_wait [label="Wait for next Order",fillcolor="white"]
order_sort [label="Sort Orders",fillcolor="white"]
order_move [label="Move Orders to Next Job",fillcolor="white"]
order_C [shape="ellipse",label="Order C",fillcolor="violet"]
order_B [shape="ellipse",label="Order B",fillcolor="violet"]
order_A [shape="ellipse",label="Order A",fillcolor="violet"]

sorted_order_A [shape="ellipse",label="Order A",fillcolor="violet"] 
sorted_order_B [shape="ellipse",label="Order B",fillcolor="violet"]
sorted_order_C [shape="ellipse",label="Order C",fillcolor="violet"]
 
order_A -> job_chain
order_B -> job_chain
order_C -> job_chain
job_chain -> job_sorter
job_sorter -> sorter_orders_completed
sorter_orders_completed -> order_sort [label=" yes "]
sorter_orders_completed -> order_suspend [label=" no "]
order_suspend -> order_wait -> job_sorter
order_sort -> order_move -> sorted_order_C
sorted_order_C -> sorted_order_B
sorted_order_B -> sorted_order_A
sorted_order_A -> job_next_job

...

  • The solution implements a job named sorter that can be added to at the top start of any job chain.
    • This job implements a spooler_process() function that suspends all incoming orders.
    • This job is configured for a single task and with an idle timeout attribute, i.e. it executes . This means that it will execute incoming orders sequentuallysequentially.
    • Having received the last available order this job waits will wait for the duration specified with the idle_timeout attribute for new orders. 
      • The idle timeout is configured by using, for example <job idle_timeout="10"> with the sorter job definition.
      • With Once the idle timeout being has expired this job will execute its spooler_exit() function and will then sort and move all orders that have previously been suspended.
        • Sorting is done in alphabetical order.
        • The orders are moved to the next job chain node that follows the sorter job in the job chain.
  • The sample makes use of download example uses a job chain named job_chain1 that includes the job nodes for the sorter job and a hello job. The This job chain accepts Ad Hoc ad hoc orders that are added by use of JOC and the job chain it can easily be modified to watch for incoming files and to create one an order for each file.
  • Hint: to re-use the sorter job you can:
    • store the job in a central folder and reference the job in individual job chains.
    • move the job's JavaScript code to a central location and use an appropriate <include> element for individual job scripts.

 

Code Block
languagejs
titleJob sorter
collapsetrue
var jobChainPath = null;
var jobChainNodeState = null;
var jobChainNodeNextState = null;

function spooler_init() {
  spooler_log.info( ".. performing spooler_init()" );

  jobChainPath = spooler_task.order.job_chain.path;
  jobChainNodeState = spooler_task.order.job_chain_node.state;
  jobChainNodeNextState = spooler_task.order.job_chain_node.next_state;

  return true;
}

function spooler_process() {
  var rc = true;

  // suspend all orders, sorting is done on spooler_exit()
  spooler_log.info( ".. suspending current order" );
  spooler_task.order.suspended = true;
  spooler_task.order.state = jobChainNodeState;

  return rc;
}

function spooler_exit() {
  spooler_log.info( ".. peforming spooler_exit()" );
  var rc = true;
  var orderList = Array();

  // select all orders of the current job node
  var response = spooler.execute_xml( "<show_job_chain job_chain='" + jobChainPath + "' what='job_chain_orders'/>" );
  var orderDOM = new Packages.sos.xml.SOSXMLXPath( new java.lang.StringBuffer( response ) );
  var orderNodes = orderDOM.selectNodeList( "/spooler/answer/job_chain/job_chain_node[@state = '" + jobChainNodeState + "']/order_queue/order" );

  // traverse order list and add orders to sort array
  for( orderIndex=0; orderIndex<orderNodes.getLength(); orderIndex++ ) {
    var orderNode = orderNodes.item(orderIndex);
    var orderID = orderDOM.selectSingleNodeValue( orderNode, "@id" );
    if (orderID == null) {
      continue;
    }
    spooler_log.info( ".... order found: " + orderID );
    orderList.push( orderID );
  }

  // alphabetical string sort
  orderList.sort(function(a, b){return (a > b) - (a < b) });
  // numeric sort
  // orderList.sort(function(a, b){return b - a) });

  for(i=0; i<orderList.length; i++) {
    spooler_log.info( ".... moving order: " + orderList[i] );
    var response = spooler.execute_xml( "<modify_order job_chain='" + jobChainPath + "' order='" + orderList[i] + "' state='" + jobChainNodeNextState + "' suspended='no'/>" );
    var orderDOM = new Packages.sos.xml.SOSXMLXPath( new java.lang.StringBuffer( response ) );
    var errorCode = orderDOM.selectSingleNodeValue( "//ERROR/@code" );
    var errorText = orderDOM.selectSingleNodeValue( "//ERROR/@text" );
    if ( errorCode || errorText ) {
      spooler_log.error( "........ modify order state response: errorCode=" + errorCode + ", errorText=" + errorText );
      rc = false;
    }  
  }

  return rc;
}

...

  • Add two orders to the job_chain1 job chain. 
    • Use an order id ID in descending alphabetical order, e.g. "cba" for the order id order ID of the first order and "abc" for the order id order ID of the second order.
  • Both orders will be suspended in at the first node of the job chain.
  • After an idle timeout of 10s both orders are will be moved to the next job node in the job chain. 
    • This time the orders are will be processed in ascending alphabetical order.

...