Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question: redirectOutput to outputstream #116

Open
Panlq opened this issue Jan 13, 2025 · 2 comments
Open

Question: redirectOutput to outputstream #116

Panlq opened this issue Jan 13, 2025 · 2 comments

Comments

@Panlq
Copy link

Panlq commented Jan 13, 2025

public class CommandUtilsTest {
    @Test
    public void testPingCommandReadStreamOutput() throws IOException, InterruptedException {
        // Create receivers to capture stdout and stderr
        PipedOutputStream pipedStdout = new PipedOutputStream();
        PipedInputStream pipedStdoutInput = new PipedInputStream(pipedStdout);

        // Start the thread that writes stdout to the PipedOutputStream
        Thread stdoutThread = new Thread(() -> {
            try {
                new ProcessExecutor().command("ping", "www.google.com", "-c",
                        "5")
                        .redirectOutput(pipedStdout)
                        .execute();
                pipedStdout.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (TimeoutException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        });

        // Start the threads to capture the output from the PipedInputStream
        Thread readerThread = new Thread(() -> {
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(pipedStdoutInput))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    // Print to console as it is read (for real-time output)
                    System.out.println(line);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {

            }
        });

        // Start the stdout capturing thread
        stdoutThread.start();
        readerThread.start();

        // Now, wait for the threads to finish
        readerThread.join();
        stdoutThread.join();

    }

    @Test
    public void testPipeOutputConnected2Input() throws IOException, InterruptedException {
        final PipedOutputStream output = new PipedOutputStream();
        final PipedInputStream input = new PipedInputStream(output);

        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    output.write("Hello Piped Streams!! Used for Inter Thread Communication".getBytes());
                    output.close();

                } catch (IOException io) {
                    io.printStackTrace();
                }
            }
        });

        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {

                try (BufferedReader reader = new BufferedReader(new InputStreamReader(input))) {
                    String line;
                    while ((line = reader.readLine()) != null) {
                        System.out.println(line);
                    }
                } catch (IOException io) {
                    io.printStackTrace();
                }

            }
        });

        thread1.start();
        thread2.start();

    }
}

why testPingCommandReadStreamOutput raise error
testPipeOutputConnected2Input well done!

java.io.IOException: Write end dead
	at java.io.PipedInputStream.read(PipedInputStream.java:310)
PipedInputStream.java:310
	at java.io.PipedInputStream.read(PipedInputStream.java:377)
PipedInputStream.java:377
	at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
StreamDecoder.java:284
	at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
StreamDecoder.java:326
	at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
StreamDecoder.java:178
	at java.io.InputStreamReader.read(InputStreamReader.java:184)
	at java.io.BufferedReader.fill(BufferedReader.java:161)
BufferedReader.java:161
	at java.io.BufferedReader.readLine(BufferedReader.java:324)
BufferedReader.java:324
	at java.io.BufferedReader.readLine(BufferedReader.java:389)
CommandUtilsTest.java:163
	at java.lang.Thread.run(Thread.java:750)
@nemecec
Copy link
Collaborator

nemecec commented Jan 15, 2025

There indeed seems to be an issue when using stream redirection with PipedOutputStream -- zt-exec seems to shut the writing thread down before closing the stream but PipedOutputStream needs the opposite to happen.

As a workaround, I can suggest the following:

        new ProcessExecutor().command("ping", "www.google.com", "-c", "5")
            .redirectOutput(line -> {
              try {
                pipedStdout.write((line + "\n").getBytes(StandardCharsets.UTF_8));
              }
              catch (IOException e) {
                throw new RuntimeException(e);
              }
            })
            .execute();

Basically, handling output line by line and writing it yourself to the PipedOutputStream instance.

@nemecec
Copy link
Collaborator

nemecec commented Jan 16, 2025

Ok, I investigated this a bit more and found a more elegant solution, see below.

First, you define your own PumpStreamHandler in order to set closeWhenExhausted=true and flushImmediately=true when creating a new pump:

  private static class MyPumpStreamHandler extends PumpStreamHandler {

    public MyPumpStreamHandler(OutputStream outAndErr) {
      super(outAndErr);
    }

    @Override
    protected Thread createPump(InputStream is, OutputStream os) {
      return createPump(is, os, true, true);
    }
  }

And then you use your custom PumpStreamHandler implementation:

        new ProcessExecutor().command("ping", "www.google.com", "-c", "5")
            .streams(new MyPumpStreamHandler(pipedStdout))
            .execute();
        pipedStdout.close();

We should really make this easier to override.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants