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.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.xsocket.ClosedException;
import org.xsocket.DataConverter;
import org.xsocket.Execution;
import org.xsocket.IDataSource;
import org.xsocket.MaxReadSizeExceededException;



/**
 * 
 * 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) {
		this.delegee = delegee;
		delegee.setDataHandler(handler);
	}


	
	/**
	 * 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();
	}

	


	/**
	 * 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 (isOpen()) {
						waitForData(readGuard, remainingTime);
						
					} else {
						throw new ClosedException("source is already closed");
					}
				}
			} 

			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 (isOpen()) {
						waitForData(readGuard, remainingTime);
						
					} else {
						throw new ClosedException("source is already closed");
					}
				}
			} 

			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(), delegee.getEncoding()); 
				} else {
					if (isOpen()) {
						waitForData(readGuard, remainingTime);
						
					} else {
						throw new ClosedException("source is already closed");
					}
				}
			} 

			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) {
					if (size > availableSize) {
						size = availableSize;
					}
					
					ByteBuffer[] bufs = readByteBufferByLength(size);
					for (ByteBuffer buf : bufs) {
						buffer.put(buf);
					}

					return size;

				// ... or wait for at least one byte
				}else {
					if (isOpen()) {
						waitForData(readGuard, remainingTime);
						
					} else {
						throw new ClosedException("data source is already closed");
					}
				}
				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 (isOpen()) {
						waitForData(readGuard, remainingTime);
						
					} else {
						throw new ClosedException("source is already closed");
					}
				}
			} 

			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 (isOpen()) {
						waitForData(readGuard, remainingTime);								
					} else {
						throw new ClosedException("source is already closed");
					}
				}
			} 

			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 (isOpen()) {
						waitForData(readGuard, remainingTime);								
					} else {
						throw new ClosedException("source is already closed");
					}
				}
			} 

			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 (isOpen()) {
						waitForData(readGuard, remainingTime);								
					} else {
						throw new ClosedException("source is already closed");
					}
				}
			} 

			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 (isOpen()) {
						waitForData(readGuard, remainingTime);								
					} else {
						throw new ClosedException("source is already closed");
					}
				}
			} 

			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 {
		return readByteBufferByDelimiter(delimiter, delegee.getEncoding(), maxLength);
	}
	
	/**
	 * {@inheritDoc}
	 */
	public ByteBuffer[] readByteBufferByDelimiter(String delimiter, String encoding, int maxLength) throws IOException, MaxReadSizeExceededException {

		long start = System.currentTimeMillis();
		long remainingTime = receiveTimeout;

		
		do {
			synchronized (readGuard) {
				try {
					return delegee.readByteBufferByDelimiter(delimiter, encoding, maxLength);
						
				} catch (MaxReadSizeExceededException mre) {
					throw mre;

				} catch (BufferUnderflowException bue) {
					if (isOpen()) {
						waitForData(readGuard, remainingTime);								
					} else {
						throw new ClosedException("source is already closed");
					}
				}
			} 

			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 (isOpen()) {
						waitForData(readGuard, remainingTime);
					} else {
						throw new ClosedException("source is already closed");
					}
				}
			} 

			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 readBytesByDelimiter(delimiter, delegee.getEncoding(), maxLength);
	}
	
	
	public byte[] readBytesByDelimiter(String delimiter, String encoding, int maxLength) throws IOException, MaxReadSizeExceededException {
		return DataConverter.toBytes(readByteBufferByDelimiter(delimiter, encoding, 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 readStringByLength(length, delegee.getEncoding());
	}
	
	/**
	 * {@inheritDoc}
	 */
	public final String readStringByLength(int length, String encoding) throws IOException, BufferUnderflowException, UnsupportedEncodingException, SocketTimeoutException {
		return DataConverter.toString(readByteBufferByLength(length), encoding);
	}
	
	
	/**
	 * {@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;
	}
		
	
	
	private void waitForData(Object readGuard, long maxWaittime) {
		try {
			readGuard.wait(maxWaittime);
		} catch (InterruptedException ignore) {  }
	}
	

	private void onReadDataInserted() {
		synchronized (readGuard) {
			readGuard.notifyAll();
		}
	}
	
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public String toString() {
		try {
			return readString();
		} catch(Exception e) {
			return "error occured within toString method " + e.toString();
		}
	}
	
	
	@Execution(Execution.Mode.NONTHREADED)
	private final class ReadNotificationHandler implements IBodyHandler {

		public boolean onData(NonBlockingBodyDataSource bodyDataSource) throws IOException {
			onReadDataInserted();
			return true;
		}		
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy