Last active
August 29, 2015 13:58
-
-
Save getvictor/9997403 to your computer and use it in GitHub Desktop.
Java InputStream that handles Docker's attach stream
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.victoreda.playground.services.exec; | |
import java.io.DataInputStream; | |
import java.io.EOFException; | |
import java.io.IOException; | |
import java.io.InputStream; | |
/** | |
* Reader for reading the attach stream from Docker. | |
* @author Victor Lyuboslavsky | |
* @version 1.0 | |
*/ | |
public class DockerAttachInputStream extends InputStream { | |
/** | |
* The initial size of the buffer. | |
*/ | |
private static final int BUFFER_SIZE = 2048; | |
/** | |
* The input stream to use for reading. | |
*/ | |
private DataInputStream is; | |
/** | |
* The buffer to use. | |
*/ | |
private byte[] buffer = new byte[BUFFER_SIZE]; | |
/** | |
* The current index of the buffer. | |
*/ | |
private int index = 0; | |
/** | |
* The current size of the buffer. | |
*/ | |
private int size = 0; | |
/** | |
* Constructor. | |
* @param is the input stream to use | |
*/ | |
public DockerAttachInputStream(InputStream is) { | |
this.is = new DataInputStream(is); | |
} | |
/** | |
* Read the next byte from the input stream. | |
* @return Reads the next byte of data from the input stream. The value byte is returned as an int in the | |
* range 0 to 255. If no byte is available because the end of the stream has been reached, the | |
* value -1 is returned. This method blocks until input data is available, the end of the stream | |
* is detected, or an exception is thrown. | |
* @throws IOException if an error occurs | |
*/ | |
@Override | |
public int read() throws IOException { | |
while (index >= size) { | |
try { | |
// We don't care what type of attach stream it is at this point | |
is.readInt(); | |
// But we do care about its size | |
int payloadSize = is.readInt(); | |
if (payloadSize <= 0) { | |
continue; | |
} | |
if (payloadSize > buffer.length) { | |
buffer = new byte[payloadSize]; | |
} | |
size = payloadSize; | |
index = 0; | |
is.readFully(buffer, index, size); | |
} catch (EOFException e) { | |
return -1; | |
} | |
} | |
return buffer[index++]; | |
} | |
/** | |
* Closes the stream and releases any system resources associated with it. | |
* @throws IOException if an error occurs | |
*/ | |
@Override | |
public void close() throws IOException { | |
is.close(); | |
} | |
/** | |
* Reads up to <code>len</code> bytes of data from the input stream into an array of bytes. An attempt is | |
* made to read as many as <code>len</code> bytes, but a smaller number may be read. The number of bytes | |
* actually read is returned as an integer. | |
* <p> | |
* This method blocks until input data is available, end of file is detected, or an exception is thrown. | |
* <p> | |
* If <code>len</code> is zero, then no bytes are read and <code>0</code> is returned; otherwise, there is | |
* an attempt to read at least one byte. If no byte is available because the stream is at end of file, the | |
* value <code>-1</code> is returned; otherwise, at least one byte is read and stored into <code>b</code>. | |
* <p> | |
* The first byte read is stored into element <code>b[off]</code>, the next one into <code>b[off+1]</code> | |
* , and so on. The number of bytes read is, at most, equal to <code>len</code>. Let <i>k</i> be the | |
* number of bytes actually read; these bytes will be stored in elements <code>b[off]</code> through | |
* <code>b[off+</code><i>k</i><code>-1]</code>, leaving elements <code>b[off+</code><i>k</i><code>]</code> | |
* through <code>b[off+len-1]</code> unaffected. | |
* <p> | |
* In every case, elements <code>b[0]</code> through <code>b[off]</code> and elements | |
* <code>b[off+len]</code> through <code>b[b.length-1]</code> are unaffected. | |
* <p> | |
* The <code>read(b,</code> <code>off,</code> <code>len)</code> method for class <code>InputStream</code> | |
* simply calls the method <code>read()</code> repeatedly. If the first such call results in an | |
* <code>IOException</code>, that exception is returned from the call to the <code>read(b,</code> | |
* <code>off,</code> <code>len)</code> method. If any subsequent call to <code>read()</code> results in a | |
* <code>IOException</code>, the exception is caught and treated as if it were end of file; the bytes read | |
* up to that point are stored into <code>b</code> and the number of bytes read before the exception | |
* occurred is returned. The default implementation of this method blocks until the requested amount of | |
* input data <code>len</code> has been read, end of file is detected, or an exception is thrown. | |
* Subclasses are encouraged to provide a more efficient implementation of this method. | |
* @param b the buffer into which the data is read. | |
* @param off the start offset in array <code>b</code> at which the data is written. | |
* @param len the maximum number of bytes to read. | |
* @return the total number of bytes read into the buffer, or <code>-1</code> if there is no more data | |
* because the end of the stream has been reached. | |
* @exception IOException If the first byte cannot be read for any reason other than end of file, or if | |
* the input stream has been closed, or if some other I/O error occurs. | |
* @exception NullPointerException If <code>b</code> is <code>null</code>. | |
* @exception IndexOutOfBoundsException If <code>off</code> is negative, <code>len</code> is negative, or | |
* <code>len</code> is greater than <code>b.length - off</code> | |
* @see java.io.InputStream#read() | |
*/ | |
@Override | |
public int read(byte[] b, int off, int len) throws IOException { | |
if (b == null) { | |
throw new NullPointerException(); | |
} else if (off < 0 || len < 0 || len > b.length - off) { | |
throw new IndexOutOfBoundsException(); | |
} else if (len == 0) { | |
return 0; | |
} | |
int c = read(); | |
if (c == -1) { | |
return -1; | |
} | |
b[off] = (byte) c; | |
int i = 1; | |
try { | |
// Checking the index/size is the only change here | |
for (; i < len && index < size; i++) { | |
c = read(); | |
if (c == -1) { | |
break; | |
} | |
b[off + i] = (byte) c; | |
} | |
} catch (IOException ee) { | |
// ignore and return what we have so far | |
} | |
return i; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment