org.infinispan.client.hotrod.impl.protocol.ChannelInputStream Maven / Gradle / Ivy
package org.infinispan.client.hotrod.impl.protocol;
import java.io.IOException;
import java.util.LinkedList;
import org.infinispan.client.hotrod.VersionedMetadata;
import org.infinispan.client.hotrod.impl.transport.netty.ChannelInboundHandlerDefaults;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.timeout.IdleStateEvent;
public class ChannelInputStream extends AbstractVersionedInputStream implements ChannelInboundHandlerDefaults {
public static final String NAME = "stream";
private final int totalLength;
private final LinkedList bufs = new LinkedList<>();
private int totalReceived, totalRead;
private Throwable throwable;
public ChannelInputStream(VersionedMetadata versionedMetadata, Runnable afterClose, int totalLength) {
super(versionedMetadata, afterClose);
this.totalLength = totalLength;
}
@Override
public synchronized int read() throws IOException {
for (;;) {
while (bufs.isEmpty()) {
if (totalRead >= totalLength) {
return -1;
}
try {
wait();
} catch (InterruptedException e) {
IOException ioException = new IOException(e);
if (throwable != null) {
ioException.addSuppressed(throwable);
}
throw ioException;
}
if (throwable != null) {
throw new IOException(throwable);
}
}
ByteBuf buf = bufs.peekFirst();
if (buf.isReadable()) {
++totalRead;
assert totalRead <= totalLength;
return buf.readUnsignedByte();
} else {
buf.release();
bufs.removeFirst();
}
}
}
@Override
public synchronized int read(byte[] b, int off, int len) throws IOException {
int numRead = 0;
for (;;) {
while (numRead == 0 && bufs.isEmpty()) {
if (totalRead >= totalLength) {
return -1;
}
try {
wait();
} catch (InterruptedException e) {
IOException ioException = new IOException(e);
if (throwable != null) {
ioException.addSuppressed(throwable);
}
throw ioException;
}
if (throwable != null) {
throw new IOException(throwable);
}
}
if (bufs.isEmpty()) {
return numRead;
}
ByteBuf buf = bufs.peekFirst();
int readable = buf.readableBytes();
if (readable > 0) {
int prevReaderIndex = buf.readerIndex();
buf.readBytes(b, off + numRead, Math.min(len - numRead, readable));
int nowRead = buf.readerIndex() - prevReaderIndex;
numRead += nowRead;
totalRead += nowRead;
assert totalRead <= totalLength : "Now read: " + nowRead + ", read: " + totalRead + ", length" + totalLength;
if (numRead >= len) {
return numRead;
}
} else {
buf.release();
bufs.removeFirst();
}
}
}
@Override
public synchronized void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof ByteBuf) {
ByteBuf buf = (ByteBuf) msg;
if (totalReceived + buf.readableBytes() <= totalLength) {
bufs.add(buf);
totalReceived += buf.readableBytes();
if (totalReceived == totalLength) {
ctx.pipeline().remove(this);
}
} else if (totalReceived < totalLength) {
bufs.add(buf.retainedSlice(buf.readerIndex(), totalLength - totalReceived));
buf.readerIndex(buf.readerIndex() + totalLength - totalReceived);
totalReceived = totalLength;
ctx.pipeline().remove(this);
ctx.fireChannelRead(buf);
} else {
ctx.fireChannelRead(buf);
}
notifyAll();
} else {
ctx.fireChannelRead(msg);
}
}
@Override
public synchronized void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
this.throwable = cause;
notifyAll();
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
// swallow the event, this channel is not idle
} else {
ctx.fireUserEventTriggered(evt);
}
}
@Override
public synchronized void close() throws IOException {
super.close();
for (ByteBuf buf : bufs) {
buf.release();
}
}
public boolean moveReadable(ByteBuf buf) {
if (buf.isReadable()) {
int numReceived = Math.min(totalLength - totalReceived, buf.readableBytes());
bufs.add(buf.readBytes(numReceived));
totalReceived += numReceived;
}
return totalReceived < totalLength;
}
}