/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 * 
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, 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/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 * 
 * Contributor(s):
 * 
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */
/*
 *  Copyright 1999-2004 The Apache Software Foundation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.sun.enterprise.web.connector.grizzly;

import com.sun.enterprise.web.connector.grizzly.blocking.SelectorBlockingThread;
import com.sun.enterprise.web.connector.grizzly.ssl.SSLSelectorThread;
import com.sun.enterprise.web.portunif.PortUnificationPipeline;
import com.sun.enterprise.web.portunif.ProtocolFinder;
import com.sun.enterprise.web.portunif.ProtocolHandler;
import java.net.InetAddress;
import java.net.URLEncoder;
import java.util.Enumeration;
import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.management.ObjectName;

import com.sun.org.apache.commons.modeler.Registry;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.apache.coyote.http11.Http11Protocol;
import org.apache.tomcat.util.net.SSLImplementation;
import org.apache.tomcat.util.net.ServerSocketFactory;

/**
 * Abstract the protocol implementation, including threading, etc.
 * Processor is single threaded and specific to stream-based protocols.
 * This is an adaptation of org.apache.coyote.http11.Http11Processor except here
 * NIO is used.
 * 
 * @author Jean-Francois Arcand
 */
public class GrizzlyHttpProtocol extends Http11Protocol {    
    
    private int socketBuffer = 9000;
    
    /**
     * The <code>SelectorThread</code> used by this object.
     */
    private SelectorThread selectorThread;
    
    
    /**
     * The JMX management class.
     */
    private Management jmxManagement = null;

    // ------------------------------------------------------ Compression ---/
    
    
    /**
     * Compression value.
     */
    private String compression = "off";
    private String noCompressionUserAgents = null;
    private String restrictedUserAgents = null;
    private String compressableMimeTypes = "text/html,text/xml,text/plain";
    private int compressionMinSize    = 2048;
    
    
    /**
     * List of proxied protocol supported by the listener.
     */
    private String proxiedProtocols;
    
    
    /**
     * The default proxied protocol.
     */
    private final static ConcurrentLinkedQueue<String> supportedHandlers 
            = new ConcurrentLinkedQueue<String>();
    
    
    /**
     * The default proxied protocol.
     */
    private final static ConcurrentHashMap<String,String> supportedProtocols 
            = new ConcurrentHashMap<String,String>();  
    
    
    private final static String TLS = "tls";    
    private final static String HTTP = "http";
    
    static{
        supportedProtocols.put(
                HTTP,com.sun.enterprise.web.portunif.
                HttpProtocolFinder.class.getName());
        supportedProtocols.put(
                TLS,com.sun.enterprise.web.portunif.
                TlsProtocolFinder.class.getName());
        supportedProtocols.put(
                "ws/tcp","com.sun.xml.ws.transport.tcp.grizzly.WSTCPProtocolFinder");
        supportedHandlers.add(com.sun.enterprise.web.portunif.
                HttpProtocolHandler.class.getName());
        supportedHandlers.add("com.sun.xml.ws.transport.tcp.grizzly.WSTCPProtocolHandler");        
    }


    // ------------------------------------------------------- Constructor --//
    
    public GrizzlyHttpProtocol(boolean secure, boolean blocking, 
                               String selectorThreadImpl) {
        super(secure,blocking,selectorThreadImpl);     
    }   
    
    
    /**
     * This method is called by the constructor of the Http11Protocol
     * superclass.
     */
    protected void create() {         
        if (blocking){
            selectorThread = new SelectorBlockingThread();   
            ((SelectorBlockingThread)selectorThread).setSecure(secure);
        } else if ( !secure ){
            if (selectorThreadImpl != null){
                try{               
                    Class clazz = Class.forName(selectorThreadImpl);
                    selectorThread = (SelectorThread)clazz.newInstance();
                } catch (Throwable t){
                    selectorThread.logger().log(Level.WARNING,
                      "Unable to load SelectorThread: " + selectorThreadImpl,t);
                }
            } 
            
            if ( selectorThread == null ){
                selectorThread = new SelectorThread(); 
            }            
        } else { 
            selectorThread = new SSLSelectorThread();
        }
        setSoLinger(-1);
        setSoTimeout(Constants.DEFAULT_TIMEOUT * 1000);
        setServerSoTimeout(Constants.DEFAULT_SERVER_SOCKET_TIMEOUT);
        setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
    }

    
    // ---------------------------------------------------- Public methods --//
    /** 
     * Start the protocol
     */
    public void init() throws Exception {
        try {
            checkSocketFactory();
        } catch( Exception ex ) {
            SelectorThread.logger().log(Level.SEVERE,
                       "grizzlyHttpProtocol.socketfactory.initerror",ex);
            throw ex;
        }

        if( socketFactory!=null ) {
            Enumeration attE=attributes.keys();
            while( attE.hasMoreElements() ) {
                String key=(String)attE.nextElement();
                Object v=attributes.get( key );
                socketFactory.setAttribute( key, v );
            }
        }

        if ( secure && !blocking){
            socketFactory.init();
            ((SSLSelectorThread)selectorThread)
                    .setSSLContext(socketFactory.getSSLContext());
        }
        
        try {
             selectorThread.setAdapter(adapter);
             if (proxiedProtocols != null|| 
                  System.getProperty(
                     PortUnificationPipeline.PROTOCOL_HANDLERS) != null){
                 
                 selectorThread.pipelineClassName = com.sun.enterprise.web.
                         portunif.PortUnificationPipeline.class.getName();
             }
             selectorThread.initEndpoint();
             if (proxiedProtocols != null && !blocking){
                configureProxiedProtocols();
             }
        } catch (Exception ex) {
            SelectorThread.logger().log(Level.SEVERE, 
                       "grizzlyHttpProtocol.endpoint.initerror", ex);
            throw ex;
        }
    }
    
    
    public void start() throws Exception {
        try {            
            if ( this.oname != null ) {
                jmxManagement = new ModelerManagement();
                selectorThread.setManagement(jmxManagement);
                
                try {
                    ObjectName sname = new ObjectName(domain
                                   +  ":type=Selector,name=http"
                                   + selectorThread.getPort());                   
                    jmxManagement.registerComponent(selectorThread, sname, null);
                } catch (Exception ex) {
                    SelectorThread.logger().log(Level.SEVERE,
                      "grizzlyHttpProtocol.selectorRegistrationFailed", ex);
                }
            } else {
                SelectorThread.logger().log(Level.INFO,
                           "grizzlyHttpProtocol.selectorRegisterProtocol");
            }

            selectorThread.start();
        } catch (Exception ex) {
            SelectorThread.logger().log(Level.SEVERE, 
                       "grizzlyHttpProtocol.endpoint.starterror", 
                       ex);
            throw ex;
        }
        
        SelectorThread.logger().log(Level.INFO, 
                   "grizzlyHttpProtocol.start", String.valueOf(getPort()));
    }


    public void destroy() throws Exception {
        SelectorThread.logger().log(Level.INFO, 
                   "grizzlyHttpProtocol.stop", 
                                String.valueOf(getPort()));
        
        if ( domain != null ){
           jmxManagement.
                    unregisterComponent(new ObjectName(domain,"type", "Selector"));
        }
        selectorThread.stopEndpoint();
    }
    
    
    // -------------------- Pool setup --------------------


    public int getMaxThreads() {
        return selectorThread.getMaxThreads();
    }
    
    public void setMaxThreads( int maxThreads ) {
        selectorThread.setMaxThreads(maxThreads);
        setAttribute("maxThreads", "" + maxThreads);
    }
    
    
    public void setMaxPostSize(int maxPostSize) {
        selectorThread.setMaxPostSize(maxPostSize);
        setAttribute("maxPostSize", maxPostSize);
    }    

    
    public int getProcessorThreadsIncrement() {
        return selectorThread.getThreadsIncrement();
    }

    
    public void setProcessorThreadsIncrement( int threadsIncrement ) {
        selectorThread.setThreadsIncrement(threadsIncrement);
        setAttribute("threadsIncrement", "" + threadsIncrement);  
    }  
    
    
    public void setProcessorWorkerThreadsTimeout(int timeout){
        selectorThread.setThreadsTimeout(timeout);
        setAttribute("threadsTimeout", "" + timeout);     
    }
    
    
    public int getProcessorWorkerThreadsTimeout(){
        return selectorThread.getThreadsTimeout();
    }
    // -------------------- Tcp setup --------------------

