/*
 * The contents of this file are subject to the terms
 * of the Common Development and Distribution License
 * (the License).  You may not use this file except in
 * compliance with the License.
 *
 * You can obtain a copy of the license at
 * https://glassfish.dev.java.net/public/CDDLv1.0.html or
 * glassfish/bootstrap/legal/CDDLv1.0.txt.
 * See the License for the specific language governing
 * permissions and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL
 * Header Notice in each file and include the License file
 * at glassfish/bootstrap/legal/CDDLv1.0.txt.
 * If applicable, add the following below the CDDL Header,
 * with the fields enclosed by brackets [] replaced by
 * you own identifying information:
 * "Portions Copyrighted [year] [name of copyright owner]"
 *
 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
 */

package com.sun.enterprise.web.connector.grizzly.comet;

import com.sun.enterprise.web.connector.grizzly.TaskBase;
import com.sun.enterprise.web.connector.grizzly.TaskEvent;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.logging.Level;

/**
 * A <code>Task</code> implementation that allow Grizzly ARP to notify
 * <code>CometHandler</code> when new data (bytes) are available from the 
 * <code>CometSelector</code>.
 *
 * @author Jeanfrancois Arcand
 */
public class CometTask extends TaskBase{
    
    /**
     * The <code>CometContext</code> associated with this instance.
     */
    private CometContext cometContext;
    
    
    /**
     * The <code>SelectionKey</code>.
     */
    private SelectionKey key;     
    
    
    /**
     * The <code>CometSelector</code> .
     */
    private CometSelector cometSelector;
    
    
    /**
     * The time in milliseconds before this object was registered the 
     * <code>SelectionKey</code> on the <code>CometSelector</code>
     */
    private long expireTime = 0L;


    /**
     * The delay before interrupting the polled request and cancelling 
     * the <code>SelectionKey</code>.
     */
    private long expirationDelay = 30 * 1000;
    
    
    /**
     * The InputStream used to read bytes from the <code>CometSelector</code>
     */
    private CometInputStream cometInputStream;
    
    
    private ByteBuffer byteBuffer;
    
    
    private SelectionKey cometKey;
    

    /**
     * <tt>true</tt> if socketChannel.read no longer produces data (-1) or throw
     * an IOException.
     */
    private boolean connectionClosed;
   
    
    /**
     * New <code>CometTask</code>.
     */
    public CometTask() {
        cometInputStream = new CometInputStream();
    }
    
    
    /**
     * Notify the <code>CometHandler</code> that bytes are available for read.
     * The notification will invoke all <code>CometContext</code>
     */
    public void doTask() throws IOException{     
        try{
            connectionClosed = false;
            cometInputStream.setSelectionKey(key);
            if (byteBuffer == null){
                byteBuffer = ByteBuffer.allocate(8192);
            }
            cometInputStream.setByteBuffer(byteBuffer);            
            SocketChannel socketChannel = (SocketChannel)key.channel();
            if (socketChannel.read(byteBuffer) == -1){
                connectionClosed = true;
            } else {
                cometContext.notify(cometInputStream,CometEvent.READ,key);  
            }
        } catch (IOException ex){
            connectionClosed = true;
            // Bug 6403933 & GlassFish 2013
            if (selectorThread.logger().isLoggable(Level.FINEST)){
                selectorThread.logger().log(Level.FINEST,"Comet exception",ex);
            }
        } catch (Throwable t){
            selectorThread.logger().log(Level.SEVERE,"Comet exception",t);
        } finally {   
            // Bug 6403933
            if (connectionClosed){
                selectorThread.cancelKey(key);
                cometSelector.cancelKey(cometKey);
            }
        }
    }
    
    
    /**
     * Not used.
     */
    public void taskEvent(TaskEvent event) {
    }

    
    /**
     * Return the <code>CometContext</code> associated with this instance.
     * @return CometContext the <code>CometContext</code> associated with this 
     *         instance.
     */
    public CometContext getCometContext() {
        return cometContext;
    }
    
    
    /**
     * Set the <code>CometContext</code> used to notify <code>CometHandler</code>.
     * @param cometContext the <code>CometContext</code> used to notify <code>CometHandler</code>
     */
    public void setCometContext(CometContext cometContext) {
        this.cometContext = cometContext;
    }

    
    /**
     * Return the <code>SelectionKey</code>
     * @return SelectionKey <code>SelectionKey</code>
     */
    public SelectionKey getSelectionKey() {
        return key;
    }

    
    /**
     * Set the <code>SelectionKey</code>
     * @param SelectionKey <code>SelectionKey</code>
     */    
    public void setSelectionKey(SelectionKey key) {
        this.key = key;
    }
    
    
    /**
     * Recycle this object.
     */
    public void recycle(){
        super.recycle();
        key = null;
        cometContext = null;
        cometInputStream.recycle();
    }

    
    /**
     * Return the <code>CometSelector</code>
     * @return CometSelector the <code>CometSelector</code>
     */
    public CometSelector getCometSelector() {
        return cometSelector;
    }

    
    /**
     * Set the <code>CometSelector</code>
     * @param cometSelector the <code>CometSelector</code>
     */   
    public void setCometSelector(CometSelector cometSelector) {
        this.cometSelector = cometSelector;
    }
    
    
    /**
     * Return the time in milliseconds before this object was registered the 
     * <code>SelectionKey</code> on the <code>CometSelector</code>
     * @return long Return the time in milliseconds before this object was
     *         registered the <code>SelectionKey</code> on the
     *         <code>CometSelector</code>
     */
    public long getExpireTime() {
        return expireTime;
    }

    
    /**
     * Set the time in milliseconds before this object was registered the 
     * <code>SelectionKey</code> on the <code>CometSelector</code>
     * @param expireTime Return the time in milliseconds before this object was
     *                   registered the <code>SelectionKey</code> on the
     *                   <code>CometSelector</code>
     */   
    public void setExpireTime(long expireTime) {
        this.expireTime = expireTime;
    }
    
    
    /**
     * Return the delay before interrupting the polled request and cancelling 
     * the <code>SelectionKey</code>.
     * @return long Return the delay before interrupting the polled request and cancelling 
     *              the <code>SelectionKey</code>.
     */
    public long getExpirationDelay() {
        return expirationDelay;
    }

    
    /**
     * Set the delay before interrupting the polled request and cancelling 
     * the <code>SelectionKey</code>.
     * @param expirationDelay Return the delay before interrupting the polled 
     *                        request and cancelling 
     *                        the <code>SelectionKey</code>.
     */    
    public void setExpirationDelay(long expirationDelay) {
        this.expirationDelay = expirationDelay;
    }

    
    public SelectionKey getCometKey() {
        return cometKey;
    }

    
    public void setCometKey(SelectionKey cometKey) {
        this.cometKey = cometKey;
    }
}
