Damien Martin-Guillerez | f88f4d8 | 2015-09-25 13:56:55 +0000 | [diff] [blame] | 1 | // Copyright 2014 The Bazel Authors. All rights reserved. |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
| 15 | package com.google.devtools.build.lib.shell; |
| 16 | |
| 17 | import java.io.IOException; |
| 18 | import java.io.InputStream; |
| 19 | import java.io.OutputStream; |
| 20 | |
| 21 | /** |
| 22 | * Provides sinks for input streams. Continuously read an input stream |
| 23 | * until the end-of-file is encountered. The stream may be redirected to |
| 24 | * an {@link OutputStream}, or discarded. |
| 25 | * <p> |
| 26 | * This class is useful for handing the {@code stdout} and {@code stderr} |
| 27 | * streams from a {@link Process} started with {@link Runtime#exec(String)}. |
| 28 | * If these streams are not consumed, the Process may block resulting in a |
| 29 | * deadlock. |
| 30 | * |
| 31 | * @see <a href="http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html"> |
| 32 | * JavaWorld: When Runtime.exec() won't</a> |
| 33 | */ |
| 34 | public final class InputStreamSink { |
| 35 | |
| 36 | /** |
| 37 | * Black hole into which bytes are sometimes discarded by {@link NullSink}. |
| 38 | * It is shared by all threads since the actual contents of the buffer |
| 39 | * are irrelevant. |
| 40 | */ |
| 41 | private static final byte[] DISCARD = new byte[4096]; |
| 42 | |
| 43 | // Supresses default constructor; ensures non-instantiability |
| 44 | private InputStreamSink() { |
| 45 | } |
| 46 | |
| 47 | /** |
| 48 | * A {@link Thread} which reads and discards data from an |
| 49 | * {@link InputStream}. |
| 50 | */ |
| 51 | private static class NullSink implements Runnable { |
| 52 | private final InputStream in; |
| 53 | |
| 54 | public NullSink(InputStream in) { |
| 55 | this.in = in; |
| 56 | } |
| 57 | |
| 58 | @Override |
| 59 | public void run() { |
| 60 | try { |
| 61 | try { |
| 62 | // Attempt to just skip all input |
| 63 | do { |
| 64 | in.skip(Integer.MAX_VALUE); |
| 65 | } while (in.read() != -1); // Need to test for EOF |
| 66 | } catch (IOException ioe) { |
| 67 | // Some streams throw IOException when skip() is called; |
| 68 | // resort to reading off all input with read(): |
| 69 | while (in.read(DISCARD) != -1) { |
| 70 | // no loop body |
| 71 | } |
| 72 | } |
| 73 | } catch (IOException e) { |
| 74 | throw new RuntimeException(e); |
| 75 | } |
| 76 | } |
| 77 | } |
| 78 | |
| 79 | /** |
| 80 | * A {@link Thread} which reads data from an {@link InputStream}, |
| 81 | * and translates it into an {@link OutputStream}. |
| 82 | */ |
| 83 | private static class CopySink implements Runnable { |
| 84 | |
| 85 | private final InputStream in; |
| 86 | private final OutputStream out; |
| 87 | |
| 88 | public CopySink(InputStream in, OutputStream out) { |
| 89 | this.in = in; |
| 90 | this.out = out; |
| 91 | } |
| 92 | |
| 93 | @Override |
| 94 | public void run() { |
| 95 | try { |
| 96 | byte[] buffer = new byte[2048]; |
| 97 | int bytesRead; |
| 98 | while ((bytesRead = in.read(buffer)) >= 0) { |
| 99 | out.write(buffer, 0, bytesRead); |
| 100 | out.flush(); |
| 101 | } |
| 102 | } catch (IOException e) { |
| 103 | throw new RuntimeException(e); |
| 104 | } |
| 105 | } |
| 106 | } |
| 107 | |
| 108 | /** |
| 109 | * Creates a {@link Runnable} which consumes the provided |
| 110 | * {@link InputStream} 'in', discarding its contents. |
| 111 | */ |
| 112 | public static Runnable newRunnableSink(InputStream in) { |
| 113 | if (in == null) { |
| 114 | throw new NullPointerException("in"); |
| 115 | } |
| 116 | return new NullSink(in); |
| 117 | } |
| 118 | |
| 119 | /** |
| 120 | * Creates a {@link Runnable} which copies everything from 'in' |
| 121 | * to 'out'. 'out' will be written to and flushed after each |
| 122 | * read from 'in'. However, 'out' will not be closed. |
| 123 | */ |
| 124 | public static Runnable newRunnableSink(InputStream in, OutputStream out) { |
| 125 | if (in == null) { |
| 126 | throw new NullPointerException("in"); |
| 127 | } |
| 128 | if (out == null) { |
| 129 | throw new NullPointerException("out"); |
| 130 | } |
| 131 | return new CopySink(in, out); |
| 132 | } |
| 133 | } |