Details
-
Bug
-
Status: Open
-
Major
-
Resolution: Unresolved
-
1.1
-
None
-
Windows 7/64 bit, JDK 1.6.0_27
Description
DefaultExecutor#execute(CommandLine, ExecuteResultHandler) can, and usually does, return before the target process has actually started. This can result in a race condition where several asynchronous processes are coupled by PipedInputStream/PipedOutputStream objects.
The following example shows the issue:
Borken.java
import java.io.*; import org.apache.commons.exec.*; public class Borken { public static int pipe(OutputStream out, OutputStream err, InputStream in, CommandLine cmd0, CommandLine cmd1) throws IOException { PipedInputStream pipeIn = new PipedInputStream(); PipedOutputStream pipeOut = new PipedOutputStream(pipeIn); DefaultExecutor exec0 = new DefaultExecutor(); exec0.setStreamHandler(new PumpStreamHandler(pipeOut, null, in)); exec0.execute(cmd0, new DefaultExecuteResultHandler()); // If the following line is commented, deadlock occurs //try { Thread.sleep(100); } catch (InterruptedException e) { } DefaultExecutor exec1 = new DefaultExecutor(); exec1.setStreamHandler(new PumpStreamHandler(out, err, pipeIn)); return exec1.execute(cmd1); } public static void main(String... args) { CommandLine cmd0 = new CommandLine("cmd").addArgument("/c").addArgument("dir"); //CommandLine cmd0 = new CommandLine("ls").addArgument("-l"); CommandLine cmd1 = new CommandLine("sort"); ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream err = new ByteArrayOutputStream(); ByteArrayInputStream in = new ByteArrayInputStream(new byte[0]); try { int result = pipe(out, err, in, cmd0, cmd1); System.out.format("Result code: %d%n", result); System.out.format("Out: %s%n", out.toString()); } catch (Exception e) { e.printStackTrace(); } } }
One possible solution is to pass in a semaphore object into DefaultExecutor#executeInternal which is notified once the process is started. The execute(CommandLine, Map, ExecuteResultHandler) method can then wait on this before returning.