| // 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.devtools.build.lib.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'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]; |
| |
| // Suppresses 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); |
| } |
| } |