001package Torello.Java.Additional;
002
003import java.io.*;
004import java.util.concurrent.locks.*;
005
006/**
007 * A helper class performing <CODE>Thread</CODE> management for class <CODE>OSResponse</CODE>.
008 * 
009 * <BR /><BR /><B STYLE="color: red;">ISPT: InputStream Printing Thread</B>
010 * 
011 * <EMBED CLASS="external-html" DATA-FILE-ID=OSR_HELPER_NOTE>
012 * <EMBED CLASS="external-html" DATA-FILE-ID=ISPT>
013 */
014public class ISPT extends Thread
015{
016    // "Input Stream Printer Runnable"
017    private static class ISPR implements Runnable
018    {
019        private Thread      thread;
020        private InputStream inputStream;
021        private Appendable  appendable;
022        private Completed   completed;
023        private int         numCharsRead;
024
025        public ISPR(InputStream inputStream, Appendable appendable, Completed completed)
026        {
027            this.inputStream  = inputStream;
028            this.appendable   = appendable;
029            this.completed    = completed;
030            this.numCharsRead = 0;
031        }
032
033        public void setThread(Thread thread)
034        { this.thread=thread; }
035
036        public int numCharsRead()
037        { return numCharsRead; }
038
039        public void run()
040        {
041            try
042            {
043                String          line  = null;           
044                BufferedReader  inErr = new BufferedReader(new InputStreamReader(inputStream));
045
046                while ((line = inErr.readLine()) != null)
047                {
048                    appendable.append(line + '\n');
049                    numCharsRead += line.length() + 1;
050                }
051            }
052            catch (Exception e)
053                { completed.exceptionWasThrown(thread, e); }
054            finally
055                { completed.finished(thread); }
056        }
057    }
058
059    private static final Lock   lock                    = new ReentrantLock();
060    private static ISPR         static_temporary_ispr   = null;
061    private ISPR                ispr                    = null;
062
063    // This whole thing is **ONLY** needed since 'this' cannot be accessed inside of a constructor
064    private static Runnable GET_SUPER_CONSTRUCTOR_PARAMETERS
065        (InputStream inputStream, Appendable appendable, Completed completed)
066    {
067        lock.lock();
068        return static_temporary_ispr = new ISPR(inputStream, appendable, completed);
069    }
070
071    /**
072     * This shall build a {@code Thread} that asynchronously prints text read from an
073     * {@code InputStream} directly to the provided {@code 'Appendable'}.
074     * @param inputStream The {@code InputStream} to be read.
075     * @param appendable Where the read text shall be sent
076     * @param completed This class only works in coordination with the {@code class 'Completed'}.
077     * This class is used to allow the user to {@code 'wait'} until this independent
078     * {@code Thread} has run to completion.
079     * @param name Since this class ({@code ISPT}) inherits from {@code class Thread}), it has a
080     * {@code 'name'} field.  All {@code Thread's} have names.  The {@code name} of the this
081     * {@code Thread} will be set to the value provided to this parameter.
082     */
083    public ISPT(InputStream inputStream, Appendable appendable, Completed completed, String name)
084    {
085        // This whole thing is only needed since Java **DOES NOT** allow a constructor to reference
086        // the key-word 'new' inside of a constructor when calling 'super(...)'
087        //
088        // REMEMBER: This class implements 'Thread' and ISPR implements 'Runnable'
089        //           We are trying to create a Thread (ISPT) that runs the Runnable ISPR.  But
090        //           we cannot create the Runnable using the word 'new ISRP' in the call to
091        //           super(new ISPR(...))...  So instead, there is a static field called 
092        //           'static_temporary_ispr' which means that this lock/unlock crap is also needed
093        //
094        // OTHERWISE: The next line of code would simply read:
095        //            super(new ISPR(inputStream, appendable, completed), name);
096
097        super(GET_SUPER_CONSTRUCTOR_PARAMETERS(inputStream, appendable, completed), name);
098
099        // since 'super' must be the first line in a Java constructor, this is the 2nd line.
100        if (name == null) throw new NullPointerException
101            ("Parameter 'name' to this ISPT constructor may not be null");
102
103        // Cannot call 'new ISPR' inside the above call to super, so instead use a static field
104        // named 'static_temporary_ispr'
105        //
106        // SINCE: it is static, it means it is shared across all invocations of 'ISPT' so it must
107        //        be locked and unlocked when it is used.  This makes the whole thing look TEN
108        //        TIMES MORE COMPLICATED than it actually is.
109        this.ispr = static_temporary_ispr;
110
111        lock.unlock();
112 
113        // The inner (private) java.lang.Runnable (ISPR) needs to know the pointer to 'this'
114        // ISPT-Thread, because part of it's machinations are to tell class "Completed" when
115        // this (ISPT) Thread is finished reading from its input-stream.
116        ispr.setThread(this);
117
118        // Register this newly created reader thread with the "Completed" class.  The "Completed"
119        // class really only sits there and waits for the Output-Stream and Error-Stream threads to
120        // read all the output text sent to Standard-Output and Standard-Error.
121        completed.addThread(this);
122    }
123
124    /**
125     * This returns a count on the number of characters that have been read from the
126     * {@code InputStream}
127     * 
128     * @return Number of characters that have been read from the {@code InputStream}
129     */
130    public int numCharsRead() { return ispr.numCharsRead(); }
131}