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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
package Torello.Java;

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>
 */
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; }

        // This just reads characters and sends them to the appendable.
        // If an exception occurs it reports them to the class "Completed"

        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); }
        }
    }


    // ********************************************************************************************
    // ********************************************************************************************
    // Java-Hack: Call to super(...) must be on the first line of a constructor - BY-PASS!
    // ********************************************************************************************
    // ********************************************************************************************


    // This field here is the actual, non-static, field that is a member of this class (class ISPT)
    private ISPR ispr = null;

    // These two fields are needed in order to bypass the Java Requirement that a call to the
    // super(...) constructor must be the first line in a constructor.  This bypass simply creates
    // a lock around a static-field.  The static-field holds a pointer to a class that is created
    // in the "PRE-CONSTRUCTOR" which is invoked inside the call to super(...)

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

    // This is the method that actually gets invoked before the call to super(...).  This is
    // accomplished by placing this method as an argument to one of the super-constructor's
    // parameters

    private static Runnable PRE_CONSTRUCTOR
        (InputStream inputStream, Appendable appendable, Completed completed)
    {
        // This line makes sure that only a single constructor at a time is borrowing the
        // static-field with the funny name.  (The field that is assigned on the next line)

        lock.lock();

        // Sets the static-field, and also returns it as a result of this private method.
        return static_temporary_ispr = new ISPR(inputStream, appendable, completed);
    }


    // ********************************************************************************************
    // ********************************************************************************************
    // Actual Constructor for this class (class ISPT)
    // ********************************************************************************************
    // ********************************************************************************************


    /**
     * 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.
     */
    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(PRE_CONSTRUCTOR(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.

        this.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}
     */
    int numCharsRead() { return this.ispr.numCharsRead(); }
}