Clover coverage report - DrJava Test Coverage (drjava-20120422-r5456)
Coverage timestamp: Sun Apr 22 2012 03:13:25 CDT
file stats: LOC: 369   Methods: 24
NCLOC: 212   Classes: 2
 
 Source file Conditionals Statements Methods TOTAL
ProcessSequence.java 0% 0% 0% 0%
coverage
 1    /*BEGIN_COPYRIGHT_BLOCK
 2    *
 3    * Copyright (c) 2001-2010, JavaPLT group at Rice University (drjava@rice.edu)
 4    * All rights reserved.
 5    *
 6    * Redistribution and use in source and binary forms, with or without
 7    * modification, are permitted provided that the following conditions are met:
 8    * * Redistributions of source code must retain the above copyright
 9    * notice, this list of conditions and the following disclaimer.
 10    * * Redistributions in binary form must reproduce the above copyright
 11    * notice, this list of conditions and the following disclaimer in the
 12    * documentation and/or other materials provided with the distribution.
 13    * * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
 14    * names of its contributors may be used to endorse or promote products
 15    * derived from this software without specific prior written permission.
 16    *
 17    * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 18    * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 19    * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 20    * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 21    * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 22    * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 23    * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 24    * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 25    * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 26    * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 27    * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 28    *
 29    * This software is Open Source Initiative approved Open Source Software.
 30    * Open Source Initative Approved is a trademark of the Open Source Initiative.
 31    *
 32    * This file is part of DrJava. Download the current version of this project
 33    * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
 34    *
 35    * END_COPYRIGHT_BLOCK*/
 36   
 37    package edu.rice.cs.util;
 38   
 39    import java.io.*;
 40   
 41    import edu.rice.cs.drjava.ui.DrJavaErrorHandler;
 42   
 43    /**
 44    * This class represents a sequence of processes. The inputs and outputs of the processes
 45    * are not connected. One process wait until the preceding process has terminated.
 46    * The class allows the entire sequence to be treated as if it were just one process.
 47    * The constructor starts the first subprocess.
 48    */
 49   
 50    public class ProcessSequence extends Process {
 51    /** The process creators that create the processes in this process sequence. */
 52    protected ProcessCreator[] _creators;
 53   
 54    /** The processes inside this process sequence. */
 55    protected Process[] _processes;
 56   
 57    /** Index of the currently running process. */
 58    protected volatile int _index = 0;
 59   
 60    /** True if the execution was aborted. */
 61    protected volatile boolean _aborted = false;
 62   
 63    /** The redirector thread that moves stdout output from one process
 64    * to the input of the next process. */
 65    protected StreamRedirectThread _stdOutRedirector;
 66   
 67    /** The redirector thread that moves stderr output from one process
 68    * to the input of the next process. */
 69    protected StreamRedirectThread _stdErrRedirector;
 70   
 71    /** The combined input stream of all processes. */
 72    protected PipedInputStream _combinedInputStream;
 73   
 74    /** The stream into which all outputs to stdout are written. */
 75    protected PipedOutputStream _combinedStdOutStream;
 76   
 77    /** The combined input stream of all the processes, plus a debug stream. */
 78    protected JoinInputStream _combinedInputJoinedWithDebugStream;
 79   
 80    /** Debug output that gets joined with the streams from the processes. */
 81    protected PrintWriter _debugOutput;
 82   
 83    /** Debug input and output stream. */
 84    protected PipedInputStream _debugInputStream;
 85    protected PipedOutputStream _debugOutputStream;
 86   
 87    /** The combined error stream of all processes. */
 88    protected PipedInputStream _combinedErrorStream;
 89   
 90    /** The stream into which all outputs to stderr are written. */
 91    protected PipedOutputStream _combinedStdErrStream;
 92   
 93    /** The output stream of the currently executing process. */
 94    protected volatile OutputStream _combinedOutputStream;
 95   
 96    /** Thread that monitors the subprocesses and starts the next process when the previous
 97    * one has terminated. */
 98    protected Thread _deathThread;
 99   
 100    /** Constructor for a process sequence consisting of the individual processes provided.
 101    * @param pcs array of ProcessCreators */
 102  0 public ProcessSequence(ProcessCreator[] pcs) {
 103  0 _creators = pcs;
 104  0 _processes = new Process[_creators.length];
 105  0 for(int i = 0; i < _processes.length; ++i) { _processes[i] = null; }
 106  0 _combinedInputStream = new PipedInputStream();
 107  0 try {
 108  0 _combinedStdOutStream = new PipedOutputStream(_combinedInputStream);
 109  0 _combinedInputStream.connect(_combinedStdOutStream);
 110    }
 111    catch(IOException e) { /* ignore, no output if this goes wrong */ }
 112   
 113  0 _debugInputStream = new PipedInputStream();
 114  0 try {
 115  0 _debugOutputStream = new PipedOutputStream(_debugInputStream);
 116  0 _debugInputStream.connect(_debugOutputStream);
 117    }
 118    catch(IOException e) { /* ignore, no output if this goes wrong */ }
 119  0 _combinedInputJoinedWithDebugStream = new JoinInputStream(_combinedInputStream, _debugInputStream);
 120  0 _debugOutput = new PrintWriter(new OutputStreamWriter(_debugOutputStream));
 121   
 122  0 _combinedErrorStream = new PipedInputStream();
 123  0 try {
 124  0 _combinedStdErrStream = new PipedOutputStream(_combinedErrorStream);
 125  0 _combinedErrorStream.connect(_combinedStdErrStream);
 126    }
 127    catch(IOException e) { /* ignore, no output if this goes wrong */ }
 128   
 129  0 _deathThread = new Thread(new Runnable() {
 130  0 public void run() {
 131  0 GeneralProcessCreator.LOG.log("ProcessSequence._deathThread running");
 132  0 boolean interrupted = false;
 133    // wait for the completion of each of the subprocesses
 134  0 while(_index < _processes.length) {
 135  0 GeneralProcessCreator.LOG.log("Waiting for process " + _index);
 136  0 do {
 137  0 interrupted = false;
 138  0 try {
 139  0 _processes[_index].waitFor();
 140    }
 141  0 catch(InterruptedException e) { interrupted = true; }
 142  0 } while(interrupted);
 143  0 GeneralProcessCreator.LOG.log("Process " + _index + " terminated");
 144    // a process has just terminated
 145  0 if (_index < _processes.length-1) {
 146    // increase index;
 147  0 ++_index;
 148  0 try {
 149  0 _processes[_index] = _creators[_index].start();
 150  0 GeneralProcessCreator.LOG.log("Process " + _index + " started");
 151  0 connectProcess(_processes[_index]);
 152    }
 153    catch(IOException e) {
 154  0 GeneralProcessCreator.LOG.log("\nIOException in external process: " + e.getMessage() + "\nCheck your command line.\n");
 155    // could not start the process, record error but continue
 156  0 _debugOutput.println("\nIOException in external process: " + e.getMessage() + "\nCheck your command line.\n");
 157  0 _debugOutput.flush();
 158  0 _processes[_index] = DUMMY_PROCESS;
 159    }
 160    }
 161    else {
 162  0 ++_index;
 163  0 GeneralProcessCreator.LOG.log("Closing StdOut and StdErr streams.");
 164  0 try {
 165  0 stopAllRedirectors();
 166  0 _combinedStdOutStream.flush();
 167  0 _combinedStdOutStream.close();
 168  0 _combinedStdErrStream.flush();
 169  0 _combinedStdErrStream.close();
 170    }
 171    catch(IOException e) { /* ignore, just don't close the streams */ }
 172    }
 173    }
 174    }
 175    },"Process Sequence Death Thread");
 176  0 _index = 0;
 177  0 try {
 178  0 _processes[_index] = _creators[_index].start();
 179    }
 180    catch(IOException e) {
 181  0 GeneralProcessCreator.LOG.log("\nIOException in external process: " + e.getMessage() + "\nCheck your command line.\n");
 182    // could not start the process, record error but continue
 183  0 _processes[_index] = DUMMY_PROCESS;
 184  0 _debugOutput.println("\nIOException in external process: " + e.getMessage() + "\nCheck your command line.\n");
 185  0 _debugOutput.flush();
 186    }
 187  0 connectProcess(_processes[_index]);
 188  0 _deathThread.start();
 189    // _debugOutput.println("\n\nProcessSequence started\n\n");
 190    // _debugOutput.flush();
 191    }
 192   
 193    /**
 194    * Gets the output stream of the process sequence, i.e. the combined
 195    * output stream of all the processes in the sequence.
 196    *
 197    * @return the output stream of the process sequence.
 198    */
 199  0 public OutputStream getOutputStream() {
 200  0 return new BufferedOutputStream(new OutputStream() {
 201  0 public void write(int b) throws IOException {
 202  0 _combinedOutputStream.write(b);
 203    }
 204  0 public void flush() throws IOException {
 205  0 _combinedOutputStream.flush();
 206    }
 207  0 public void close() throws IOException {
 208  0 _combinedOutputStream.close();
 209    }
 210    });
 211    }
 212   
 213    /**
 214    * Gets the error stream of the process sequence, i.e. the combined
 215    * error stream of all the processes in the sequence.
 216    *
 217    * @return the error stream of the process sequence.
 218    */
 219  0 public InputStream getErrorStream() {
 220  0 return _combinedErrorStream;
 221    }
 222   
 223    /**
 224    * Gets the input stream of the process sequence, i.e. the combined
 225    * input stream of all the processes in the sequence.
 226    *
 227    * @return the input stream of the process chain
 228    */
 229  0 public InputStream getInputStream() {
 230  0 return _combinedInputJoinedWithDebugStream;
 231    }
 232   
 233    /**
 234    * Causes the current thread to wait, if necessary, until the
 235    * process sequence has terminated, i.e. until all processes in
 236    * the sequence have terminated. This method returns immediately if
 237    * all subprocesses have already terminated. If any of the
 238    * subprocess has not yet terminated, the calling thread will be
 239    * blocked until all subprocesses exit.
 240    *
 241    * @return the exit value of the process chain, i.e. the
 242    * exit code of the last process in the sequence.
 243    * @exception InterruptedException if the current thread is
 244    * {@link Thread#interrupt() interrupted} by another thread
 245    * while it is waiting, then the wait is ended and an
 246    * {@link InterruptedException} is thrown.
 247    */
 248  0 public int waitFor() throws InterruptedException {
 249  0 if (_aborted) { return -1; }
 250  0 int exitCode = 0;
 251  0 for(int i = 0; i < _processes.length; ++i) {
 252  0 while((!_aborted) && (_processes[i] == null)) {
 253  0 try {
 254    // next process has not been assigned and started yet, sleep a bit
 255  0 Thread.sleep(100);
 256    }
 257    catch(InterruptedException e) { /* interruptions are ok, just ignore */ }
 258    }
 259  0 if (_aborted) { return -1; }
 260  0 exitCode = _processes[i].waitFor();
 261    }
 262  0 stopAllRedirectors();
 263  0 return exitCode;
 264    }
 265   
 266    /**
 267    * Returns the exit value for the subprocess.
 268    *
 269    * @return the exit value of the subprocess represented by this
 270    * <code>Process</code> object. by convention, the value
 271    * <code > 0</code> indicates normal termination.
 272    * @exception IllegalThreadStateException if the subprocess represented
 273    * by this <code>Process</code> object has not yet terminated.
 274    */
 275  0 public int exitValue() {
 276  0 if (_aborted) { return -1; }
 277  0 if ((_index < _processes.length-1) || (_processes[_processes.length-1] == null)) {
 278  0 throw new IllegalThreadStateException("Process sequence has not terminated yet, exit value not available.");
 279    }
 280    // just returning the exit value of the last process is sufficient:
 281    // the last process gets started when the previous one has already terminated
 282  0 return _processes[_processes.length-1].exitValue();
 283    }
 284   
 285    /**
 286    * Kills all subprocesses. The subprocesses represented by this
 287    * <code>ProcessChain</code> object is forcibly terminated.
 288    */
 289  0 public void destroy() {
 290  0 _aborted = true;
 291  0 for(int i = 0; i < _processes.length; ++i) {
 292  0 if (_processes[i] != null) { _processes[i].destroy(); }
 293    }
 294  0 stopAllRedirectors();
 295    }
 296   
 297    /** Set the stop flags for all redirector threads. */
 298  0 protected void stopAllRedirectors() {
 299  0 _stdOutRedirector.setStopFlag();
 300  0 _stdErrRedirector.setStopFlag();
 301    }
 302   
 303    /** Connect the streams of the specified process. */
 304  0 protected void connectProcess(Process p) {
 305    // redirect all stdout from all the processes into a combined output stream
 306    // that pipes all the data into a combined input stream that serves as this
 307    // process sequence's input stream
 308  0 if (_stdOutRedirector == null) {
 309  0 _stdOutRedirector = new StreamRedirectThread("stdout Redirector " + _index,
 310    p.getInputStream(),
 311    _combinedStdOutStream,
 312    false/*close*/,
 313    new ProcessSequenceThreadGroup(this),
 314    true/*keepRunning*/);
 315  0 _stdOutRedirector.start();
 316    }
 317    else {
 318  0 _stdOutRedirector.setInputStream(p.getInputStream());
 319    }
 320  0 if (_stdErrRedirector == null) {
 321  0 _stdErrRedirector = new StreamRedirectThread("stderr Redirector " + _index,
 322    p.getErrorStream(),
 323    _combinedStdErrStream,
 324    false/*close*/,
 325    new ProcessSequenceThreadGroup(this),
 326    true/*keepRunning*/);
 327  0 _stdErrRedirector.start();
 328    }
 329    else {
 330  0 _stdErrRedirector.setInputStream(p.getErrorStream());
 331    }
 332  0 _combinedOutputStream = p.getOutputStream();
 333    }
 334   
 335    /** Thread group for all threads that deal with this process sequence. */
 336    protected static class ProcessSequenceThreadGroup extends ThreadGroup {
 337    private PrintWriter _debugOut;
 338  0 public ProcessSequenceThreadGroup(ProcessSequence seq) {
 339  0 super("Process Sequence Thread Group");
 340  0 _debugOut = seq._debugOutput;
 341    }
 342  0 public void uncaughtException(Thread t, Throwable e) {
 343  0 if ((e instanceof StreamRedirectException) &&
 344    (e.getCause() instanceof IOException)) {
 345  0 _debugOut.println("\n\n\nAn exception occurred during the execution of the command line:\n" +
 346    e.toString() + "\n\n");
 347    }
 348    else {
 349  0 DrJavaErrorHandler.record(e);
 350    }
 351    }
 352    }
 353   
 354    /** A process that does nothing. */
 355    protected static final Process DUMMY_PROCESS = new Process() {
 356  0 public void destroy() { }
 357  0 public int exitValue() { return -1; }
 358  0 public InputStream getErrorStream() { return new InputStream() {
 359  0 public int read() { return -1; }
 360    }; }
 361  0 public InputStream getInputStream() { return new InputStream() {
 362  0 public int read() { return -1; }
 363    }; }
 364  0 public OutputStream getOutputStream() { return new OutputStream() {
 365  0 public void write(int b) { }
 366    }; }
 367  0 public int waitFor() { return -1; }
 368    };
 369    }