process.waitFor() never returns
There are many reasons that waitFor()
doesn't return.
But it usually boils down to the fact that the executed command doesn't quit.
This, again, can have many reasons.
One common reason is that the process produces some output and you don't read from the appropriate streams. This means that the process is blocked as soon as the buffer is full and waits for your process to continue reading. Your process in turn waits for the other process to finish (which it won't because it waits for your process, ...). This is a classical deadlock situation.
You need to continually read from the processes input stream to ensure that it doesn't block.
There's a nice article that explains all the pitfalls of Runtime.exec()
and shows ways around them called "When Runtime.exec() won't" (yes, the article is from 2000, but the content still applies!)
Process hangs waitFor() method
The javadoc says that the argument to the ProcessBuilder
constructor is:
"... a string array containing the program and its arguments".
and the example makes it clear that it means separate strings for each argument. You have passed all of the arguments as one String. In addition, you have added a spurious space to the end of the command pathname.
I suggest that you look at the example in the javadoc to see how you are supposed to instantiate a ProcessBuilder
object.
Another problem is that you appear to be calling waitFor()
before you read the output from adb
. If you do that and adb
produces more output than can be buffered in the pipe, then you will get a deadlock. Call waitFor()
after you have read all of the output.
process.waitFor() not returning
If a process executed by Runtime.exec()
produces output -- either to stdout
or stderr
-- then for it to operate robustly you need to start consuming the output before calling Process.waitFor()
. Most likely, your psql
process is blocked because it's waiting for something to read its output, and you don't start to do that until Process.waitFor()
is complete.
You probably need to consume the output in a separate thread, unless you know exactly what output is expected. In any case, there may, or may not, be a problem with your invocation of psql
-- unless you capture its output you probably won't know.
Robust and portable use of Runtime.exec()
is surprisingly difficult. I've written about this at length, with code samples, here:
http://kevinboone.me/exec.html
Java process.waitFor() does not return
xcopy
probably just happens to produce more output than move
, filling up the out-buffer and blocking until it is flushed. The default behavior in Java is to pipe the subprocess's stdout/stderr into InputStream
s that you are then required to read programmatically lest the subprocess's buffers overflow.
If the latter is the case, the solution is simple, and in fact you should do that anyway: use ProcessBuilder
to prepare the system call and call inheritIO
on it. This will reuse your parent process`s stdin and stdout for the subprocess.
A side note, xcopy
is a regular .exe
file and doesn't need wrapping into cmd.exe /c
.
Why process.waitFor() is not returning?
The exec
command is returning a process-handle (like the PID).
If the process has already terminated, you can query its exit-value withprocess.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);
}
Why does Process.waitFor() never return?
This is an OS thing. The child process is writing to stdout, and that's being buffered waiting for your Java process to read it. When you don't read it, the buffer eventually fills up and the child process blocks writing to stdout waiting for buffer space.
You would have to processes the child process' stdout (and stderr) whichever language you were using.
I suggest you read this article (all 4 pages of it) and implement the recommendations there.
ProcessBuilder waitFor() never returns
The problem isn't with handling the external process, it's the incorrect use of the ExecutorService
returned by Executors.newSingleThreadExecutor()
. This method creates a thread running in the background, and because you never shut down this executor (which would stop the thread), this thread prevents the JVM from exiting.
If you want to, you can prove that the ls
process exits by adding a line which prints out its exit code. Note however that this line might not appear at the bottom of the output.
To address this, we need to keep a reference to the ExecutorService
created, and then shut it down when we don't need it any more. Try replacing
Executors.newSingleThreadExecutor().submit(streamGobbler);
int exitCode = process.waitFor();
with
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(streamGobbler);
int exitCode = process.waitFor();
executor.shutdown();
I made this change to your code and it went from hanging to exiting cleanly.
Java process.waitFor(time, unit) does not time out
Thanks all for your comments. According to the comments above, one solution is to remove the piece of code between the process start and the process wait process (waitFor(...)
)
p = pb.start()
// Everything in-between removed
if(p.waitFor(10l, TimeUnit.MILLISECONDS)) {
// Now this code is not processed in case of time out
}
The reason is (see @TreffnonX comment)
Because I used a while loop which only returns false, once the input is closed. Since my input only closes when the process terminates, my check (waitFor) happens after the process has ended.
Another solution could be to launch a separate thread that will run this while loop.
Related Topics
Http Servlet Request Lose Params from Post Body After Read It Once
Static Method in a Generic Class
Jquery, Spring MVC @Requestbody and JSON - Making It Work Together
Java String Split with "." (Dot)
Java Securityexception: Signer Information Does Not Match
How to Perform Mouseover Function in Selenium Webdriver Using Java
Accessing Members of Items in a JSONarray with Java
How to Switch to the New Browser Window, Which Opens After Click on the Button
Parse String to Date with Different Format in Java
Places Where Javabeans Are Used
How to Convert Java String into Byte[]
Why Is Hibernate Open Session in View Considered a Bad Practice
Java Wait Cursor Display Problem
Difference Between Thread's Context Class Loader and Normal Classloader
Under What Conditions Is a Jsessionid Created
How to Add Custom Method to Spring Data JPA
If Profiler Is Not the Answer, What Other Choices Do We Have
How to Map an Entity Field Whose Name Is a Reserved Word in JPA