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                    String content = new String(Arrays.copyOf(cbuf, nRead)); // retain only filled chars
037
038                    // The following used to be runOnGui (i.e. not "Eventually")
039                    // but that occasionally caused the Swing/AWT thread to block
040                    // with very large input strings.  Please don't change it back.
041                    jmri.util.ThreadingUtil.runOnGUIEventually(() -> {
042                        ta.append(content);
043                    });
044
045                } catch (IOException ex) {
046                    if ( "Write end dead".equals(ex.getMessage()) || "Pipe broken".equals(ex.getMessage())) {
047                        // happens when the writer thread, possibly a script, terminates
048                        synchronized (this) {
049                            try {
050                                wait(500);
051                            } catch (InterruptedException exi) {
052                                Thread.currentThread().interrupt(); // retain if needed later
053                            }
054                        }
055                    } else {
056                        throw ex;
057                    }
058                }
059            }
060        } catch (IOException ex) {
061            ta.append("PipeListener Exiting on IOException:" + ex);
062        }
063    }
064}