Clover coverage report - DrJava Test Coverage (drjava-20120422-r5456)
Coverage timestamp: Sun Apr 22 2012 03:13:25 CDT
file stats: LOC: 378   Methods: 11
NCLOC: 150   Classes: 2
 
 Source file Conditionals Statements Methods TOTAL
ProcessChain.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    import java.util.Set;
 41    import java.util.HashSet;
 42   
 43    import edu.rice.cs.drjava.ui.DrJavaErrorHandler;
 44    import edu.rice.cs.util.JoinInputStream;
 45   
 46    /**
 47    * This class represents a piping chain of processes, in which the output of the first
 48    * process is piped into the input of the second process, and so on. The class allows
 49    * the entire chain to be treated as if it were just one process.
 50    * The constructor also sets up the input and output streams of the individual processes
 51    * so they can function as a chain.
 52    */
 53   
 54    public class ProcessChain extends Process {
 55    /** Separator used between different process chains. */
 56    public static final char PROCESS_SEPARATOR_CHAR = '#';
 57   
 58    /** Separator used between different process chains. */
 59    public static final String PROCESS_SEPARATOR = String.valueOf(PROCESS_SEPARATOR_CHAR);
 60   
 61    /** Separator used between processes inside the same process chain. */
 62    public static final char PIPE_SEPARATOR_CHAR = '|';
 63   
 64    /** Separator used between processes inside the same process chain. */
 65    public static final String PIPE_SEPARATOR = String.valueOf(PIPE_SEPARATOR_CHAR);
 66   
 67    /** The process creators that create the processes in this process chain. */
 68    protected ProcessCreator[] _creators;
 69   
 70    /** The processes inside this piping chain. */
 71    protected Process[] _processes;
 72   
 73    /** True when execution of this chain has been aborted. */
 74    protected boolean _aborted = false;
 75   
 76    /** The redirector threads that move output (both stdout and stderr) from one process
 77    * to the input of the next process. */
 78    protected Set<StreamRedirectThread> _redirectors = new HashSet<StreamRedirectThread>();
 79   
 80    /** The combined input stream of all processes. */
 81    protected PipedInputStream _combinedInputStream;
 82   
 83    /** The stream into which all outputs to stdout are written. */
 84    protected PipedOutputStream _combinedStdOutStream;
 85   
 86    /** The combined input stream of all the processes, plus a debug stream. */
 87    protected JoinInputStream _combinedInputJoinedWithDebugStream;
 88   
 89    /** Debug output that gets joined with the streams from the processes. */
 90    protected PrintWriter _debugOutput;
 91   
 92    /** Debug input and output stream. */
 93    protected PipedInputStream _debugInputStream;
 94    protected PipedOutputStream _debugOutputStream;
 95   
 96    /** The combined error stream of all processes. */
 97    protected PipedInputStream _combinedErrorStream;
 98   
 99    /** The stream into which all outputs to stderr are written. */
 100    protected PipedOutputStream _combinedStdErrStream;
 101   
 102    /** Threads that wait for the subprocesses to terminate. */
 103    // protected Thread[] _deathThreads;
 104   
 105    /** Constructor for a process chain consisting of the individual processes provided.
 106    * @param pcs array of ProcessCreators */
 107  0 public ProcessChain(ProcessCreator[] pcs) {
 108  0 _creators = pcs;
 109  0 _processes = new Process[_creators.length];
 110   
 111  0 _combinedInputStream = new PipedInputStream();
 112  0 try {
 113  0 _combinedStdOutStream = new PipedOutputStream(_combinedInputStream);
 114  0 _combinedInputStream.connect(_combinedStdOutStream);
 115    }
 116    catch(IOException e) { /* ignore, no output if this goes wrong */ }
 117   
 118  0 _debugInputStream = new PipedInputStream();
 119  0 try {
 120  0 _debugOutputStream = new PipedOutputStream(_debugInputStream);
 121  0 _debugInputStream.connect(_debugOutputStream);
 122    }
 123    catch(IOException e) { /* ignore, no output if this goes wrong */ }
 124  0 _combinedInputJoinedWithDebugStream = new JoinInputStream(_combinedInputStream, _debugInputStream);
 125  0 _debugOutput = new PrintWriter(new OutputStreamWriter(_debugOutputStream));
 126   
 127  0 _combinedErrorStream = new PipedInputStream();
 128  0 try {
 129  0 _combinedStdErrStream = new PipedOutputStream(_combinedErrorStream);
 130  0 _combinedErrorStream.connect(_combinedStdErrStream);
 131    }
 132    catch(IOException e) { /* ignore, no output if this goes wrong */ }
 133   
 134    // _deathThreads = new Thread[_creators.length];
 135  0 for(int i = 0; i < _processes.length; ++i) {
 136    // final int index = i;
 137  0 try {
 138  0 _processes[i] = _creators[i].start();
 139    // _deathThreads[i] = new Thread(new Runnable() {
 140    // public void run() {
 141    // boolean interrupted = false;
 142    // do {
 143    // interrupted = false;
 144    // try {
 145    // _processes[index].waitFor();
 146    // }
 147    // catch(InterruptedException e) { interrupted = true; }
 148    // } while(interrupted);
 149    // GeneralProcessCreator.LOG.log("Process " + index + " has terminated");
 150    // }
 151    // });
 152    // _deathThreads[i].start();
 153    }
 154    catch(IOException e) {
 155  0 GeneralProcessCreator.LOG.log("\nIOException in external process: " + e.getMessage() + "\nCheck your command line.\n");
 156    // could not start the process, record error and abort
 157  0 _debugOutput.println("\nIOException in external process: " + e.getMessage() + "\nCheck your command line.\n");
 158  0 _debugOutput.flush();
 159  0 _aborted = true;
 160  0 destroy();
 161  0 return;
 162    }
 163    }
 164   
 165  0 for(int i = 0; i < _processes.length-1; ++i) {
 166    // _processes.length-1 because we're processing the gaps between the processes:
 167    // (P0 P1 P2) has two gaps: P0-P1 and P1-P2. There's always one less gap than processes.
 168  0 StreamRedirectThread r = new StreamRedirectThread("stdout Redirector " + i,
 169    _processes[i].getInputStream(),
 170    _processes[i+1].getOutputStream(),
 171    new ProcessChainThreadGroup(this));
 172  0 _redirectors.add(r);
 173  0 r.start();
 174  0 r = new StreamRedirectThread("stderr Redirector " + i,
 175    _processes[i].getErrorStream(),
 176    _processes[i+1].getOutputStream(),
 177    new ProcessChainThreadGroup(this));
 178  0 _redirectors.add(r);
 179  0 r.start();
 180    }
 181    // now pipe output from the last process into our output streams
 182  0 StreamRedirectThread r = new StreamRedirectThread("stdout Redirector " + (_processes.length-1),
 183    _processes[_processes.length-1].getInputStream(),
 184    _combinedStdOutStream,
 185    new ProcessChainThreadGroup(this));
 186  0 _redirectors.add(r);
 187  0 r.start();
 188  0 r = new StreamRedirectThread("stderr Redirector " + (_processes.length-1),
 189    _processes[_processes.length-1].getErrorStream(),
 190    _combinedStdErrStream,
 191    new ProcessChainThreadGroup(this));
 192  0 _redirectors.add(r);
 193  0 r.start();
 194    // _debugOutput.println("\n\nProcessChain started\n\n");
 195    // _debugOutput.flush();
 196    }
 197   
 198    // /**
 199    // * Gets the output stream of the process chain, i.e. the output
 200    // * stream of the first process in the chain.
 201    // *
 202    // * @return the output stream of the process chain.
 203    // */
 204    // public OutputStream getOutputStream() {
 205    // if (_aborted) {
 206    // return new OutputStream() {
 207    // public void write(int b) throws IOException { }
 208    // };
 209    // }
 210    // return _processes[0].getOutputStream();
 211    // }
 212    //
 213    // /**
 214    // * Gets the error stream of the process chain, i.e. the error
 215    // * stream of the last process in the chain.
 216    // *
 217    // * @return the error stream of the process chain.
 218    // */
 219    // public InputStream getErrorStream() {
 220    // if (_aborted) {
 221    // return new InputStream() {
 222    // public int read() throws IOException { return -1; }
 223    // };
 224    // }
 225    // return _processes[_processes.length-1].getErrorStream();
 226    // }
 227    //
 228    // /**
 229    // * Gets the input stream of the process chain, i.e. the input
 230    // * stream of the first process in the chain.
 231    // *
 232    // * @return the input stream of the process chain
 233    // */
 234    // public InputStream getInputStream() {
 235    // if (_aborted) {
 236    // return new InputStream() {
 237    // public int read() throws IOException { return -1; }
 238    // };
 239    // }
 240    // return _processes[_processes.length-1].getInputStream();
 241    // }
 242   
 243    /**
 244    * Gets the output stream of the process sequence, i.e. the combined
 245    * output stream of all the processes in the sequence.
 246    *
 247    * @return the output stream of the process sequence.
 248    */
 249  0 public OutputStream getOutputStream() {
 250  0 if (_aborted) {
 251  0 return new OutputStream() {
 252  0 public void write(int b) throws IOException { }
 253    };
 254    }
 255    else {
 256  0 return new BufferedOutputStream(_processes[0].getOutputStream());
 257    }
 258    }
 259   
 260    /**
 261    * Gets the error stream of the process sequence, i.e. the combined
 262    * error stream of all the processes in the sequence.
 263    *
 264    * @return the error stream of the process sequence.
 265    */
 266  0 public InputStream getErrorStream() {
 267  0 return _combinedErrorStream;
 268    }
 269   
 270    /**
 271    * Gets the input stream of the process sequence, i.e. the combined
 272    * input stream of all the processes in the sequence.
 273    *
 274    * @return the input stream of the process chain
 275    */
 276  0 public InputStream getInputStream() {
 277  0 return _combinedInputJoinedWithDebugStream;
 278    }
 279   
 280    /**
 281    * Causes the current thread to wait, if necessary, until the
 282    * process chain has terminated, i.e. until all processes in the
 283    * chain have terminated. This method returns immediately if
 284    * all subprocesses have already terminated. If any of the
 285    * subprocess has not yet terminated, the calling thread will be
 286    * blocked until all subprocesses exit.
 287    *
 288    * @return the exit value of the process chain, i.e. the
 289    * exit code of the last process in the chain.
 290    * @exception InterruptedException if the current thread is
 291    * {@link Thread#interrupt() interrupted} by another thread
 292    * while it is waiting, then the wait is ended and an
 293    * {@link InterruptedException} is thrown.
 294    */
 295  0 public int waitFor() throws InterruptedException {
 296  0 if (_aborted) { return -1; }
 297  0 int exitCode = 0;
 298  0 for(int i = 0; i < _processes.length; ++i) {
 299  0 exitCode = _processes[i].waitFor();
 300    // if (i < _processes.length-1) {
 301    // _stdOutRedirectors.get(i).setStopFlag();
 302    // _stdErrRedirectors.get(i).setStopFlag();
 303    // }
 304    // try {
 305    // _processes[i].getInputStream().close();
 306    // _processes[i].getErrorStream().close();
 307    // if (i < _processes.length-1) {
 308    // _processes[i+1].getOutputStream().close();
 309    // }
 310    // }
 311    // catch(IOException e) { /* ignore, just don't close streams */ }
 312    }
 313    // stopAllRedirectors();
 314  0 return exitCode;
 315    }
 316   
 317    /**
 318    * Returns the exit value for the subprocess.
 319    *
 320    * @return the exit value of the subprocess represented by this
 321    * <code>Process</code> object. by convention, the value
 322    * <code > 0</code> indicates normal termination.
 323    * @exception IllegalThreadStateException if the subprocess represented
 324    * by this <code>Process</code> object has not yet terminated.
 325    */
 326  0 public int exitValue() {
 327  0 if (_aborted) { return -1; }
 328  0 int exitCode = 0;
 329    // executing this loop guarantees that the IllegalThreadStateException
 330    // is thrown when at least one thread has not terminated yet.
 331    // just returning the exit value of the last process is not sufficient:
 332    // the last process could have terminated while other processes have not.
 333  0 for(int i = 0; i < _processes.length; ++i) {
 334  0 exitCode = _processes[i].exitValue();
 335    }
 336  0 return exitCode;
 337    }
 338   
 339    /**
 340    * Kills all subprocesses. The subprocesses represented by this
 341    * <code>ProcessChain</code> object is forcibly terminated.
 342    */
 343  0 public void destroy() {
 344  0 _aborted = true;
 345  0 for(int i = 0; i < _processes.length; ++i) {
 346  0 _processes[i].destroy();
 347    }
 348  0 stopAllRedirectors();
 349    }
 350   
 351    /** Set the stop flags for all redirector threads. */
 352  0 protected void stopAllRedirectors() {
 353  0 for(StreamRedirectThread r: _redirectors) { r.setStopFlag(); }
 354  0 _redirectors.clear();
 355    }
 356   
 357    /** Thread group for all threads that deal with this process sequence. */
 358    protected static class ProcessChainThreadGroup extends ThreadGroup {
 359    private ProcessChain _chain;
 360    private PrintWriter _debugOut;
 361  0 public ProcessChainThreadGroup(ProcessChain chain) {
 362  0 super("Process Chain Thread Group");
 363  0 _chain = chain;
 364  0 _debugOut = _chain._debugOutput;
 365    }
 366  0 public void uncaughtException(Thread t, Throwable e) {
 367  0 destroy();
 368  0 if ((e instanceof StreamRedirectException) &&
 369    (e.getCause() instanceof java.io.IOException)) {
 370  0 _debugOut.println("\n\n\nAn exception occurred during the execution of the command line:\n" +
 371    e.toString() + "\n\n");
 372    }
 373    else {
 374  0 DrJavaErrorHandler.record(e);
 375    }
 376    }
 377    }
 378    }