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