/*******************************************************************************
* gMix open source project - https://svs.informatik.uni-hamburg.de/gmix/
* Copyright (C) 2014 SVS
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
package staticContent.framework.socket.stream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import staticContent.framework.AnonNode;
public class BasicInputStreamMix extends InputStream {
private boolean isClosed = false;
private ByteBuffer buffer= null;
private StreamAnonSocketMixImpl socket;
public BasicInputStreamMix(
AnonNode owner,
StreamAnonSocketMixImpl socket
) {
this.socket = socket;
String requestBufferSize = owner.LAYER_4_MIX_INPUT_STREAM_REQUEST_BUFFER_SIZE;
if (requestBufferSize.equalsIgnoreCase("AUTO")) {
this.buffer = ByteBuffer.allocate((int)Math.ceil(((double)owner.SOCKET_MIX_BACKEND_QUEUE_SIZE * (double)owner.getTransportLayerControllerMix().getMaxSizeOfNextRead() * 1.5)));
} else {
this.buffer = ByteBuffer.allocate(Integer.parseInt(requestBufferSize));
}
this.buffer.flip();
}
// will block till "len" bytes are read
private synchronized int forceRead(byte[] b, int off, int len) throws IOException {
if (isClosed)
throw new IOException("InputStream closed");
if (buffer.remaining() >= len) { // enough data in buffer
buffer.get(b, off, len);
tryFillBuffer();
return len;
} else { // not enough data in buffer
int transfered = 0;
while (true) {
forceFillBuffer();
if (buffer.remaining() >= len-transfered) { // enough data
buffer.get(b, off, len-transfered);
return len;
} else { // not enough data
int nowAvailable = buffer.remaining();
buffer.get(b, off, nowAvailable);
off += nowAvailable;
transfered += nowAvailable;
}
}
}
}
private synchronized void tryFillBuffer() {
if (socket.availableRequests() > 0) {
buffer.compact();
while (socket.availableRequests() > 0 && buffer.remaining() >= socket.sizeOfNextRequest())
buffer.put(socket.getNextRequest().getByteMessage());
buffer.flip();
}
}
private synchronized void forceFillBuffer() {
buffer.compact();
byte[] data = socket.getNextRequest().getByteMessage();
if (data.length > buffer.remaining()) { // resize buffer
System.out.println("resizing buffer (" +buffer.capacity() +"->" +(buffer.position() +data.length) +")");
ByteBuffer old = buffer;
buffer = ByteBuffer.allocate(buffer.position() +data.length);
buffer.put(old);
}
buffer.put(data);
while (socket.availableRequests() > 0 && buffer.remaining() >= socket.sizeOfNextRequest())
buffer.put(socket.getNextRequest().getByteMessage());
buffer.flip();
}
// "Reads the next byte of data from the input stream."
@Override
public synchronized int read() throws IOException {
if (buffer.hasRemaining())
return buffer.get();
else {
byte[] result = new byte[1];
read(result);
return result[0];
}
}
@Override
public synchronized int read(byte[] b) throws IOException {
return forceRead(b, 0, b.length);
}
@Override
public synchronized int read(byte[] b, int off, int len) throws IOException {
return forceRead(b, off, len);
}
@Override
public synchronized int available() throws IOException {
tryFillBuffer();
return buffer.remaining();
}
@Override
public synchronized void close() throws IOException {
if (this.isClosed) {
throw new IOException("InputStream already closed!");
} else {
this.isClosed = true;
}
}
}