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

org.xsocket.connection.http.BodyDataSink 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.Flushable;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.WritableByteChannel;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.xsocket.IDataSink;
import org.xsocket.connection.AbstractNonBlockingStream;
import org.xsocket.connection.IConnection;
import org.xsocket.connection.INonBlockingConnection;
import org.xsocket.connection.IConnection.FlushMode;
import org.xsocket.connection.http.AbstractHttpMessage.BodyType;




/**
 * 
 * I/O resource capable of receiving the body data.
 * 
 * @author grro
 *
 */
public abstract class BodyDataSink extends AbstractNonBlockingStream implements IDataSink, WritableByteChannel, Closeable, Flushable {
	
	private static final Logger LOG = Logger.getLogger(BodyDataSink.class.getName());
	
	
	private final ArrayList closeListeners = new ArrayList();
	
	private AbstractHttpConnection httpConnection = null;
	private boolean isCloseHttpConnectionAfterWritten = false;

	private AbstractMessageHeader header = null;
	
	// write transfer rate 
	private Integer bytesPerSecond = null;
	
	// close flag
	private boolean isOpen = true;
	
	
	BodyDataSink(AbstractHttpConnection httpConnection, AbstractMessageHeader header) {
		this.httpConnection = httpConnection;	
		this.header = header;
		httpConnection.setBodyDataSink(this);
	}

	
	abstract BodyType getBodyType();
	
	
	/**
	 * set if the connection should be closed if the body is complete received
	 * 
	 * @param isCloseHttpConnectionAfterWritten  the close flag
	 */
	final void setCloseHttpConnectionAfterWritten(boolean isCloseHttpConnectionAfterWritten) {
		this.isCloseHttpConnectionAfterWritten = isCloseHttpConnectionAfterWritten;
	}
	
	
	
	public void addCloseListener(IBodyCloseListener closeListener) {
		synchronized (closeListeners) {
			closeListeners.add(closeListener);
		}
	}
	
	

	public void setWriteTransferRate(int bytesPerSecond) throws ClosedChannelException, IOException {
		if (bytesPerSecond != INonBlockingConnection.UNLIMITED) {
			if (getFlushmode() != FlushMode.ASYNC) {
				LOG.warning("setWriteTransferRate is only supported for FlushMode ASYNC. Ignore update of the transfer rate");
				return;
			}
		}
		
		this.bytesPerSecond = bytesPerSecond;
	}
	
	
	public boolean removeCloseListener(IBodyCloseListener closeListener) {
		synchronized (closeListeners) {
			return closeListeners.remove(closeListener);
		}
	}
	
	final AbstractHttpConnection getHttpConnection() {
		return httpConnection;
	}
	
	
	final AbstractMessageHeader getHeader() {
		return header;
	}

	
	
	/**
	 * {@inheritDoc}
	 */
	public final void close() throws IOException {

		if (isOpen) {
			isOpen = false;
			
			try {
				finalizeBody();	
				
			} finally {

				// restore the settings
				httpConnection.setFlushmode(FlushMode.ASYNC);
				if (bytesPerSecond != null) {
					httpConnection.setWriteTransferRate(INonBlockingConnection.UNLIMITED);
				}

				// handle if connection should be closed
				if (isCloseHttpConnectionAfterWritten && (httpConnection != null)) {
					httpConnection.removeBodyDataSink(this);
					
					if (LOG.isLoggable(Level.FINE)) {
						LOG.fine("[" + httpConnection.getId() + "] closing http server connection (isCloseAfterResponse=true");
					}
							
					httpConnection.closeSilence();
				} 
						
				onClosed();
			}
		}
	}
	
	
	public boolean isOpen() {
		if (!isOpen) {
			return false;
		}
		
		return httpConnection.isOpen();
	}
	
	
	@Override
	protected boolean isDataWriteable() {
		return isOpen;
	}

	@Override
	protected boolean isMoreInputDataExpected() {
		return isOpen;
	}
	
	
	abstract void finalizeBody() throws IOException;
	
	
	@SuppressWarnings("unchecked")
	void onUnderlyingHttpConnectionClosed() {
		//onClosed();
		try {
			close();
		} catch (IOException ignore) { }
	}
	
	
	@SuppressWarnings("unchecked")
	private void onClosed() {		
		ArrayList closeListenersCopy = null;
		synchronized (closeListeners) {
			closeListenersCopy = (ArrayList) closeListeners.clone();
		}
		
		
		for (IBodyCloseListener bodyCloseListener : closeListenersCopy) {
			removeCloseListener(bodyCloseListener);
			callCompleteListener(bodyCloseListener);
		}
		
		
	}
	
	private void callCompleteListener(final IBodyCloseListener listener) {
		
		Runnable task = new Runnable() {
			
			public void run() {
				try {
					listener.onClose();
				} catch (IOException ioe) {
					if (LOG.isLoggable(Level.FINE)) {
						LOG.fine("Error occured by calling close listener " + listener + " " + ioe.toString());
					}
				}
			}
		};
			
		if (httpConnection != null) {
			if (HttpUtils.isMutlithreaded(listener)) {
				httpConnection.processMultiThreaded(task);
			} else {
				httpConnection.processNonThreaded(task);
			}

		} else {
			if (LOG.isLoggable(Level.FINE)) {
				LOG.fine("httpconnection not set. perform body listener call back method non threaded");
			}
			task.run();
		}
	}
	
	
	public final void destroy() {
		isOpen = false;
			
		if (httpConnection != null) {
			httpConnection.destroy();
		}
		
		onClosed();
	}

	
	
	final void onStartWriting() throws IOException {
		httpConnection.setFlushmode(this.getFlushmode());
		if (bytesPerSecond != null) {
			httpConnection.setWriteTransferRate(bytesPerSecond);
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy