/*******************************************************************************
* Copyright 2014,
* Luis Pina <luis@luispina.me>,
* Michael Hicks <mwh@cs.umd.edu>
*
* This file is part of Rubah.
*
* Rubah is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Rubah is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Rubah. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
package rubah.io;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import rubah.Rubah;
public class UpdatableInputStream extends InputStream {
private static final int DEFAULT_BUFFER_SIZE = 16 * 1024;
private ByteBuffer buf;
private SocketChannel channel;
private Selector selector;
public UpdatableInputStream(SocketChannel channel) throws IOException {
this(channel, DEFAULT_BUFFER_SIZE);
}
public UpdatableInputStream(SocketChannel channel, int bufferSize) throws IOException {
this.buf = ByteBuffer.allocate(bufferSize);
this.buf.flip();
this.selector = Selector.open();
this.channel = channel;
this.channel.register(this.selector, SelectionKey.OP_READ);
}
private Integer blockingRead() throws IOException, rubah.io.InterruptedException {
Integer ret = null;
this.buf.clear();
try {
ret = Rubah.read(this.selector, this.channel, this.buf);
} finally {
this.buf.flip();
}
return ret;
}
@Override
public int read() throws IOException {
if (!this.buf.hasRemaining()) {
while (true) {
Integer ret;
try {
ret = this.blockingRead();
} catch (rubah.io.InterruptedException e) {
System.out.println("Thread "+Thread.currentThread()+" interrupted while reading, ignoring...");
continue; // Interrupt, carry on reading
}
if (ret == -1) {
System.out.println("Thread "+Thread.currentThread()+" attempted to read from closed stream...");
return -1; // End of stream occurred
}
else {
break; // Process the read value
}
}
}
return this.buf.get() & 0xFF;
}
@Override
public int read(byte[] bytes, int off, int len)
throws IOException {
if (!this.buf.hasRemaining()) {
while (true) {
Integer ret ;
try {
ret = this.blockingRead();
} catch (rubah.io.InterruptedException e) {
continue; // Interrupt, carry on reading
}
if (ret == -1) {
return -1; // End of stream occurred
}
else {
break; // Process the read value
}
}
}
len = Math.min(len, this.buf.remaining());
this.buf.get(bytes, off, len);
return len;
}
/**
* Returns the next byte that read() will return and blocks until available data.
* @return
* @throws IOException
* @throws rubah.io.InterruptedException If read is interrupted by an update request
*/
public Integer peek() throws IOException, rubah.io.InterruptedException {
if (!this.buf.hasRemaining()) {
while (true) {
Integer ret = this.blockingRead();
if (ret == -1) {
return -1; // End of stream occurred
}
else {
break; // Process the read value
}
}
}
this.buf.mark();
int ret = this.buf.get() & 0xFF;
this.buf.reset();
return ret;
}
}