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

org.xsocket.connection.http.NonBlockingBodyDataSource 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.Closeable;
import java.io.IOException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.util.ArrayList;
import java.util.List;
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.AbstractNonBlockingStream;
import org.xsocket.connection.http.AbstractHttpConnection.ICompleteListener;
import org.xsocket.connection.spi.DefaultIoProvider;




/**
 * 
 * I/O resource capable of providing body data in a non blocking way. 
 * Read operations returns immediately  
 * 
 * @author grro
 *
 */
public class NonBlockingBodyDataSource extends AbstractNonBlockingStream implements IDataSource, ReadableByteChannel, Closeable, Cloneable {
	
	
	private static final Logger LOG = Logger.getLogger(NonBlockingBodyDataSource.class.getName());
	
	private final HandlerCaller handlerCaller = new HandlerCaller();
	
	
	private List completeListeners = new ArrayList();	
	private boolean isComplete = false;

	private IBodyHandler handler = null;
	private Execution.Mode handlerExecutionMode = null;
	
	
	/**
	 * constructor 
	 * 
	 * @param encoding the encoding 
	 */
	NonBlockingBodyDataSource(String encoding) {
		setEncoding(encoding);
	}
	

	/**
	 * constructor 
	 * 
	 * @param body the body
	 * @param encoding the encoding 
	 */
	NonBlockingBodyDataSource(String body, String encoding) {
		setEncoding(encoding);
		appendDataToReadBuffer(new ByteBuffer[] {DataConverter.toByteBuffer(body, encoding)});
		isComplete = true;
	}

	/**
	 * constructor 
	 * 
	 * @param body the body
	 * @param encoding the encoding 
	 */
	NonBlockingBodyDataSource(byte[] body, String encoding) {
		setEncoding(encoding);
		appendDataToReadBuffer(new ByteBuffer[] { DataConverter.toByteBuffer(body) });
		isComplete = true;
	}

	
	/**
	 * constructor 
	 * 
	 * @param body the body
	 * @param encoding the encoding 
	 */
	NonBlockingBodyDataSource(ByteBuffer[] body, String encoding) {
		setEncoding(encoding);
		appendDataToReadBuffer(body);
		isComplete = true;
	}

	
	/**
	 * constructor 
	 * 
	 * @param bodyDatasource the body data source
	 * @param encoding the encoding 
	 */

	NonBlockingBodyDataSource(ReadableByteChannel bodyDatasource, String encoding) throws IOException {
		setEncoding(encoding);
		
		int chunkSize = 8192;
		
		List buffers = new ArrayList();
		
		int read = 0;
		do {
			ByteBuffer transferBuffer = ByteBuffer.allocate(chunkSize);
			read = bodyDatasource.read(transferBuffer);

			if (read > 0) {
				if (transferBuffer.remaining() == 0) {
					transferBuffer.flip();
					buffers.add(transferBuffer);

				} else {
					transferBuffer.flip();
					buffers.add(transferBuffer.slice());
				}
			}
		} while (read > 0);
		
		appendDataToReadBuffer(buffers.toArray(new ByteBuffer[buffers.size()]));
		
		isComplete = true;
	}
	
	

	/**
	 * adds a complete listener 
	 * 
	 * @param listener  the complete listener
	 */
	void addListener(ICompleteListener listener) {
	
		assert (DefaultIoProvider.isDispatcherThread());
		
		completeListeners.add(listener);
		
		if (isComplete) {
			listener.onComplete();
		}
	}
	
	
	/**
	 * set complete flag
	 * 
	 * @param isComplete  the complete flag 
	 */
	void setComplete(boolean isComplete) {

		assert (DefaultIoProvider.isDispatcherThread());

		
		this.isComplete = isComplete;

		// call body handler
		callBodyHandler(true);

		
		// notify listeners 
		if (!completeListeners.isEmpty()) {
			for (ICompleteListener listener : completeListeners) {
				listener.onComplete();				
			}
		}		
	}


	/**
	 * return true, if all body data has been received
	 *  
	 * @return true, if all body data has been received
	 */
	public boolean isComplete() {
		return isComplete;
	}

	
	/**
	 * set the body handler
	 * 
	 * @param bodyHandler  the body handler
	 */
	public void setDataHandler(IBodyHandler bodyHandler) {
		setDataHandler(bodyHandler, HttpUtils.getExecutionMode(bodyHandler));
	}
	
	
	public IBodyHandler getDataHandler() {
		return handler;
	}


