1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package Torello.Java.Additional;

import java.io.*;
import java.util.concurrent.locks.*;

/**
 * A helper class performing <CODE>Thread</CODE> management for class <CODE>OSResponse</CODE>.
 * 
 * <BR /><BR /><B STYLE="color: red;">ISPT: InputStream Printing Thread</B>
 * 
 * <EMBED CLASS="external-html" DATA-FILE-ID=OSR_HELPER_NOTE>
 * <EMBED CLASS="external-html" DATA-FILE-ID=ISPT>
 */
public class ISPT extends Thread
{
    // "Input Stream Printer Runnable"
    private static class ISPR implements Runnable
    {
        private Thread      thread;
        private InputStream inputStream;
        private Appendable  appendable;
        private Completed   completed;
        private int         numCharsRead;

        public ISPR(InputStream inputStream, Appendable appendable, Completed completed)
        {
            this.inputStream  = inputStream;
            this.appendable   = appendable;
            this.completed    = completed;
            this.numCharsRead = 0;
        }

        public void setThread(Thread thread)
        { this.thread=thread; }

        public int numCharsRead()
        { return numCharsRead; }

        public void run()
        {
            try
            {
                String          line  = null;           
                BufferedReader  inErr = new BufferedReader(new InputStreamReader(inputStream));

                while ((line = inErr.readLine()) != null)
                {
                    appendable.append(line + '\n');
                    numCharsRead += line.length() + 1;
                }
            }
            catch (Exception e)
                { completed.exceptionWasThrown(thread, e); }
            finally
                { completed.finished(thread); }
        }
    }

    private static final Lock   lock                    = new ReentrantLock();
    private static ISPR         static_temporary_ispr   = null;
    private ISPR                ispr                    = null;

    // This whole thing is **ONLY** needed since 'this' cannot be accessed inside of a constructor
    private static Runnable GET_SUPER_CONSTRUCTOR_PARAMETERS
        (InputStream inputStream, Appendable appendable, Completed completed)
    {
        lock.lock();
        return static_temporary_ispr = new ISPR(inputStream, appendable, completed);
    }

    /**
     * This shall build a {@code Thread} that asynchronously prints text read from an
     * {@code InputStream} directly to the provided {@code 'Appendable'}.
     * @param inputStream The {@code InputStream} to be read.
     * @param appendable Where the read text shall be sent
     * @param completed This class only works in coordination with the {@code class 'Completed'}.
     * This class is used to allow the user to {@code 'wait'} until this independent
     * {@code Thread} has run to completion.
     * @param name Since this class ({@code ISPT}) inherits from {@code class Thread}), it has a
     * {@code 'name'} field.  All {@code Thread's} have names.  The {@code name} of the this
     * {@code Thread} will be set to the value provided to this parameter.
     */
    public ISPT(InputStream inputStream, Appendable appendable, Completed completed, String name)
    {
        // This whole thing is only needed since Java **DOES NOT** allow a constructor to reference
        // the key-word 'new' inside of a constructor when calling 'super(...)'
        //
        // REMEMBER: This class implements 'Thread' and ISPR implements 'Runnable'
        //           We are trying to create a Thread (ISPT) that runs the Runnable ISPR.  But
        //           we cannot create the Runnable using the word 'new ISRP' in the call to
        //           super(new ISPR(...))...  So instead, there is a static field called 
        //           'static_temporary_ispr' which means that this lock/unlock crap is also needed
        //
        // OTHERWISE: The next line of code would simply read:
        //            super(new ISPR(inputStream, appendable, completed), name);

        super(GET_SUPER_CONSTRUCTOR_PARAMETERS(inputStream, appendable, completed), name);

        // since 'super' must be the first line in a Java constructor, this is the 2nd line.
        if (name == null) throw new NullPointerException
            ("Parameter 'name' to this ISPT constructor may not be null");

        // Cannot call 'new ISPR' inside the above call to super, so instead use a static field
        // named 'static_temporary_ispr'
        //
        // SINCE: it is static, it means it is shared across all invocations of 'ISPT' so it must
        //        be locked and unlocked when it is used.  This makes the whole thing look TEN
        //        TIMES MORE COMPLICATED than it actually is.
        this.ispr = static_temporary_ispr;

        lock.unlock();
 
        // The inner (private) java.lang.Runnable (ISPR) needs to know the pointer to 'this'
        // ISPT-Thread, because part of it's machinations are to tell class "Completed" when
        // this (ISPT) Thread is finished reading from its input-stream.
        ispr.setThread(this);

        // Register this newly created reader thread with the "Completed" class.  The "Completed"
        // class really only sits there and waits for the Output-Stream and Error-Stream threads to
        // read all the output text sent to Standard-Output and Standard-Error.
        completed.addThread(this);
    }

    /**
     * This returns a count on the number of characters that have been read from the
     * {@code InputStream}
     * 
     * @return Number of characters that have been read from the {@code InputStream}
     */
    public int numCharsRead() { return ispr.numCharsRead(); }
}