    public int getBacklog() {
        return selectorThread.getSsBackLog();
    }
    
    public void setBacklog( int i ) {
        ;
    }
    
    public int getPort() {
        return selectorThread.getPort();
    }
    
    public void setPort( int port ) {
        selectorThread.setPort(port);
        setAttribute("port", "" + port);
        //this.port=port;
    }

    public InetAddress getAddress() {
        return selectorThread.getAddress();
    }
    
    public void setAddress(InetAddress ia) {
        selectorThread.setAddress( ia );
        setAttribute("address", "" + ia);
    }
    
    public String getName() {
        String encodedAddr = "";
        if (getAddress() != null) {
            encodedAddr = "" + getAddress();
            if (encodedAddr.startsWith("/"))
                encodedAddr = encodedAddr.substring(1);
            encodedAddr = URLEncoder.encode(encodedAddr) + "-";
        }
        return ("http-" + encodedAddr + selectorThread.getPort());
    }
    
    public boolean getTcpNoDelay() {
        return selectorThread.getTcpNoDelay();
    }
    
    public void setTcpNoDelay( boolean b ) {
        selectorThread.setTcpNoDelay( b );
        setAttribute("tcpNoDelay", "" + b);
    }

    public boolean getDisableUploadTimeout() {
        return disableUploadTimeout;
    }
    
    public void setDisableUploadTimeout(boolean isDisabled) {
        disableUploadTimeout = isDisabled;
        selectorThread.setDisableUploadTimeout(disableUploadTimeout);
    }

    public int getSocketBuffer() {
        return socketBuffer;
    }
    
    public void setSocketBuffer(int valueI) {
        socketBuffer = valueI;
    }

    public void setMaxHttpHeaderSize(int maxHttpHeaderSize) {
        super.setMaxHttpHeaderSize(maxHttpHeaderSize);
        selectorThread.setMaxHttpHeaderSize(maxHttpHeaderSize);
    }
   
    public String getCompression() {
        return compression;
    }

    public void setCompression(String valueS) {
        compression = valueS;
        selectorThread.setCompression(compression);
    }
    
    public String getRestrictedUserAgents() {
        return restrictedUserAgents;
    }
    
    public void setRestrictedUserAgents(String valueS) {
        restrictedUserAgents = valueS;
        selectorThread.setRestrictedUserAgents(valueS);
    }

    public String getNoCompressionUserAgents() {
        return noCompressionUserAgents;
    }
    
    public void setNoCompressionUserAgents(String valueS) {
        noCompressionUserAgents = valueS;
        selectorThread.setNoCompressionUserAgents(valueS);
    }

    public String getCompressableMimeType() {
        return compressableMimeTypes;
    }
    
    public void setCompressableMimeType(String valueS) {
        compressableMimeTypes = valueS;
        selectorThread.setCompressableMimeTypes(valueS);
    }

    public int getCompressionMinSize() {
        return compressionMinSize;
    }
    
    public void setCompressionMinSize(int valueI) {
        compressionMinSize = valueI;
        selectorThread.setCompressionMinSize(valueI);
    }

    public int getSoLinger() {
        return selectorThread.getSoLinger();
    }
    
    public void setSoLinger( int i ) {
        selectorThread.setSoLinger( i );
        setAttribute("soLinger", "" + i);
    }

    public int getSoTimeout() {
        return selectorThread.getSoTimeout();
    }
    
    public void setSoTimeout( int i ) {
        selectorThread.setSoTimeout(i);
        setAttribute("soTimeout", "" + i);
    }
    
    public int getServerSoTimeout() {
        return selectorThread.getServerSoTimeout();
    }
    
    public void setServerSoTimeout( int i ) {
        selectorThread.setServerSoTimeout(i);
        setAttribute("serverSoTimeout", "" + i);
    }
    
    public void setSecure( boolean b ) {
        super.setSecure(b);
    }

    /** 
     * Set the maximum number of Keep-Alive requests that we will honor.
     */
    public void setMaxKeepAliveRequests(int mkar) {
        selectorThread.setMaxKeepAliveRequests(mkar);
    }   
   

