content: // Copyright 2014 The Bazel Authors. All rights reserved.
  //
  // Licensed under the Apache License, Version 2.0 (the "License");
  // you may not use this file except in compliance with the License.
  // You may obtain a copy of the License at
  //
  //    http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing, software
  // distributed under the License is distributed on an "AS IS" BASIS,
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  // See the License for the specific language governing permissions and
  // limitations under the License.
  
  package com.google.android.apps.common.testing.broker.shell;
  
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.OutputStream;
  
  /**
   * Provides sinks for input streams.  Continuously read an input stream
   * until the end-of-file is encountered.  The stream may be redirected to
   * an {@link OutputStream}, or discarded.
   * <p>
   * This class is useful for handing the {@code stdout} and {@code stderr}
   * streams from a {@link Process} started with {@link Runtime#exec(String)}.
   * If these streams are not consumed, the Process may block resulting in a
   * deadlock.
   *
   * @see <a href="http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html">
   *      JavaWorld: When Runtime.exec() won&apos;t</a>
   */
  public final class InputStreamSink {
  
    /**
     * Black hole into which bytes are sometimes discarded by {@link NullSink}.
     * It is shared by all threads since the actual contents of the buffer
     * are irrelevant.
     */
    private static final byte[] DISCARD = new byte[4096];
  
    // Supresses default constructor; ensures non-instantiability
    private InputStreamSink() {
    }
  
    /**
     * A {@link Thread} which reads and discards data from an
     * {@link InputStream}.
     */
    private static class NullSink implements Runnable {
      private final InputStream in;
  
      public NullSink(InputStream in) {
        this.in = in;
      }
  
      @Override
      public void run() {
        try {
          try {
            // Attempt to just skip all input
            do {
              in.skip(Integer.MAX_VALUE);
            } while (in.read() != -1); // Need to test for EOF
          } catch (IOException ioe) {
            // Some streams throw IOException when skip() is called;
            // resort to reading off all input with read():
            while (in.read(DISCARD) != -1) {
              // no loop body
            }
          }
        } catch (IOException e) {
          throw new RuntimeException(e);
        }
      }
    }
  
    /**
     * A {@link Thread} which reads data from an {@link InputStream},
     * and translates it into an {@link OutputStream}.
     */
    private static class CopySink implements Runnable {
  
      private final InputStream in;
      private final OutputStream out;
  
      public CopySink(InputStream in, OutputStream out) {
        this.in = in;
        this.out = out;
      }
  
      @Override
      public void run() {
        try {
          byte[] buffer = new byte[2048];
          int bytesRead;
          while ((bytesRead = in.read(buffer)) >= 0) {
            out.write(buffer, 0, bytesRead);
            out.flush();
          }
        } catch (IOException e) {
          throw new RuntimeException(e);
        }
      }
    }
  
    /**
     * Creates a {@link Runnable} which consumes the provided
     * {@link InputStream} 'in', discarding its contents.
     */
    public static Runnable newRunnableSink(InputStream in) {
      if (in == null) {
        throw new NullPointerException("in");
      }
      return new NullSink(in);
    }
  
    /**
     * Creates a {@link Runnable} which copies everything from 'in'
     * to 'out'. 'out' will be written to and flushed after each
     * read from 'in'. However, 'out' will not be closed.
     */
    public static Runnable newRunnableSink(InputStream in, OutputStream out) {
      if (in == null) {
        throw new NullPointerException("in");
      }
      if (out == null) {
        throw new NullPointerException("out");
      }
      return new CopySink(in, out);
    }
  }
  