/*
 * 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.coyote;

import com.sun.enterprise.web.connector.extension.GrizzlyConfig;
import java.lang.reflect.Method;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.catalina.Request;
import org.apache.catalina.Response;
import org.apache.coyote.tomcat5.CoyoteConnector;
import org.apache.coyote.tomcat5.CoyoteResponse;
import org.apache.coyote.tomcat5.Constants;

import org.apache.catalina.LifecycleException;

public class PECoyoteConnector extends CoyoteConnector{

    private static final String USE_COYOTE_CONNECTOR = 
                "com.sun.enterprise.web.connector.useCoyoteConnector";

    private static final String GRIZZLY_CONNECTOR = 
                "com.sun.enterprise.web.connector.grizzly.GrizzlyHttpProtocol";

   
    /**
     * Are we recycling objects
     */
    protected boolean recycleObjects;
    
    
     /**
     * The number of acceptor threads.
     */   
    protected int maxAcceptWorkerThreads;
    
    
    /**
     * The number of reader threads.
     */
    protected int maxReadWorkerThreads;
    
    
    /**
     * The request timeout value used by the processor threads.
     */
    protected int processorWorkerThreadsTimeout;
    
    
    /**
     * The increment number used by the processor threads.
     */
    protected int minProcessorWorkerThreadsIncrement;
    
    
    /**
     * The size of the accept queue.
     */
    protected int minAcceptQueueLength;
    
    
    /**
     * The size of the read queue
     */
    protected int minReadQueueLength;
    
    
    /**
     * The size of the processor queue.
     */ 
    protected int minProcessorQueueLength;   
    
    
    /**
     * Use direct or non direct byte buffer.
     */
    protected boolean useDirectByteBuffer;
    
    
    // Are we using the NIO Connector or the CoyoteConnector
    private boolean coyoteOn = false;
    
    /*
     * Number of seconds before idle keep-alive connections expire
     */
    private int keepAliveTimeoutInSeconds;

    /*
     * Number of keep-alive threads
     */
    private int keepAliveThreadCount;

    /*
     * Specifies whether response chunking is enabled/disabled
     */
    private boolean chunkingDisabled;

    /**
     * Maximum pending connection before refusing requests.
     */
    private int queueSizeInBytes = 4096;
  
    /**
     * Server socket backlog.
     */
    protected int ssBackLog = 4096;    
    
    
    /**
     * Set the number of <code>Selector</code> used by Grizzly.
     */
    public int selectorReadThreadsCount = 0;
    
      
    /**
     * The default response-type
     */
    protected String defaultResponseType = Constants.DEFAULT_RESPONSE_TYPE;


    /**
     * The forced response-type
     */
    protected String forcedResponseType = Constants.DEFAULT_RESPONSE_TYPE; 
    
    /**
     * The monitoring classes used to gather stats.
     */
    private GrizzlyConfig grizzlyMonitor;
    
    
    /**
     *  The JMX domain.
     */       
    private String domain;
    
    
    /**
     * The root folder where application are deployed
     */
    private String rootFolder = "";    
    
    
    /**
     * The http-listener name
     */
    private String name;
    // ------------------------------------------------- FileCache support --//
    
    /**
     * Timeout before remove the static resource from the cache.
     */
    private int secondsMaxAge = -1;
    
    
    /**
     * The maximum entries in the <code>fileCache</code>
     */
    private int maxCacheEntries = 1024;
    
 
    /**
     * The maximum size of a cached resources.
     */
    private long minEntrySize = 2048;
            
               
    /**
     * The maximum size of a cached resources.
     */
    private long maxEntrySize = 537600;
    
    
    /**
     * The maximum cached bytes
     */
    private long maxLargeFileCacheSize = 10485760;
 
    
    /**
     * The maximum cached bytes
     */
    private long maxSmallFileCacheSize = 1048576;
    
    
    /**
     * Is the FileCache enabled.
     */
    private boolean fileCacheEnabled = true;
    
    
    /**
     * Is the large FileCache enabled.
     */
    private boolean isLargeFileCacheEnabled = true;    
    
    
    /**
     * The logger used by the <code>ProtocolHandler</code>
     */
    private Logger logger;

    // ----------------------------------------------------------------------//        
   
    
    public PECoyoteConnector() {
        boolean coyoteOn = false;
        if (System.getProperty(USE_COYOTE_CONNECTOR) != null){
            coyoteOn =
                   Boolean.valueOf(System.getProperty(USE_COYOTE_CONNECTOR))
                                                               .booleanValue();
        }
        
        // By default, turn on the Coyote Connector
        if (!coyoteOn) 
            setProtocolHandlerClassName(GRIZZLY_CONNECTOR);
    }
    

    /**
     * Enables or disables chunked encoding for any responses returned by this
     * Connector.
     *
     * @param chunkingDisabled true if chunking is to be disabled, false
     * otherwise
     */
    public void setChunkingDisabled(boolean chunkingDisabled) {
        this.chunkingDisabled = chunkingDisabled;
    }


    /**
     * @return true if chunking is disabled on this Connector, and false
     * otherwise
     */
    public boolean isChunkingDisabled() {
        return this.chunkingDisabled;
    }


    /** 
     * Create (or allocate) and return a Request object suitable for
     * specifying the contents of a Request to the responsible Container.
     */
    public Request createRequest() {
        
        PwcCoyoteRequest request = new PwcCoyoteRequest();
        request.setConnector(this);
        return (request);

    }


    /**
     * Creates and returns Response object.
     *
     * @return Response object
     */ 
    public Response createResponse() {

        CoyoteResponse response = new CoyoteResponse(isChunkingDisabled());
        response.setConnector(this);
        return (response);

    }


    /**
     * Gets the number of seconds before a keep-alive connection that has
     * been idle times out and is closed.
     *
     * @return Keep-alive timeout in number of seconds
     */
    public int getKeepAliveTimeoutInSeconds() {
        return keepAliveTimeoutInSeconds;
    }


    /**
     * Sets the number of seconds before a keep-alive connection that has
     * been idle times out and is closed.
     *
     * @param timeout Keep-alive timeout in number of seconds
     */
    public void setKeepAliveTimeoutInSeconds(int timeout) {
        keepAliveTimeoutInSeconds = timeout;
        setProperty("keepAliveTimeoutInSeconds", String.valueOf(timeout));
    }


    /**
     * Gets the number of keep-alive threads.
     *
     * @return Number of keep-alive threads
     */
    public int getKeepAliveThreadCount() {
        return keepAliveThreadCount;
    }


    /**
     * Sets the number of keep-alive threads.
     *
     * @param threadCount Number of keep-alive threads
     */
    public void setKeepAliveThreadCount(int threadCount) {
        keepAliveThreadCount = threadCount;
        setProperty("keepAliveThreadCount", String.valueOf(threadCount));
    }


    /**
     * Set the maximum pending connection this <code>Connector</code>
     * can handle.
     */
    public void setQueueSizeInBytes(int queueSizeInBytes){
        this.queueSizeInBytes = queueSizeInBytes;
        setProperty("queueSizeInBytes", queueSizeInBytes);
    }


    /**
     * Return the maximum pending connection.
     */
    public int getQueueSizeInBytes(){
        return queueSizeInBytes;
    }

 
    /**
     * Set the <code>SocketServer</code> backlog.
     */
    public void setSocketServerBacklog(int ssBackLog){
        this.ssBackLog = ssBackLog;
        setProperty("socketServerBacklog", ssBackLog);
    }


    /**
     * Return the maximum pending connection.
     */
    public int getSocketServerBacklog(){
        return ssBackLog;
    }

    
    /**
     * Set the <code>recycle-tasks</code> used by this <code>Selector</code>
     */   
    public void setRecycleObjects(boolean recycleObjects){
        this.recycleObjects= recycleObjects;
        setProperty("recycleObjects", 
                    String.valueOf(recycleObjects));
    }
    
    
    /**
     * Return the <code>recycle-tasks</code> used by this 
     * <code>Selector</code>
     */      
    public boolean getRecycleObjects(){
        return recycleObjects;
    }
   
   
    /**
     * Set the <code>reader-thread</code> from domian.xml.
     */    
    public void setMaxReadWorkerThreads(int maxReadWorkerThreads){
        this.maxReadWorkerThreads = maxReadWorkerThreads;
        setProperty("maxReadWorkerThreads", 
                    String.valueOf(maxReadWorkerThreads));
    }
    
    
    /**
     * Return the <code>read-thread</code> used by this <code>Selector</code>
     */  
    public int getMaxReadWorkerThreads(){
        return maxReadWorkerThreads;
    }

    
    /**
     * Set the <code>reader-thread</code> from domian.xml.
     */    
    public void setMaxAcceptWorkerThreads(int maxAcceptWorkerThreads){
        this.maxAcceptWorkerThreads = maxAcceptWorkerThreads;
        setProperty("maxAcceptWorkerThreads", 
                    String.valueOf(maxAcceptWorkerThreads));
    }
    
    
    /**
     * Return the <code>read-thread</code> used by this <code>Selector</code>
     */  
    public int getMaxAcceptWorkerThreads(){
        return maxAcceptWorkerThreads;
    }
    
    
    /**
     * Set the <code>acceptor-queue-length</code> value 
     * on this <code>Selector</code>
     */
    public void setMinAcceptQueueLength(int minAcceptQueueLength){
        this.minAcceptQueueLength = minAcceptQueueLength;
        setProperty("minAcceptQueueLength", 
                    String.valueOf(minAcceptQueueLength));
    }
 
    
    /**
     * Return the <code>acceptor-queue-length</code> value
     * on this <code>Selector</code>
     */
    public int getMinAcceptQueueLength(){
        return minAcceptQueueLength;
    }
    
    
    /**
     * Set the <code>reader-queue-length</code> value 
     * on this <code>Selector</code>
     */
    public void setMinReadQueueLength(int minReadQueueLength){
        this.minReadQueueLength = minReadQueueLength;
        setProperty("minReadQueueLength", 
                    String.valueOf(minReadQueueLength));
    }
    
    
    /**
     * Return the <code>reader-queue-length</code> value
     * on this <code>Selector</code>
     */
    public int getMinReadQueueLength(){
        return minReadQueueLength;
    }
    
    
    /**
     * Set the <code>processor-queue-length</code> value 
     * on this <code>Selector</code>
     */
    public void setMinProcessorQueueLength(int minProcessorQueueLength){
        this.minProcessorQueueLength = minProcessorQueueLength;
        setProperty("minProcessorQueueLength", 
                    String.valueOf(minProcessorQueueLength));
    }
    
    
    /**
     * Return the <code>processor-queue-length</code> value
     * on this <code>Selector</code>
     */  
    public int getMinProcessorQueueLength(){
        return minProcessorQueueLength;
    }
    
    
    /**
     * Set the <code>use-nio-non-blocking</code> by this <code>Selector</code>
     */  
    public void setUseDirectByteBuffer(boolean useDirectByteBuffer){
        this.useDirectByteBuffer = useDirectByteBuffer;
        setProperty("useDirectByteBuffer", 
                    String.valueOf(useDirectByteBuffer));
    }
    
    
    /**
     * Return the <code>use-nio-non-blocking</code> used by this 
     * <code>Selector</code>
     */    
    public boolean getUseDirectByteBuffer(){
        return useDirectByteBuffer;
    }

    
    public void setProcessorWorkerThreadsTimeout(int timeout){
        this.processorWorkerThreadsTimeout = timeout;
        setProperty("processorWorkerThreadsTimeout", 
                    String.valueOf(timeout));        
    }
    
    
    public int getProcessorWorkerThreadsTimeout(){
        return processorWorkerThreadsTimeout;
    }
    
    
    public void setProcessorWorkerThreadsIncrement(int increment){
        this.minProcessorWorkerThreadsIncrement = increment;
        setProperty("processorThreadsIncrement", 
                    String.valueOf(increment));      
    }
    
    
    public int getMinProcessorWorkerThreadsIncrement(){
        return minProcessorWorkerThreadsIncrement;
    }
 
    public void setSelectorReadThreadsCount(int selectorReadThreadsCount){
        setProperty("selectorReadThreadsCount", 
                     String.valueOf(selectorReadThreadsCount)); 
    }
    
    
    /**
     * Set the default response type used. Specified as a semi-colon
     * delimited string consisting of content-type, encoding,
     * language, charset
     */
    public void setDefaultResponseType(String defaultResponseType){
        this.defaultResponseType = defaultResponseType;
        setProperty("defaultResponseType", defaultResponseType);             
    }


    /**
     * Return the default response type used
     */
    public String getDefaultResponseType(){
         return defaultResponseType;
    }
    
    
    /**
     * Set the forced response type used. The response type to be forced 
     * if the content served cannot be matched by any of the MIME mappings
     * for extensions. Specified as a semi-colon delimited string consisting of
     * content-type, encoding, language, charset
     */
    public void setForcedResponseType(String forcedResponseType){
        this.forcedResponseType = forcedResponseType;
        setProperty("forcedResponseType", forcedResponseType);                     
    }  
    
        
    /**
     * Return the default response type used
     */
    public String getForcedResponseType(){
        return forcedResponseType;
    } 
    
    
    /**
     * Set the domain of this object.
     */
    public void setDomain(String domain){
        this.domain = domain;
    }
    
    
    public void start() throws LifecycleException {
        super.start();  
        if ( grizzlyMonitor != null ) {
            grizzlyMonitor.initConfig();
            grizzlyMonitor.registerMonitoringLevelEvents();
        }
    }
    
    
    public void stop() throws LifecycleException {
        super.stop(); 
        if ( grizzlyMonitor != null ) {
            grizzlyMonitor.unregisterMonitoringLevelEvents();
        }
    }
   //------------------------------------------------- FileCache config -----/

   
    /**
     * The timeout in seconds before remove a <code>FileCacheEntry</code>
     * from the <code>fileCache</code>
     */
    public void setSecondsMaxAge(int sMaxAges){
        secondsMaxAge = sMaxAges;
        setProperty("secondsMaxAge", String.valueOf(secondsMaxAge));        
    }
    
    
    /**
     * Set the maximum entries this cache can contains.
     */
    public void setMaxCacheEntries(int mEntries){
        maxCacheEntries = mEntries;
        setProperty("maxCacheEntries", String.valueOf(maxCacheEntries));         
    }

    
    /**
     * Return the maximum entries this cache can contains.
     */    
    public int getMaxCacheEntries(){
        return maxCacheEntries;
    }
    
    
    /**
     * Set the maximum size a <code>FileCacheEntry</code> can have.
     */
    public void setMinEntrySize(long mSize){
        minEntrySize = mSize;
        setProperty("minEntrySize", String.valueOf(minEntrySize));         
    }
    
    
    /**
     * Get the maximum size a <code>FileCacheEntry</code> can have.
     */
    public long getMinEntrySize(){
        return minEntrySize;
    }
     
    
    /**
     * Set the maximum size a <code>FileCacheEntry</code> can have.
     */
    public void setMaxEntrySize(long mEntrySize){
        maxEntrySize = mEntrySize;
        setProperty("maxEntrySize", String.valueOf(maxEntrySize));        
    }
    
    
    /**
     * Get the maximum size a <code>FileCacheEntry</code> can have.
     */
    public long getMaxEntrySize(){
        return maxEntrySize;
    }
    
    
    /**
     * Set the maximum cache size
     */ 
    public void setMaxLargeCacheSize(long mCacheSize){
        maxLargeFileCacheSize = mCacheSize;
        setProperty("maxLargeFileCacheSize", 
                String.valueOf(maxLargeFileCacheSize));          
    }

    
    /**
     * Get the maximum cache size
     */ 
    public long getMaxLargeCacheSize(){
        return maxLargeFileCacheSize;
    }
    
    
    /**
     * Set the maximum cache size
     */ 
    public void setMaxSmallCacheSize(long mCacheSize){
        maxSmallFileCacheSize = mCacheSize;
        setProperty("maxSmallFileCacheSize", 
                String.valueOf(maxSmallFileCacheSize));         
    }
    
    
    /**
     * Get the maximum cache size
     */ 
    public long getMaxSmallCacheSize(){
        return maxSmallFileCacheSize;
    }    

    
    /**
     * Is the fileCache enabled.
     */
    public boolean isFileCacheEnabled(){
        return fileCacheEnabled;
    }

    
    /**
     * Is the file caching mechanism enabled.
     */
    public void setFileCacheEnabled(boolean fileCacheEnabled){
        this.fileCacheEnabled = fileCacheEnabled;
        setProperty("fileCacheEnabled",String.valueOf(fileCacheEnabled));          
    }
   
    
    /**
     * Is the large file cache support enabled.
     */
    public void setLargeFileCacheEnabled(boolean isLargeEnabled){
        this.isLargeFileCacheEnabled = isLargeEnabled;
        setProperty("largeFileCacheEnabled",
                String.valueOf(isLargeFileCacheEnabled));                 
    }
   
    
    /**
     * Is the large file cache support enabled.
     */
    public boolean getLargeFileCacheEnabled(){
        return isLargeFileCacheEnabled;
    } 

    // --------------------------------------------------------------------//
         
    
    /**
     * Set the documenr root folder
     */
    public void setWebAppRootPath(String rootFolder){
        this.rootFolder = rootFolder;
        setProperty("webAppRootPath",rootFolder);     
    }
    
    
    /**
     * Return the folder's root where application are deployed.
     */
    public String getWebAppRootPath(){
        return rootFolder;
    }
    
    
    /**
     * Set the <code>Logger</code> of the <code>ProtocolHandler</code> instance 
     * used by this class.
     */
    public void setLogger(Logger logger){
        this.logger = logger;
    }
    
    
    /**
     * Initialize this connector.
     */
    public void initialize() throws LifecycleException{
        super.initialize();
        
        if ( !getProtocolHandler().getClass()
                .getName().equals(GRIZZLY_CONNECTOR)){
            return;
        }
 
        // Set the monitoring.
        grizzlyMonitor = new GrizzlyConfig(domain,getPort());
 
        // Set the logger
        try{
            Method loggerMethod = getProtocolHandler().getClass().getMethod
                   ("setLogger", new Class[]{java.util.logging.Logger.class});
            loggerMethod.invoke(getProtocolHandler(), new Object[]{logger});
        } catch(Exception ex) {
            logger.log(Level.WARNING,
                    "Unable to set logger on " + getProtocolHandler(), ex);
        }
    }
    
    
    /**
     * Set the name of this connector.
     */
    public void setName(String name){
        this.name = name;
    }
    
    
    /**
     * Get the name of this connector;
     */
    public String getName(){
        return name;
    }
}