    /** 
     * 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) {
        selectorThread.setKeepAliveTimeoutInSeconds(timeout);
    }


    /** 
     * Sets the number of keep-alive threads.
     *
     * @param threadCount Number of keep-alive threads
     */    
    public void setKeepAliveThreadCount(int threadCount) {
        selectorThread.setKeepAliveThreadCount(threadCount);
    }


    /**
     * The minimun threads created at startup.
     */ 
    public void setMinThreads(int minThreads){
        selectorThread.setMinThreads(minThreads);
    }
    

    /**
     * Set the request input buffer size
     */
    public void setBufferSize(int requestBufferSize){
        super.setBufferSize(requestBufferSize);
        selectorThread.setBufferSize(requestBufferSize);
    }
    
    
    /**
     * Set the <code>Selector</code> times out value.
     */ 
    public void setSelectorTimeout(int selectorTimeout){
        selectorThread.setSelectorTimeout(selectorTimeout);
    }
    
    
    /**
     * Return the <code>Selector</code> times out value.
     */
    public int getSelectorTimeout(){
        return selectorThread.getSelectorTimeout();
    }
    
    
    /**
     * Set the upload timeout.
     */
    public void setTimeout(int timeout) {
        selectorThread.setUploadTimeout(timeout);
    }

    
    /**
     * Get the upload timeout.
     */
    public int getTimeout() {
        return selectorThread.getTimeout();
    }
    
    
    /**
     * Set the <code>reader-thread</code> from domain.xml.
     */
    public void setMaxReadWorkerThreads(int maxReadWorkerThreads){
        selectorThread.setMaxReadWorkerThreads(maxReadWorkerThreads);
    }
    
    
    /**
     * Return the <code>read-thread</code> used by this <code>Selector</code>
     */   
    public int getMaxReadWorkerThreads(){
        return selectorThread.getMaxReadWorkerThreads();
    }  

    
    public void setDisplayConfiguration(boolean displayConfiguration){
        selectorThread.setDisplayConfiguration(displayConfiguration);
    }
        
    
    public boolean getDisplayConfiguration(){
        return selectorThread.isDisplayConfiguration();
    }

    
    /**
     * Set the <code>recycle-tasks</code> by this <code>Selector</code>
     */
    public void setRecycleTasks(boolean recycleTasks){
        selectorThread.setRecycleTasks(recycleTasks);
    }
    
    
    /**
     * Return the <code>recycle-tasks</code> used by this 
     * <code>Selector</code>
     */     
    public boolean getRecycleTasks(){
        return selectorThread.isRecycleTasks();
    }    
    
     
    public void setUseByteBufferView(boolean useByteBufferView){
        selectorThread.setUseByteBufferView(useByteBufferView);
    }
    
            
    public boolean getUseByteBufferView(){
        return selectorThread.isUseByteBufferView() ;
    }

    
    /**
     * Set the <code>processor-thread</code> from domain.xml
     */   
    public void setMaxProcessorWorkerThreads(int maxProcessorWorkerThreads){
        selectorThread.setMaxProcessorWorkerThreads(maxProcessorWorkerThreads);
    }
    
    
    /**
     * Return the <code>processor-thread</code> used by this <code>Selector</code>
     */   
    public int getMaxProcessorWorkerThreads(){
        return selectorThread.getMaxProcessorWorkerThreads();
    }
 
   
    /**
     * Set the <code>reader-queue-length</code> value 
     * on this <code>Selector</code>
     */
    public void setMinReadQueueLength(int minReadQueueLength){
        selectorThread.setMinReadQueueLength(minReadQueueLength);
    }

    
    /**
     * Return the <code>reader-queue-length</code> value
     * on this <code>Selector</code>
     */
    public int getMinReadQueueLength(){
        return selectorThread.getMinReadQueueLength();
    }
 
  
    /**
     * Set the <code>processor-queue-length</code> value 
     * on this <code>Selector</code>
     */
    public void setMinProcessorQueueLength(int minProcessorQueueLength){
        selectorThread.setMinProcessorQueueLength(minProcessorQueueLength);
    }
 
     
    /**
     * Return the <code>processor-queue-length</code> value
     * on this <code>Selector</code>
     */   
    public int getMinProcessorQueueLength(){
        return selectorThread.getMinProcessorQueueLength();
    }
    
    
    /**
     * Set the <code>use-nio-non-blocking</code> by this <code>Selector</code>
     */    
    public void setUseDirectByteBuffer(boolean useDirectByteBuffer){
        selectorThread.setUseDirectByteBuffer(useDirectByteBuffer);
    }
    
    
    /**
     * Return the <code>use-nio-non-blocking</code> used by this 
     * <code>Selector</code>
     */     
    public boolean getUseDirectByteBuffer(){
        return selectorThread.isUseDirectByteBuffer();
    }
    
    
    /**
     * Set the maximum pending connection this <code>Pipeline</code>
     * can handle.
     */
    public void setQueueSizeInBytes(int maxQueueSizeInBytes){
        selectorThread.setMaxQueueSizeInBytes(maxQueueSizeInBytes);
    }
    
    
    /**
     * Set the <code>SocketServer</code> backlog.
     */
    public void setSocketServerBacklog(int ssBackLog){
        selectorThread.setSsBackLog(ssBackLog);
    }
    
    /**
     * Set the number of <code>SelectorThread</code> Grizzly will uses.
     */
    public void setSelectorReadThreadsCount(int selectorReadThreadsCount){
        selectorThread.setSelectorReadThreadsCount(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){
         selectorThread.setDefaultResponseType(defaultResponseType);
    }


    /**
     * Return the default response type used
     */
    public String getDefaultResponseType(){
         return  selectorThread.getDefaultResponseType();
    }
    
    
    /**
     * Sets the forced request type, which is forced onto requests that
     * do not already specify any MIME type.
     */
    public void setForcedRequestType(String forcedResponseType){
        selectorThread.setForcedRequestType(forcedResponseType);
    }  
    
        
    /**
     * Return the default request type used
     */
    public String getForcedRequestType(){
        return  selectorThread.getForcedRequestType();
    }   
    
    
    //------------------------------------------------- FileCache config -----/

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

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

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

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

    
    /**
     * Is the file caching mechanism enabled.
     */
    public void setFileCacheEnabled(boolean isFileCacheEnabled){
        selectorThread.isFileCacheEnabled = isFileCacheEnabled;
    }
   
    
    /**
     * Is the large file cache support enabled.
     */
    public void setLargeFileCacheEnabled(boolean isLargeEnabled){
        selectorThread.isLargeFileCacheEnabled = isLargeEnabled;
    }
   
    
    /**
     * Is the large file cache support enabled.
     */
    public boolean getLargeFileCacheEnabled(){
        return selectorThread.isLargeFileCacheEnabled;
    }    
   
    
    /**
     * Set the documenr root folder
     */
    public void setWebAppRootPath(String rootFolder){
        selectorThread.setRootFolder(rootFolder);
    }
    
    
    /**
     * Return the folder's root where application are deployed.
     */
    public String getWebAppRootPath(){
        return selectorThread.getRootFolder();
    }     
    
    
    /**
     * Set the logger
     */
    public static void setLogger(Logger logger){
        if ( logger != null )
            SelectorThread.setLogger(logger);
    }
    
    
    /**
     * Return the logger used by the Grizzly classes.
     */
    public static Logger getLogger(){
        return SelectorThread.getLogger();
    }  
    
    
    /**
     * Return the instance of SelectorThread used by this class.
     */
    public SelectorThread selectorThread(){
        return selectorThread;
    }


    public void setCometSupport(boolean cometSupport){
        selectorThread.enableCometSupport(cometSupport);
    }
    
    
    public void setRcmSupport(boolean rcmSupport){
        selectorThread.enableRcmSupport(rcmSupport);
    }
    
    
    // --------------------------------------------------------- Private method

    /** Sanity check and socketFactory setup.
     *  IMHO it is better to stop the show on a broken connector,
     *  then leave Tomcat running and broken.
     *  @exception TomcatException Unable to resolve classes
     */
    private void checkSocketFactory() throws Exception {
        if ( !blocking && !secure) return;
        
        SecureSelector secureSel = (SecureSelector)selectorThread;
        if (secure) {
            // The SSL setup code has been moved into
            // SSLImplementation since SocketFactory doesn't
            // provide a wide enough interface
            sslImplementation =
                SSLImplementation.getInstance(sslImplementationName);
            
            socketFactory = sslImplementation.getServerSocketFactory();
            
            secureSel.setSSLImplementation(sslImplementation);
            secureSel.setEnabledCipherSuites(toStringArray(getCiphers()));
            secureSel.setEnabledProtocols(toStringArray(getProtocols()));
            String clientAuthStr = (String) getAttribute("clientauth");
            if (clientAuthStr != null){
                secureSel.setNeedClientAuth(
                        Boolean.valueOf(clientAuthStr).booleanValue());
            }            
        } else if (socketFactoryName != null) {
            socketFactory = string2SocketFactory(socketFactoryName);
        }
        
        if(socketFactory==null)
            socketFactory=ServerSocketFactory.getDefault();

        secureSel.setServerSocketFactory(socketFactory);
    }
    
    
    private static final String[] toStringArray(String list){
        
        if ( list == null ) return null;
        
        StringTokenizer st = new StringTokenizer(list,",");
        String[] array = new String[st.countTokens()];
        int i = 0;
        while(st.hasMoreTokens()){
            array[i++] = st.nextToken();
        }
        return array;
    }
       
    
    /**
     * JMX Wrapper around a JMX implementation.
     */
    class ModelerManagement implements Management{

        public void registerComponent(Object bean, ObjectName oname, String type) 
                throws Exception{
            Registry.getRegistry().registerComponent(bean,oname,type);
        }

        public void unregisterComponent(ObjectName oname) throws Exception{
            Registry.getRegistry().
                    unregisterComponent(oname);
        }     
    }
 

    public void setReuseAddress(boolean reuseAddress){
        selectorThread.setReuseAddress(reuseAddress);
    }

    public boolean getReuseAddress(){
        return selectorThread.getReuseAddress();
    }

    public String getProxiedProtocols() {
        return proxiedProtocols;
    }

    
    public void setProxiedProtocols(String proxiedProtocols) {
        this.proxiedProtocols = proxiedProtocols;
    }

    
    /**
     * Configure the domain.xml proxied protocols.
     */
    private void configureProxiedProtocols() {
        PortUnificationPipeline pipeline = (PortUnificationPipeline)
            selectorThread.getProcessorPipeline();
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        StringTokenizer st = new StringTokenizer(proxiedProtocols,",");
          
        String className;
        ProtocolFinder finder;
        // By default, we must add an https finder if secure.
        if (secure){
            addProtocol(TLS,pipeline,classLoader);  
        } 

        String protocol;
        while (st.hasMoreTokens()){
            protocol = st.nextToken().toLowerCase();
            if (protocol.equals(TLS) && secure){
                continue;
            } else if (protocol.equals(HTTP)){
                continue;
            }
            addProtocol(protocol,pipeline,classLoader);
        }
                
        // Always add http finder at the end.
        addProtocol(HTTP,pipeline,classLoader);   

        Iterator<String> iterator = supportedHandlers.iterator();
        while(iterator.hasNext()){
            ProtocolHandler handler = 
                    (ProtocolHandler)loadInstance(iterator.next(),classLoader);
            if (handler != null){
                pipeline.addProtocolHandler(handler);
            }
        }               
    }
    
    
    private void addProtocol(String protocol,PortUnificationPipeline pipeline,
            ClassLoader classLoader){
        String className = supportedProtocols.get(protocol);
        if (className == null) {
            SelectorThread.logger().log(Level.WARNING, 
                       "Invalid proxied protocol value: " + protocol);            
            return;
        }
        
        ProtocolFinder finder = 
                (ProtocolFinder)loadInstance(className,classLoader);
        if (finder != null){
            pipeline.addProtocolFinder(finder);
        }          
    }
    
    
    /**
     * Util to load classes using reflection.
     */
    private Object loadInstance(String name,ClassLoader classLoader){     
        if (name == null) return null;
        Class className = null;                               
        try{                              
            className = Class.forName(name,true,classLoader);
            return className.newInstance();
        } catch (Throwable t) {
            SelectorThread.logger().log(Level.WARNING, 
                       "Error loading proxied protocol " + name, t);            
        }   
        return null;
    }
}

