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

org.xsocket.connection.spi.IoThrottledReadHandler Maven / Gradle / Ivy

There is a newer version: 2.8.15
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.spi;

import java.io.IOException;
import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.xsocket.connection.INonBlockingConnection;



/**
 * Delayed read IO handler
 *
 * @author [email protected]
 */
final class IoThrottledReadHandler extends ChainableIoHandler {

	private static final Logger LOG = Logger.getLogger(IoThrottledReadHandler.class.getName());

	private static final int CHECK_PERIOD_MILLIS = 500;

	// event handling
	private final IOEventHandler ioEventHandler = new IOEventHandler();
	
	// timer handling
	private int readBytesPerSec = INonBlockingConnection.UNLIMITED;
	private final AtomicInteger currentReceived = new AtomicInteger();
	private TimerTask readControlTask = null;
	
	// suspend flag 
	private boolean isSuspended = false;
	
	private int orgReadBufferSize = 0;




	/**
	 * constructor
	 * @param successor  the successor
	 */
	IoThrottledReadHandler(ChainableIoHandler successor) {
		super(successor);
		
		readControlTask = new ReadControlTask(this);
		DefaultIoProvider.getTimer().schedule(readControlTask, CHECK_PERIOD_MILLIS, CHECK_PERIOD_MILLIS);
	}



	/**
	 * {@inheritDoc}
	 */
	public void init(IIoHandlerCallback callbackHandler) throws IOException {
		setPreviousCallback(callbackHandler);
		getSuccessor().init(ioEventHandler);
		
		orgReadBufferSize = (Integer) getSuccessor().getOption(IClientIoProvider.SO_RCVBUF);
		
		getSocketHandler().setRetryRead(false);
	}


	/**
	 * {@inheritDoc}
	 */
	public boolean reset() {
		readBytesPerSec = INonBlockingConnection.UNLIMITED;
		if (readControlTask != null) {
			readControlTask.cancel();
			readControlTask = null;
		}
		
		getSocketHandler().setRetryRead(true);
		
		try {
			getSuccessor().setOption(IClientIoProvider.SO_RCVBUF, orgReadBufferSize);
			getSuccessor().resumeRead();
		} catch (IOException ioe) {
			if (LOG.isLoggable(Level.FINE)) {
				LOG.fine("Error occured by resuming read " + ioe.toString());
			}
		}

		return super.reset();
	}


	/**
	 * set the read rate in sec
	 *
	 * @param readRateSec  the read rate
	 */
	void setReadRateSec(int readRateSec) throws IOException {
		
		if (readRateSec < orgReadBufferSize) {
			getSuccessor().setOption(IClientIoProvider.SO_RCVBUF, readRateSec);
		} else {
			getSuccessor().setOption(IClientIoProvider.SO_RCVBUF, orgReadBufferSize);
		}
		this.readBytesPerSec = readRateSec;
	}



	/**
	 * {@inheritDoc}
	 */
	public void close(boolean immediate) throws IOException {
		if (!immediate) {
			flushOutgoing();
		}

		getSuccessor().close(immediate);
	}


	public void write(ByteBuffer[] buffers) throws ClosedChannelException, IOException {
		getSuccessor().write(buffers);
	}
	
	@Override
	public void flushOutgoing() throws IOException {
		getSuccessor().flushOutgoing();
	}

	

	/**
	 * {@inheritDoc}
	 */
	public void setPreviousCallback(IIoHandlerCallback callbackHandler) {
		super.setPreviousCallback(callbackHandler);
		getSuccessor().setPreviousCallback(ioEventHandler);
	}

	
	private IoSocketHandler getSocketHandler() {
		ChainableIoHandler successor = this;
		do {
			successor = getSuccessor();
			if (successor != null) {
				if (successor instanceof IoSocketHandler) {
					return (IoSocketHandler) successor;
				}
			}
			
		} while (successor != null);

		return null;
	}
	

	private static final class ReadControlTask extends TimerTask {

		private WeakReference ioThrottledReadHandlerRef = null;
		
		private int outstanding = 0;
		
		public ReadControlTask(IoThrottledReadHandler ioThrottledReadHandler) {
			ioThrottledReadHandlerRef = new WeakReference(ioThrottledReadHandler);
		}
		
		
		@Override
		public void run() {
			
			IoThrottledReadHandler ioThrottledReadHandler = ioThrottledReadHandlerRef.get();
			
			if (ioThrottledReadHandler == null) {
				cancel();
				
			} else  {
				outstanding += ioThrottledReadHandler.currentReceived.getAndSet(0);
				
				if (outstanding > 0) {
					double periodSec = ((double) CHECK_PERIOD_MILLIS) / 1000;
					int delta = (int) (ioThrottledReadHandler.readBytesPerSec * periodSec);
					outstanding -= delta;
				}
				
				if (outstanding < 0) {
					outstanding = 0;
				}
				
				if (outstanding > 0) {
					if (!ioThrottledReadHandler.isSuspended) {
						try {
							if (LOG.isLoggable(Level.FINE)) {
								LOG.fine("suspending read");
							}
							ioThrottledReadHandler.getSuccessor().suspendRead();
							ioThrottledReadHandler.isSuspended = true;

						} catch (IOException ioe) {
							if (LOG.isLoggable(Level.FINE)) {
								LOG.fine("Error occured by suspendig read " + ioe.toString());
							}
						}
					}
				} else {
					
					if (ioThrottledReadHandler.isSuspended) {
						try {
							if (LOG.isLoggable(Level.FINE)) {
								LOG.fine("resuming read");
							}
							ioThrottledReadHandler.getSuccessor().resumeRead();
							ioThrottledReadHandler.isSuspended = false;
							
						} catch (IOException ioe) {
							if (LOG.isLoggable(Level.FINE)) {
								LOG.fine("Error occured by resuming read " + ioe.toString());
							}
						}
					}
				}
				
			}
		}
	}
	
	

	private final class IOEventHandler implements IIoHandlerCallback {


		public void onData(ByteBuffer[] data) {
			
			int read = 0;
			
			for (ByteBuffer byteBuffer : data) {
				read += byteBuffer.remaining();
			}
			
			currentReceived.addAndGet(read);
			
			
			getPreviousCallback().onData(data);
		}
		

		public void onConnect() {
			getPreviousCallback().onConnect();
		}


		public void onWriteException(IOException ioException, ByteBuffer data) {
			getPreviousCallback().onWriteException(ioException, data);
		}
		
		public void onWritten(ByteBuffer data) {
			getPreviousCallback().onWritten(data);
		}

		public void onDisconnect() {
			getPreviousCallback().onDisconnect();
		}

		public void onConnectionAbnormalTerminated() {
			getPreviousCallback().onConnectionAbnormalTerminated();
		}

		public void onConnectionTimeout() {
			getPreviousCallback().onConnectionTimeout();
		}

		public void onIdleTimeout() {
			getPreviousCallback().onIdleTimeout();
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy