importPackage(java.lang);
importPackage(java.util);
importPackage(java.io);

load("integrationservices/applications/ping-plan/configuration.js");
load("lib/integrationservices/javascript/event.js");


function apia_event(form) {
    // APClient.bin injection has been converted to a JavaScript object
    // that can be serialized and sent to xMatters.
    IALOG.info(JSON.stringify(form, null, 2));
    var device = form.properties.device;
    var pingResult = ping(device).toString();

    // process single recipient into format we are looking for.
    form.recipients = [ form.recipients ];

    form.properties.pingResult = "" + pingResult;
    IALOG.debug('apia_event: Sending the following json \n' + JSON.stringify(form, null, 2));
    return form;
}

function apia_callback(msg) {
  var str = "Received message from xMatters:"
    + "\nIncident: " + msg.incident_id
    + "\nEvent Id: " + msg.eventidentifier
    + "\nCallback Type: " + msg.xmatters_callback_type;
  IALOG.info(str);
  IALOG.info(JSON.stringify(msg, null, 2));
}

/**
 * Performs the actual ping and returns the response, regardless
 * of whether or not the command succeeded.  The response is created 
 * with values from the input message so that routing criteria 
 * and priority are preserved.  If the input message has no priority,
 * the response defaults to high priority.
 */
function ping(device) {
  try {
    if (device == null || device.trim().length == 0) {
      throw new IllegalArgumentException("The request must contain a device token whose value is non-empty after trimming leading/trailing whitespace.");
    }
    var command = getPingCommand(device);

    IALOG.info("Executing command: " + command);
        
    return execute(command);
  } catch (ex) {
    IALOG.error(ex);
    return ex.getMessage();
  }
}

/**
 * This method returns the OS-specific command that will ping the specified 
 * device.
 *
 * @param device - the device to ping, which is an IP address or hostname
 *
 * @return a non-empty String
 */
function getPingCommand(device) 
{
  var os = System.getProperty("os.name").toLowerCase();

  // Windows ping command.
  if (os.startsWith("win")) 
  {
    return "ping -n 1 " + device;
  } 

  // SunOS ping command.
  if (os.startsWith("sun")) 
  {
    return "/usr/sbin/ping " + device;
  }

  // HP-UX ping command.
  if (os.startsWith("hp")) 
  {
    return "/usr/sbin/ping " + device + " -n 1";
  }
  
  // If we get here, then use a generic ping command that
  // should work on Linux-compatible systems.
  return "ping -c 1 " + device;
}

/**
 * This method returns the output produced by executing the specified command.
 *
 * @param cmd - the command to execute
 *
 * @throws SecurityException - If a security manager exists and its checkExec method doesn't allow creation of the subprocess 
 * @throws IOException - If an I/O error occurs
 * @throws NullPointerException - If command is null
 * @throws IllegalArgumentException - If command is empty
 * @throws RuntimeException - If executing command results in a non-0 exit value
 *
 * @return a non-null String
 */
function execute(cmd) 
{
  // Start the process.
  var process = Runtime.getRuntime().exec(cmd);

  // Wait for the process to complete.
  process.waitFor();

  // Handle the normal and error cases.
  if (process.exitValue() == 0) 
  {
    return copyToString(process.getInputStream()); 
  } 
  else 
  {
    throw new RuntimeException("The command '" + cmd + "' failed with exit value " + process.exitValue());
  }
}

/**
 * This method returns the contents of the specified input stream as a string.
 *
 * @throws IOException - If an I/O error occurs
 * @throws NullPointerException - If inputstream is null
 * 
 * @return a non-null String
 */
function copyToString(inputstream) 
{
  // Store output in a string buffer.
  var buf = new StringWriter();
  var writer = new BufferedWriter(buf);
  
  // Always close reader before returning.
  var reader = new BufferedReader(new InputStreamReader(inputstream));
  try 
  {
    // Copy one line at a time.
    var line = reader.readLine();
    while (line != null)
    {
      writer.write(line);
      writer.newLine();
      line = reader.readLine();
    } 
    
    // Return the output.
    writer.flush();
    return buf.toString();
    
  } 
  finally 
  {
    reader.close();
  }
}