Printing a Java InputStream from a Process
Asked Answered
O

2

7

UPDATE: I found a crucial part to why this probably isn't working! I used System.setOut(out); where out is a special PrintStream to a JTextArea

This is the code, but the issue I'm having is that the information is only printed out once I end the process.

public Constructor() {
    main();
}

private void main() {
    btnStart.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            try {
                ProcessBuilder builder = new ProcessBuilder("java", textFieldMemory.getText(), "-jar", myJar);
                Process process = builder.start();
                InputStream inputStream = process.getInputStream();
                BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream), 1);
                String line;
                while ((line = bufferedReader.readLine()) != null) {
                    System.out.println(line);
                }
                inputStream.close();
                bufferedReader.close();
            } catch (IOException ioe) {
                ioe.printStackTrace();
            }
        }
    });
} 

Current output:

Line 1
Line 2
Line 3
Line 4
Line 5

This is the correct output, but it is only being printed as one big block when I end the process.

Does anyone know what the issue is? If so can you help explain to me why this is happening, thank-you in advance.

Outandout answered 4/4, 2013 at 1:29 Comment(13)
The output is been buffered in the BufferedReader, try just reading the contents from the InputStream directly to see if it makes a differenceCalan
I agree with @Calan that the problem is probably with the buffer. However, I'd suggest just setting the BufferedReader buffer size to 1 with its two parameter constructor: new BufferedReader(new InputStreamReader(inputStream), 1)Sand
Would using a ByteArrayOutputStream work any better? I'm completely new to streams, and this is bugging me XDOutandout
In this situation, I just use a small byte array and read directly from the InputStream. The main reason I do this is because not all processes send newline characters as part of there output which means sometimes, you don't get any output and the process seems to never dieCalan
I doubt your buffered reader is the problem. Two quick questions: are you using process.waitFor? And are you reading the stream output on a separate thread?Customary
@Customary no I'm not using process.waitFor and I haven't created any additional threads.Outandout
@Outandout - you should do both. If you post a more complete sample of your code (from when you create the process to when you are done with it, including reading the output) I can provide recommendations.Customary
@Customary added some more content.Outandout
I found a crucial part to why this probably isn't working! I used System.setOut(out); where out is a special PrintStream to a JTextAreaOutandout
@Outandout I would STRONGLY discourage you from redirecting output from System.out to a Swing component without ensuring that the output is correctly re-synced with the EDTCalan
@Calan The program that starts is not mine so I cannot edit it, and the useful information it prints out is within the console. what is the best way to display this information within my application if it is not a good idea to redirect the output to my text area?Outandout
Ugh I'm way too ambitious for my knowledge as a first year, I'll sleep on this.Outandout
@Outandout If you have control over the PrintStream then you need to ensure that any time it outputs to the text area, it's done within the context of the EDT. Take a look at my updated answerCalan
C
11

Processing the output stream of the process in a separate thread might help here. You also want to explicitly wait for the process to end before continuing with your logic:

ProcessBuilder builder = new ProcessBuilder("java",
        textFieldMemory.getText(), "-jar", myJar);
final Process process = builder.start();
final Thread ioThread = new Thread() {
    @Override
    public void run() {
        try {
            final BufferedReader reader = new BufferedReader(
                    new InputStreamReader(process.getInputStream()));
            String line = null;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
            reader.close();
        } catch (final Exception e) {
            e.printStackTrace();
        }
    }
};
ioThread.start();

process.waitFor();
Customary answered 4/4, 2013 at 2:20 Comment(6)
Didn't work for me, like I said, I think it has to do with that I redirected the console output to a JTextArea.Outandout
Yea, thats a pretty important detail. More than likely you have to explicitly flush the stream on each write then trigger a repaint of the JTextArea, in order to get the output to show up in real time.Customary
I found something interesting if you look at my original code in the question and moved inputStream.close(); into the while loop, I successfully get the first line printed in my JTextArea and then the stream is obviously closed.Outandout
Well, close does a flush, so try doing that instead and see how that affects the program.Customary
Unfortunately InputStream doesn't have a flush method :/Outandout
Sorry, I misunderstood what you wrote. I meant a flush on your custom output stream, not on the input.Customary
C
4

Basically, from what little information there is, it sounds like your executing the process and reading the InputStream from within the Event Dispatching Thread.

Anything to blocks the EDT will prevent it from processing repaint requests, instead, you should use something like a SwingWorker which has functionality to allow you to update the UI from with the EDT.

Take a look at Concurrency in Swing for more details

enter image description here

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingWorker;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class PBDemo {

    public static void main(String[] args) throws Exception {
        new PBDemo();
    }

    public PBDemo() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        public TestPane() {
            setLayout(new BorderLayout());
            JTextArea ta = new JTextArea();
            add(new JScrollPane(ta));

            new ProcessWorker(ta).execute();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }
    }

    public interface Consumer {
        public void consume(String value);            
    }

    public class ProcessWorker extends SwingWorker<Integer, String> implements Consumer {

        private JTextArea textArea;

        public ProcessWorker(JTextArea textArea) {
            this.textArea = textArea;
        }

        @Override
        protected void process(List<String> chunks) {
            for (String value : chunks) {
                textArea.append(value);
            }
        }

        @Override
        protected Integer doInBackground() throws Exception {
            // Forced delay to allow the screen to update
            Thread.sleep(5000);
            publish("Starting...\n");
            int exitCode = 0;
            ProcessBuilder pb = new ProcessBuilder("java.exe", "-jar", "HelloWorld.jar");
            pb.directory(new File("C:\\DevWork\\personal\\java\\projects\\wip\\StackOverflow\\HelloWorld\\dist"));
            pb.redirectError();
            try {
                Process pro = pb.start();
                InputConsumer ic = new InputConsumer(pro.getInputStream(), this);
                System.out.println("...Waiting");
                exitCode = pro.waitFor();

                ic.join();

                System.out.println("Process exited with " + exitCode + "\n");

            } catch (Exception e) {
                System.out.println("sorry" + e);
            }
            publish("Process exited with " + exitCode);
            return exitCode;
        }

        @Override
        public void consume(String value) {
            publish(value);
        }
    }

    public static class InputConsumer extends Thread {

        private InputStream is;
        private Consumer consumer;

        public InputConsumer(InputStream is, Consumer consumer) {
            this.is = is;
            this.consumer = consumer;
            start();
        }

        @Override
        public void run() {
            try {
                int in = -1;
                while ((in = is.read()) != -1) {
//                    System.out.print((char) in);
                    consumer.consume(Character.toString((char)in));
                }
            } catch (IOException exp) {
                exp.printStackTrace();
            }
        }
    }
}
Calan answered 4/4, 2013 at 2:21 Comment(5)
Didn't work for me, like I said, I think it has to do with that I redirected the console output to a JTextArea.Outandout
There was no mention of a JTextArea? Are you executing the process within in the EDT?Calan
Sorry, yes I realise that this was the crucial part causing the issue as I commented earlier. And we haven't covered threads yet on my course sorry ><"Outandout
Code borrowed for this answer to a similar question. Many thanks!Bloodfin
@HovercraftFullOfEels I know nothing, I "borrow" everything ;)Calan

© 2022 - 2024 — McMap. All rights reserved.