Force BufferedInputStream to return captured content

110 Views Asked by At

I have a Spring Boot REST application with 2 endpoints: first starts the Process (db shell) with a command like 'mysql -e root'. Second one accepts command (query) and writes it to OutputStream (which is BufferedOutputStream from Process implementation).

Starting the Process (MySQL shell):

ProcessBuilder builder = new ProcessBuilder(commands);
builder.redirectErrorStream(true);
process = builder.start();
out = process.getInputStream();
in = process.getOutputStream();

Executing a command (e.g. 'select * from db.some_table;\n'):

byte[] commandBytes = command.getBytes(Charset.defaultCharset());
in.write(commandBytes, 0, commandBytes.length);
in.flush();

After running a command (query) I want to return its result (or at least, output it to the console) with:

int no = out.available();
  if (no > 0) {
    int n = out.read(buffer, 0, Math.min(no, buffer.length));
    System.out.println(new String(buffer, 0, n));
  }

The problem is that out.available() is always 0. If I call close() on an output stream, out.available() returns all the input stream length and I can read from it. But that is not what I want.

Can I somehow force BufferedInputStream to make result available to be read without closing the stream?

I see that internally BufferedInputStream uses FileInputStream and FileChannel, but I haven't found a way to capture the result when output stream is not closed.

1

There are 1 best solutions below

0
Tim Moore On

I think what's happening is that the mysql client detects that standard input is not a terminal, and runs in batch mode rather than in interactive mode. This isn't caused by the behaviour of BufferedReader: it's blocking indefinitely on read, and reporting 0 bytes available because there genuinely isn't anything to read from the output of the subprocess.

In batch mode, the client expects to read a list of commands from standard input, and only executes them once the end of file is reached. In other words, the subprocess will not produce any output on the InputStream you see in your parent process until your parent process closes the OutputStream of the subprocess.

It appears that there's no way to force mysql to run in interactive mode (according to this question: "How to force mysql.exe to run in "interactive" mode?", and the documentation of command line options).

The mysqlsh client can be forced into interactive mode, but it is worth considering whether this is really the best solution for your use case. Other alternatives include:

  1. Embracing batch mode and executing all of the commands together, if the form of each command does not depend on the results of previous ones
  2. Sequentially invoking the subprocess multiple times in batch mode, if subsequent commands do depend on the results of previous ones
  3. Performing the queries using JDBC (as g00se recommended in the comments)