Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Code Block
languagejs
titleJob expect
collapsetrue
var jobChainPath = null;
var jobChainNodeState = null;

var orderExpectedValue = null;
var orderExpectedDefaultValue = null;

var orderExpectedValueFunction = "parseInt(currentValue) + 1";
var controlOrderPrefix = "control_order_";
var controlOrderID = null;

function executeXML( command ) {
  var rc = false;

  spooler_log.debug( ".... executing xml command: " + command );
  var response = spooler.execute_xml( command );
  var xmlDOM = new Packages.sos.xml.SOSXMLXPath( new java.lang.StringBuffer( response ) );
  var errorCode = xmlDOM.selectSingleNodeValue( "//ERROR/@code" );
  var errorText = xmlDOM.selectSingleNodeValue( "//ERROR/@text" );
  if ( errorCode || errorText ) {
    spooler_log.error( ".... xml response: errorCode=" + errorCode + ", errorText=" + errorText );
  } else {
    rc = xmlDOM;
  }

  return rc;
}

function getOrderExpectedValue() {
  if (orderExpectedValue == null) {
    orderDOM = executeXML( "<show_order job_chain='" + jobChainPath + "' order='" + controlOrderID + "' what='payload'/>" );
    if ( orderDOM ) {
      orderExpectedValue = orderDOM.selectSingleNodeValue( "/spooler/answer/order/payload/params/param[@name = '" + controlOrderID + "']/@value" );
    }
  }

  if ( !orderExpectedValue ) {
    orderExpectedValue = orderExpectedDefaultValue;
    spooler_log.info( ".... getOrderExpectedValue(): " + orderExpectedValue + " (default)" );
  } else {
    spooler_log.info( ".... getOrderExpectedValue(): " + orderExpectedValue );
  }

  return orderExpectedValue;
}

function setOrderExpectedValue( expectedValue ) {
  spooler_log.info( ".... setOrderExpectedValue(): " + expectedValue );
  var rc = true;
  orderExpectedValue = expectedValue;

  // check existence of control order, if historic then its state does not match the job node state
  var orderDOM = executeXML( "<show_order job_chain='" + jobChainPath + "' order='" + controlOrderID + "' what='payload'/>" );
  if ( orderDOM ) {
    var orderState = orderDOM.selectSingleNodeValue( "/spooler/answer/order/@state" );
    if (!orderState || orderState != jobChainNodeState) {
      rc = false;
    }
  } else {
    rc = false;
  }

  // add or modify control order
  if (!rc) {
    orderDOM = executeXML( "<add_order job_chain='" + jobChainPath + "' id='" + controlOrderID + "' state='" + jobChainNodeState + "' suspended='yes'><params><param name='" + controlOrderID + "' value='" + orderExpectedValue + "'/></params><run_time/></add_order>" );
  } else {
    orderDOM = executeXML( "<modify_order job_chain='" + jobChainPath + "' order='" + controlOrderID + "' state='" + jobChainNodeState + "' suspended='yes'><params><param name='" + controlOrderID + "' value='" + orderExpectedValue + "'/></params><run_time/></modify_order>" );
  }
  spooler_job.state_text = "next expected value: " + orderExpectedValue;
}

function calculateOrderExpectedValue( currentValue ) {
  // calculate next date for a date parameter
  // var expectedValue = (new Date( (new Date(currentValue)).setDate( (new Date(currentValue)).getDate()+1 ) )).toISOString().substring(0,10);
  // calculate increment for a numeric parameter or order id
  // var expectedValue = parseInt(currentValue) + 1;

  var expectedValue = eval( orderExpectedValueFunction );
  return expectedValue;
}

function spooler_init() {
  jobChainPath = spooler_task.order.job_chain.path;
  jobChainNodeState = spooler_task.order.job_chain_node.state;
  controlOrderID = controlOrderPrefix + spooler_task.order.job_chain_node.state;

  return true;
}

