I need to execute an external batch file in java with a specific timeout. which means that if the batch execution take longer than specified timeout, i need to cancel the execution.
here is a sample code that i wrote:
public static void main(String[] args) throws IOException, InterruptedException {
ProcessBuilder p = new ProcessBuilder("c:\\wait.bat", "25"); // batch file execution will take 25 seconds.
final long l = System.currentTimeMillis();
System.out.println("starting..." + (System.currentTimeMillis() - l));
final Process command = p.start();
System.out.println("started..." + (System.currentTimeMillis() - l));
Timer t = new Timer();
t.schedule(new TimerTask() {
@Override
public void run() {
command.destroy();
}
}, 5000); // it will kill the process after 5 seconds (if it's not finished yet).
int i = command.waitFor();
t.cancel();
System.out.println("done..." + (System.currentTimeMillis() - l));
System.out.println("result : " + i);
System.out.println("Really Done..." + (System.currentTimeMillis() - l));
}
the batch file "wait.bat" is something like this:
@echo off
echo starting the process...
@ping 127.0.0.1 -n 2 -w 1000 > nul
@ping 127.0.0.1 -n %1% -w 1000> nul
echo process finished succesfully
@echo on
As you see in the code, batch file will take 25 seconds to finish (first line in main method) and the Timer will destroy the command after 5 seconds.
here is the output of my code:
starting...0
started...0
done...5000
result : 1
Really Done...5000
BUILD SUCCESSFUL (total time: 25 seconds)
as you see in output, the last line ("Really Done...") is executed in 5th second but the application is finished after 25 seconds.
my question is that : even though i called the destroy method in my timer, why jvm still waiting for the process to be finished ?
You can call destroy() method on it.
Class ProcessBuilder. This class is used to create operating system processes. Each ProcessBuilder instance manages a collection of process attributes. The start() method creates a new Process instance with those attributes.
The subprocess is started with its IO passing up to the process that executed it. This is required to see both any stdout s and stderr s it produces. inheritIO is a convenience method and can also be achieved by calling chaining the following code instead (also configures the stdin of the subprocess): builder .
It is a bug in Java's Process.destroy()
implementation on Windows. The problem is that the batch-script (or its executing shell) is killed, but does not kill its own child processes (the ping here). Thus, ping is still running after the .destroy()
, and also after the .waitFor()
. But somehow the VM still waits for the ping to finish before finishing itself.
It seems there is nothing you can do here from the Java side to really kill the ping reliably.
You may think about using start
(in your batch script or outside) to invoke your ping as a separate process.
(See also this previous discussion.)
Or change to a unix-like operation system.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With