Reputation: 5146
I am executing a shell script from my Java program using Process
and I want to kill/destroy that process if my script takes long time. What is the best way to do this?
Below is my code:
public static void main(String[] args) throws IOException, InterruptedException {
// Your script
String script = "#!/bin/bash\n\necho \"Hello World\"\n\n readonly PARAM1=$param1\n echo $PARAM1\n\nreadonly PARAM2=$param2\n echo $PARAM2\n\n";
// create a temp file and write your script to it
File tempScript = File.createTempFile("temp_scripts_", "");
tempScript.setExecutable(true);
try (OutputStream output = new FileOutputStream(tempScript)) {
output.write(script.getBytes());
}
// build the process object and start it
List<String> commandList = new ArrayList<>();
commandList.add(tempScript.getAbsolutePath());
ProcessBuilder builder = new ProcessBuilder(commandList);
builder.redirectErrorStream(true);
builder.environment().put("param1", "abc");
builder.environment().put("param2", "xyz");
Process shell = builder.start();
// read the output and show it
try (BufferedReader reader = new BufferedReader(new InputStreamReader(shell.getInputStream()))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
}
// wait for the process to finish
// but I want to kill/destroy the process if it takes too much time
int exitCode = shell.waitFor();
// delete your temp file
tempScript.delete();
// check the exit code (exit code = 0 usually means "executed ok")
System.out.println("EXIT CODE: " + exitCode);
}
Upvotes: 2
Views: 3551
Reputation: 17422
UPDATE
The class Process
doesn't have a "waitFor" method with a timeout unless you're using java 8. As an alternative, you can try starting a thread that waits for the process to finish and join such thread with join(timeout)
.
The following is a proof of concept with your code, modified to work with a thread:
import java.io.*;
import java.util.*;
public class Test {
public static void main(String[] args) throws IOException, InterruptedException {
// Your script
String script = getScriptFromSomewhere();
// create a temp file and write your script to it
File tempScript = File.createTempFile("temp_scripts_", "");
tempScript.setExecutable(true);
try (OutputStream output = new FileOutputStream(tempScript)) {
output.write(script.getBytes());
}
// build the process object and start it
List<String> commandList = new ArrayList<>();
commandList.add(tempScript.getAbsolutePath());
ProcessBuilder builder = new ProcessBuilder(commandList);
builder.redirectErrorStream(true);
builder.environment().put("param1", "abc");
builder.environment().put("param2", "xyz");
Process shell = builder.start();
// Start the interrupting thread
long timeoutMillis = 5000;
ExecutingThread thread = new ExecutingThread(shell, timeoutMillis);
thread.start();
// read the output and show it
byte[] buffer = new byte[1024];
try (InputStream input = shell.getInputStream()) {
int read;
while ((read = input.read(buffer)) != -1) {
String text = new String(buffer, 0, read);
System.out.print(text);
}
}
// wait for the process to finish (or be interrupted)
thread.join();
if(!thread.isFinished()) {
System.out.println("PROCESS WAS INTERRUPTED");
} else {
// check the exit code (exit code = 0 usually means "executed ok")
System.out.println("PROCESS FINISHED, EXIT CODE: " + thread.getExitValue());
}
// delete your temp file
tempScript.delete();
}
}
class ExecutingThread extends Thread {
private long timeoutMillis;
private WaitingThread waitingThread;
public ExecutingThread(Process shell, long timeoutMillis) {
this.timeoutMillis = timeoutMillis;
this.waitingThread = new WaitingThread(shell);
}
@Override
public void run() {
waitingThread.start();
try {
waitingThread.join(timeoutMillis);
} catch (InterruptedException ignore) {
}
if(waitingThread.isAlive()) {
waitingThread.interrupt();
}
}
public int getExitValue() {
return waitingThread.getExitValue();
}
public boolean isFinished() {
return waitingThread.isFinished();
}
}
class WaitingThread extends Thread {
private Process shell;
private volatile int exitValue;
private volatile boolean finished = false;
public WaitingThread(Process shell) {
this.shell = shell;
}
@Override
public void run() {
try {
exitValue = shell.waitFor();
finished = true;
} catch (InterruptedException e) {
shell.destroy();
}
}
public int getExitValue() {
return exitValue;
}
public boolean isFinished() {
return finished;
}
}
Upvotes: 2
Reputation: 363
For what i see you are currently waiting infinitely long for a result, because
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
will block due to readline() being a blocking operation.
The naive approach would be to externalize the reading from the streams into an own thread, which you could interrupt at any time.
So basically you would have a thread object which in its constructor would get the appropriate streams and a common lockObject, and then in the run() method starts to try reading from the stream, and do whatever you wanna do with the data.
In your main thread, you could then, after starting the script and getting the wanted streams, construct such a thread instance and start it in its own thread. Then you would wait in your main thread for the second "reader" thread to finish (keyword here is wait(timeout), where timeout is the max (allowed) run time of your script.
So now your main thread is waiting for your reader thread to finish it's task, but at most for timeout milliseconds (or was it seconds?) After that wait(timeout) you could then either kill the script (if it hangs, because your set timeout expired) or do whatever you want if it exited nicely.
I know there is no actual code included, but the approach should be valid. Good luck
Upvotes: 0
Reputation: 2823
Try something like:
long startTime = System.currentTimeMillis();
long endTime = start + 60*1000;
while (System.currentTimeMillis() < endTime)
{
// your code
}
Upvotes: 1