views:

198

answers:

2

I've a batch file that needs to be invoked from a java program. The batch file in-turn invokes an EXE. The EXE program will return data which I want to handle. If the EXE prints data to console am able to capture it as follows. But when the EXE is returning data after its completion, am not able to capture it.

        ProcessBuilder pb = new ProcessBuilder("foo.bat");
     Process p = pb.start();
     int exitValue = p.waitFor();
     BufferedReader reader;
     // System.out.println("Exit Value" + exitValue);
     if (exitValue == 0) {
      reader = new BufferedReader(new InputStreamReader(p
        .getInputStream()));
     } else {
      reader = new BufferedReader(new InputStreamReader(p
        .getErrorStream()));
     }
     StringBuffer sb = new StringBuffer();
     String temp = reader.readLine();
     while (temp != null) {
      sb.append(temp);
      temp = reader.readLine();
     }

     reader.close();
     System.out.println(sb.toString());

How do i need to capture the data returned by the EXE executed from a batch file?

The EXE is basically a C program. When I invoke the C program, the main method returns me the data, which I want to handle it.

+1  A: 

Do you have control over the script? I'd try storing the return value (that's what you want?) from the executable to an environment variable. You may have to export it. Here's a tutorial on how to handle environment variables with Java.

Thanks to jitters comment - no, it doesn't work. We can't change values of the environment variable in a 'global' way (now I know..)

But, the idea works with a little adaptation: I'd still try to store the return value in a global accessible resource: Simply send the return value to a file (exec myapp > result.txt) and read the value from that file in your java application.

Andreas_D
AFAIK you can't access modifications to the environment made by the external program. Are you sure this works? Did you try it out. AFAIK you only can set a modified environment for the program to be executed and not the other way around
jitter
+1  A: 

I think i had the same problem some time ago. One problem with the previous strategy is that you're waiting for the process to finish (waitFor) to capture the data returned from it. You may have problems if the process fails or hang ups. A better aproach would be something like this:

You should create two threads to consume the input stream and the error stream of the process, independently of the waitFor call. Something like these should work:

1.- Create a class that wraps the execution of the process:

public class ExecutionWrapper {

private int exitStatus;
private String[] command;
private String[] environment;
private String directory;
private boolean running;

private Process process;
private ExecutionWrapperOutput error;
private ExecutionWrapperOutput output;

public ExecutionWrapper(String command, String[] environment, String directory) {
 this.command = new String[] { command };
 this.environment = environment;
 this.directory = directory;
 this.exitStatus = -1;
}

public ExecutionWrapper(List<String> command, List<String> environment, String directory) {
 if (command != null)
  this.command = command.toArray(new String[command.size()]);
 if (environment != null)
  this.environment = environment.toArray(new String[environment.size()]);
 this.directory = directory;
 this.exitStatus = -1;
}

public void start() {
 try {
  this.process = Runtime.getRuntime().exec(this.command, this.environment, new File(this.directory));
  this.running = true;

  // Error and information messages
  this.error = new ExecutionWrapperOutput(this.process.getErrorStream());
  this.output = new ExecutionWrapperOutput(this.process.getInputStream());

  // Start the messaging threads
  this.error.start();
  this.output.start();

  // Final status
  Runnable runner = new Runnable() {
   public void run() {
    try {
     ExecutionWrapper.this.exitStatus = ExecutionWrapper.this.process.waitFor();
     ExecutionWrapper.this.running = false;
     ExecutionWrapper.this.process.destroy();
    } catch (Exception ex) {
     LoggingUtiles.exception(ex);
     ExecutionWrapper.this.exitStatus = -1;
    }
   }
  };
  new Thread(runner).start();

 } catch (Throwable t) {
  LoggingUtiles.exception(t);
 }
}

public void stop() {
 this.running = false;
 this.process.destroy();
}

public boolean isRunning() {
 return running;
}

public int getExitStatus() {
 return exitStatus;
}

public String[] getError(boolean clear) {
 return this.error.getLines(clear);
}

public String[] getOutput(boolean clear) {
 return this.output.getLines(clear);
}

public String[] getCommand() {
 return command;
}

public String getDirectory() {
 return directory;
}

public void waitFor() {
 try {
  process.waitFor();
 } catch (Throwable t) {
  LoggingUtiles.exception(t);
 }
}

}

2.- Then, create the ExecutionWrapperOutput class, that processes the output of the process streams:

public class ExecutionWrapperOutput extends Thread {

private InputStream is;

private List<String> output;

private Object mutex = new Object();

ExecutionWrapperOutput(InputStream is) {
 this.is = is;
 this.output = new ArrayList<String>();
}

public void run() {
 try {
  InputStreamReader isr = new InputStreamReader(is);
  BufferedReader br = new BufferedReader(isr);
  String line = null;
  while ((line = br.readLine()) != null) {
   synchronized (mutex) {
    output.add(line);
   }
  }
 } catch (IOException ioe) {
  ioe.printStackTrace();
 }
}

public String[] getLines(boolean clear) {
 String[] lines = null;
 synchronized (mutex) {
  lines = output.toArray(new String[] {});
  if (clear)
   output.clear();
 }
 return lines;
}

}

Maybe all this works for you. Let me now if it works...

pgarbusi