6

Sorry for this odd-sounding title...

I have the following situation: I want my Java program to interact with an external console. In order to "send" the individual commands to that console, I need to simulate what would be an "enter key pressed" on a normal console. To clarify what I want, imagine mysql had no other API and I would need to interact via console. Although this is not my actual problem, it is close enough.

I have the following code:

        String command = "/usr/local/mysql/bin/mysql";
        Process child = Runtime.getRuntime().exec(command);

        StreamGobbler gobbler = new StreamGobbler(child.getInputStream());
        gobbler.start();

        BufferedWriter out = new BufferedWriter(new OutputStreamWriter(child.getOutputStream()));
        out.write("help");
        // here enter key needs to be pressed
        out.flush();
        // out.close();

If the call to out.close() is executed, everything is fine. But of course, this way I can only send a single command, which is not what I want. But if out.close() is omitted, the other program never executes the command. My guess is that it still waits for the command to "finish", which on a normal console would be done by pressing enter. out.write(System.getProperty("line.separator")); and out.newLine(); (which are the same) do not solve the problem, neither does out.write("\r\n"); and out.write((char) 26); (EOF).

Of course, it might be, that I am doing it completely wrong (i.e., wrong approach). Then I would appreciate a pointer into the right direction...

Any help on this highly appreciated.

roesslerj
  • 2,611
  • 5
  • 30
  • 44
  • 1
    You should be consuming the standard output **and** standard error output streams. What do you receive on these streams? – Mark Peters Feb 08 '11 at 17:12
  • 1
    What system are you on? You use Unix paths, but tried the Windows EOF (Ctrl-Z = 26)? The Unix EOF is Ctrl-D (=4), but it's certainly not the right thing to send. – Sergei Tachenov Feb 08 '11 at 17:17
  • @Mark Peters: Sorry, I should have added a gobbler for the error output stream as well, but both streams receive nothing unless `out.close()` is executed as pointed out. @Sergey Tachenov: I work on a Mac, and Ctrl-D didn't work either, but thanks for pointing this out. – roesslerj Feb 09 '11 at 09:44

3 Answers3

8

The following code works fine on both Windows 7 using Java 1.6.0_23 and on Ubuntu 8.04 using Java 1.6.0_22:

public class Laj {

  private static class ReadingThread extends Thread {
    private final InputStream inputStream;
    private final String name;

    public ReadingThread(InputStream inputStream, String name) {
      this.inputStream = inputStream;
      this.name = name;
    }

    public void run() {
      try {
        BufferedReader in = new BufferedReader(
            new InputStreamReader(inputStream));
        for (String s = in.readLine(); s != null; s = in.readLine()) {
          System.console().writer().println(name + ": " + s);
        }
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  }

  public static void main(String[] args) throws Exception {
    String command = "psql -U archadm arch";
    final Process child = Runtime.getRuntime().exec(command);
    new ReadingThread(child.getInputStream(), "out").start();
    new ReadingThread(child.getErrorStream(), "err").start();
    BufferedWriter out = new BufferedWriter(
        new OutputStreamWriter(child.getOutputStream()));
    out.write("\\h");
    out.newLine();
    out.flush();
    out.write("\\q");
    out.newLine();
    out.flush();
  }

}

newLine() is the same as writing the platform line separator. As one would expect, it prints help preceded with "out: ", then exits. If I don't send "\q", it doesn't exit (obviously) but still prints help. Using "\r\n" or "\r" instead of the platform line separator doesn't look like a good idea to me, because such command-line utilities will usually detect that they don't get input from the terminal and assume it is in the native text format (think "psql < script.sql"). Good software should properly detect and accept all reasonable line endings though.

Sergei Tachenov
  • 24,345
  • 8
  • 57
  • 73
  • Thanks for posting your example. Since you do basically the same than I do, the difference is probably in the called process or underlying OS (MacOsX in my case) or even Java version. I will see what I can find out, thanks for your reply! – roesslerj Feb 09 '11 at 11:37
  • @roesslerj, you should still check the stderr first. Could be a silly bug that prevents normal operation of the command being executed. Or perhaps it is trying to write some sort of warning into stderr and gets blocked, because stderr is unbuffered, and the other side (your process) aren't reading anything. – Sergei Tachenov Feb 09 '11 at 14:31
  • Thanks for delivering this piece of code. It turns out that your code works for me as well... and that I had a stupid mistake in the omitted code of the `StreamGobbler` class (which as of your recommendation I used for both stdout and stderr... Just one minor remark: `out.write(System.getProperty("line.separator"));` results in the same as calling `out.newLine();`. – roesslerj Feb 10 '11 at 09:02
  • @roesslerj, of course it does. I completely forgot about newLine(). I thought there was something silly about that piece of code... – Sergei Tachenov Feb 11 '11 at 08:19
0

What about out.write((char) 13)? See this Wikipedia article. I don't have enough code to test this for you.

rancidfishbreath
  • 3,944
  • 2
  • 30
  • 44
  • The carriage return character in Java is denoted with '\r', so I already tried this (see text), but still thanks for the input. – roesslerj Feb 09 '11 at 09:50
0

You also might want to try looking at this API

http://download.oracle.com/javase/6/docs/api/java/io/Console.html

From my experience, I've never tried doing anything more than running one process from the Process API. It seems like you want to enter multiple commands I think this API might let you do that.

EDIT: Found a tutorial on it to help you further. http://download.oracle.com/javase/tutorial/essential/io/cl.html

Hope this helps,

jluzwick
  • 2,005
  • 1
  • 15
  • 23
  • 2
    I maybe getting it wrong, but this API seems more like what you want when you write an interactive console yourself. But this is not what I want to do; I have to interact with such an console in the form of an external process. – roesslerj Feb 09 '11 at 09:53