/ Java

Runtime.exec hangs, a complete solution

If you Google "Runtime.exec hangs" you will get alot of results. Executing external commands from Java seems very easy but it is actually very complicated. I was stuck at this problem for a long time before I finally got it. I never found a complete solution so I tought a post like this may boost my user count =)

If you execute a command using Runtime.exec on Windows and wait for it to complete, like follows, your program may hang on waitFor.

Process p = Runtime.getRuntime().exec("myCommand");
p.waitFor();

The reason for the hanging is the communication between Java and the external operating system process. And more specifically the communication buffers. The buffers for STDERR and STDOUT has to be flushed for the program not to hang.

Flushing these buffers are very easy, but! You can not flush them one by one, you have to do it simultaniously for the program not to hang while flushing! So you have to start at least one new thread to succeed.

I have made a complete class that executes a command and serves the content of STDOUT and STDERR. It also provides functionality for sending data to STDIN.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.concurrent.Semaphore;

public class ExecCommand {
private Semaphore outputSem;
private String output;
private Semaphore errorSem;
private String error;
private Process p;

private class InputWriter extends Thread {
private String input;

public InputWriter(String input) {
this.input = input;
}

public void run() {
PrintWriter pw = new PrintWriter(p.getOutputStream());
pw.println(input);
pw.flush();
}
}

private class OutputReader extends Thread {
public OutputReader() {
try {
outputSem = new Semaphore(1);
outputSem.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

public void run() {
try {
StringBuffer readBuffer = new StringBuffer();
BufferedReader isr = new BufferedReader(new InputStreamReader(p
.getInputStream()));
String buff = new String();
while ((buff = isr.readLine()) != null) {
readBuffer.append(buff);
System.out.println(buff);
}
output = readBuffer.toString();
outputSem.release();
} catch (IOException e) {
e.printStackTrace();
}
}
}

private class ErrorReader extends Thread {
public ErrorReader() {
try {
errorSem = new Semaphore(1);
errorSem.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

public void run() {
try {
StringBuffer readBuffer = new StringBuffer();
BufferedReader isr = new BufferedReader(new InputStreamReader(p
.getErrorStream()));
String buff = new String();
while ((buff = isr.readLine()) != null) {
readBuffer.append(buff);
}
error = readBuffer.toString();
errorSem.release();
} catch (IOException e) {
e.printStackTrace();
}
if (error.length() > 0)
System.out.println(error);
}
}

public ExecCommand(String command, String input) {
try {
p = Runtime.getRuntime().exec(makeArray(command));
new InputWriter(input).start();
new OutputReader().start();
new ErrorReader().start();
p.waitFor();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

public ExecCommand(String command) {
try {
p = Runtime.getRuntime().exec(makeArray(command));
new OutputReader().start();
new ErrorReader().start();
p.waitFor();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

public String getOutput() {
try {
outputSem.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
String value = output;
outputSem.release();
return value;
}

public String getError() {
try {
errorSem.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
String value = error;
errorSem.release();
return value;
}

private String[] makeArray(String command) {
ArrayList<String> commandArray = new ArrayList<String>();
String buff = "";
boolean lookForEnd = false;
for (int i = 0; i < command.length(); i++) {
if (lookForEnd) {
if (command.charAt(i) == '\"') {
if (buff.length() > 0)
commandArray.add(buff);
buff = "";
lookForEnd = false;
} else {
buff += command.charAt(i);
}
} else {
if (command.charAt(i) == '\"') {
lookForEnd = true;
} else if (command.charAt(i) == ' ') {
if (buff.length() > 0)
commandArray.add(buff);
buff = "";
} else {
buff += command.charAt(i);
}
}
}
if (buff.length() > 0)
commandArray.add(buff);

String[] array = new String[commandArray.size()];
for (int i = 0; i < commandArray.size(); i++) {
array[i] = commandArray.get(i);
}

return array;
}
}