001package jmri.util;
002
003import java.io.IOException;
004import java.io.PipedReader;
005import java.util.Arrays;
006import javax.swing.JTextArea;
007
008/**
009 * Small service class to read characters from a pipe and post them to a
010 * JTextArea for display.
011 *
012 * This expects the pipe to remain open, so has no code to handle
013 * a broken pipe gracefully.
014 *
015 * @author Bob Jacobsen Copyright (C) 2004, 2023
016 */
017public class PipeListener extends Thread {
018
019    private final PipedReader pr;
020    private final JTextArea ta;
021
022    public PipeListener(PipedReader pr, javax.swing.JTextArea ta) {
023        this.pr = pr;
024        this.ta = ta;
025    }
026
027    static final int BUFFER_SIZE = 120;
028
029    @Override
030    public void run() {
031        try {
032            char[] cbuf = new char[BUFFER_SIZE];
033            while (true) {
034                try {
035                    int nRead = pr.read(cbuf, 0, BUFFER_SIZE);  // blocking read
036                    if (nRead == -1) {
037                        return;     // End of file reached.
038                    }
039                    String content = new String(Arrays.copyOf(cbuf, nRead)); // retain only filled chars
040
041                    // The following used to be runOnGui (i.e. not "Eventually")
042                    // but that occasionally caused the Swing/AWT thread to block
043                    // with very large input strings.  Please don't change it back.
044                    jmri.util.ThreadingUtil.runOnGUIEventually(() -> {
045                        ta.append(content);
046                    });
047
048                } catch (IOException ex) {
049                    if ( "Write end dead".equals(ex.getMessage()) || "Pipe broken".equals(ex.getMessage())) {
050                        // happens when the writer thread, possibly a script, terminates
051                        synchronized (this) {
052                            try {
053                                wait(500);
054                            } catch (InterruptedException exi) {
055                                Thread.currentThread().interrupt(); // retain if needed later
056                            }
057                        }
058                    } else {
059                        throw ex;
060                    }
061                }
062            }
063        } catch (IOException ex) {
064            ta.append("PipeListener Exiting on IOException:" + ex);
065        }
066    }
067}