Executing a Command Line Executable From Java

In this post we’ll deal with a common need for Java developers. Execute and manage an external process from within Java. Since this task is quite common we set out to find a Java library to help us accomplish it.
 
 
 
 
 
 
 
 
 
The requirements from such a library are:

  • Execute the process asynchronously.
  • Ability to abort the process execution.
  • Ability to wait for process completion.
  • On process output notifications.
  • Ability to kill the process in case it hung.
  • Get the process exit code.

The native JDK does not help much. Fortunately, we have Apache Commons Exec. Indeed it is much easier but still not as straightforward as we hoped. We wrote a small wrapper on top of it.

Here is the method signature we expose:

   1: public static Future<Long> runProcess(final CommandLine commandline, final ProcessExecutorHandler handler, final long watchdogTimeout) throws IOException;
  1. It returns a Future<Long>. This covers section 1,2,3,6.
  2. Instance of ProcessExecutorHandler is passed to the function. This instance is actually a listener for any process output. This covers section 4 in our requirement.
  3. Last but not least you supply a timeout. If the process execution takes more than said timeout you assume the process hung and you will end it. In that case the error code returned by the process will be -999.

That’s it! Here is the method implantation. Enjoy.

  import org.apache.commons.exec.*;


  import org.apache.commons.exec.Executor;


   


  import java.io.IOException;


  import java.util.concurrent.*;


   


   


  public class ProcessExecutor {


      public static final Long  WATCHDOG_EXIST_VALUE = -999L;


   


      public static Future<Long> runProcess(final CommandLine commandline, final ProcessExecutorHandler handler, final long watchdogTimeout) throws IOException{


 


       ExecutorService executor = Executors.newSingleThreadExecutor();


       return executor.submit(new ProcessCallable(watchdogTimeout, handler, commandline));


  


     }


  


     private static class ProcessCallable implements Callable<Long>{


  


  


         private long watchdogTimeout;


         private ProcessExecutorHandler handler;


         private CommandLine commandline;


  


         private ProcessCallable(long watchdogTimeout, ProcessExecutorHandler handler, CommandLine commandline) {


        this.watchdogTimeout = watchdogTimeout;


        this.handler = handler;


        this.commandline = commandline;


    }





    @Override


    public Long call() throws Exception {


        Executor executor = new DefaultExecutor();


        executor.setProcessDestroyer(new ShutdownHookProcessDestroyer());


        ExecuteWatchdog watchDog = new ExecuteWatchdog(watchdogTimeout);


              executor.setWatchdog(watchDog);


              executor.setStreamHandler(new PumpStreamHandler(new MyLogOutputStream(handler, true),new MyLogOutputStream(handler, false)));


           Long exitValue;


           try {


               exitValue =  new Long(executor.execute(commandline));





           } catch (ExecuteException e) {


               exitValue =  new Long(e.getExitValue());


           }


           if(watchDog.killedProcess()){


               exitValue =WATCHDOG_EXIST_VALUE;


           }





            return exitValue;


 


 


        }


 


   }


 


   private static class MyLogOutputStream extends  LogOutputStream{


 


       private ProcessExecutorHandler handler;


       private boolean forewordToStandardOutput;


 


       private MyLogOutputStream(ProcessExecutorHandler handler, boolean forewordToStandardOutput) {


               this.handler = handler;


               this.forewordToStandardOutput = forewordToStandardOutput;


           }


    


           @Override


           protected void processLine(String line, int level) {


               if (forewordToStandardOutput){


                   handler.onStandardOutput(line);


             }


             else{


                 handler.onStandardError(line);


             }


         }


     }


  


     public static void main(String[] args) throws IOException {


         CommandLine cl = CommandLine.parse("test.bat");


         Future<Long> exitValue = runProcess(cl, new ProcessExecutorHandler() {


  


             @Override


             public void onStandardOutput(String msg) {


                 System.out.println("output msg = " + msg);


             }


  


             @Override


             public void onStandardError(String msg) {


                 System.out.println("error msg = " + msg);


             }


         }, 1);


         try {


  


             Long aVoid = exitValue.get();


             System.out.println("Finished with  " + aVoid);


         } catch (InterruptedException e) {


             e.printStackTrace();  //To change body of catch statement use File | ngs | File Templates.


         } catch (ExecutionException e) {


             e.printStackTrace();  //To change body of catch statement use File | ngs | File Templates.


         }


  


  


     }


  


  


 }


 

Reference: Executing a Command Line Executable From Java from our JCG partner Nadav Azaria at the DeveloperLife blog.

Related Whitepaper:

Bulletproof Java Code: A Practical Strategy for Developing Functional, Reliable, and Secure Java Code

Use Java? If you do, you know that Java software can be used to drive application logic of Web services or Web applications. Perhaps you use it for desktop applications? Or, embedded devices? Whatever your use of Java code, functional errors are the enemy!

To combat this enemy, your team might already perform functional testing. Even so, you're taking significant risks if you have not yet implemented a comprehensive team-wide quality management strategy. Such a strategy alleviates reliability, security, and performance problems to ensure that your code is free of functionality errors.Read this article to learn about this simple four-step strategy that is proven to make Java code more reliable, more secure, and easier to maintain.

Get it Now!  

2 Responses to "Executing a Command Line Executable From Java"

  1. It seems the code is missing the interface declaration:
    interface ProcessExecutorHandler {
    public void onStandardOutput(String line);
    public void onStandardError(String line);
    }

    Increse the watchdog timeout expressed in millisecs (i.e. to 3000)

    Then you can try it with a simple bat file like “C:/Users/username/Desktop/test/test.bat”:

    cd C:UsersusernameDesktoptest
    echo 123
    copy one.txt two.txt
    exit /b 55

Leave a Reply


9 − five =



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

20,709 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books