Evan Knowles
Evan Knowles

Reputation: 7511

Apache CommonsExec async call preventing sync call

I have two calls that I perform using CommonsExec - one is a standard synchronous call in which I call a batch file to compile a maven project, and the next is a asynchronous call also to a batch file that runs the compiled command line project.

The maven batch files look like

call mvn package

This is done twice, to compile and launch two programs.

This works fine the first time, but the second sync build call does not return for some reason, although the logged output shows the build completes successfully. The program then obviously is not launched.

I can also recreate this by doing a run, then a compile - it seems that so long as a asynchronous call is running, the synchronous call will not complete.

Can anyone help here?

Code for the above is

 private static final String LAUNCH_CLIENT_FORMAT = "\"%s\\start.bat\" http://localhost:%d" + ENDPOINT;
 private static final String COMPILE_FORMAT = "\"%s\\compile.bat\"";

 private static boolean compileAndLaunch(String aiDirectory, int port) {
   System.out.println("Compiling " + aiDirectory + " for port " + port);
   if (!run(String.format(COMPILE_FORMAT, aiDirectory), aiDirectory))
     return false;

   System.out.println("Done compiling " + aiDirectory + " for port " + port + ", launching...");
   if (!runAsync(String.format(LAUNCH_CLIENT_FORMAT, aiDirectory, port), aiDirectory))
     return false;

   return true;
 }

 private static boolean run(String command, String directory) {
   DefaultExecutor executor = getExecutor(directory);
   System.out.println("Running " + command);
   CommandLine commandLine = CommandLine.parse(command);
   try {
     executor.execute(commandLine);
   }
   catch (ExecuteException e) {
     System.out.println("Failed to execute " + command);
     return false;
   }
   catch (IOException e) {
     System.out.println("IO Exception running " + command);
     return false;
   }
   return true;
 }

 private static DefaultExecutor getExecutor(String directory) {
   ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
   PumpStreamHandler streamHandler = new PumpStreamHandler(outputStream);
   DefaultExecutor executor = new DefaultExecutor();
   executor.setWorkingDirectory(new File(directory));
   executor.setStreamHandler(streamHandler);
   return executor;
 }

 private static boolean runAsync(String command, String directory) {

   CommandLine commandLine = CommandLine.parse(command);
   System.out.println("Running async " + command);
   DefaultExecutor executor = getExecutor(directory);
   DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler();
   try {
     executor.execute(commandLine, resultHandler);
   }
   catch (ExecuteException e) {
     System.out.println("Failed to execute " + command);
     return false;
   }
   catch (IOException e) {
     System.out.println("IO Exception running " + command);
     return false;
   }
   return true;
 }

Upvotes: 0

Views: 2160

Answers (1)

Marcos Zolnowski
Marcos Zolnowski

Reputation: 2797

This is your callback:

DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler();

This is your asynchronous executor:

executor.execute(commandLine, resultHandler);

But, in a asynchronous mode, right after calling executor.execute() the method will continue, but until something happens in another thread and resultHandler.onProcessComplete() or resultHandler.onProcessFailed() are called, you still don't know if the execution ended, and should not exit your runAsync() method.

I believe changing your method to something like this will work:

private static boolean runAsync(String command, String directory) {

   CommandLine commandLine = CommandLine.parse(command);
   System.out.println("Running async " + command);
   DefaultExecutor executor = getExecutor(directory);
   DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler();
   try {
     executor.execute(commandLine, resultHandler);
     resultHandler.waitFor();
     if(resultHandler.getException() != null){
       throw resultHandler.getException();
     }
   }
   catch (ExecuteException e) {
     System.out.println("Failed to execute " + command);
     return false;
   }
   catch (IOException e) {
     System.out.println("IO Exception running " + command);
     return false;
   }
   return true;
 }

Because resultHandler.waitFor() will make the thread wait until the execution ends.

But this is the same as using the synchronous mode. Only if both calls are asynchronous, you would win in performance, because they would run in parallel, and you would be waiting until both finish.

Upvotes: 2

Related Questions