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 ?
If you use Unix/Linux, then write a wrapper bash shell script to interrupt an external command by timeout, then call the wrapper from Java.
The wrapper script looks like
You can detect if timeout expired by checking the script exit code which is 124 in case of timeout
See
I could be a problem with the cancel method of the timer. try to start the timer as daemon thread.
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.