org.xsocket.connection.http.BlockingBodyDataSource Maven / Gradle / Ivy
/*
* Copyright (c) xsocket.org, 2006 - 2008. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Please refer to the LGPL license at: http://www.gnu.org/copyleft/lesser.txt
* The latest copy of this software may be found on http://www.xsocket.org/
*/
package org.xsocket.connection.http;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.io.Closeable;
import java.net.SocketTimeoutException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.xsocket.DataConverter;
import org.xsocket.Execution;
import org.xsocket.IDataSource;
import org.xsocket.MaxReadSizeExceededException;
import org.xsocket.connection.http.client.IHttpResponseHandler;
/**
*
* I/O resource capable of providing body data in a blocking way. Read operations will be suspended,
* if not enough data is available.
*
* The BlockingBodyDataSource wraps a {@linkplain NonBlockingBodyDataSource}
*
* @author grro
*
*/
public final class BlockingBodyDataSource implements IDataSource, ReadableByteChannel, Closeable {
private static final Logger LOG = Logger.getLogger(BlockingBodyDataSource.class.getName());
public static final int DEFAULT_RECEIVE_TIMEOUT = Integer.MAX_VALUE;
private final ReadNotificationHandler handler = new ReadNotificationHandler();
private final Object readGuard = new Object();
private NonBlockingBodyDataSource delegee = null;
private int receiveTimeout = DEFAULT_RECEIVE_TIMEOUT;
/**
* constructor
*
* @param delegee the underlying non NonBlockingBodyDataSource
*/
BlockingBodyDataSource(NonBlockingBodyDataSource delegee) throws IOException {
this.delegee = delegee;
delegee.setSystemDataHandler(handler);
}
NonBlockingBodyDataSource getUnderliyingBodyDataSource() {
return delegee;
}
/**
* sets the receive time out by reading data
*
* @param timeout the receive timeout
*/
public void setReceiveTimeoutMillis(int timeout) {
this.receiveTimeout = timeout;
}
/**
* gets receive time out by reading data
* @return the receive timeout
*/
public final int getReceiveTimeoutMillis() {
return receiveTimeout;
}
/**
* returns, if the connection is open.
*
* @return true if the connection is open
*/
public boolean isOpen() {
return delegee.isOpen();
}
/**
* {@inheritDoc}
*/
public void close() throws IOException {
delegee.close();
}
/**
* get the body size
*
* @return the body size
* @throws IOException if an exception occurs
*/
public int size() throws IOException {
long start = System.currentTimeMillis();
long remainingTime = receiveTimeout;
do {
synchronized (readGuard) {
if (delegee.isComplete()) {
return delegee.available();
} else {
if (!delegee.isComplete()) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("body has not been complete received (current size=" + delegee.available() + "). waiting");
}
waitForData(readGuard, remainingTime);
} else {
throw new ClosedChannelException();
}
}
}
remainingTime = (start + receiveTimeout) - System.currentTimeMillis();
} while (remainingTime > 0);
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("receive timeout " + DataConverter.toFormatedDuration(receiveTimeout) + " reached. throwsing timeout exception");
}
throw new SocketTimeoutException("timeout " + DataConverter.toFormatedDuration(receiveTimeout) + " reached");
}
/**
* read the body
*
* @return the body as byte buffer
*
* @throws IOException if an exception occurs
*/
public ByteBuffer[] readByteBuffer() throws IOException {
long start = System.currentTimeMillis();
long remainingTime = receiveTimeout;
do {
synchronized (readGuard) {
if (delegee.isComplete()) {
return readByteBufferByLength(delegee.available());
} else {
if (!delegee.isComplete()) {
waitForData(readGuard, remainingTime);
} else {
throw new ClosedChannelException();
}
}
}
remainingTime = (start + receiveTimeout) - System.currentTimeMillis();
} while (remainingTime > 0);
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("receive timeout " + DataConverter.toFormatedDuration(receiveTimeout) + " reached. throwsing timeout exception");
}
throw new SocketTimeoutException("timeout " + DataConverter.toFormatedDuration(receiveTimeout) + " reached");
}
/**
* read the body
*
* @return the body as bytes
*
* @throws IOException if an exception occurs
*/
public byte[] readBytes() throws IOException {
long start = System.currentTimeMillis();
long remainingTime = receiveTimeout;
do {
synchronized (readGuard) {
if (delegee.isComplete()) {
return readBytesByLength(delegee.available());
} else {
if (!delegee.isComplete()) {
waitForData(readGuard, remainingTime);
} else {
throw new ClosedChannelException();
}
}
}
remainingTime = (start + receiveTimeout) - System.currentTimeMillis();
} while (remainingTime > 0);
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("receive timeout " + DataConverter.toFormatedDuration(receiveTimeout) + " reached. throwsing timeout exception");
}
throw new SocketTimeoutException("timeout " + DataConverter.toFormatedDuration(receiveTimeout) + " reached");
}
/**
* read the body
*
* @return the body as string
*
* @throws IOException if an exception occurs
*/
public String readString() throws IOException {
long start = System.currentTimeMillis();
long remainingTime = receiveTimeout;
do {
synchronized (readGuard) {
if (delegee.isComplete()) {
return readStringByLength(delegee.available());
} else {
if (!delegee.isComplete()) {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("body has not been complete received (current size=" + delegee.available() + "). waiting");
}
waitForData(readGuard, remainingTime);
} else {
throw new ClosedChannelException();
}
}
}
remainingTime = (start + receiveTimeout) - System.currentTimeMillis();
} while (remainingTime > 0);
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("receive timeout " + DataConverter.toFormatedDuration(receiveTimeout) + " reached. throwsing timeout exception");
}
throw new SocketTimeoutException("timeout " + DataConverter.toFormatedDuration(receiveTimeout) + " reached");
}
/**
* {@inheritDoc}.
*/
public final int read(ByteBuffer buffer) throws IOException {
int size = buffer.remaining();
if (size < 1) {
return 0;
}
long start = System.currentTimeMillis();
long remainingTime = receiveTimeout;
synchronized (readGuard) {
do {
int availableSize = delegee.available();
// if at least one byte is available -> read and return
if (availableSize > 0) {
int read = delegee.read(buffer);
if (read > 0) {
return read;
}
}
if (availableSize == -1) {
// check if channel is closed by reading with length 0
// is closed a ClosedChannelException will be thrown
delegee.read(ByteBuffer.allocate(0));
}
// no data available
if (isOpen()) {
waitForData(readGuard, remainingTime);
} else {
return -1;
}
remainingTime = (start + receiveTimeout) - System.currentTimeMillis();
} while (remainingTime > 0);
}
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("receive timeout " + DataConverter.toFormatedDuration(receiveTimeout) + " reached. throwsing timeout exception");
}
throw new SocketTimeoutException("timeout " + DataConverter.toFormatedDuration(receiveTimeout) + " reached");
}
/**
* {@inheritDoc}
*/
public final byte readByte() throws IOException, BufferUnderflowException, SocketTimeoutException {
long start = System.currentTimeMillis();
long remainingTime = receiveTimeout;
do {
synchronized (readGuard) {
try {
return delegee.readByte();
} catch (BufferUnderflowException bue) {
if (!delegee.isComplete()) {
waitForData(readGuard, remainingTime);
} else {
throw new ClosedChannelException();
}
}
}
remainingTime = (start + receiveTimeout) - System.currentTimeMillis();
} while (remainingTime > 0);
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("receive timeout " + DataConverter.toFormatedDuration(receiveTimeout) + " reached. throwsing timeout exception");
}
throw new SocketTimeoutException("timeout " + DataConverter.toFormatedDuration(receiveTimeout) + " reached");
}
/**
* {@inheritDoc}
*/
public final short readShort() throws IOException, BufferUnderflowException, SocketTimeoutException {
long start = System.currentTimeMillis();
long remainingTime = receiveTimeout;
do {
synchronized (readGuard) {
try {
return delegee.readShort();
} catch (BufferUnderflowException bue) {
if (!delegee.isComplete()) {
waitForData(readGuard, remainingTime);
} else {
throw new ClosedChannelException();
}
}
}
remainingTime = (start + receiveTimeout) - System.currentTimeMillis();
} while (remainingTime > 0);
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("receive timeout " + DataConverter.toFormatedDuration(receiveTimeout) + " reached. throwsing timeout exception");
}
throw new SocketTimeoutException("timeout " + DataConverter.toFormatedDuration(receiveTimeout) + " reached");
}
/**
* {@inheritDoc}
*/
public final int readInt() throws IOException, BufferUnderflowException, SocketTimeoutException {
long start = System.currentTimeMillis();
long remainingTime = receiveTimeout;
do {
synchronized (readGuard) {
try {
return delegee.readInt();
} catch (BufferUnderflowException bue) {
if (!delegee.isComplete()) {
waitForData(readGuard, remainingTime);
} else {
throw new ClosedChannelException();
}
}
}
remainingTime = (start + receiveTimeout) - System.currentTimeMillis();
} while (remainingTime > 0);
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("receive timeout " + DataConverter.toFormatedDuration(receiveTimeout) + " reached. throwsing timeout exception");
}
throw new SocketTimeoutException("timeout " + DataConverter.toFormatedDuration(receiveTimeout) + " reached");
}
/**
* {@inheritDoc}
*/
public final long readLong() throws IOException, BufferUnderflowException, SocketTimeoutException {
long start = System.currentTimeMillis();
long remainingTime = receiveTimeout;
do {
synchronized (readGuard) {
try {
return delegee.readLong();
} catch (BufferUnderflowException bue) {
if (!delegee.isComplete()) {
waitForData(readGuard, remainingTime);
} else {
throw new ClosedChannelException();
}
}
}
remainingTime = (start + receiveTimeout) - System.currentTimeMillis();
} while (remainingTime > 0);
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("receive timeout " + DataConverter.toFormatedDuration(receiveTimeout) + " reached. throwsing timeout exception");
}
throw new SocketTimeoutException("timeout " + DataConverter.toFormatedDuration(receiveTimeout) + " reached");
}
/**
* {@inheritDoc}
*/
public final double readDouble() throws IOException, BufferUnderflowException, SocketTimeoutException {
long start = System.currentTimeMillis();
long remainingTime = receiveTimeout;
do {
synchronized (readGuard) {
try {
return delegee.readDouble();
} catch (BufferUnderflowException bue) {
if (!delegee.isComplete()) {
waitForData(readGuard, remainingTime);
} else {
throw new ClosedChannelException();
}
}
}
remainingTime = (start + receiveTimeout) - System.currentTimeMillis();
} while (remainingTime > 0);
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("receive timeout " + DataConverter.toFormatedDuration(receiveTimeout) + " reached. throwsing timeout exception");
}
throw new SocketTimeoutException("timeout " + DataConverter.toFormatedDuration(receiveTimeout) + " reached");
}
/**
* {@inheritDoc}
*/
public final ByteBuffer[] readByteBufferByDelimiter(String delimiter) throws IOException, BufferUnderflowException, SocketTimeoutException {
return readByteBufferByDelimiter(delimiter, Integer.MAX_VALUE);
}
public final ByteBuffer[] readByteBufferByDelimiter(String delimiter, int maxLength) throws IOException, BufferUnderflowException, MaxReadSizeExceededException, SocketTimeoutException {
long start = System.currentTimeMillis();
long remainingTime = receiveTimeout;
do {
synchronized (readGuard) {
try {
return delegee.readByteBufferByDelimiter(delimiter, maxLength);
} catch (MaxReadSizeExceededException mre) {
throw mre;
} catch (BufferUnderflowException bue) {
if (!delegee.isComplete()) {
waitForData(readGuard, remainingTime);
} else {
throw new ClosedChannelException();
}
}
}
remainingTime = (start + receiveTimeout) - System.currentTimeMillis();
} while (remainingTime > 0);
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("receive timeout " + DataConverter.toFormatedDuration(receiveTimeout) + " reached. throwsing timeout exception");
}
throw new SocketTimeoutException("timeout " + DataConverter.toFormatedDuration(receiveTimeout) + " reached");
}
/**
* {@inheritDoc}
*/
public final ByteBuffer[] readByteBufferByLength(int length) throws IOException, BufferUnderflowException, SocketTimeoutException {
if (length <= 0) {
return null;
}
long start = System.currentTimeMillis();
long remainingTime = receiveTimeout;
do {
synchronized (readGuard) {
try {
return delegee.readByteBufferByLength(length);
} catch (BufferUnderflowException bue) {
if (!delegee.isComplete()) {
waitForData(readGuard, remainingTime);
} else {
throw new ClosedChannelException();
}
}
}
remainingTime = (start + receiveTimeout) - System.currentTimeMillis();
} while (remainingTime > 0);
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("receive timeout " + DataConverter.toFormatedDuration(receiveTimeout) + " reached. throwsing timeout exception");
}
throw new SocketTimeoutException("timeout " + DataConverter.toFormatedDuration(receiveTimeout) + " reached");
}
/**
* {@inheritDoc}
*/
public final byte[] readBytesByDelimiter(String delimiter) throws IOException, BufferUnderflowException, SocketTimeoutException {
return readBytesByDelimiter(delimiter, Integer.MAX_VALUE);
}
/**
* {@inheritDoc}
*/
public final byte[] readBytesByDelimiter(String delimiter, int maxLength) throws IOException, BufferUnderflowException, MaxReadSizeExceededException, SocketTimeoutException {
return DataConverter.toBytes(readByteBufferByDelimiter(delimiter, maxLength));
}
/**
* {@inheritDoc}
*/
public final byte[] readBytesByLength(int length) throws IOException, BufferUnderflowException, SocketTimeoutException {
return DataConverter.toBytes(readByteBufferByLength(length));
}
/**
* {@inheritDoc}
*/
public final String readStringByDelimiter(String delimiter) throws IOException, BufferUnderflowException, UnsupportedEncodingException, SocketTimeoutException {
return readStringByDelimiter(delimiter, Integer.MAX_VALUE);
}
/**
* {@inheritDoc}
*/
public final String readStringByDelimiter(String delimiter, int maxLength) throws IOException, BufferUnderflowException, UnsupportedEncodingException, MaxReadSizeExceededException, SocketTimeoutException {
return DataConverter.toString(readByteBufferByDelimiter(delimiter, maxLength), delegee.getEncoding());
}
/**
* {@inheritDoc}
*/
public final String readStringByLength(int length) throws IOException, BufferUnderflowException, UnsupportedEncodingException, SocketTimeoutException {
return DataConverter.toString(readByteBufferByLength(length), delegee.getEncoding());
}
/**
* {@inheritDoc}
*/
public final long transferTo(WritableByteChannel target, int length) throws IOException, BufferUnderflowException, SocketTimeoutException {
long written = 0;
ByteBuffer[] buffers = readByteBufferByLength(length);
for (ByteBuffer buffer : buffers) {
written += target.write(buffer);
}
return written;
}
/**
* transfers the content to the given channel
*
* @param target the target channel
* @return the number of transfered bytes
* @return the number of transfered bytes
* @throws ClosedChannelException If either this channel or the target channel is closed
* @throws IOException If some other I/O error occurs
*/
public final long transferTo(WritableByteChannel target) throws IOException, BufferUnderflowException, SocketTimeoutException {
long written = 0;
long start = System.currentTimeMillis();
long remainingTime = receiveTimeout;
do {
int available = delegee.available();
if (available > 0) {
for (ByteBuffer buffer : delegee.readByteBufferByLength(available)) {
while(buffer.hasRemaining()) {
written += target.write(buffer);
}
}
}
synchronized (readGuard) {
if (delegee.available() == -1) {
return written;
} else {
waitForData(readGuard, remainingTime);
}
}
remainingTime = (start + receiveTimeout) - System.currentTimeMillis();
} while (remainingTime > 0);
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("receive timeout " + DataConverter.toFormatedDuration(receiveTimeout) + " reached. throwsing timeout exception");
}
throw new SocketTimeoutException("timeout " + DataConverter.toFormatedDuration(receiveTimeout) + " reached");
}
private void waitForData(Object readGuard, long maxWaittime) {
try {
readGuard.wait(maxWaittime);
} catch (InterruptedException ignore) { }
}
private void onReadDataInserted() {
if (LOG.isLoggable(Level.FINE)) {
LOG.fine("read notification called");
}
synchronized (readGuard) {
readGuard.notifyAll();
}
if (LOG.isLoggable(Level.FINE)) {
if (delegee.getHttpConnection() != null) {
LOG.fine("[" + delegee.getHttpConnection().getId() + "] on read data inserted called");
} else {
LOG.fine("on read data inserted called");
}
}
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
try {
return readString();
} catch(Exception e) {
return "error occured within toString method " + e.toString();
}
}
// NONTHREAD can cause dead locks e.g. in case of
//
// IHttpResponseHandler responseHandler = new IHttpResponseHandler() {
//
// public void onResponse(HttpResponse resp) throws IOException {
// // ..
// resp.getBlockingBody();
// }
//
@Execution(Execution.NONTHREADED)
private final class ReadNotificationHandler implements IBodyDataHandler {
public boolean onData(NonBlockingBodyDataSource bodyDataSource) {
onReadDataInserted();
return true;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy