简体   繁体   中英

How can I read and write a process in real time and display output in JTextArea in real time? (Java Swing)

I copied and modified the answer code posted here: Print Output of Process to JTextArea over client server Network in java

The problem with my code is that it only works once when the code starts up. Any subsequent calls to write will simply do nothing.

Here is my code:

The MainFrame class

public class MainFrame extends JFrame {

    private JTextArea Output;
    private JScrollPane outputScrollPane;
    private JButton clickMe;

    private ProcessThread processThread;
    private ExecutorService execService;

    public MainFrame() {
        setTitle("StackOverflow");
        setSize(700, 850);
        setMinimumSize(new Dimension(650,800));
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLayout(new BorderLayout());

        Output = new JTextArea();
        Output.setEditable(false);
        outputScrollPane = new JScrollPane(Output);
        outputScrollPane.setPreferredSize(new Dimension(100, 150));
        outputScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);

        add(outputScrollPane, BorderLayout.CENTER);

        clickMe = new JButton("click Me");
        add(clickMe, BorderLayout.SOUTH);

        execService = Executors.newSingleThreadExecutor();
        processThread = new ProcessThread(Output);
        execService.submit(processThread);

        clickMe.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {

                SwingUtilities.invokeLater(new Runnable() {

                    @Override
                    public void run() {
                        processThread.write("some command\n"); //does nothing (doesn't update JTextArea with the process' response)
                    }
                });
            }
        });

        setVisible(true);
    }
}

The Process Thread class:

public class ProcessThread implements Runnable {

     private JTextArea ta;

     private WriteWorker writeWorker;
     private ReadWorker readWorker;
     private CountDownLatch shutDownLatch;

     public ProcessThread(JTextArea ta) {
            this.ta = ta;
        }

     public void write(String text) {
            if (writeWorker != null) {
                if (writeWorker.getState() == SwingWorker.StateValue.STARTED) {
                    writeWorker.write(text);
                } else {
                    throw new IllegalStateException("Write worker is not running");
                }
            } else {
                throw new NullPointerException("Write worker is null");
            }
        }

     public void close() {
            if (writeWorker != null) {
                writeWorker.cancel(true);
            }
            if (readWorker != null) {
                readWorker.cancel(true);
            }

            // Force the CountDownLatch to release
            if (shutDownLatch != null) {
                shutDownLatch.countDown();
                shutDownLatch.countDown();
            }
        }

    @Override
    public void run() {
        try {
            Process myProcess;
            myProcess = new ProcessBuilder("C:\\Folder\\executable.exe").start();
            InputStream processInputStream = myProcess.getInputStream();
            OutputStream processOutputStream = myProcess.getOutputStream();

            writeWorker = new WriteWorker(processOutputStream);
            readWorker = new ReadWorker(processInputStream, ta);

            writeWorker.addPropertyChangeListener(new PropertyChangeHandler());
            readWorker.addPropertyChangeListener(new PropertyChangeHandler());

            writeWorker.execute();
            readWorker.execute();

            shutDownLatch = new CountDownLatch(2);
            shutDownLatch.await();
        } catch (IOException | InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    protected class PropertyChangeHandler implements PropertyChangeListener {

        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            SwingWorker worker = (SwingWorker) evt.getSource();
            if (worker.getState() == SwingWorker.StateValue.DONE) {
                shutDownLatch.countDown();

                // Not interested in the return value, only interested in the
                // exception if one was thrown...
                try {
                    worker.get();
                } catch (InterruptedException | ExecutionException ex) {
                    // Resync the error with the UI, probably using SwingUtilities.invokeLater
                    // and call some error handling method
                    ex.printStackTrace();
                }
            }
        }

    }
}

The ReadWorker class:

public class ReadWorker extends SwingWorker<Void,String> {

     private InputStream is;
     private JTextArea ta;

        public ReadWorker(InputStream is, JTextArea ta) {
            this.is = is;
            this.ta = ta;
        }

    @Override
    protected Void doInBackground() throws Exception {
        byte[] buffer = new byte[4096];
        int bytesRead = -1;
        while (!isCancelled() && (bytesRead = is.read(buffer)) != -1) {
            String text = new String(buffer, 0, bytesRead);
            publish(text);
        }
        return null;
    }

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

The write worker class:

public class WriteWorker extends SwingWorker {

    private OutputStream os;

    private List<String> queue = new ArrayList<String>(25);
    private ReentrantLock queueLock = new ReentrantLock();
    private Condition queueCondition = queueLock.newCondition();

    @Override
    protected Object doInBackground() throws Exception {
        while (!isCancelled()) {
            String text = null;
            while (text == null && !isCancelled()) {
                queueLock.lock();
                try {
                    if (queue.isEmpty()) {
                        queueCondition.await();
                    }

                    if (!queue.isEmpty()) {
                        text = queue.remove(0);
                    }
                } finally {
                    queueLock.unlock();
                }
                if (text != null) {
                    os.write(text.getBytes());
                }
            }
        }
        return null;
    }

     public WriteWorker(OutputStream os) {
            this.os = os;
        }

     public void write(String text) {
            queueLock.lock();
            try {
                queue.add(text);
                queueCondition.signal();
            } finally {
                queueLock.unlock();
            }
     }
}

And finally the Main class:

public class Main {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {

                MainFrame frame = new MainFrame();
            }
        });
    }
}

What did I do wrong and why?

You're going to be kicking yourself for this.

You just needed one line.

Any time you are writing to stdin of a file (or any stream really) make sure you flush the buffer.

In your doInBackground() method of the WriteWorker , you need to add:

os.flush();

after your call to os.write(...)

So your doInBackground() method should look like:

@Override
protected Object doInBackground() throws Exception {
    while (!isCancelled()) {
        String text = null;
        while (text == null && !isCancelled()) {
            queueLock.lock();
            try {
                if (queue.isEmpty()) {
                    queueCondition.await();
                }

                if (!queue.isEmpty()) {
                    text = queue.remove(0);
                }
            } finally {
                queueLock.unlock();
            }
            if (text != null) {
                os.write(text.getBytes());
                os.flush();
            }
        }
    }
    return null;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM