0

I'm trying to execute task with Runtime.getRuntime().exec.

As expected

I am getting "task started" in console. And the task gets executed and was completed.

Issue

Never got "task completed" printed.

Do you know what could be the reason? or How can I handle?

Code

System.out.println("task started");
Process process = Runtime.getRuntime().exec("cmd.exe /c task.bat task.job");
process.waitFor();
System.out.println("task completed");

Why is process.waitFor() not returning?

hc_dev
  • 8,389
  • 1
  • 26
  • 38
Jason
  • 415
  • 1
  • 6
  • 16
  • 1
    Does the task `cmd.exe /c task.bat task.job` terminate (succesfully or not)? You should first test the command independently. Also, it would be useful not to discard the exitcode of the executed subprocess, but to examine it: `int exitcode = process.waitFor();` – Michail Alexakis May 22 '22 at 18:55
  • @MichailAlexakis running command independently meaning running in cmd without java code? and yes it's terminated successfully – Jason May 22 '22 at 18:59
  • yes exactly, run in a CMD terminal – Michail Alexakis May 22 '22 at 19:01
  • 1
    Implement all the suggestions in [When Runtime.exec() won't](https://www.infoworld.com/article/2071275/when-runtime-exec---won-t.html) then ignore that it uses `exec(..)` & instead use a `ProcessBuilder`. It should either fix the problem or at least provide better information on what is failing. – Andrew Thompson May 22 '22 at 19:02
  • @AndrewThompson like this `ProcessBuilder pb = new ProcessBuilder("cmd.exe /c task.bat task.job"); pb.start();` ? – Jason May 22 '22 at 19:12
  • Always break the command `String` into an array (`String[]`). – Andrew Thompson May 22 '22 at 19:26
  • @AndrewThompson I didn't get you? – Jason May 22 '22 at 19:30
  • See this [answer](https://stackoverflow.com/a/1421220/5730279) which explains passing the array and use the _path relative_ to your Java-execution directory. – hc_dev May 22 '22 at 20:21
  • See Ilya’s answer for proper usage of ProcessBuilder. – VGR May 22 '22 at 21:13

3 Answers3

1

According to the JavaDoc of class Process some platforms have a limited buffer for the output:

All its standard I/O (i.e. stdin, stdout, stderr) operations [..] can be accessed via [..] getOutputStream(), getInputStream(), and getErrorStream(). Because some native platforms only provide limited buffer size for standard input and output streams, failure to promptly write the input stream or read the output stream of the subprocess may cause the subprocess to block, or even deadlock.

Try the following, before you call process.waitFor():

  1. get the output-streams from the process using getErrorStream() and getOutputStream() and
  2. read their contents to flush the buffer
hc_dev
  • 8,389
  • 1
  • 26
  • 38
lane.maxwell
  • 5,002
  • 1
  • 20
  • 30
  • 1
    Good advice! As for most modules dealing with system I/O, the docs explain important limitations and expected behavior in case of errors. Here the only means to monitor process-execution is either their __output__ (like on console) or their [__state incl. locked resources__](https://cloudchef.medium.com/linux-process-states-and-signals-a967d18fab64) (like in system-utilities, e.g. Linux: top, Windows: Task Manager, macOS: Activity Monitor). – hc_dev May 25 '22 at 19:12
1

The problem might be that you should read all output (standard and error, so that process could quit)

So, you probably need to do this

final ProcessBuilder processBuilder = new ProcessBuilder("cmd.exe", "/c", "task.bat", "task.job");

// if you want process output to be printed by you program
processBuilder.redirectError(ProcessBuilder.Redirect.INHERIT);
processBuilder.redirectOutput(ProcessBuilder.Redirect.INHERIT);

// if you don't want to show anything
// processBuilder.redirectError(ProcessBuilder.Redirect.DISCARD);
// processBuilder.redirectOutput(ProcessBuilder.Redirect.DISCARD);

Process process = processBuilder.start();
int exitCode = process.waitFor();
System.out.println(exitCode);

Ilya Sazonov
  • 1,006
  • 10
  • 16
0

The exec command is returning a process-handle (like the PID).

If the process has already terminated, you can query its exit-value with process.exitValue() as int:

By convention, the value 0 indicates normal termination.

otherwise if the process is still running:

IllegalThreadStateException - if the subprocess represented by this Process object has not yet terminated

Same for waitFor():

This method returns immediately if the subprocess has already terminated. If the subprocess has not yet terminated, the calling thread will be blocked until the subprocess exits.

Since the blocking is not what you probably want, can set a timeout here, then return of waitFor(long, TimeUnit) changes.

So you can wrap in a try-catch block to get complete control:

Process process = null;
System.out.println("Task will start soon ..");
try {
    process = Runtime.getRuntime().exec("cmd.exe /c task.bat task.job");
    System.out.println("Task started.");
    boolean alreadyTerminated = process.waitFor(50L, TimeUnit.SECONDS);  // you can also set a timeout
    if (!alreadyTerminated)
        System.out.println("Task still executing. Will wait for 50 seconds.");
    int exitValue = process.exitValue();
    System.out.println("Task completed with exit-code: " + exitValue);
} catch (IllegalThreadStateException e) {
    System.out.println(e);
}
hc_dev
  • 8,389
  • 1
  • 26
  • 38