function spooler_process() {
  var rc = true;

  // control order is always suspended
  if (spooler_task.order.id == controlOrderID) {
    spooler_log.info( ".. control order identified, processing suspended" );
    suspendOrder();
    return rc;
  }

  // merge parameters from task and order
  var params = spooler.create_variable_set();
  params.merge( spooler_task.params );
  params.merge( spooler_task.order.params );

  // get expected value from a parameter name or from the Order ID
  spooler_log.info( ".. control parameter for expected value is looked up: " + controlOrderPrefix + "expected_parameter" );
  var expectedParameter = params.value( controlOrderPrefix + "expected_parameter" );
  if (expectedParameter) {
    var currentValue = params.value( expectedParameter );
    spooler_log.info( ".. current value is used from control parameter [" + expectedParameter + "]: " + currentValue );
  } else {
    var currentValue = spooler_task.order.id;
    spooler_log.info( ".. current value is used from order id: " + currentValue );
  }

  // get expected default value from a parameter or from the order id
  if (!orderExpectedDefaultValue) {
    orderExpectedDefaultValue = params.value( controlOrderPrefix + "expected_default_value" );
    if (orderExpectedDefaultValue) {
      spooler_log.info( ".. default value is used from control parameter [" + controlOrderPrefix + "expected_default_value]: " + orderExpectedDefaultValue );
    } else {
      spooler_log.info( ".. default value is used from order id: " + spooler_task.order.id );
      orderExpectedDefaultValue = spooler_task.order.id;
    }
  }

  // get expected value calculation function
  var expectedValueFunction = params.value( controlOrderPrefix + "expected_value_function" );
  if (expectedValueFunction) {
    orderExpectedValueFunction = expectedValueFunction;
    spooler_log.info( ".. using expected value function: " + orderExpectedValueFunction );
  } else {
    spooler_log.info( ".. using expected value default function: " + orderExpectedValueFunction );
  }

  // check if order value matches expectation
  if ( getOrderExpectedValue() == currentValue ) {
    // after processing of the current order all suspended orders are activated
    spooler_log.info( ".. current order provides expected value: " + getOrderExpectedValue() );
    activateSuspendedOrders();
    setOrderExpectedValue( calculateOrderExpectedValue( getOrderExpectedValue() ) );
  } else {
    // suspend non-matching order
    spooler_log.info( ".. suspending current order: expected value=" + getOrderExpectedValue() + ", current value=" + currentValue );
    suspendOrder();
  }

  return rc;
}

function suspendOrder() {
    spooler_task.order.suspended = true;
    spooler_task.order.state = jobChainNodeState;
}

function activateSuspendedOrders() {
  var rc = true;
  var orderList = Array();

  // select suspended orders of the current job node
  var orderDOM = executeXML( "<show_job_chain job_chain='" + jobChainPath + "' what='job_chain_orders'/>" );
  var orderNodes = orderDOM.selectNodeList( "/spooler/answer/job_chain/job_chain_node[@state = '" + jobChainNodeState + "']/order_queue/order[@suspended = 'yes']" );

  // 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 || controlOrderID) {
      continue;
    }
    spooler_log.info( ".... suspended 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( ".... activating order: " + orderList[i] );
    orderDOM = executeXML( "<modify_order job_chain='" + jobChainPath + "' order='" + orderList[i] + "' state='" + jobChainNodeState + "' suspended='no'/>" );
    if ( !orderDOM ) {
      rc = false;
    }  
  }

  return rc;
}

Usage

  • Add two thre orders to the job_chain_expect_orders job chain
    • Use an order id in descending alphabetical order, e.g. "cba" for the order id of the first order and "abc" for the order id of the second order.
  • Both orders will be suspended in the first node of the job chain.
  • After an idle timeout of 10s both orders are moved to the next job node in the job chain. 
    • This time the orders are processed in ascending alphabetical order
  • . For your convenience the orders 1, 2 and 3 are provided with the sample.
    • Each order uses a parameter sequence with a sequential number such as 1, 2, 3.
    • The expect job uses the parameters 
      • control_order_expected_parameter with the value: sequence
      • control_order_expected_default_value with the value: 1
      • control_order_expected_value_function with the value: parseInt(currentValue) + 1
  • Start order 1 that matches the expected value (parameter sequence=1). 
    • The order is processed and moved to the next_job job.
    • A control order control_order_expect is dynamically created and suspended that contains the next expected value (parameter control_order_expect=2)
  • Start order 3 that does not match the expected value (parameter sequence=3).
    • The order is suspended.
    • The control order with the expected value (parameter control_order_expect=2) remains unchanged.
  • Start order 2 that does match the expected value (parameter sequence=2) .
    • Order 2 is processed and moved to the next_job job. 
      • The control order is automatically modified to carry the next expected value (parameter control_order_expect=3).
    • Order 3 is processed and moved to the next_job job.
      • The control order is automatically modified to carry the next expected value (parameter control_order_expect=4).

.