1

I'm relative newbie to java and am trying to automate command line using java. I tried to search for the solution here, but couldn't find it.

I created a simple test shell script like below for testing my program:


#!/bin/bash
echo "What is your name?";
read name;
echo "Hello, $name"
echo "What is your contact number?";
read num;
echo "Saved contact number $num for $name"

The Java code is below:

import java.io.*;
import java.util.*;

public class CmdLineMain {
    public static void main(String args[]) throws InterruptedException, IOException {
        List<String> command = new ArrayList<String>();
        command.add("./test.sh");

        ProcessBuilder builder = new ProcessBuilder(command);

        final Process process = builder.start();
        InputStream is = process.getInputStream();
        InputStreamReader isr = new InputStreamReader(is);
        BufferedReader br = new BufferedReader(isr);
        String line = "";
        BufferedWriter bw = null;

        while (process.isAlive()) {
            line = br.readLine();
            // since stream may be closed earlier, re-open it
            bw = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
            System.out.println(line);

            if (line != null) {
                switch (line) {
                case "What is your name?":
                    bw.write("John Doe");
                    bw.close();
                    break;
                case "What is your contact number?":
                    bw.write("123456789");
                    bw.close();
                    break;

                }
            }

        }

        System.out.println("Program terminated!");
    }
}

Problem: The second input to the process fails with error:


    What is your name?
    Hello, John Doe
    What is your contact number?
    Exception in thread "main" java.io.IOException: Stream Closed
        at java.io.FileOutputStream.writeBytes(Native Method)
        at java.io.FileOutputStream.write(FileOutputStream.java:326)
        at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82)
        at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:140)
        at java.io.FilterOutputStream.close(FilterOutputStream.java:158)
        at sun.nio.cs.StreamEncoder.implClose(StreamEncoder.java:320)
        at sun.nio.cs.StreamEncoder.close(StreamEncoder.java:149)
        at java.io.OutputStreamWriter.close(OutputStreamWriter.java:233)
        at java.io.BufferedWriter.close(BufferedWriter.java:266)
        at nkh.app.CmdLineMain.main(CmdLineMain.java:34)

5
  • 1
    Did you try to set the buffered writer and close it outside the loop? By the way it's not a good idea to reinstance a new one every loop turn. Commented Oct 25, 2018 at 12:15
  • I noted that unless I close() the writer, the script wouldn't proceed further. close seems to be the only way the script would proceed to "Hello, John Doe" and the next output Commented Oct 25, 2018 at 12:21
  • Could you elaborate or post a working sample? before trying close() I tried flush() but it didn't continue with the script either. Thanks, Commented Oct 25, 2018 at 12:25
  • @user2054627 did my code not work? I have a sample. Commented Oct 25, 2018 at 12:25
  • Yes it did, and I've accepted your answer! Thanks! Commented Oct 25, 2018 at 12:29

1 Answer 1

2

Closing the BufferedWriter will close its underlying streams, this includes the process OutputStream which you get through process.getOutputStream(). Thus, once it is closed in one loop, in the next loops you have your BufferedWriter wrapping a closed stream. Instead wrap the output stream only once and reuse that.

Like this:

BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
// while the stream is open and there is something to read
// probably a better condition than `process.isAlive()`
while ((line = br.readLine()) != null) {
    switch (line) {
        case "What is your name?":
            bw.write("John Doe");
            bw.newLine();
            bw.flush();
            break;
        case "What is your contact number?":
            bw.write("123456789");
            bw.newLine();
            bw.flush();
            break;
    }
}

The reason your original code died on close() rather than on the write() that came before it when your new BufferedWriter was wrapping the closed underlying stream was because BufferedOutputStream.write() may not actually write to the underlying stream yet, since it's buffered. Calling flush() should tell the stream to actually write, and as you see in the stack trace close() is calling flush() which is ultimately writing the buffered bytes to the underlying FileOutputStream, which is then realizing that the FileOutputStream is already closed.

Sign up to request clarification or add additional context in comments.

6 Comments

Thanks, I think I tried this earlier, but will re-test and let you know
So the bw.newLine() it what's making the script go ahead. Seems obvious now after seeing your answer. :)
@user2054627 No problem. I recommend also using a try-with-resources, to ensure your streams get closed at the end and probably use (line = br.readLine()) != null as your loop invariant.
@Amessihel I believe so, just like when you're typing into a console you press enter to create a newline and complete a command. The process needs to know where a command ends.
Mmh, question: if the BufferedWriter wraps a closed stream on the second part, why it's not the write() call which throws the Exception, but the close() one?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.