package org.maachang.comet.net.nio ;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.maachang.comet.net.HttpdRequestImpl;
import org.maachang.comet.net.ReadHttpdRequest;
import org.maachang.util.SequenceSync;
import org.maachang.util.thread.LoopThread;

/**
 * 処理用プーリングスレッド.
 * 
 * @version 2008/05/28
 * @author  masahito suzuki
 * @since   MaachangComet 1.1B
 */
class NioPoolThread extends LoopThread {
    
    /**
     * ログ.
     */
    private static final Log LOG = LogFactory.getLog( NioPoolThread.class ) ;
    
    /**
     * 受信タイムアウト値.
     */
    private static final long TIMEOUT = 5000L ;
    
    /**
     * コールバックオブジェクト.
     */
    private HttpReceiveCallback call = null ;
    
    /**
     * キューオブジェクト.
     */
    private ReceiveLinkQueue queue = null ;
    
    /**
     * シーケンスID発行オブジェクト.
     */
    private SequenceSync sequence = null ;
    
    /**
     * コンストラクタ.
     */
    private NioPoolThread() {
        
    }
    
    /**
     * コンストラクタ.
     * @param call コールバックオブジェクトを設定します.
     * @param queue キューオブジェクトを設定します.
     * @param sequence シーケンスID割り当てオブジェクトを設定します.
     * @exception Exception 例外.
     */
    public NioPoolThread( HttpReceiveCallback call,ReceiveLinkQueue queue,SequenceSync sequence )
        throws Exception {
        if( call == null || queue == null || sequence == null ) {
            throw new IllegalArgumentException( "引数は不正です" ) ;
        }
        this.call = call ;
        this.queue = queue ;
        this.sequence = sequence ;
        this.startThread() ;
    }
    
    /**
     * デストラクタ.
     */
    protected void finalize() throws Exception {
        this.destroy() ;
    }
    
    /**
     * オブジェクト破棄.
     */
    public void destroy() {
        super.stopThread() ;
    }
    
    /**
     * オブジェクトクリア.
     */
    protected void clear() {
        queue = null ;
        call = null ;
    }
    
    /**
     * 実行処理.
     * @exception Exception 例外.
     */
    protected boolean execution() throws Exception {
        NioElement element = null ;
        try {
            element = queue.getQueue() ;
            if( element != null ) {
                executionServer( element ) ;
                return true ;
            }
            return false ;
        } catch( Exception e ) {
            if( element != null ) {
                element.destroy() ;
            }
            element = null ;
            throw e ;
        }
    }
    
    /**
     * １つのコネクションを処理.
     */
    private void executionServer( NioElement element ) throws Exception {
        try {
            int id = sequence.getId() ;
            ConnectionInfo info = new ConnectionInfoImpl( element ) ;
            if( LOG.isInfoEnabled() ) {
                LOG.info( new StringBuilder().append( "receive(" ).append( id ).append( ") - [" ).
                    append( info.getInetAddress().getHostAddress() ).
                    append( ":" ).append( info.getPort() ).
                    append( "]" ).toString() ) ;
            }
            HttpdRequestImpl req = readHttpdRequest( info,id ) ;
            req.setConnectionInfo( info ) ;
            this.call.execution( info,id,req ) ;
            if( info.isUse() == true ) {
                if( info.isCloseFlag() == true ) {
                    element.setCloseFlag( true ) ;
                }
                else {
                    if( info.recyclingConnection() == false ) {
                        element.setCloseFlag( true ) ;
                    }
                    else {
                        element.setCloseFlag( false ) ;
                    }
                }
            }
            else {
                element.destroy() ;
            }
        } catch( Exception e ) {
            if( element != null ) {
                element.destroy() ;
            }
            element = null ;
            throw e ;
        }
    }
    
    /**
     * 受信データをHttpdRequestに変換して取得.
     */
    private static final HttpdRequestImpl readHttpdRequest( ConnectionInfo info,int seqId )
        throws Exception {
        if( info == null || info.isUse() == false ) {
            return null ;
        }
        try {
            HttpdRequestImpl ret = ReadHttpdRequest.receiveHttpRequest( info.getInputStream( TIMEOUT ),seqId ) ;
            return ret ;
        } finally {
            info.getElement().getBuffer().reset() ;
        }
    }
}
