1

What I want to do is, launch a .exe console program with Java, and use Java to manipulate the input and output streams from the console window. I know I can get the input and output streams from an application, and this is how I am currently doing it:

    try {
        process = Runtime.getRuntime().exec("C:\\Users\\Owner\\Documents\\testApp\\console.exe");
    } catch (IOException e1) {
        e1.printStackTrace();
        return;
    }

    stdin = process.getOutputStream();
    stdout = process.getInputStream();

Then, I can use a BufferedReader to show output that the .exe would normally display, however I cannot figure out how to pass input from the Java application console program to the actual .exe input stream. I need some help with how to do this.

Edit: Ok, I now have this, which works concurrently; however, I can't seem to get any output related to any input I take from the Java console window.

    new Thread(new Runnable() {
        public void run() {
            String line;
            BufferedReader br = new BufferedReader(new InputStreamReader(stdout));
            try {
                while ((line = br.readLine()) != null) {
                    System.out.println("[OUT] " + line);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }).start();

    new Thread(new Runnable() {
        public void run() {
            try {
                byte[] buffer = new byte[1024];

                int bytesRead;
                while ((bytesRead = System.in.read(buffer)) != -1) {
                    for(int i = 0; i < buffer.length; i++) {
                        int intValue = new Byte(buffer[i]).intValue();
                        if (intValue == 0) {
                            bytesRead = i;
                            break;
                        }
                    }
                    // for some reason there are 2 extra bytes on the end
                    stdin.write(buffer, 0, bytesRead-2);
                    System.out.println("[IN] " + new String(buffer, 0, bytesRead-2) + " [/IN]");
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }).start();
calabria04
  • 19
  • 1
  • 4

3 Answers3

1

Firstly, you need to be consuming stdout/stderr concurrently (via separate threads). Otherwise you can block your spawned process since you're not consuming its output. See this answer for more details.

To write to the process, I would wrap the stdin OutputStream with a BufferedWriter, and simply write to that from System.in

Community
  • 1
  • 1
Brian Agnew
  • 268,207
  • 37
  • 334
  • 440
  • Thank you, that was one thing that I messed up on in my first attempt, I forgot about using separate threads. – calabria04 Mar 08 '11 at 23:29
1

You can create another thread that constantly reads from Java's console, re-writing it to the process's standard input:

new Thread(
    new Runnable() {
      public void run() {
        try {
          byte[] buffer = new byte[1024];

          int bytesRead;
          while ((bytesRead = System.in.read(buffer)) != -1) {
            process.getOutputStream().write(buffer, 0, bytesRead);
          }
        } catch (IOException e) {
          // Do something with the error...
        }
      }
    }
  ).start();
Adam Paynter
  • 46,244
  • 33
  • 149
  • 164
  • Sorry if this is the wrong way to do this, but this is my first question on Stack. Could you please take a look at my edit and see if I am doing something wrong? – calabria04 Mar 08 '11 at 23:33
  • @calabria04: Did my code work for you (before your modifications)? You will probably have to keep those last 2 bytes when you write them to the subprocess. They are most likely a carriage return (13) followed by a line feed (10). – Adam Paynter Mar 09 '11 at 00:41
  • It did not work. If I typed something like '/help' into the .exe application (this is before wrapping the streams with Java), it would list various commands. After wrapping them with Java, if I typed in '/help', nothing relating to /help is output. The program will continue normal output like if a file-save event debug fires. – calabria04 Mar 09 '11 at 01:00
  • I figured it out, I forgot about flushing the output stream. – calabria04 Mar 09 '11 at 03:05
0

Use the ProcessBuilder API. It has the very convenient option to join stderr and stdout streams, so you don't have to use multiple threads to read from the streams.

redirectErrorStream(true) 

is your friend here.

If you want to pass data to the process:

proc.getOutputStream().write(allmydata);

To read data:

proc.getInputStream().read(byteBuffer);

Please be aware of the following: If the input data is more than the process can buffer, and if the process already filled its output buffer, and you do not read from the output buffer, you will have a classic deadlock situation. In this case you either have to read output of the process in a separate process and buffer it yourself, or you make the process redirecting the data to a temporary file first, and read this in in a second step. The first variant is more performant, but the second is more easy to understand and might also be a valid option if there is much of data and performance doesn't count.

Daniel
  • 27,718
  • 20
  • 89
  • 133
  • redirectErrorStream(true) is only your friend if you don't want to distinguish errors from output! – Brian Agnew Mar 08 '11 at 19:45
  • Is this doing what the OP asked for? Doesn't that simply merge the sub-process's STDERR and STDOUT into one, single stream? This doesn't connect the parent process's STDIN with the child process's STDIN, correct? – Adam Paynter Mar 08 '11 at 19:45
  • @Brian: Yes, this is right. But I had many cases where I didn't need to care for it, because I just dumped the output into a logfile anyway, and searched in it for a String like "error". It is just an option. – Daniel Mar 08 '11 at 19:48
  • @Adam: Sorry, you are right. I didn't understand the question right. +1 for you. – Daniel Mar 08 '11 at 19:49