All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.xsocket.connection.http.BlockingBodyDataSource Maven / Gradle / Ivy

There is a newer version: 2.0-beta-1
Show newest version
/*
 *  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