Skip to content

Instantly share code, notes, and snippets.

@getvictor
Last active August 29, 2015 13:58
Show Gist options
  • Save getvictor/9997403 to your computer and use it in GitHub Desktop.
Save getvictor/9997403 to your computer and use it in GitHub Desktop.
Java InputStream that handles Docker's attach stream
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