	private void setDataHandler(IBodyHandler bodyHandler, Execution.Mode executionMode) {
		this.handler = bodyHandler;
		this.handlerExecutionMode = executionMode;
		
		callBodyHandler(false);
	}
	
	
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	protected void onPostRead() {
		if ((available() == 0) && (isComplete)) {
			try {
				close();
			} catch (IOException ioe) {
				if (LOG.isLoggable(Level.FINE)) {
					LOG.fine("error occured by closing body data source " + ioe.toString());
				}
			}
		}

	}
	
	/**
	 * appends data 
	 * 
	 * @param data the data to append 
	 */
	void append(ByteBuffer data) {
		append(new ByteBuffer[] { data });
	}

	
	/**
	 * appends data 
	 * 
	 * @param data the data to append 
	 */
	void append(ByteBuffer[] data) {
		appendDataToReadBuffer(data);
		
		callBodyHandler(false);
	}
	
	
	
	private void callBodyHandler(boolean forceCall) {
		if (handler != null) {
			handlerCaller.setForceCall(forceCall);
			doCallBodyHandler(handlerCaller, handlerExecutionMode);
		}
	}
	
	
	/**
	 * calls th ebody handler call back
	 * 
	 * @param handlerCaller            the handler caller 
	 * @param handlerExecutionMode     the execution mode
	 */
	void doCallBodyHandler(HandlerCaller handlerCaller, Execution.Mode handlerExecutionMode) {
		try {
			handler.onData(this);
		} catch (IOException ioe) {
			if (LOG.isLoggable(Level.FINE)) {
				LOG.fine("error occured by onData " + handler + " " + ioe.toString());
			}
		}		
	}
	
	


	/**
	 * {@inheritDoc}
	 */
	@Override
	protected Object clone() throws CloneNotSupportedException {
		NonBlockingBodyDataSource copy = (NonBlockingBodyDataSource) super.clone();
		
		copy.handler = null;
		copy.completeListeners = new ArrayList();
		copy.handlerExecutionMode = null;
		
		return copy;		
	}
	

	/**
	 * duplicates the body data source
	 * @return the copy
	 */
	public NonBlockingBodyDataSource duplicate() {
		try {
			return (NonBlockingBodyDataSource) clone();
		} catch (CloneNotSupportedException cnse) {
			throw new RuntimeException("could not clone " + this + " " + cnse.toString());
		}
	}

	
	private void closeSilence() {
		try {
			close();
		} catch (IOException ioe) {
			if (LOG.isLoggable(Level.FINE)) {
				LOG.fine("Error occured by closing data source " + this + " " + ioe.toString());
			}
		}
	}
	
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public String toString() {
		return printReadBuffer(getEncoding());
	}
	
	
	/**
	 * the handler caller 
	 *
	 */
	final class HandlerCaller implements Runnable {
	
		
		private boolean forceCall = false;
		
		
		private void setForceCall(boolean forceCall) {
			this.forceCall = forceCall;
		}
		
		
		/**
		 * {@inheritDoc}
		 */
		public void run() {
			
			while ((available() > 0) || forceCall) {
				int version = getReadBufferVersion();
				
				boolean success = call();
				if (!success) {
					return;
				}
					
				int newVersion = getReadBufferVersion();
				if (newVersion != version) {
					version = newVersion;
				} else {
					return;
				}
			}
		}
		
		private boolean call() {
			try {
				handler.onData(NonBlockingBodyDataSource.this);
				return true;
				
			} catch (MaxReadSizeExceededException mee) {
				closeSilence();

			} catch (BufferUnderflowException bue) {
				// 	ignore

			} catch (RuntimeException re) {
				if (LOG.isLoggable(Level.FINE)) {
					LOG.fine("closing data source because an error has been occured by handling data by bodyHandler. " + handler + " Reason: " + re.toString());
				}
				closeSilence();
						
			} catch (IOException ioe) {
				if (LOG.isLoggable(Level.FINE)) {
					LOG.fine("closing data source because an error has been occured by handling data by bodyHandler. " + handler + " Reason: " + ioe.toString());
				}
				closeSilence();
			}
			
			return false;
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy