Fixed issues where the shell executor could hang
This commit is contained in:
parent
2ca0b14164
commit
79c41e2a95
@ -79,12 +79,12 @@ class ShellExecutor {
|
|||||||
* @return true if the process ran successfully (exit value was 0), otherwise false
|
* @return true if the process ran successfully (exit value was 0), otherwise false
|
||||||
*/
|
*/
|
||||||
public static boolean run(String executableName, String... args) {
|
public static boolean run(String executableName, String... args) {
|
||||||
ShellExecutor shellExecutor = new ShellExecutor();
|
ShellExecutor shell = new ShellExecutor();
|
||||||
shellExecutor.setExecutable(executableName);
|
shell.setExecutable(executableName);
|
||||||
shellExecutor.addArguments(args);
|
shell.addArguments(args);
|
||||||
|
|
||||||
// blocks until finished
|
// blocks until finished
|
||||||
return shellExecutor.start() == 0;
|
return shell.start() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -246,6 +246,11 @@ class ShellExecutor {
|
|||||||
|
|
||||||
public
|
public
|
||||||
int start() {
|
int start() {
|
||||||
|
return start(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public
|
||||||
|
int start(final boolean waitForProcesses) {
|
||||||
fullCommand = new ArrayList<String>();
|
fullCommand = new ArrayList<String>();
|
||||||
if (executeAsShell) {
|
if (executeAsShell) {
|
||||||
if (OS.isWindows()) {
|
if (OS.isWindows()) {
|
||||||
@ -288,7 +293,7 @@ class ShellExecutor {
|
|||||||
fullCommand.add("-c");
|
fullCommand.add("-c");
|
||||||
}
|
}
|
||||||
|
|
||||||
fullCommand.add(this.executableName);
|
// fullCommand.add(this.executableName); // done elsewhere!
|
||||||
} else {
|
} else {
|
||||||
// shell and working/exe directory are mutually exclusive
|
// shell and working/exe directory are mutually exclusive
|
||||||
if (this.workingDirectory != null) {
|
if (this.workingDirectory != null) {
|
||||||
@ -316,6 +321,8 @@ class ShellExecutor {
|
|||||||
// when a shell AND on *nix, we have to place ALL the args into a single "arg" that is passed in
|
// when a shell AND on *nix, we have to place ALL the args into a single "arg" that is passed in
|
||||||
final StringBuilder stringBuilder = new StringBuilder(1024);
|
final StringBuilder stringBuilder = new StringBuilder(1024);
|
||||||
|
|
||||||
|
stringBuilder.append(this.executableName).append(" ");
|
||||||
|
|
||||||
for (String arg : this.arguments) {
|
for (String arg : this.arguments) {
|
||||||
stringBuilder.append(arg).append(" ");
|
stringBuilder.append(arg).append(" ");
|
||||||
}
|
}
|
||||||
@ -390,10 +397,11 @@ class ShellExecutor {
|
|||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
if (outputErrorStream != null) {
|
if (outputErrorStream != null) {
|
||||||
this.outputErrorStream.println("There was a problem executing the program. Details:");
|
this.outputErrorStream.println("There was a problem executing the program. Details:");
|
||||||
|
ex.printStackTrace(this.outputErrorStream);
|
||||||
} else {
|
} else {
|
||||||
System.err.println("There was a problem executing the program. Details:");
|
System.err.println("There was a problem executing the program. Details:");
|
||||||
|
ex.printStackTrace();
|
||||||
}
|
}
|
||||||
ex.printStackTrace(this.outputErrorStream);
|
|
||||||
|
|
||||||
if (this.process != null) {
|
if (this.process != null) {
|
||||||
try {
|
try {
|
||||||
@ -458,10 +466,39 @@ class ShellExecutor {
|
|||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void run() {
|
void run() {
|
||||||
|
try {
|
||||||
|
// wait for the READER threads to die (meaning their streams have closed/EOF'd)
|
||||||
|
if (writeToProcess_input != null) {
|
||||||
|
// the INPUT (from stdin). It should be via the InputConsole, but if it's in eclipse,etc -- then this doesn't do anything
|
||||||
|
// We are done reading input, since our program has closed...
|
||||||
|
writeToProcess_input.close();
|
||||||
|
if (createReadWriterThreads) {
|
||||||
|
writeToProcess_input.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
readFromProcess_output.close();
|
||||||
|
if (createReadWriterThreads) {
|
||||||
|
readFromProcess_output.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (readFromProcess_error != null) {
|
||||||
|
readFromProcess_error.close();
|
||||||
|
if (createReadWriterThreads) {
|
||||||
|
readFromProcess_error.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread()
|
||||||
|
.interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
// forcibly terminate the process when it's streams have closed.
|
||||||
|
// this is for cleanup ONLY, not to actually do anything.
|
||||||
ShellExecutor.this.process.destroy();
|
ShellExecutor.this.process.destroy();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
hook.setName("ShellProcess Shutdown Hook");
|
hook.setName("ShellExecutor Shutdown Hook for " + this.executableName);
|
||||||
|
|
||||||
// add a shutdown hook to make sure that we properly terminate our spawned processes.
|
// add a shutdown hook to make sure that we properly terminate our spawned processes.
|
||||||
// hook is NOT set to daemon mode, because this is run during shutdown
|
// hook is NOT set to daemon mode, because this is run during shutdown
|
||||||
@ -488,6 +525,7 @@ class ShellExecutor {
|
|||||||
else {
|
else {
|
||||||
readFromProcess_output.run();
|
readFromProcess_output.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (readFromProcess_error != null) {
|
if (readFromProcess_error != null) {
|
||||||
if (createReadWriterThreads) {
|
if (createReadWriterThreads) {
|
||||||
readFromProcess_error.start();
|
readFromProcess_error.start();
|
||||||
@ -499,38 +537,22 @@ class ShellExecutor {
|
|||||||
|
|
||||||
int exitValue = 0;
|
int exitValue = 0;
|
||||||
|
|
||||||
try {
|
if (waitForProcesses) {
|
||||||
this.process.waitFor();
|
try {
|
||||||
|
this.process.waitFor();
|
||||||
exitValue = this.process.exitValue();
|
exitValue = this.process.exitValue();
|
||||||
|
hook.run();
|
||||||
// wait for the READER threads to die (meaning their streams have closed/EOF'd)
|
} catch (InterruptedException e) {
|
||||||
if (writeToProcess_input != null) {
|
Thread.currentThread()
|
||||||
// the INPUT (from stdin). It should be via the InputConsole, but if it's in eclipse,etc -- then this doesn't do anything
|
.interrupt();
|
||||||
// We are done reading input, since our program has closed...
|
|
||||||
writeToProcess_input.close();
|
|
||||||
writeToProcess_input.join();
|
|
||||||
}
|
|
||||||
readFromProcess_output.close();
|
|
||||||
readFromProcess_output.join();
|
|
||||||
if (readFromProcess_error != null) {
|
|
||||||
readFromProcess_error.close();
|
|
||||||
readFromProcess_error.join();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// forcibly terminate the process when it's streams have closed.
|
// remove the shutdown hook now that we've shutdown.
|
||||||
// this is for cleanup ONLY, not to actually do anything.
|
try {
|
||||||
this.process.destroy();
|
Runtime.getRuntime().removeShutdownHook(hook);
|
||||||
} catch (InterruptedException e) {
|
} catch (IllegalStateException ignored) {
|
||||||
Thread.currentThread()
|
// can happen, safe to ignore
|
||||||
.interrupt();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// remove the shutdown hook now that we've shutdown.
|
|
||||||
try {
|
|
||||||
Runtime.getRuntime().removeShutdownHook(hook);
|
|
||||||
} catch (IllegalStateException ignored) {
|
|
||||||
// can happen, safe to ignore
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return exitValue;
|
return exitValue;
|
||||||
|
Loading…
Reference in New Issue
Block a user