Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Easier Execution of External Processes in Java

0.00/5 (No votes)
7 Apr 2004 1  
Execute any external Process from your Java code without worry
Sample Image - ProcessHelper.jpg

Introduction

Sometimes, when you depend on the execution of External Processes from your Java code, it can be a real pain to actually get that code running properly.

There are several pitfalls while trying to run external processes:

  1. Input and Output streams from and to the process aren't handled properly.
  2. Although the Process is running, your application isn't waiting for it to finish.
  3. Your application IS waiting for the process to finish by simply halting.
  4. The process terminated, but you did not receive the result code.
  5. You simply have no idea how to actually run an External Process!

If any of these problems ever troubled you, read on.

I will use the DOS prompt (under WinXP) as an External Process for this article's demonstration.

The sources are composed as follows:

  • Reusable code
    • dev.exec.util.ExecHelper
    • dev.exec.util.ExecProcessor
  • Demo code
    • dev.exec.tester.TestApplication
    • dev.exec.tester.TestFrame

The idea is simple:

  1. TestApplication creates an instance of TestFrame.
  2. TestFrame is displayed to the user.
  3. The user requests the execution of the Command Processor.
  4. TestFrame registers itself to the ExecHelper as an ExecProcessor.
  5. TestFrame uses the ExecHelper to run the Command Processor.
  6. ExecHelper notifies the TestFrame about new output text.
  7. The user uses the TestFrame to input commands for the Command Processor.
  8. TestFrames notifies the ExecHelper about new user input.
  9. ExecHelper notifies the Command Processor about new user input.
  10. While the Command Processor is running, loop through 6 - 10.
  11. While the program is running, loop through 3 - 11.
  12. Quit.

The source files are documented (at least the ExecHelper and ExecProcessor), and the demo ZIP contains a JBuilder project and the compiled classes plus a JAR file.

I will now explain how the concept is put to work in the Reusable code.

First off, I declared ExecProcessor as an interface to handle all the events coming from the External Process.

public interface ExecProcessor {
  // This method gets called when the process sends us a new input String..
  public void processNewInput(String input);
  // This method gets called when the process sends us a new error String..
  public void processNewError(String error);
  // This method gets called when the process has ended..
  public void processEnded(int exitValue);
}

TestFrame which implements the ExecProcessor interface has the following methods:

public void processNewInput(String input) {
  updateTextArea(jTextArea1, input);
}

public void processNewError(String error) {
  updateTextArea(jTextArea1, error);
}

public processEnded(int exitValue) {
  exh = null;
  statusBar.setText("Command.exe ended..");
  JOptionPane.showMessageDialog(this, "Exit value for Command.exe was [" 
    + exitValue + "]", "Command.exe is done!", JOptionPane.INFORMATION_MESSAGE);
  try {
    Thread.sleep(1000);
  } catch (InterruptedException ex) {
  }
  jTextArea1.setText(null);
  statusBar.setText("Ready..");
}

As you see, things are kept simple. The methods processNewInput() and processNewError() update a TextArea with the new line of text sent from the Command Processor, while the processEnded() method notifies the program that the Command Processor has terminated, and displays the exit value sent from the Command Processor.

The TestFrame itself uses the runCommandActionPerformed() method to invoke the ExecHelper's exec() method thus executing the Command Processor as the following code demonstrates:

void runCommandActionPerformed(ActionEvent e) {
  if (exh == null) {
    try {
      exh = ExecHelper.exec(this, "cmd.exe");
      statusBar.setText("Command.exe running..");
    } catch (IOException ex) {
      processNewError(ex.getMessage());
    }
}

All that is really left is to describe how the ExecHelper performs its magic. First the ExecHelper is a Runnable object, which means it has a run() method which lets it perform its tasks while the application itself keeps running (i.e. multi-threading).

ExecHelper has three streams to handle, and three Threads to work with. Yes, this version of the code requires that three separate Threads be activated for each Process. There probably is a way to do this with less, but currently I haven't had the time to research it yet.

As a Runnable object, the ExecHelper's run() method is the most interesting:

public void run() {
  // Are we on the process Thread?
  if (processThread == Thread.currentThread()) {
    try {
      // This Thread just waits for the process to end and notifies the handler..
      processEnded(process.waitFor());
    } catch (InterruptedException ex) {
      ex.printStackTrace();
    }
  // Are we on the InputRead Thread?
  } else if (inReadThread == Thread.currentThread()) {
    try {
      // Read the InputStream in a loop until we find no more bytes to read..
      for (int i = 0; i > -1; i = pInputStream.read(inBuffer)) {
        // We have a new segment of input, so process it as a String..
        processNewInput(new String(inBuffer, 0, i));
      }
    } catch (IOException ex) {
      ex.printStackTrace();
    }
  // Are we on the ErrorRead Thread?
  } else if (errReadThread == Thread.currentThread()) {
    try {
      // Read the ErrorStream in a loop until we find no more bytes to read..
      for (int i = 0; i > -1; i = pErrorStream.read(errBuffer)) {
        // We have a new segment of error, so process it as a String..
        processNewError(new String(errBuffer, 0, i));
      }
    } catch (IOException ex) {
      ex.printStackTrace();
    }
  }
}

I leave you to look at the source code and find out for yourself how the user input flows from TestFrame to the Command Processor.

I hope this will be useful to anyone who needs this functionality in Java.

History

  • 7th April, 2004: Initial post

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here