1

I have a Java microservice which shells out to execute a program and then monitors stderr until nothing is returned:

  Process p = Runtime.getRuntime().exec("/bin/sh /var/task/bin/iTMSTransporter " + commandLine);
  BufferedReader stdError = new BufferedReader(new InputStreamReader(p.getErrorStream()));

  logger.log("StdErr:\n");
  while ((s = stdError.readLine()) != null && p.isAlive()) {
    System.out.println("stderr: " + s);
    if (s.indexOf("DBG-X") == -1) {
      result.stdErr += s + "\n";
    }
  }
  this.logger.log("finished reading stderr\n");

This was effective at detecting the completion of the external program and it always worked. Now the external program has been updated and it seems to start outputting stderr but then just stops (there should be more to the stderr stream) and eventually it times out.

I then added the p.isAlive() as an attempt to capture the shelled program's completion. This seemed to have no impact. Now here's the frustrating part ... in an older version of my microservice I used NodeJS instead of Java to shell out to run this program. The NodeJS version still works by listening for the close event:

shell.stdout.on('data', data => {
  stdout += data;
});
shell.stderr.on('data', data => {
  stderr += data;
});
shell.on('error', error => {
  reject(error);
});
shell.on('close', () => {
  resolve({
    stdout: stdout,
    stderr: stderr
  });
});

Is there something equivalent I can do with Java?

---- Addition -----

I tried something I'd seen online:

  Runtime rt = Runtime.getRuntime();
  rt.addShutdownHook(new Thread(new Runnable() {
    public void run() {
      System.out.println("\nGOT HERE\n");
    }
  }));
  Process p = rt.exec("/bin/sh /var/task/bin/iTMSTransporter " + commandLine);

thinking I'd get it detect in a new thread and output "GOT HERE" but that never is sent to console.

ken
  • 8,763
  • 11
  • 72
  • 133
  • This help? https://beradrian.wordpress.com/2008/11/03/detecting-process-exit-in-java/ – daniu Apr 25 '18 at 19:22
  • @daniu it scares me a bit as it's quite different than my approach and I'm too much of a Java rookie to bend it into shape for my needs. – ken Apr 25 '18 at 19:26
  • Wait... Doesn't a simple `p.waitFor()` work? – daniu Apr 25 '18 at 19:34
  • How would that be used? I'm struggling because the `while` loop off of stderr is hanging and I can't detect completion. If I could just test for completion and then pull off `stderr` (and ideally `stdout`) as a string that would be perfect. – ken Apr 25 '18 at 19:37
  • So do you just want to know when it's finished or do you actually need the output? It's not entirely clear what you're trying to do....like what you even start the other process for. – daniu Apr 25 '18 at 19:58
  • yeah i need at least `stderr` with `stdout` a nice-to-have – ken Apr 25 '18 at 19:59
  • Try swapping the order of the conditions in the while loop. `while (p.isAlive() && (s = stdError.readLine()) != null) { ... }` – smac89 Apr 25 '18 at 20:11
  • yeah I did try that ... no difference. – ken Apr 25 '18 at 20:12
  • The second one you tried won't work because `Runtime.getRuntime()` gets the runtime for the current java application (not that process you wanted to run). So the shutdown will only be called when your application exits, not when the process exits – smac89 Apr 25 '18 at 20:17
  • Have you tried [`Process.waitFor`](https://docs.oracle.com/javase/7/docs/api/java/lang/Process.html#waitFor%28%29)? Also why are you adamant on using stderr as an indicator that the process has finished? Is it documented for the application you are running? – smac89 Apr 25 '18 at 20:18
  • I am not adamant about means of capturing `stderr` ... happy with any way that works. – ken Apr 25 '18 at 20:26
  • I don't know how to use `Process.waitFor()` ... I guess I should look it up. – ken Apr 25 '18 at 20:27

1 Answers1

1

If you just want to wait for the process to finish, waitFor should be appropriate.

Process p = Runtime.getRuntime().exec("/bin/sh /var/task/bin/iTMSTransporter " + commandLine);
try {
    p.waitFor();
    // now, the process has terminated
} catch (InterruptedException e) {
    // something went wrong
    e.printStackTrace();
}

If you do want to capture the out- and err-streams, that's actually somewhat complicated because you'd need to create new threads for that (not that that's a huge problem, but it's OTT if you just want to know that the process has finished).

Also maybe see process.waitFor() never returns.

daniu
  • 14,137
  • 4
  • 32
  • 53
  • thanks for the push; it does indeed work. part of what had been getting my fowled up is that the third party library's new release takes orders of magnitude longer than prior release so my timeout timeframe no longer made sense. – ken Apr 27 '18 at 17:33