org.quickserver.net.server.QuickServer.java Source code

Java tutorial

Introduction

Here is the source code for org.quickserver.net.server.QuickServer.java

Source

/*
 * This file is part of the QuickServer library 
 * Copyright (C) QuickServer.org
 *
 * Use, modification, copying and distribution of this software is subject to
 * the terms and conditions of the GNU Lesser General Public License. 
 * You should have received a copy of the GNU LGP License along with this 
 * library; if not, you can download a copy from <http://www.quickserver.org/>.
 *
 * For questions, suggestions, bug-reports, enhancement-requests etc.
 * visit http://www.quickserver.org
 *
 */

package org.quickserver.net.server;

import java.io.*;
import java.lang.management.ManagementFactory;
import java.net.*;

import org.quickserver.net.*;
//v1.1
import org.quickserver.net.qsadmin.*;
//v1.2
import java.util.logging.*;
//v1.3
import org.quickserver.util.pool.*;
import org.quickserver.util.pool.thread.*;
import org.apache.commons.pool.*;
import org.quickserver.util.xmlreader.*;
import org.quickserver.sql.*;
//v1.3.1
import java.util.logging.Formatter;
import java.util.*;
//v1.3.2
import org.quickserver.util.*;
//v1.3.3
import org.quickserver.security.*;
//v1.4.0
import javax.net.ssl.*;
import javax.net.*;
import java.security.*;
import java.security.cert.*;
//v1.4.5
import java.nio.channels.*;
import org.quickserver.net.server.impl.*;

/**
 * Main class of QuickServer library. This class is used to create 
 * multi client servers quickly.
 * <p>
 * Ones a client is connected, it creates {@link ClientHandler} object, 
 * which is run using any thread available from the pool of threads 
 * maintained by {@link org.quickserver.util.pool.thread.ClientPool}, which 
 * handles the client. <br/>
 * QuickServer divides the application logic of its developer over eight 
 * class, <br>
 *    <ul>
 *      <li>ClientEventHandler<br>
 *          &nbsp;Handles client events [Optional Class].
 *       <li>ClientCommandHandler [#]<br>
 *          &nbsp;Handles client character/string commands.
 *       <li>ClientObjectHandler [#]<br>
 *          &nbsp;Handles client interaction - Object commands.
 *      <li>ClientBinaryHandler [#]<br>
 *          &nbsp;Handles client interaction - binary data.
 *      <li>ClientWriteHandler [Optional Class]<br>
 *          &nbsp;Handles client interaction - writing data (Only used in non-blocking mode).
 *       <li>ClientAuthenticationHandler [Optional Class]<br>
 *          &nbsp;Used to Authencatet a client.
 *       <li>ClientData [Optional Class]<br>
 *          &nbsp;Client data carrier (support class)
 *      <li>ClientExtendedEventHandler [Optional Class]<br>
 *          &nbsp;Handles extended client events.
 *    </ul>
 *
 * [#] = Any one of these have to be set based on default DataMode for input. 
 * The default DataMode for input is String so if not changes you will
 * have to set ClientCommandHandler.
 * </p>
 * <p>
 *  Eg:
 * <code><BLOCKQUOTE><pre>
package echoserver;
    
import org.quickserver.net.*;
import org.quickserver.net.server.*;
    
import java.io.*;
    
public class EchoServer {
   public static void main(String args[])   {
  String cmdHandle = "echoserver.EchoCommandHandler";
       
  QuickServer myServer = new QuickServer();
  myServer.setClientCommandHandler(cmdHandle);
  myServer.setPort(4123);
  myServer.setName(Echo Server v1.0");
  try {
     myServer.startServer();
  } catch(AppException e) {
     System.err.println("Error in server : "+e);
     e.printStackTrace();
  }
   }
}
</pre></BLOCKQUOTE></code></p>
 * 
 * @version 1.4.8
 * @author Akshathkumar Shetty
 */
public class QuickServer implements Runnable, Service, Cloneable, Serializable {
    //Some variable are not initialised to any value because the 
    //default java value was desired initial value. 

    //'dev ' = development build not yet final
    //'beta' = test build all features
    private final static String VER = "2.1.0";//change also in QSAdminMain
    private final static String NEW_LINE;
    private final static String pid;

    static {
        if (System.getProperty("org.quickserver.useOSLineSeparator") != null
                && System.getProperty("org.quickserver.useOSLineSeparator").equals("true")) {
            NEW_LINE = System.getProperty("line.separator");
        } else {
            NEW_LINE = "\r\n";
        }
        String _pid = ManagementFactory.getRuntimeMXBean().getName();
        int i = _pid.indexOf("@");
        pid = _pid.substring(0, i);
        System.out.print("Loading QuickServer v" + getVersion() + " [PID:" + pid + "]");
    }

    private String serverBanner;

    private String clientAuthenticationHandlerString; //v1.4.6
    private String clientEventHandlerString; //v1.4.6
    private String clientExtendedEventHandlerString; //v1.4.6
    private String clientCommandHandlerString;
    private String clientObjectHandlerString; //v1.2
    private String clientBinaryHandlerString; //v1.4
    private String clientWriteHandlerString; //v1.4.5
    private String clientDataString;

    private Authenticator authenticator;
    private ClientAuthenticationHandler clientAuthenticationHandler; //v1.4.6
    private ClientEventHandler clientEventHandler; //v1.4.6
    private ClientExtendedEventHandler clientExtendedEventHandler; //v1.4.6
    private ClientCommandHandler clientCommandHandler;
    private ClientObjectHandler clientObjectHandler; //v1.2
    private ClientBinaryHandler clientBinaryHandler; //v1.4
    private ClientWriteHandler clientWriteHandler; //v1.4.5
    private ClientData clientData;
    protected Class clientDataClass;

    private int serverPort = 9876;
    private Thread t; //Main thread
    protected ServerSocket server;
    private String serverName = "QuickServer";
    private long maxConnection = -1;
    private int socketTimeout = 60 * 1000; //1 min socket timeout
    private String maxConnectionMsg = "-ERR Server Busy. Max Connection Reached";
    private String timeoutMsg = "-ERR Timeout";
    private String maxAuthTryMsg = "-ERR Max Auth Try Reached";
    private int maxAuthTry = 5; //v1.2   

    static {
        System.out.print(".");
    }

    //--v1.1
    private InetAddress ipAddr;
    protected boolean stopServer;
    private Object[] storeObjects;
    private QSAdminServer adminServer;

    //--v1.2
    //Logger for QuickServer
    private static final Logger logger = Logger.getLogger(QuickServer.class.getName());
    //Logger for the application using this QuickServer
    private Logger appLogger;

    //for Service interface
    private long suspendMaxConnection; //backup
    private String suspendMaxConnectionMsg; //backup
    private int serviceState = Service.UNKNOWN;

    static {
        System.out.print(".");
    }

    //--v1.3
    private QuickServerConfig config = new QuickServerConfig();
    private String consoleLoggingformatter;
    private String consoleLoggingLevel = "INFO";
    private ClientPool pool;
    private ObjectPool clientHandlerPool;
    private ObjectPool clientDataPool;
    private DBPoolUtil dBPoolUtil;

    //--v1.3.1
    private String loggingLevel = "INFO";

    //--v1.3.2
    private boolean skipValidation = false;
    private boolean communicationLogging = true;

    //--v1.3.3
    private String securityManagerClass;
    private AccessConstraintConfig accessConstraintConfig;
    private ClassLoader classLoader;
    private String applicationJarPath;
    private ServerHooks serverHooks;
    private ArrayList listOfServerHooks;

    static {
        System.out.print(".");
    }

    //--v1.4.0
    private Secure secure;
    private BasicServerConfig basicConfig = config;
    private SSLContext sslc;
    private KeyManager km[] = null;
    private TrustManager tm[] = null;
    private boolean runningSecure = false;
    private SecureStoreManager secureStoreManager = null;

    private Exception exceptionInRun = null;

    //--v1.4.5
    private ServerSocketChannel serverSocketChannel;
    private Selector selector;
    private boolean blockingMode = true;
    private ObjectPool byteBufferPool;
    private java.util.Date lastStartTime;
    private ClientIdentifier clientIdentifier;
    private GhostSocketReaper ghostSocketReaper;
    private PoolManager poolManager;
    private QSObjectPoolMaker qsObjectPoolMaker;

    //--v1.4.6
    private DataMode defaultDataModeIN = DataMode.STRING;
    private DataMode defaultDataModeOUT = DataMode.STRING;

    //-v1.4.7
    private Throwable serviceError;
    private Map registerChannelRequestMap;

    //v-1.4.8
    private boolean rawCommunicationLogging = false;
    private int rawCommunicationMaxLength = 100;

    static {
        System.out.println(" Done");
        //should be commented if not a patch release
        //System.out.println("[Includes patch(#): t=152&p=532]");
        //should be commented if not a dev release
        //System.out.println("[Dev Build Date: Saturday, October 29, 2005]");
        logger.log(Level.FINE, "PID: {0}", pid);
    }

    /** Returns the version of the library. */
    public static final String getVersion() {
        return VER;
    }

    /** 
     * Returns the numerical version of the library.
     * @since 1.2
     */
    public static final float getVersionNo() {
        return getVersionNo(VER);
    }

    /** 
     * Returns the numerical version of the library.
     * @since 1.4.5
     */
    public static final float getVersionNo(String ver) {
        //String ver = getVersion();
        float version = 0;
        int i = ver.indexOf(" "); //check if beta
        if (i == -1)
            i = ver.length();
        ver = ver.substring(0, i);

        i = ver.indexOf("."); //check for sub version
        if (i != -1) {
            int j = ver.indexOf(".", i);
            if (j != -1) {
                ver = ver.substring(0, i) + "." + MyString.replaceAll(ver.substring(i + 1), ".", "");
            }
        }

        try {
            version = Float.parseFloat(ver);
        } catch (NumberFormatException e) {
            throw new RuntimeException("Corrupt QuickServer");
        }
        return version;
    }

    /**
     * Returns the new line string used by QuickServer.
     * @since 1.2
     */
    public static String getNewLine() {
        return NEW_LINE;
    }

    /**
      * Returns the Server name : port of the QuickServer.
      */
    public String toString() {
        return serverName + " : " + getPort();
    }

    /**
     * Creates a new server without any configuration.
     * Make sure you configure the QuickServer, before 
     * calling startServer()
     * @see org.quickserver.net.server.ClientEventHandler
     * @see org.quickserver.net.server.ClientCommandHandler
     * @see org.quickserver.net.server.ClientObjectHandler
     * @see org.quickserver.net.server.ClientBinaryHandler
      * @see org.quickserver.net.server.ClientWriteHandler
     * @see org.quickserver.net.server.ClientAuthenticationHandler
     * @see org.quickserver.net.server.ClientHandler
      * @see #configQuickServer
     * @see #initService
     * @see #setPort
     * @see #setClientCommandHandler
     * @since 1.2
     */
    public QuickServer() {
    }

    /**
     * Creates a new server with the specified  
     * <code>commandHandler</code> has it {@link ClientCommandHandler}.
     * @param commandHandler the fully qualified name of the 
     *  desired class that implements {@link ClientCommandHandler}
     *
     * @see org.quickserver.net.server.ClientCommandHandler
     * @see org.quickserver.net.server.ClientAuthenticationHandler
     * @see org.quickserver.net.server.ClientHandler
     * @see #setPort
     */
    public QuickServer(String commandHandler) {
        setClientCommandHandler(commandHandler);
    }

    /**
     * Creates a new server at <code>port</code> with the specified  
     * <code>commandHandler</code> has it {@link ClientCommandHandler}.
     *
     * @param commandHandler fully qualified name of the class that
     * implements {@link ClientCommandHandler}
     * @param port to listen on.
     *
     * @see org.quickserver.net.server.ClientCommandHandler
     * @see org.quickserver.net.server.ClientAuthenticationHandler
     * @see org.quickserver.net.server.ClientHandler
     */
    public QuickServer(String commandHandler, int port) {
        this(commandHandler); //send to another constructor
        setPort(port);
    }

    /**
     * Starts the QuickServer.
     *
     * @exception org.quickserver.net.AppException 
     *  if Server already running or if it could not load the classes
     *  [ClientCommandHandler, ClientAuthenticationHandler, ClientData].
     * @see #startService
     */
    public void startServer() throws AppException {
        logger.log(Level.FINE, "Starting {0}", getName());

        if (isClosed() == false) {
            logger.log(Level.WARNING, "Server {0} already running.", getName());
            throw new AppException("Server " + getName() + " already running.");
        }

        blockingMode = getBasicConfig().getServerMode().getBlocking();

        if (getSecure().isEnable() && blockingMode == false) {
            //TODO we need to fix this..
            logger.warning(
                    "QuickServer does not support secure servers under non-blocking mode! So will run in blocking mode.");
            blockingMode = true;
            getBasicConfig().getServerMode().setBlocking(blockingMode);
        }

        if (serverBanner == null) {
            serverBanner = "\n-------------------------------" + "\n Name : " + getName() + "\n Port : " + getPort()
                    + "\n-------------------------------\n";
            logger.finest("Default Server Banner Generated");
        }
        try {
            loadApplicationClasses();

            //load class from Advanced Settings
            Class clientIdentifierClass = getClass(getBasicConfig().getAdvancedSettings().getClientIdentifier(),
                    true);
            clientIdentifier = (ClientIdentifier) clientIdentifierClass.newInstance();
            clientIdentifier.setQuickServer(QuickServer.this);

            //load class from ObjectPoolConfig
            Class poolManagerClass = getClass(getBasicConfig().getObjectPoolConfig().getPoolManager(), true);
            poolManager = (PoolManager) poolManagerClass.newInstance();

            //load class QSObjectPoolMaker
            Class qsObjectPoolMakerClass = getClass(getBasicConfig().getAdvancedSettings().getQsObjectPoolMaker(),
                    true);
            qsObjectPoolMaker = (QSObjectPoolMaker) qsObjectPoolMakerClass.newInstance();

            loadServerHooksClasses();
            processServerHooks(ServerHook.PRE_STARTUP);

            if (getSecure().isLoad() == true)
                loadSSLContext(); //v1.4.0

            loadBusinessLogic();
        } catch (ClassNotFoundException e) {
            logger.log(Level.SEVERE, "Could not load class/s: " + e, e);
            throw new AppException("Could not load class/s : " + e);
        } catch (InstantiationException e) {
            logger.log(Level.SEVERE, "Could not instantiate class/s: " + e, e);
            throw new AppException("Could not instantiate class/s: " + e);
        } catch (IllegalAccessException e) {
            logger.log(Level.SEVERE, "Illegal access to class/s: " + e, e);
            throw new AppException("Illegal access to class/s: " + e);
        } catch (IOException e) {
            logger.log(Level.SEVERE, "IOException: " + e, e);
            throw new AppException("IOException: " + e);
        } catch (Exception e) {
            logger.log(Level.SEVERE, "Exception: " + e, e);
            logger.log(Level.FINE, "StackTrace:\n{0}", MyString.getStackTrace(e));
            throw new AppException("Exception : " + e);
        }

        //v1.3.3
        if (getSecurityManagerClass() != null) {
            System.setSecurityManager(getSecurityManager());
        }

        setServiceState(Service.INIT);
        t = new Thread(this, "QuickServer - " + getName());
        t.start();

        do {
            Thread.yield();
        } while (getServiceState() == Service.INIT);

        if (getServiceState() != Service.RUNNING) {
            if (exceptionInRun != null)
                throw new AppException("Could not start server " + getName() + "! Details: " + exceptionInRun);
            else
                throw new AppException("Could not start server " + getName());
        }
        lastStartTime = new java.util.Date();
        logger.log(Level.FINE, "Started {0}, Date: {1}", new Object[] { getName(), lastStartTime });
    }

    /**
     * Stops the QuickServer.
     *
     * @exception org.quickserver.net.AppException 
     *  if could not stop server
     * @since 1.1
     * @see #stopService
     */
    public void stopServer() throws AppException {
        processServerHooks(ServerHook.PRE_SHUTDOWN);
        logger.log(Level.WARNING, "Stopping {0}", getName());
        stopServer = true;
        Socket death = null;
        if (isClosed() == true) {
            logger.log(Level.WARNING, "Server {0} is not running!", getName());
            throw new AppException("Server " + getName() + " is not running!");
        }
        try {
            if (getBlockingMode() == true) {
                if (getSecure().isEnable() == false) {
                    death = new Socket(server.getInetAddress(), server.getLocalPort());
                    death.getInputStream().read();
                    death.close();
                } else {
                    death = getSSLSocketFactory().createSocket(server.getInetAddress(), server.getLocalPort());
                    Thread.sleep(100);
                    death.close();
                }
            }

            if (serverSocketChannel != null) {
                serverSocketChannel.close();
            }

        } catch (IOException e) {
            logger.log(Level.FINE, "IOError stopping {0}: {1}", new Object[] { getName(), e });
        } catch (Exception e) {
            logger.log(Level.WARNING, "Error stopping {0}: {1}", new Object[] { getName(), e });
            throw new AppException("Error in stopServer " + getName() + ": " + e);
        }

        for (int i = 0; getServiceState() != Service.STOPPED; i++) {
            try {
                Thread.sleep(60);
            } catch (Exception e) {
                logger.log(Level.WARNING, "Error waiting for {0} to fully stop. Error: {1}",
                        new Object[] { getName(), e });
            }
            if (i > 1000) {
                logger.severe("Server was not stopped even after 10sec.. will terminate now.");
                System.exit(-1);
            }
        }
        if (adminServer == null || getQSAdminServer().getServer() != this) {
            //so this is not qsadmin
            setClassLoader(null);
        }
        logger.log(Level.INFO, "Stopped {0}", getName());
    }

    /**
     * Restarts the QuickServer.
     *
     * @exception org.quickserver.net.AppException 
     *  if could not stop server or if it could not start the server.
     * @since 1.2
     */
    public void restartServer() throws AppException {
        stopServer();
        startServer();
    }

    /**
     * Returns the name of the QuickServer. Default is 'QuickServer'.
     * @see #setName
     */
    public String getName() {
        return serverName;
    }

    /**
     * Sets the name for the QuickServer
     * @param name for the QuickServer
     * @see #getName
     */
    public void setName(String name) {
        serverName = name;
        logger.log(Level.FINEST, "Set to : {0}", name);
    }

    /**
      * Returns the Server Banner of the QuickServer
      * @see #setServerBanner
      */
    public String getServerBanner() {
        return serverBanner;
    }

    /**
     * Sets the serverBanner for the QuickServer
    * that will be displayed on the standard output [console]
    * when server starts. <br>&nbsp;<br>
    * To set welcome message to your client
    * {@link ClientEventHandler#gotConnected}
     * @param banner for the QuickServer
     * @see #getServerBanner
     */
    public void setServerBanner(String banner) {
        serverBanner = banner;
        logger.log(Level.FINEST, "Set to : {0}", banner);
    }

    /**
     * Sets the port for the QuickServer to listen on.
    * If not set, it will run on Port 9876 
    * @param port to listen on.
     * @see #getPort
     */
    public void setPort(int port) {
        if (port < 0) {
            throw new IllegalArgumentException("Port number can not be less than 0!");
        }
        serverPort = port;
        logger.log(Level.FINEST, "Set to {0}", port);
    }

    /**
      * Returns the port for the QuickServer.
      * @see #setPort
      */
    public int getPort() {
        if (isClosed() == false) {
            return server.getLocalPort();
        }

        if (getSecure().isEnable() == false) {
            return serverPort;
        } else {
            int _port = getSecure().getPort();
            if (_port == -1)
                return serverPort;
            else
                return _port;
        }
    }

    /**
      * Sets the ClientCommandHandler class that interacts with 
     * client sockets.
     * @param handler the fully qualified name of the class that 
     *  implements {@link ClientCommandHandler}
     * @see #getClientCommandHandler
      */
    public void setClientCommandHandler(String handler) {
        clientCommandHandlerString = handler;
        logger.log(Level.FINEST, "Set to {0}", handler);
    }

    /**
      * Returns the ClientCommandHandler class that interacts with 
     * client sockets.
     * @see #setClientCommandHandler
     * @since 1.1
      */
    public String getClientCommandHandler() {
        return clientCommandHandlerString;
    }

    /**
      * Sets the ClientAuthenticationHandler class that 
     * handles the authentication of a client.
     * @param authenticator the fully qualified name of the class 
     * that implements {@link ClientAuthenticationHandler}.
     * @see #getClientAuthenticationHandler
     * @since 1.4.6
      */
    public void setClientAuthenticationHandler(String authenticator) {
        clientAuthenticationHandlerString = authenticator;
        logger.log(Level.FINEST, "Set to {0}", authenticator);
    }

    /**
      * Returns the ClientAuthenticationHandler class that 
     * handles the authentication of a client.
     * @see #setClientAuthenticationHandler
     * @since 1.4.6
      */
    public String getClientAuthenticationHandler() {
        return clientAuthenticationHandlerString;
    }

    /**
      * Sets the Authenticator class that 
     * handles the authentication of a client.
     * @param authenticator the fully qualified name of the class 
     * that implements {@link Authenticator} or {@link ClientAuthenticationHandler}.
     * @see #getAuthenticator
     * @deprecated since 1.4.6 use setClientAuthenticationHandler
     * @since 1.3
      */
    public void setAuthenticator(String authenticator) {
        clientAuthenticationHandlerString = authenticator;
        logger.log(Level.FINEST, "Set to {0}", authenticator);
    }

    /**
      * Returns the Authenticator class that 
     * handles the authentication of a client.
     * @see #setAuthenticator
     * @deprecated since 1.4.6 use getClientAuthenticationHandler
     * @since 1.3
      */
    public String getAuthenticator() {
        return clientAuthenticationHandlerString;
    }

    /**
      * Sets the ClientData class that carries client data.
     * @param data the fully qualified name of the class that 
     * extends {@link ClientData}.
     * @see #getClientData
      */
    public void setClientData(String data) {
        this.clientDataString = data;
        logger.log(Level.FINEST, "Set to {0}", data);
    }

    /**
      * Returns the ClientData class string that carries client data  
     * @return the fully qualified name of the class that 
     * implements {@link ClientData}.
     * @see #setClientData
      */
    public String getClientData() {
        return clientDataString;
    }

    /**
      * Sets the client socket's timeout.
     * @param time client socket timeout in milliseconds.
     * @see #getTimeout
      */
    public void setTimeout(int time) {
        if (time > 0)
            socketTimeout = time;
        else
            socketTimeout = 0;
        logger.log(Level.FINEST, "Set to {0}", socketTimeout);
    }

    /**
      * Returns the Client socket timeout in milliseconds.
     * @see #setTimeout
      */
    public int getTimeout() {
        return socketTimeout;
    }

    /** 
     * Sets max allowed login attempts.
     * @since 1.2
     * @see #getMaxAuthTry
     */
    public void setMaxAuthTry(int authTry) {
        maxAuthTry = authTry;
        logger.log(Level.FINEST, "Set to {0}", authTry);
    }

    /** 
     * Returns max allowed login attempts. Default is <code>5</code>.
     * @since 1.2
     * @see #setMaxAuthTry
     */
    public int getMaxAuthTry() {
        return maxAuthTry;
    }

    /** 
     * Sets message to be displayed when maximum allowed login 
     * attempts has reached.
     * Default is : -ERR Max Auth Try Reached
     * @see #getMaxAuthTryMsg
     */
    public void setMaxAuthTryMsg(String msg) {
        maxAuthTryMsg = msg;
        logger.log(Level.FINEST, "Set to {0}", msg);
    }

    /** 
     * Returns message to be displayed when maximum allowed login 
     * attempts has reached.
     * @see #getMaxAuthTryMsg
     */
    public String getMaxAuthTryMsg() {
        return maxAuthTryMsg;
    }

    /**
     * Sets timeout message. 
     * Default is : -ERR Timeout
     * @see #getTimeoutMsg
     */
    public void setTimeoutMsg(String msg) {
        timeoutMsg = msg;
        logger.log(Level.FINEST, "Set to {0}", msg);
    }

    /** 
     * Returns timeout message.
     * @see #setTimeoutMsg
     */
    public String getTimeoutMsg() {
        return timeoutMsg;
    }

    private TheClient initTheClient() {
        TheClient theClient = new TheClient();
        theClient.setServer(QuickServer.this);
        theClient.setTimeoutMsg(getTimeoutMsg());
        theClient.setMaxAuthTry(getMaxAuthTry()); //v1.2
        theClient.setMaxAuthTryMsg(getMaxAuthTryMsg());

        theClient.setClientEventHandler(clientEventHandler);
        theClient.setClientExtendedEventHandler(clientExtendedEventHandler); //v1.4.6
        theClient.setClientCommandHandler(clientCommandHandler);
        theClient.setClientObjectHandler(clientObjectHandler); //v1.2
        theClient.setClientBinaryHandler(clientBinaryHandler); //v1.4
        theClient.setClientWriteHandler(clientWriteHandler); //v1.4.5
        theClient.setAuthenticator(authenticator); //v1.3
        theClient.setClientAuthenticationHandler(clientAuthenticationHandler); //v1.4.6
        theClient.setTimeout(socketTimeout);
        theClient.setMaxConnectionMsg(maxConnectionMsg);
        theClient.setCommunicationLogging(getCommunicationLogging()); //v1.3.2
        return theClient;
    }

    public void run() {
        exceptionInRun = null;
        TheClient theClient = initTheClient();
        try {
            stopServer = false;

            closeAllPools();
            initAllPools();

            makeServerSocket();

            if (getServerBanner().length() > 0) {
                System.out.println(getServerBanner()); //print banner
            }

            setServiceState(Service.RUNNING); //v1.2

            processServerHooks(ServerHook.POST_STARTUP); //v1.3.3
            if (getBlockingMode() == false) {
                runNonBlocking(theClient);
                if (stopServer == true) {
                    logger.log(Level.FINEST, "Closing selector for {0}", getName());
                    selector.close();
                }
                return;
            } else {
                runBlocking(theClient);
            }
        } catch (BindException e) {
            exceptionInRun = e;
            logger.log(Level.SEVERE, "{0} BindException for Port {1} @ {2} : {3}",
                    new Object[] { getName(), getPort(), getBindAddr().getHostAddress(), e.getMessage() });
        } catch (javax.net.ssl.SSLException e) {
            exceptionInRun = e;
            logger.log(Level.SEVERE, "SSLException {0}", e);
            logger.log(Level.FINE, "StackTrace:\n{0}", MyString.getStackTrace(e));
        } catch (IOException e) {
            exceptionInRun = e;
            logger.log(Level.SEVERE, "IOError {0}", e);
            logger.log(Level.FINE, "StackTrace:\n{0}", MyString.getStackTrace(e));
        } catch (Exception e) {
            exceptionInRun = e;
            logger.log(Level.SEVERE, "Error {0}", e);
            logger.log(Level.FINE, "StackTrace:\n{0}", MyString.getStackTrace(e));
        } finally {
            if (getBlockingMode() == true) {
                logger.log(Level.WARNING, "Closing {0}", getName());
                try {
                    if (isClosed() == false) {
                        server.close();
                    }
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
                server = null;
                serverSocketChannel = null;

                setServiceState(Service.STOPPED);
                logger.log(Level.WARNING, "Closed {0}", getName());

                processServerHooks(ServerHook.POST_SHUTDOWN);
            } else if (getBlockingMode() == false && exceptionInRun != null) {
                logger.log(Level.WARNING, "Closing {0} - Had Error: {1}",
                        new Object[] { getName(), exceptionInRun });
                try {
                    if (isClosed() == false) {
                        if (serverSocketChannel != null)
                            serverSocketChannel.close();
                        if (server != null)
                            server.close();
                    }
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }

                server = null;
                serverSocketChannel = null;

                setServiceState(Service.STOPPED);
                logger.log(Level.WARNING, "Closed {0}", getName());

                processServerHooks(ServerHook.POST_SHUTDOWN);
            }
        }
    } //end of run

    /**
     * Sets the maximum number of client connection allowed.
     * @since 1.1
     * @see #getMaxConnection
     */
    public void setMaxConnection(long maxConnection) {
        if (getServiceState() == Service.SUSPENDED)
            suspendMaxConnection = maxConnection;
        else
            this.maxConnection = maxConnection;
        logger.log(Level.FINEST, "Set to {0}", maxConnection);
    }

    /** 
     * Returns the maximum number of client connection allowed.
     * @since 1.1
     * @see #setMaxConnection
     */
    public long getMaxConnection() {
        return maxConnection;
    }

    /** 
     * Returns number of clients connected.
     * @since 1.1
     */
    public long getClientCount() {
        if (clientHandlerPool != null) {
            try {
                return getClientHandlerPool().getNumActive();
            } catch (Exception e) {
                return 0;
            }
        }
        return 0;
    }

    /** 
     * Returns highest number of clients connected.
     * @since 2.1.0
     */
    public long getHighestActiveClientCount() {
        if (clientHandlerPool != null) {
            try {
                return ((QSObjectPool) getClientHandlerPool()).getHighestActiveCount();
            } catch (Exception e) {
                return 0;
            }
        }
        return 0;
    }

    /**
     * Sets the message to be sent to any new client connected after
     * maximum client connection has reached. 
     * Default is : <code>-ERR Server Busy. Max Connection Reached</code>
     * @since 1.1
     * @see #getMaxConnectionMsg
     */
    public void setMaxConnectionMsg(String maxConnectionMsg) {
        if (getServiceState() == Service.SUSPENDED)
            suspendMaxConnectionMsg = maxConnectionMsg;
        else
            this.maxConnectionMsg = maxConnectionMsg;
        logger.log(Level.FINEST, "Set to {0}", maxConnectionMsg);
    }

    /**
     * Returns the message to be sent to any new client connected 
     * after maximum client connection has reached.
     * @since 1.1
     * @see #setMaxConnectionMsg
     */
    public String getMaxConnectionMsg() {
        return maxConnectionMsg;
    }

    /**
     * Sets the Ip address to bind to. 
     * @param bindAddr argument can be used on a multi-homed host for a 
     * QuickServer that will only accept connect requests to one 
     * of its addresses. If not set, it will default accepting 
     * connections on any/all local addresses.
     * @exception java.net.UnknownHostException if no IP address for 
     * the host could be found
     * @since 1.1
     * @see #getBindAddr
     */
    public void setBindAddr(String bindAddr) throws UnknownHostException {
        ipAddr = InetAddress.getByName(bindAddr);
        logger.log(Level.FINEST, "Set to {0}", bindAddr);
    }

    /**
     * Returns the IP address binding to. 
     * @since 1.1
     * @see #setBindAddr
     */
    public InetAddress getBindAddr() {
        if (ipAddr == null) {
            try {
                ipAddr = InetAddress.getByName("0.0.0.0");
            } catch (Exception e) {
                logger.log(Level.WARNING, "Unable to create default ip(0.0.0.0) : {0}", e);
                throw new RuntimeException("Error: Unable to find servers own ip : " + e);
            }
        }
        return ipAddr;
    }

    /**
     * Sets the store of objects to QuickServer, it is an array of objects  
     * that main program or the class that created QuickServer passes to 
     * the QuickServer. 
     * @param storeObjects array of objects
     * @see #getStoreObjects
     * @since 1.1
     */
    public void setStoreObjects(Object[] storeObjects) {
        this.storeObjects = storeObjects;
    }

    /**
     * Returns store of objects from QuickServer, if nothing was set will
     * return <code>null</code>.
     * @see #setStoreObjects
     * @since 1.1
     */
    public Object[] getStoreObjects() {
        return storeObjects;
    }

    /** 
     * Set the port to run QSAdminServer on.
     * @since 1.2
     */
    public void setQSAdminServerPort(int port) {
        getQSAdminServer().getServer().setPort(port);
    }

    /** 
     * Returns the port to run QSAdminServer on.
     * @since 1.2
     */
    public int getQSAdminServerPort() {
        return getQSAdminServer().getServer().getPort();
    }

    /** 
     * Set the ClientAuthenticationHandler class of 
     * QSAdminServer that handles the authentication of a client.
     * @since 1.2
     */
    public void setQSAdminServerAuthenticator(String authenticator) {
        getQSAdminServer().getServer().setClientAuthenticationHandler(authenticator);
    }

    /** 
     * Returns the Authenticator or ClientAuthenticationHandler class of 
     * QSAdminServer that handles the authentication of a client.
     * @since 1.2
     */
    public String getQSAdminServerAuthenticator() {
        return getQSAdminServer().getServer().getAuthenticator();
    }

    /**
     * Starts QSAdminServer for this QuickServer.
     * @see org.quickserver.net.qsadmin.QSAdminServer
     * @param authenticator sets the ClientAuthenticationHandler class that 
     *   handles the authentication of a client, 
     *   if null uses {@link org.quickserver.net.qsadmin.Authenticator}.
     * @param port to run QSAdminServer on
      * @exception org.quickserver.net.AppException 
     *  if Server already running or if it could not load the classes
     *  [ClientCommandHandler, ClientAuthenticationHandler, ClientData].
     * @since 1.1
     */
    public void startQSAdminServer(int port, String authenticator) throws AppException {
        getQSAdminServer().setClientAuthenticationHandler(authenticator);
        getQSAdminServer().startServer(port);
    }

    /**
     * Starts QSAdminServer for this QuickServer.
     * @see org.quickserver.net.qsadmin.QSAdminServer
     * @since 1.2
     */
    public void startQSAdminServer() throws AppException {
        getQSAdminServer().startServer();
    }

    /**
     * Returns {@link QSAdminServer} associated with this QuickServer
     * @since 1.1
     */
    public QSAdminServer getQSAdminServer() {
        if (adminServer == null)
            adminServer = new QSAdminServer(QuickServer.this);
        return adminServer;
    }

    /**
     * Sets {@link QSAdminServer} associated with this QuickServer
     * @since 1.3.3
     */
    public void setQSAdminServer(QSAdminServer adminServer) {
        if (adminServer == null)
            this.adminServer = adminServer;
    }

    /** 
     * Returns the closed state of the QuickServer Socket.
     * @since 1.1
     */
    public boolean isClosed() {
        if (server == null)
            return true;
        return server.isClosed();
    }

    /** 
     * Returns the application logger associated with QuickServer.
     * If it was not set will return QuickServer's own logger.
     * @since 1.2
     */
    public Logger getAppLogger() {
        if (appLogger != null)
            return appLogger;
        return logger;
    }

    /** 
     * Sets the application logger associated with QuickServer
     * @since 1.2
     */
    public void setAppLogger(Logger appLogger) {
        this.appLogger = appLogger;
    }

    /**
      * Sets the ClientObjectHandler class that interacts with 
     * client sockets to handle java objects.
     * @param handler object the fully qualified name of the class that 
     *  implements {@link ClientObjectHandler}
     * @see #getClientObjectHandler
     * @since 1.2
      */
    public void setClientObjectHandler(String handler) {
        clientObjectHandlerString = handler;
        logger.log(Level.FINEST, "Set to {0}", handler);
    }

    /**
      * Returns the ClientObjectHandler class that interacts with 
     * client sockets.
     * @see #setClientObjectHandler
     * @since 1.2
      */
    public String getClientObjectHandler() {
        return clientObjectHandlerString;
    }

    /**
     * Sets the console log handler formatter.
     * @param formatter fully qualified name of the class that implements 
     * {@link java.util.logging.Formatter}
     * @since 1.2
     */
    public void setConsoleLoggingFormatter(String formatter)
            throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        if (formatter == null)
            return;
        consoleLoggingformatter = formatter;

        java.util.logging.Formatter conformatter = (java.util.logging.Formatter) getClass(formatter, true)
                .newInstance();
        Logger jdkLogger = Logger.getLogger("");
        Handler[] handlers = jdkLogger.getHandlers();
        for (int index = 0; index < handlers.length; index++) {
            if (ConsoleHandler.class.isInstance(handlers[index])) {
                handlers[index].setFormatter(conformatter);
            }
        }
        logger.log(Level.FINEST, "Set to {0}", formatter);
    }

    /**
     * Gets the console log handler formatter.
     * @since 1.3
     */
    public String getConsoleLoggingFormatter() {
        return consoleLoggingformatter;
    }

    /**
     * Sets the console log handler formater to 
     * {@link org.quickserver.util.logging.MiniFormatter}
     * @since 1.2
     */
    public void setConsoleLoggingToMini() {
        try {
            setConsoleLoggingFormatter("org.quickserver.util.logging.MiniFormatter");
        } catch (Exception e) {
            logger.log(Level.WARNING, "Setting to logging.MiniFormatter : {0}", e);
        }
    }

    /**
     * Sets the console log handler formater to 
     * {@link org.quickserver.util.logging.MicroFormatter}
     * @since 1.2
     */
    public void setConsoleLoggingToMicro() {
        try {
            setConsoleLoggingFormatter("org.quickserver.util.logging.MicroFormatter");
        } catch (Exception e) {
            logger.log(Level.WARNING, "Setting to MicroFormatter : {0}", e);
        }
    }

    /**
     * Sets the console log handler level.
     * @since 1.2
     */
    public void setConsoleLoggingLevel(Level level) {
        Logger rlogger = Logger.getLogger("");
        Handler[] handlers = rlogger.getHandlers();

        boolean isConsole = true;
        try {
            if (System.console() == null) {
                isConsole = false;
            }
        } catch (Throwable e) {
            //ignore
        }

        for (int index = 0; index < handlers.length; index++) {
            if (ConsoleHandler.class.isInstance(handlers[index])) {
                if (isConsole == false && level != Level.OFF) {
                    System.out.println("QuickServer: You do not have a console.. so turning console logger off..");
                    level = Level.OFF;
                }

                if (level == Level.OFF) {
                    logger.info("QuickServer: Removing console handler.. ");
                    rlogger.removeHandler(handlers[index]);

                    handlers[index].setLevel(level);
                    handlers[index].close();
                } else {
                    handlers[index].setLevel(level);
                }
            }
        }
        if (level == Level.SEVERE)
            consoleLoggingLevel = "SEVERE";
        else if (level == Level.WARNING)
            consoleLoggingLevel = "WARNING";
        else if (level == Level.INFO)
            consoleLoggingLevel = "INFO";
        else if (level == Level.CONFIG)
            consoleLoggingLevel = "CONFIG";
        else if (level == Level.FINE)
            consoleLoggingLevel = "FINE";
        else if (level == Level.FINER)
            consoleLoggingLevel = "FINER";
        else if (level == Level.FINEST)
            consoleLoggingLevel = "FINEST";
        else if (level == Level.OFF)
            consoleLoggingLevel = "OFF";
        else
            consoleLoggingLevel = "UNKNOWN";

        logger.log(Level.FINE, "Set to {0}", level);
    }

    /**
     * Gets the console log handler level.
     * @since 1.3
     */
    public String getConsoleLoggingLevel() {
        return consoleLoggingLevel;
    }

    /**
     * Sets the level for all log handlers.
     * @since 1.3.1
     */
    public void setLoggingLevel(Level level) {
        Logger rlogger = Logger.getLogger("");
        Handler[] handlers = rlogger.getHandlers();
        for (int index = 0; index < handlers.length; index++) {
            handlers[index].setLevel(level);
        }

        if (level == Level.SEVERE)
            loggingLevel = "SEVERE";
        else if (level == Level.WARNING)
            loggingLevel = "WARNING";
        else if (level == Level.INFO)
            loggingLevel = "INFO";
        else if (level == Level.CONFIG)
            loggingLevel = "CONFIG";
        else if (level == Level.FINE)
            loggingLevel = "FINE";
        else if (level == Level.FINER)
            loggingLevel = "FINER";
        else if (level == Level.FINEST)
            loggingLevel = "FINEST";
        else if (level == Level.OFF)
            loggingLevel = "OFF";
        else
            loggingLevel = "UNKNOWN";

        consoleLoggingLevel = loggingLevel;

        logger.log(Level.FINE, "Set to {0}", level);
    }

    //*** Start of Service interface methods
    /**
     * Returns service error if any.
     * @since 1.4.7
     */
    public Throwable getServiceError() {
        return serviceError;
    }

    /**
     * Initialise and create the service.
     * @param param of the xml configuration file.
     * @since 1.2
     */
    public synchronized boolean initService(Object param[]) {
        serviceError = null;
        try {
            initServer(param);
        } catch (Exception e) {
            serviceError = e;
            return false;
        }
        return true;
    }

    /**
     * Initialise and create the service.
     * @param qsConfig QuickServerConfig object.
     * @since 1.4.6
     */
    public synchronized boolean initService(QuickServerConfig qsConfig) {
        serviceError = null;
        try {
            initServer(qsConfig);
        } catch (Exception e) {
            serviceError = e;
            return false;
        }
        return true;
    }

    /**
     * Start the service.
     * @return true if serivce was stopped from Running state.
     * @since 1.2
     */
    public boolean startService() {
        serviceError = null;
        if (getServiceState() == Service.RUNNING)
            return false;
        try {
            startServer();
        } catch (AppException e) {
            serviceError = e;
            return false;
        }
        return true;
    }

    /**
     * Stop the service.
     * @return true if service was stopped from Running state.
     * @since 1.2
     */
    public boolean stopService() {
        serviceError = null;
        if (getServiceState() == Service.STOPPED)
            return false;
        try {
            stopServer();
            clearAllPools();
        } catch (AppException e) {
            serviceError = e;
            return false;
        } catch (Exception e) {
            serviceError = e;
            return false;
        }
        return true;
    }

    /**
     * Suspends the service.
     * @return true if service was suspended from resumed state.
     * @since 1.2
     */
    public boolean suspendService() {
        serviceError = null;
        if (getServiceState() == Service.RUNNING) {
            suspendMaxConnection = maxConnection;
            suspendMaxConnectionMsg = maxConnectionMsg;
            maxConnection = 0;
            maxConnectionMsg = "Service is suspended.";
            setServiceState(Service.SUSPENDED);
            logger.log(Level.INFO, "Service {0} is suspended.", getName());
            return true;
        }
        return false;
    }

    /**
     * Resume the service.
     * @return true if service was resumed from suspended state.
     * @since 1.2
     */
    public boolean resumeService() {
        serviceError = null;
        if (getServiceState() == Service.SUSPENDED) {
            maxConnection = suspendMaxConnection;
            maxConnectionMsg = suspendMaxConnectionMsg;
            setServiceState(Service.RUNNING);
            logger.log(Level.INFO, "Service {0} resumed.", getName());
            return true;
        }
        return false;
    }

    /** 
     * Information about the service.
     * @since 1.2
     */
    public String info() {
        serviceError = null;
        StringBuilder buf = new StringBuilder();
        buf.append(getName()).append("\n");
        buf.append(getBindAddr().getHostAddress()).append(" ");
        buf.append(getPort()).append("\n");
        return buf.toString();
    }
    // *** End of Service interface methods

    /**
     * Initialise and create the server.
     * @param param of the xml configuration file.
     * @exception AppException if QuickServerConfig creation failed from the xml config file.
     * @since 1.4.7
     */
    public synchronized void initServer(Object param[]) throws AppException {
        QuickServerConfig qsConfig = null;
        try {
            qsConfig = ConfigReader.read((String) param[0]);
        } catch (Exception e) {
            logger.log(Level.SEVERE, "Could not init server from xml file {0} : {1}",
                    new Object[] { new File((String) param[0]).getAbsolutePath(), e });
            throw new AppException("Could not init server from xml file", e);
        }
        initServer(qsConfig);
    }

    /**
     * Initialise and create the service.
     * @param qsConfig QuickServerConfig object.
     * @since 1.4.7
     */
    public synchronized void initServer(QuickServerConfig qsConfig) throws AppException {
        setConfig(qsConfig);
        try {
            configQuickServer();

            loadApplicationClasses();

            //start InitServerHooks
            InitServerHooks ish = getConfig().getInitServerHooks();
            if (ish != null) {
                Iterator iterator = ish.iterator();
                String initServerHookClassName = null;
                Class initServerHookClass = null;
                InitServerHook initServerHook = null;
                while (iterator.hasNext()) {
                    initServerHookClassName = (String) iterator.next();
                    initServerHookClass = getClass(initServerHookClassName, true);
                    initServerHook = (InitServerHook) initServerHookClass.newInstance();

                    logger.log(Level.INFO, "Loaded init server hook: {0}", initServerHookClassName);
                    logger.log(Level.FINE, "Init server hook info: {0}", initServerHook.info());
                    initServerHook.handleInit(QuickServer.this);
                }
            }
        } catch (Exception e) {
            logger.log(Level.SEVERE, "Could not load init server hook: {0}", e);
            logger.log(Level.WARNING, "StackTrace:\n{0}", MyString.getStackTrace(e));
            throw new AppException("Could not load init server hook", e);
        }
        setServiceState(Service.INIT);
        logger.log(Level.FINEST, "\r\n{0}", MyString.getSystemInfo(getVersion()));
    }

    /** 
     * Returns the state of the process 
     * As any constant of {@link Service} interface.
     * @since 1.2
     */
    public int getServiceState() {
        return serviceState;
    }

    /** 
     * Sets the state of the process 
     * As any constant of {@link Service} interface.
     * @since 1.2
     */
    public void setServiceState(int state) {
        serviceState = state;
    }

    private void configConsoleLoggingLevel(QuickServer qs, String temp) {
        if (temp.equals("SEVERE"))
            qs.setConsoleLoggingLevel(Level.SEVERE);
        else if (temp.equals("WARNING"))
            qs.setConsoleLoggingLevel(Level.WARNING);
        else if (temp.equals("INFO"))
            qs.setConsoleLoggingLevel(Level.INFO);
        else if (temp.equals("CONFIG"))
            qs.setConsoleLoggingLevel(Level.CONFIG);
        else if (temp.equals("FINE"))
            qs.setConsoleLoggingLevel(Level.FINE);
        else if (temp.equals("FINER"))
            qs.setConsoleLoggingLevel(Level.FINER);
        else if (temp.equals("FINEST"))
            qs.setConsoleLoggingLevel(Level.FINEST);
        else if (temp.equals("OFF"))
            qs.setConsoleLoggingLevel(Level.OFF);
        else
            logger.log(Level.WARNING, "unknown level {0}", temp);
    }

    /**
     * Configures QuickServer based on the passed QuickServerConfig object.
     * @since 1.2
     */
    public void configQuickServer(QuickServerConfig config) throws Exception {
        QuickServer qs = QuickServer.this;
        qs.setConfig(config); //v1.3
        qs.setBasicConfig(config);
        String temp = config.getConsoleLoggingLevel();
        configConsoleLoggingLevel(qs, temp);
        temp = null;

        qs.setConsoleLoggingFormatter(config.getConsoleLoggingFormatter());

        qs.setName(config.getName());
        qs.setPort(config.getPort());
        qs.setClientEventHandler(config.getClientEventHandler());
        qs.setClientCommandHandler(config.getClientCommandHandler());
        if (config.getAuthenticator() != null)
            qs.setAuthenticator(config.getAuthenticator()); //v1.3
        else if (config.getClientAuthenticationHandler() != null)
            qs.setClientAuthenticationHandler(config.getClientAuthenticationHandler()); //v1.4.6
        qs.setClientObjectHandler(config.getClientObjectHandler());
        qs.setClientBinaryHandler(config.getClientBinaryHandler());//v1.4
        qs.setClientWriteHandler(config.getClientWriteHandler());//v1.4.5
        qs.setClientData(config.getClientData());
        qs.setClientExtendedEventHandler(config.getClientExtendedEventHandler());
        qs.setDefaultDataMode(config.getDefaultDataMode());//v1.4.6
        qs.setServerBanner(config.getServerBanner());
        qs.setTimeout(config.getTimeout());
        qs.setMaxAuthTry(config.getMaxAuthTry());
        qs.setMaxAuthTryMsg(config.getMaxAuthTryMsg());
        qs.setTimeoutMsg(config.getTimeoutMsg());
        qs.setMaxConnection(config.getMaxConnection());
        qs.setMaxConnectionMsg(config.getMaxConnectionMsg());
        qs.setBindAddr(config.getBindAddr());
        //v1.3.2
        qs.setCommunicationLogging(config.getCommunicationLogging());
        //v1.3.3
        qs.setSecurityManagerClass(config.getSecurityManagerClass());
        qs.setAccessConstraintConfig(config.getAccessConstraintConfig());
        temp = config.getApplicationJarPath();
        if (temp != null) {
            File ajp = new File(temp);
            if (ajp.isAbsolute() == false) {
                temp = config.getConfigFile();
                ajp = new File(temp);
                temp = ajp.getParent() + File.separatorChar + config.getApplicationJarPath();
                config.setApplicationJarPath(temp);
                temp = null;
            }
            qs.setApplicationJarPath(config.getApplicationJarPath());
            //set path also to QSAdmin
            if (config.getQSAdminServerConfig() != null) {
                getQSAdminServer().getServer().setApplicationJarPath(config.getApplicationJarPath());
            }
        }
        qs.setServerHooks(config.getServerHooks());
        qs.setSecure(config.getSecure());
    }

    /**
     * Configures QSAdminServer based on the passed QuickServerConfig object.
     * @since 1.2
     */
    public void configQuickServer(QSAdminServerConfig config) throws Exception {
        QuickServer qs = getQSAdminServer().getServer();
        qs.setBasicConfig(config);

        //set the Logging Level to same as main QS
        String temp = getConsoleLoggingLevel();//config.getConsoleLoggingLevel();
        configConsoleLoggingLevel(qs, temp);

        //set the Logging Formatter to same as main QS
        //qs.setConsoleLoggingFormatter(config.getConsoleLoggingFormatter());
        qs.setConsoleLoggingFormatter(getConsoleLoggingFormatter());

        qs.setClientEventHandler(config.getClientEventHandler());//v1.4.6
        qs.setClientCommandHandler(config.getClientCommandHandler());
        qs.setName(config.getName());
        qs.setPort(config.getPort());
        if (config.getAuthenticator() != null)
            qs.setAuthenticator(config.getAuthenticator()); //v1.3
        else if (config.getClientAuthenticationHandler() != null)
            qs.setClientAuthenticationHandler(config.getClientAuthenticationHandler()); //v1.4.6
        qs.setClientObjectHandler(config.getClientObjectHandler());
        qs.setClientBinaryHandler(config.getClientBinaryHandler());//v1.4
        qs.setClientWriteHandler(config.getClientWriteHandler());//v1.4.5
        qs.setClientData(config.getClientData());
        qs.setClientExtendedEventHandler(config.getClientExtendedEventHandler());//v1.4.6
        qs.setDefaultDataMode(config.getDefaultDataMode());//v1.4.6
        qs.setServerBanner(config.getServerBanner());
        qs.setTimeout(config.getTimeout());
        qs.setMaxAuthTry(config.getMaxAuthTry());
        qs.setMaxAuthTryMsg(config.getMaxAuthTryMsg());
        qs.setTimeoutMsg(config.getTimeoutMsg());
        qs.setMaxConnection(config.getMaxConnection());
        qs.setMaxConnectionMsg(config.getMaxConnectionMsg());
        qs.setBindAddr(config.getBindAddr());
        //v1.3.2
        qs.setCommunicationLogging(config.getCommunicationLogging());
        getQSAdminServer().setCommandPlugin(config.getCommandPlugin());
        //v1.3.2
        if (config.getCommandShellEnable().equals("true"))
            getQSAdminServer().setShellEnable(true);
        getQSAdminServer().setPromptName(config.getCommandShellPromptName());
        //v1.3.3
        qs.setAccessConstraintConfig(config.getAccessConstraintConfig());
        qs.setServerHooks(config.getServerHooks());
        qs.setSecure(config.getSecure());
    }

    /**
     * Configures QSAdminServer and QuickServer based on the 
     * internal QuickServerConfig object.
     * @since 1.3
     */
    public void configQuickServer() throws Exception {
        configQuickServer(getConfig());
        if (getConfig().getQSAdminServerConfig() != null) {
            configQuickServer(getConfig().getQSAdminServerConfig());
        }
    }

    /**
     * Usage: QuickServer [-options]<br/>
     * Where options include:<br/>
     *   -about      Opens About Dialogbox<br/>
     *   -load <xml_config_file> [options]   Loads the server from xml file.
     * where options include:
     *    -fullXML2File <new_file_name>
     */
    public static void main(String args[]) {
        try {
            if (args.length >= 1) {
                if (args[0].equals("-about")) {
                    org.quickserver.net.server.gui.About.main(null);
                } else if (args[0].equals("-load") && args.length >= 2) {
                    QuickServer qs = QuickServer.load(args[1]);
                    if (qs != null)
                        handleOptions(args, qs);
                } else {
                    System.out.println(printUsage());
                }
            } else {
                System.out.println(printUsage());
                org.quickserver.net.server.gui.About.showAbout();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Loads the server from the xml file name passed.
     * @since 1.4.7
     */
    public static QuickServer load(String xml) throws AppException {
        QuickServer qs = new QuickServer();
        Object config[] = new Object[] { xml };
        qs.initServer(config);
        qs.startServer();
        if (qs.getConfig().getQSAdminServerConfig() != null) {
            qs.startQSAdminServer();
        }
        return qs;
    }

    /** Prints usage */
    private static String printUsage() {
        StringBuilder sb = new StringBuilder();
        sb.append("QuickServer - Java library/framework for creating robust multi-client TCP servers.\n");
        sb.append("Copyright (C) QuickServer.org\n\n");
        sb.append("Usage: QuickServer [-options]\n");
        sb.append("Where options include:\n");
        sb.append("  -about\t" + "Opens About Dialog box\n");
        sb.append("  -load <xml_config_file> [load-options]\t" + "Loads the server from xml file.\n");
        sb.append("  Where load-options include:\n");
        sb.append("     -fullXML2File <file_name>\t"
                + "Dumps the Full XML configuration of the QuickServer loaded.\n");
        return sb.toString();
    }

    private static void handleOptions(String args[], QuickServer quickserver) {
        if (args.length < 3)
            return;

        if (args[2].equals("-fullXML2File") && args.length >= 4) {
            File file = new File(args[3]);
            logger.log(Level.INFO, "Writing full xml configuration to file: {0}", file.getAbsolutePath());
            try {
                TextFile.write(file, quickserver.getConfig().toXML(null));
            } catch (Exception e) {
                logger.log(Level.WARNING, "Error writing full xml configuration: {0}", e);
            }
        }
    }

    /**
     * Cleans all Object and Thread pools
     * @since 1.3
     */
    public void clearAllPools() throws Exception {
        try {
            if (pool != null)
                getClientPool().clear();
            if (clientHandlerPool != null)
                getClientHandlerPool().clear();
            if (getClientDataPool() != null)
                getClientDataPool().clear();
            if (getDBPoolUtil() != null)
                getDBPoolUtil().clean();
            if (byteBufferPool != null)
                getByteBufferPool().clear();
        } catch (Exception e) {
            logger.log(Level.WARNING, "Error: {0}", e);
            throw e;
        }
    }

    /**
     * Closes all Object and Thread pools
     * @since 1.3
     */
    public void closeAllPools() throws Exception {
        if (pool == null && clientHandlerPool == null && getClientDataPool() == null && getDBPoolUtil() == null
                && byteBufferPool == null) {
            return;
        }
        logger.log(Level.FINE, "Closing pools for {0}", getName());
        try {
            if (pool != null && PoolHelper.isPoolOpen(getClientPool().getObjectPool())) {
                logger.finer("Closing ClientThread pool.");
                getClientPool().close();
            }
            if (clientHandlerPool != null && PoolHelper.isPoolOpen(getClientHandlerPool())) {
                logger.finer("Closing ClientHandler pool.");
                getClientHandlerPool().close();
            }
            if (getClientDataPool() != null && PoolHelper.isPoolOpen(getClientDataPool())) {
                logger.finer("Closing ClientData pool.");
                getClientDataPool().close();
            }
            if (getDBPoolUtil() != null) {
                logger.finer("Closing DB pool.");
                getDBPoolUtil().clean();
            }
            if (byteBufferPool != null && PoolHelper.isPoolOpen(getByteBufferPool())) {
                logger.finer("Closing ByteBuffer pool.");
                getByteBufferPool().close();
            }
            logger.log(Level.FINE, "Closed pools for {0}", getName());
        } catch (Exception e) {
            logger.log(Level.WARNING, "Error closing pools for {0}: {1}", new Object[] { getName(), e });
            throw e;
        }
    }

    /**
     * Initialise all Object and Thread pools.
     * @since 1.3
     */
    public void initAllPools() throws Exception {
        logger.fine("Creating pools");
        if (getBlockingMode() == false) {
            makeByteBufferPool(getBasicConfig().getObjectPoolConfig().getByteBufferObjectPoolConfig());
        }

        makeClientPool(getBasicConfig().getObjectPoolConfig().getThreadObjectPoolConfig());

        makeClientHandlerPool(getBasicConfig().getObjectPoolConfig().getClientHandlerObjectPoolConfig());

        //check if client data is poolable
        if (clientDataClass != null) {
            try {
                clientData = (ClientData) clientDataClass.newInstance();
                if (PoolableObject.class.isInstance(clientData) == true) {
                    PoolableObject po = (PoolableObject) clientData;
                    if (po.isPoolable() == true) {
                        makeClientDataPool(po.getPoolableObjectFactory(),
                                getBasicConfig().getObjectPoolConfig().getClientDataObjectPoolConfig());
                    } else {
                        clientDataPool = null;
                        logger.fine("ClientData is not poolable!");
                    }
                }
            } catch (Exception e) {
                logger.log(Level.WARNING, "Error: {0}", e);
                throw e;
            }
        }

        try {
            makeDBObjectPool();
        } catch (Exception e) {
            logger.log(Level.WARNING, "Error in makeDBObjectPool() : {0}", e);
            logger.log(Level.FINE, "StackTrace:\n{0}", MyString.getStackTrace(e));
            throw e;
        }
        logger.fine("Created pools");
    }

    /**
     * Returns {@link org.quickserver.util.pool.thread.ClientPool} class that 
     * managing the pool of threads for handling clients.
     * @exception IllegalStateException if pool is not created yet.
     * @since 1.3
     */
    public ClientPool getClientPool() {
        if (pool == null)
            throw new IllegalStateException("No ClientPool available yet!");
        return pool;
    }

    /** 
     * Makes the pool of ClientHandler
     * @since 1.3
     */
    private void makeClientHandlerPool(PoolConfig opConfig) throws Exception {
        logger.finer("Creating ClientHandler pool");
        PoolableObjectFactory factory = new ClientHandlerObjectFactory(getBlockingMode());
        clientHandlerPool = poolManager.makeClientHandlerPool(factory, opConfig);
        poolManager.initPool(clientHandlerPool, opConfig);
        clientHandlerPool = makeQSObjectPool(clientHandlerPool);
        clientIdentifier.setClientHandlerPool((QSObjectPool) clientHandlerPool);
    }

    /**
     * Returns ObjectPool of {@link org.quickserver.net.server.ClientHandler} 
     * class.
     * @exception IllegalStateException if pool is not created yet.
     * @since 1.3
     */
    public ObjectPool getClientHandlerPool() {
        if (clientHandlerPool == null)
            throw new IllegalStateException("No ClientHandler Pool available yet!");
        return clientHandlerPool;
    }

    /**
     * Sets the configuration of the QuickServer.
     * @since 1.3
     */
    public void setConfig(QuickServerConfig config) {
        this.config = config;
    }

    /**
     * Returns the configuration of the QuickServer.
     * @since 1.3
     */
    public QuickServerConfig getConfig() {
        return config;
    }

    /** 
     * Makes the pool of ClientData
     * @since 1.3
     */
    private void makeClientDataPool(PoolableObjectFactory factory, PoolConfig opConfig) throws Exception {
        logger.finer("Creating ClientData pool");
        clientDataPool = poolManager.makeClientDataPool(factory, opConfig);
        poolManager.initPool(clientDataPool, opConfig);
        clientDataPool = makeQSObjectPool(clientDataPool);
    }

    /**
     * Returns ObjectPool of {@link org.quickserver.net.server.ClientData} 
     * class. If ClientData was not poolable will return  null.
     * @since 1.3
     */
    public ObjectPool getClientDataPool() {
        return clientDataPool;
    }

    /**
     * Returns {@link org.quickserver.sql.DBPoolUtil} object if
     * {@link org.quickserver.util.xmlreader.DBObjectPoolConfig} was set.
     * @return DBPoolUtil object if object could be loaded, else will return <code>null</code>
     * @since 1.3
     */
    public DBPoolUtil getDBPoolUtil() {
        return dBPoolUtil;
    }

    /**
     * Sets {@link org.quickserver.util.xmlreader.DBObjectPoolConfig}
     * @since 1.3
     */
    public void setDBObjectPoolConfig(DBObjectPoolConfig dBObjectPoolConfig) {
        getConfig().setDBObjectPoolConfig(dBObjectPoolConfig);
    }

    /** 
     * Makes the pool of Database Objects
     * @since 1.3
     */
    private void makeDBObjectPool() throws Exception {
        if (getConfig().getDBObjectPoolConfig() != null) {
            logger.fine("Creating DBObject Pool");
            //logger.finest("Got:\n"+getConfig().getDBObjectPoolConfig().toXML(null));
            Class dbPoolUtilClass = getClass(getConfig().getDBObjectPoolConfig().getDbPoolUtil(), true);
            dBPoolUtil = (DBPoolUtil) dbPoolUtilClass.newInstance();
            dBPoolUtil.setDatabaseConnections(
                    getConfig().getDBObjectPoolConfig().getDatabaseConnectionSet().iterator());
            dBPoolUtil.initPool();
        }
    }

    /**
     * Tries to find the Client by the Id passed.
     * <p>
     * Note: This command is an expensive so do use it limitedly and
     * cache the returned object. But before you start sending message to the 
     * cached object do validate that ClientHandler with you is currently 
     * connected and is pointing to the same clinet has it was before.
     * This can be done as follows. <pre>
    foundClientHandler.isConnected(); //this method will through SocketException if not connected
    Date newTime = foundClientHandler.getClientConnectedTime();
    if(oldCachedTime!=newTime) {
       //Client had disconnected and ClientHandler was reused for
       //someother client, so write code to again find ur client
       foundClientHandler = handler.getServer().findFirstClientById("friendsid");
       ...
    }</pre>
     * </p>
     * @see ClientIdentifiable
     * @return ClientHandler object if client was found else <code>null</code>
     * @since 1.3.1
     */
    public ClientHandler findFirstClientById(String id) {
        return clientIdentifier.findFirstClientById(id);
    }

    /**
     * Returns an iterator containing all the 
     * {@link org.quickserver.net.server.ClientHandler} that
     * are currently handling clients. 
     * It is recommended not to change the collection under an iterator. 
     *
     * It is imperative that the user manually synchronize on the returned collection 
     * when iterating over it: 
     * <code><pre>
    Eg:
        
    ClientData foundClientData = null;
    Object syncObj = quickserver.getClientIdentifier().getObjectToSynchronize();
    synchronized(syncObj) {   
       Iterator iterator = quickserver.findAllClient();
       while(iterator.hasNext()) {
     foundClientHandler = (ClientHandler) iterator.next();
     ....
       }
    }
        
    //OR
        
    ClientData foundClientData = null;
    ClientIdentifier clientIdentifier = quickserver.getClientIdentifier();
    synchronized(clientIdentifier.getObjectToSynchronize()) {   
       Iterator iterator = clientIdentifier.findAllClient();
       while(iterator.hasNext()) {
     foundClientHandler = (ClientHandler) iterator.next();
     ....
       }
    }
    </code></pre>
     * @since 1.3.1
     */
    public Iterator findAllClient() {
        return clientIdentifier.findAllClient();
    }

    /**
     * Tries to find the Client by the matching pattern passed to the Id.
     * <p>
     * Note: This command is an expensive so do use it limitedly and
     * cache the returned object. But before you start sending message to the 
     * cached object do validate that ClientHandler with you is currently 
     * connected and is pointing to the same client has it was before.
     * This can be done as follows. <pre>
    foundClientHandler.isConnected(); //this method will through SocketException if not connected
    Date newTime = foundClientHandler.getClientConnectedTime();
    if(oldCachedTime!=newTime) {
       //Client had disconnected and ClientHandler was reused for
       //someother client, so write code to again find ur client
       foundClientHandler = handler.getServer().findFirstClientById("friendsid");
       ...
    }</pre>
     * </p>
     * @see ClientIdentifiable
     * @return ClientHandler object if client was found else <code>null</code>
     * @since 1.3.2
     */
    public Iterator findAllClientById(String pattern) {
        return clientIdentifier.findAllClientById(pattern);
    }

    /**
     * Tries to find the Client by the Key passed.
     * <p>
     * Note: This command is an expensive so do use it limitedly and
     * cache the returned object. But before you start sending message to the 
     * cached object do validate that ClientHandler with you is currently 
     * connected and is pointing to the same client has it was before.
     * This can be done as follows. <pre>
    foundClientHandler.isConnected(); //this method will through SocketException if not connected
    Date newTime = foundClientHandler.getClientConnectedTime();
    if(oldCachedTime!=newTime) {
       //Client had disconnected and ClientHandler was reused for
       //someother client, so write code to again find ur client
       foundClientHandler = handler.getServer().findClientByKey("friendskey");
       ...
    }</pre>
     * </p>
     * @see ClientIdentifiable
     * @return ClientHandler object if client was found else <code>null</code>
     * @since 1.3.1
     */
    public ClientHandler findClientByKey(String key) {
        return clientIdentifier.findClientByKey(key);
    }

    /**
     * Tries to find the Client by the matching pattern passed to the key.
     * <p>
     * Note: This command is an expensive so do use it limitedly and
     * cache the returned object. But before you start sending message to the 
     * cached object do validate that ClientHandler with you is currently 
     * connected and is pointing to the same client has it was before.
     * This can be done as follows. <pre>
    foundClientHandler.isConnected(); //this method will through SocketException if not connected
    Date newTime = foundClientHandler.getClientConnectedTime();
    if(oldCachedTime!=newTime) {
       //Client had disconnected and ClientHandler was reused for
       //some other client, so write code to again find ur client
       foundClientHandler = handler.getServer().findFirstClientByKey("friendsid");
       ...
    }</pre>
     * </p>
     * @see ClientIdentifiable
     * @return ClientHandler object if client was found else <code>null</code>
     * @since 1.4
     */
    public Iterator findAllClientByKey(String pattern) {
        return clientIdentifier.findAllClientByKey(pattern);
    }

    /**
     * Sets next client has a trusted client. 
     * <p>This will skip any authentication and will not set any timeout.</p>
     * @since 1.3.2
     */
    public void nextClientIsTrusted() {
        setSkipValidation(true);
    }

    /**
     * @since 1.3.2
     */
    private synchronized boolean getSkipValidation() {
        return skipValidation;
    }

    /**
     * @since 1.3.2
     */
    private synchronized void setSkipValidation(boolean validation) {
        skipValidation = validation;
    }

    /**
     * Sets the communication logging flag.
     * @see #getCommunicationLogging
     * @since 1.3.2
     */
    public void setCommunicationLogging(boolean communicationLogging) {
        this.communicationLogging = communicationLogging;
    }

    /**
     * Returns the communication logging flag.
     * @see #setCommunicationLogging
     * @since 1.3.2
     */
    public boolean getCommunicationLogging() {
        return communicationLogging;
    }

    /**
     * Sets the SecurityManager class
     * @param securityManagerClass the fully qualified name of the class 
     * that extends {@link java.lang.SecurityManager}.
     * @see #getSecurityManagerClass
     * @since 1.3.3
     */
    public void setSecurityManagerClass(String securityManagerClass) {
        if (securityManagerClass != null)
            this.securityManagerClass = securityManagerClass;
    }

    /**
     * Returns the SecurityManager class
     * @see #setSecurityManagerClass
     * @since 1.3.3
     */
    public String getSecurityManagerClass() {
        return securityManagerClass;
    }

    public SecurityManager getSecurityManager() throws AppException {
        if (getSecurityManagerClass() == null)
            return null;
        SecurityManager sm = null;
        try {
            sm = (SecurityManager) getClass(getSecurityManagerClass(), true).newInstance();
        } catch (ClassNotFoundException e) {
            throw new AppException(e.getMessage());
        } catch (InstantiationException e) {
            throw new AppException(e.getMessage());
        } catch (IllegalAccessException e) {
            throw new AppException(e.getMessage());
        }
        return sm;
    }

    /**
     * Sets the Access constraints
     * @since 1.3.3
     */
    public void setAccessConstraintConfig(AccessConstraintConfig accessConstraintConfig) {
        this.accessConstraintConfig = accessConstraintConfig;
    }

    /**
     * Returns Access constraints if present else <code>null</code>.
     * @since 1.3.3
     */
    public AccessConstraintConfig getAccessConstraintConfig() {
        return accessConstraintConfig;
    }

    /**
     * Sets the classloader to be used to load the dynamically resolved 
     * classes
     * @since 1.3.3
     */
    public void setClassLoader(ClassLoader classLoader) {
        this.classLoader = classLoader;
        Thread.currentThread().setContextClassLoader(classLoader);
    }

    /**
     * Gets the classloader used to load the dynamically resolved 
     * classes.
     * @since 1.4.6
     */
    public ClassLoader getClassLoader() {
        return classLoader;
    }

    /**
     * Utility method to load a class
     * @since 1.3.3
     */
    public Class getClass(String name, boolean reload) throws ClassNotFoundException {
        if (name == null)
            throw new IllegalArgumentException("Class name can't be null!");
        logger.log(Level.FINEST, "Class: {0}, reload: {1}", new Object[] { name, reload });
        if (reload == true && classLoader != null) {
            return classLoader.loadClass(name);
        } else if (reload == true && classLoader == null && this.getClass().getClassLoader() != null) {
            return this.getClass().getClassLoader().loadClass(name);
        } else if (reload == false && classLoader != null) {
            return Class.forName(name, true, classLoader);
        } else /*if(reload==false && classLoader==null)*/ {
            return Class.forName(name, true, this.getClass().getClassLoader());
        }
    }

    /**
    * Sets the applications jar/s path. This can be either absolute or
    * relative(to config file) path to the jar file or the directory containing 
    * the jars needed by the application.
    * @see #getApplicationJarPath
    * @since 1.3.3
    */
    protected void setApplicationJarPath(String applicationJarPath) {
        this.applicationJarPath = applicationJarPath;
    }

    /**
     * Returns the applications jar/s path. This can be either absolute or
     * relative(to config file) path to the jar file or the directory containing the 
     * jars needed by the application.
     * @see #setApplicationJarPath
     * @since 1.3.3
     */
    public String getApplicationJarPath() {
        return applicationJarPath;
    }

    /**
     * Sets the ServerHooks
     * @since 1.3.3
     */
    public void setServerHooks(ServerHooks serverHooks) {
        this.serverHooks = serverHooks;
    }

    /**
     * Returns ServerHooks if present else <code>null</code>.
     * @since 1.3.3
     */
    public ServerHooks getServerHooks() {
        if (serverHooks == null)
            serverHooks = new ServerHooks();
        return serverHooks;
    }

    /**
     * @since 1.3.3
     */
    private void loadServerHooksClasses() {
        if (getServerHooks() == null)
            return;
        listOfServerHooks = new ArrayList();
        ServerHook serverHook = null;
        String serverHookClassName = null;
        Class serverHookClass = null;

        //add system hooks
        serverHook = new GhostSocketReaper();
        serverHook.initHook(QuickServer.this);
        listOfServerHooks.add(serverHook);
        ghostSocketReaper = (GhostSocketReaper) serverHook;

        //add user hooks if any
        Iterator iterator = getServerHooks().iterator();
        while (iterator.hasNext()) {
            serverHookClassName = (String) iterator.next();
            try {
                serverHookClass = getClass(serverHookClassName, true);
                serverHook = (ServerHook) serverHookClass.newInstance();
                serverHook.initHook(QuickServer.this);
                listOfServerHooks.add(serverHook);
                logger.log(Level.INFO, "Loaded server hook: {0}", serverHookClassName);
                logger.log(Level.FINE, "Server hook info: {0}", serverHook.info());
            } catch (Exception e) {
                logger.log(Level.WARNING, "Could not load server hook [{0}]: {1}",
                        new Object[] { serverHookClassName, e });
                logger.log(Level.FINE, "StackTrace:\n{0}", MyString.getStackTrace(e));
            }
        } //end of while
    }

    /**
     * @since 1.3.3
     */
    protected void processServerHooks(int event) {
        if (listOfServerHooks == null) {
            logger.warning("listOfServerHooks was null!");
            return;
        }
        ServerHook serverHook = null;
        boolean result = false;
        Iterator iterator = listOfServerHooks.iterator();

        String hooktype = "UNKNOWN";
        switch (event) {
        case ServerHook.PRE_STARTUP:
            hooktype = "PRE_STARTUP";
            break;
        case ServerHook.POST_STARTUP:
            hooktype = "POST_STARTUP";
            break;
        case ServerHook.PRE_SHUTDOWN:
            hooktype = "PRE_SHUTDOWN";
            break;
        case ServerHook.POST_SHUTDOWN:
            hooktype = "POST_SHUTDOWN";
            break;
        }

        while (iterator.hasNext()) {
            serverHook = (ServerHook) iterator.next();
            try {
                result = serverHook.handleEvent(event);
            } catch (Exception e) {
                result = false;
                logger.log(Level.WARNING, "Error invoking {0} hook [{1}]: {2}",
                        new Object[] { hooktype, serverHook.getClass().getName(), e.getMessage() });
            }
            logger.log(Level.FINE, "Invoked {0} hook [{1}] was: {2}",
                    new Object[] { hooktype, serverHook.getClass().getName(), result });
        }
    }

    /**
     * Creates and returns a copy of this object.
     * @since 1.3.3
     */
    public Object clone() {
        Object object = null;
        try {
            object = super.clone();
            QuickServer _qs = (QuickServer) object;
            _qs.setQSAdminServer(new QSAdminServer(_qs));
        } catch (CloneNotSupportedException e) {
            logger.log(Level.WARNING, "Error cloning : {0}", e);//should not happ
        }
        return object;
    }

    /**
     * Sets the Secure setting for QuickServer
     * @since 1.4.0
     */
    public void setSecure(Secure secure) {
        this.secure = secure;
    }

    /**
     * Returns Secure setting for QuickServer
     * @since 1.4.0
     */
    public Secure getSecure() {
        if (secure == null)
            secure = new Secure();
        return secure;
    }

    /**
     * <p>Returns if the server is running in Secure mode [SSL or TLS].</p>
     * @since 1.4.0
     */
    public boolean isRunningSecure() {
        return runningSecure;
    }

    /**
     * <p>Sets the server mode if its running in Secure mode [SSL or TLS].</p>
     * @since 1.4.0
     */
    public void setRunningSecure(boolean runningSecure) {
        this.runningSecure = runningSecure;
    }

    private File makeAbsoluteToConfig(String fileName) {
        Assertion.affirm(fileName != null, "FileName can't be null");
        return ConfigReader.makeAbsoluteToConfig(fileName, getConfig());
    }

    /**
     * Returns a ServerSocket object to be used for listening.
     * @since 1.4.0
     */
    protected void makeServerSocket() throws BindException, IOException {
        server = null;
        logger.log(Level.FINEST, "Binding {0} to IP: {1}", new Object[] { getName(), getBindAddr() });
        InetSocketAddress bindAddress = new InetSocketAddress(getBindAddr(), getPort());

        try {
            NetworkInterface ni = NetworkInterface.getByInetAddress(getBindAddr());
            if (ni != null) {
                logger.fine("NetworkInterface: " + ni);
            }
        } catch (Exception igrnore) {
            /*ignore*/} catch (Error igrnore) {
            /*ignore*/}

        if (getSecure().isEnable() == false) {
            logger.log(Level.FINE, "Making a normal ServerSocket for {0}", getName());
            setRunningSecure(false);

            if (getBlockingMode() == false) {
                //for non-blocking
                serverSocketChannel = ServerSocketChannel.open();
                server = serverSocketChannel.socket();
                server.bind(bindAddress, getBasicConfig().getAdvancedSettings().getBacklog());
            } else {
                //for blocking
                server = new ServerSocket(getPort(), getBasicConfig().getAdvancedSettings().getBacklog(),
                        getBindAddr());
            }
        } else {
            try {
                logger.log(Level.FINE, "Making a secure ServerSocket for {0}", getName());
                getSSLContext();
                setRunningSecure(true);

                if (getBlockingMode() == false) {

                    logger.log(Level.FINE, "Making a secure ServerSocketChannel for {0}", getName());
                    //for non-blocking
                    serverSocketChannel = ServerSocketChannel.open();
                    server = serverSocketChannel.socket();
                    server.bind(bindAddress, getBasicConfig().getAdvancedSettings().getBacklog());
                } else {

                    ServerSocketFactory ssf = getSSLContext().getServerSocketFactory();
                    SSLServerSocket serversocket = (SSLServerSocket) ssf.createServerSocket(getPort(),
                            getBasicConfig().getAdvancedSettings().getBacklog(), getBindAddr());
                    serversocket.setNeedClientAuth(secure.isClientAuthEnable());
                    setRunningSecure(true);

                    secureStoreManager.logSSLServerSocketInfo(serversocket);

                    server = serversocket;
                    serverSocketChannel = server.getChannel();

                    if (serverSocketChannel == null && getBlockingMode() == false) {
                        logger.warning("Secure Server does not support Channel! So will run in blocking mode.");
                        blockingMode = false;
                    }

                } //blocking
            } catch (NoSuchAlgorithmException e) {
                logger.log(Level.WARNING, "NoSuchAlgorithmException : {0}", e);
                throw new IOException("Error creating secure socket : " + e.getMessage());
            } catch (KeyManagementException e) {
                logger.log(Level.WARNING, "KeyManagementException : {0}", e);
                throw new IOException("Error creating secure socket : " + e.getMessage());
            }
        }

        server.setReuseAddress(true);

        int connectionTime = 0;
        int latency = 0;
        int bandwidth = 0;

        connectionTime = getBasicConfig().getAdvancedSettings().getPerformancePreferencesConnectionTime();
        latency = getBasicConfig().getAdvancedSettings().getPerformancePreferencesLatency();
        bandwidth = getBasicConfig().getAdvancedSettings().getPerformancePreferencesBandwidth();

        logger.log(Level.FINE, "getPerformancePreferencesConnectionTime : {0}", connectionTime);
        logger.log(Level.FINE, "getPerformancePreferencesLatency : {0}", latency);
        logger.log(Level.FINE, "getPerformancePreferencesBandwidth : {0}", bandwidth);

        server.setPerformancePreferences(connectionTime, latency, bandwidth);

        int clientSocketReceiveBufferSize = getBasicConfig().getAdvancedSettings()
                .getClientSocketReceiveBufferSize();
        if (clientSocketReceiveBufferSize > 0) {
            logger.log(Level.FINE, "clientSocketReceiveBufferSize: {0}", clientSocketReceiveBufferSize);
            server.setReceiveBufferSize(clientSocketReceiveBufferSize);
        }

        if (getBlockingMode() == false) {
            logger.log(Level.FINE, "Server Mode {0} - Non Blocking", getName());
            if (selector == null || selector.isOpen() == false) {
                logger.finest("Opening new selector");
                selector = Selector.open();
            } else {
                logger.log(Level.FINEST, "Reusing selector: {0}", selector);
            }
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            selector.wakeup();
        } else {
            logger.log(Level.FINE, "Server Mode {0} - Blocking", getName());
        }
    }

    /**
     * Sets the basic configuration of the QuickServer.
     * @since 1.4.0
     */
    public void setBasicConfig(BasicServerConfig basicConfig) throws Exception {
        Assertion.affirm(basicConfig != null, "BasicServerConfig can't be null");
        this.basicConfig = basicConfig;
    }

    /**
     * Returns the basic configuration of the QuickServer.
     * @since 1.4.0
     */
    public BasicServerConfig getBasicConfig() {
        return basicConfig;
    }

    /**
     * Loads the <code>SSLContext</code> from Secure configuring if set.
     * @see #setSecure
     * @since 1.4.0
     */
    public void loadSSLContext() throws IOException {
        if (getSecure().isLoad() == false) {
            throw new IllegalStateException("Secure setting is not yet enabled for loading!");
        }
        logger.info("Loading Secure Context..");
        km = null;
        tm = null;
        try {
            String ssManager = "org.quickserver.security.SecureStoreManager";
            if (getSecure().getSecureStore() != null)
                ssManager = getSecure().getSecureStore().getSecureStoreManager();

            Class secureStoreManagerClass = getClass(ssManager, true);

            secureStoreManager = (SecureStoreManager) secureStoreManagerClass.newInstance();

            km = secureStoreManager.loadKeyManagers(getConfig());
            logger.fine("KeyManager got");

            tm = secureStoreManager.loadTrustManagers(getConfig());
            logger.fine("TrustManager got");

            sslc = secureStoreManager.getSSLContext(getConfig().getSecure().getProtocol());
            sslc.init(km, tm, null);
            logger.fine("SSLContext loaded");
        } catch (KeyStoreException e) {
            logger.warning("KeyStoreException : " + e);
            throw new IOException("Error creating secure socket : " + e.getMessage());
        } catch (NoSuchAlgorithmException e) {
            logger.warning("NoSuchAlgorithmException : " + e);
            throw new IOException("Error creating secure socket : " + e.getMessage());
        } catch (NoSuchProviderException e) {
            logger.warning("NoSuchProviderException : " + e);
            throw new IOException("Error creating secure socket : " + e.getMessage());
        } catch (UnrecoverableKeyException e) {
            logger.warning("UnrecoverableKeyException : " + e);
            throw new IOException("Error creating secure socket : " + e.getMessage());
        } catch (CertificateException e) {
            logger.warning("CertificateException : " + e);
            throw new IOException("Error creating secure socket : " + e.getMessage());
        } catch (KeyManagementException e) {
            logger.warning("KeyManagementException : " + e);
            throw new IOException("Error creating secure socket : " + e.getMessage());
        } catch (GeneralSecurityException e) {
            logger.warning("GeneralSecurityException : " + e);
            throw new IOException("Error creating secure socket : " + e.getMessage());
        } catch (ClassNotFoundException e) {
            logger.warning("ClassNotFoundException : " + e);
            throw new IOException("Error creating secure socket : " + e.getMessage());
        } catch (InstantiationException e) {
            logger.warning("InstantiationException : " + e);
            throw new IOException("Error creating secure socket : " + e.getMessage());
        } catch (IllegalAccessException e) {
            logger.warning("IllegalAccessException : " + e);
            throw new IOException("Error creating secure socket : " + e.getMessage());
        }
    }

    /**
     * Returns the <code>SSLContext</code> from Secure configuring.
     * @see #loadSSLContext
     * @since 1.4.0
     */
    public SSLContext getSSLContext() throws IOException, NoSuchAlgorithmException, KeyManagementException {
        return getSSLContext(null);
    }

    /**
     * Returns the <code>SSLContext</code> object that implements the specified 
     * secure socket protocol from Secure configuring.
     * @see #loadSSLContext
     * @param protocol the standard name of the requested protocol. If <code>null</code> will use the protocol set in secure configuration of the server.
     * @throws IOException
     * @throws NoSuchAlgorithmException
     * @throws KeyManagementException
     * @since 1.4.0
     */
    public SSLContext getSSLContext(String protocol)
            throws IOException, NoSuchAlgorithmException, KeyManagementException {
        if (sslc == null)
            loadSSLContext();
        if (protocol != null && secureStoreManager != null) {
            SSLContext _sslc = secureStoreManager.getSSLContext(protocol);
            _sslc.init(km, tm, null);
            return _sslc;
        }
        return sslc;
    }

    /**
     * Returns a SSLSocketFactory object to be used for creating SSLSockets. 
     * Secure socket protocol will be picked from the Secure configuring.
     * @see #setSecure
     * @throws IOException
     * @throws NoSuchAlgorithmException
     * @throws KeyManagementException
     * @since 1.4.0
     */
    public SSLSocketFactory getSSLSocketFactory()
            throws IOException, NoSuchAlgorithmException, KeyManagementException {
        if (sslc == null)
            loadSSLContext();
        return secureStoreManager.getSocketFactory(getSSLContext());
    }

    /**
     * Returns a SSLSocketFactory object to be used for creating SSLSockets. 
     * @see #setSecure
     * @param protocol the standard name of the requested protocol. If 
     * <code>null</code> will use the protocol set in secure configuration 
     * of the server.
     * @throws IOException
     * @throws NoSuchAlgorithmException
     * @throws KeyManagementException
     * @since 1.4.0
     */
    public SSLSocketFactory getSSLSocketFactory(String protocol)
            throws IOException, NoSuchAlgorithmException, KeyManagementException {
        if (sslc == null)
            loadSSLContext();
        return secureStoreManager.getSocketFactory(getSSLContext(protocol));
    }

    /**
      * Sets the ClientBinaryHandler class that interacts with 
     * client sockets to handle binary data.
     * @param handler object the fully qualified name of the class that 
     *  implements {@link ClientBinaryHandler}
     * @see #getClientBinaryHandler
     * @since 1.4
      */
    public void setClientBinaryHandler(String handler) {
        clientBinaryHandlerString = handler;
        logger.finest("Set to " + handler);
    }

    /**
      * Returns the ClientBinaryHandler class that interacts with 
     * client sockets.
     * @see #setClientBinaryHandler
     * @since 1.4
      */
    public String getClientBinaryHandler() {
        return clientBinaryHandlerString;
    }

    /**
     * Sets the Selector (NIO).
     * @since 1.4.5
     */
    public void setSelector(Selector selector) {
        this.selector = selector;
    }

    /**
     * Returns the Selector (NIO),if any.
     * @since 1.4.5
     */
    public Selector getSelector() {
        return selector;
    }

    /**
     * Starts server in blocking mode.
     * @since 1.4.5
     */
    private void runBlocking(TheClient theClient) throws Exception {
        Socket client = null;
        ClientHandler _chPolled = null;
        int linger = getBasicConfig().getAdvancedSettings().getSocketLinger();

        int socketTrafficClass = 0;
        if (getBasicConfig().getAdvancedSettings().getClientSocketTrafficClass() != null) {
            socketTrafficClass = Integer
                    .parseInt(getBasicConfig().getAdvancedSettings().getClientSocketTrafficClass());
        }

        //long stime = System.currentTimeMillis();
        //long etime = System.currentTimeMillis();
        while (true) {
            //etime = System.currentTimeMillis();
            //System.out.println("Time Taken: "+(etime-stime));
            client = server.accept();
            //stime = System.currentTimeMillis();

            if (linger < 0) {
                client.setSoLinger(false, 0);
            } else {
                client.setSoLinger(true, linger);
            }

            client.setTcpNoDelay(getBasicConfig().getAdvancedSettings().getClientSocketTcpNoDelay());

            if (getBasicConfig().getAdvancedSettings().getClientSocketTrafficClass() != null) {
                client.setTrafficClass(socketTrafficClass);//low delay=10
            }

            //logger.fine("ReceiveBufferSize: "+client.getReceiveBufferSize());

            if (getBasicConfig().getAdvancedSettings().getClientSocketSendBufferSize() != 0) {
                client.setSendBufferSize(getBasicConfig().getAdvancedSettings().getClientSocketSendBufferSize());
                //logger.fine("SendBufferSize: "+client.getSendBufferSize());
            }

            if (stopServer) {
                //Client connected when server was about to be shutdown.
                try {
                    client.close();
                } catch (Exception e) {
                }
                break;
            }

            if (checkAccessConstraint(client) == false) {
                continue;
            }

            //Check if max connection has reached
            if (getSkipValidation() != true && maxConnection != -1
                    && getClientHandlerPool().getNumActive() >= maxConnection) {
                theClient.setClientEvent(ClientEvent.MAX_CON_BLOCKING);
            } else {
                theClient.setClientEvent(ClientEvent.RUN_BLOCKING);
            }

            theClient.setTrusted(getSkipValidation());
            theClient.setSocket(client);
            theClient.setSocketChannel(client.getChannel()); //mostly null

            if (clientDataClass != null) {
                if (getClientDataPool() == null) {
                    clientData = (ClientData) clientDataClass.newInstance();
                } else {
                    clientData = (ClientData) getClientDataPool().borrowObject();
                }
                theClient.setClientData(clientData);
            }

            try {
                _chPolled = (ClientHandler) getClientHandlerPool().borrowObject();
                _chPolled.handleClient(theClient);
            } catch (java.util.NoSuchElementException nsee) {
                logger.warning("Could not borrow ClientHandler from pool. Error: " + nsee);
                logger.warning("Closing Socket [" + client + "] since no ClientHandler available.");
                client.close();
            }

            if (_chPolled != null) {
                try {
                    getClientPool().addClient(_chPolled, true);
                } catch (java.util.NoSuchElementException nsee) {
                    logger.warning("Could not borrow Thread from pool. Error: " + nsee);
                    //logger.warning("Closing Socket ["+client+"] since no Thread available.");
                    //client.close();
                    //returnClientHandlerToPool(_chPolled);
                }
                _chPolled = null;
            }
            client = null;

            //reset it back
            setSkipValidation(false);
        } //end of loop
    }

    /**
     * Starts server in non-blocking mode.
     * @since 1.4.5
     */
    private void runNonBlocking(TheClient theClient) throws Exception {
        int selectCount = 0;
        Iterator iterator = null;
        SelectionKey key = null;
        ServerSocketChannel serverChannel = null;
        SocketChannel socketChannel = null;
        Socket client = null;
        ClientHandler _chPolled = null;
        boolean stopServerProcessed = false;
        int linger = getBasicConfig().getAdvancedSettings().getSocketLinger();
        registerChannelRequestMap = new HashMap();

        int socketTrafficClass = 0;
        if (getBasicConfig().getAdvancedSettings().getClientSocketTrafficClass() != null) {
            socketTrafficClass = Integer
                    .parseInt(getBasicConfig().getAdvancedSettings().getClientSocketTrafficClass());
        }

        while (true) {
            selectCount = selector.select(500);
            //selectCount = selector.select();//for testing

            //check for any pending registerChannel req.
            synchronized (registerChannelRequestMap) {
                if (registerChannelRequestMap.size() > 0) {
                    RegisterChannelRequest req = null;
                    Object hashkey = null;
                    iterator = registerChannelRequestMap.keySet().iterator();
                    while (iterator.hasNext()) {
                        hashkey = iterator.next();
                        req = (RegisterChannelRequest) registerChannelRequestMap.get(hashkey);
                        req.register(getSelector());
                    }
                    iterator = null;
                    registerChannelRequestMap.clear();
                } //if
            } //sync

            if (stopServer == true && stopServerProcessed == false) {
                logger.warning("Closing " + getName());
                serverSocketChannel.close();
                stopServerProcessed = true;

                server = null;
                serverSocketChannel = null;

                setServiceState(Service.STOPPED);
                logger.warning("Closed " + getName());

                processServerHooks(ServerHook.POST_SHUTDOWN);
            }

            if (stopServer == false && stopServerProcessed == true) {
                logger.finest("Server must have re-started.. will break");
                break;
            }

            if (selectCount == 0 && stopServerProcessed == true) {
                java.util.Set keyset = selector.keys();
                if (keyset.isEmpty() == true && getClientCount() <= 0) {
                    break;
                } else {
                    continue;
                }
            } else if (selectCount == 0) {
                continue;
            }

            iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()) {
                key = (SelectionKey) iterator.next();

                if (key.isValid() == false) {
                    iterator.remove();
                    continue;
                }

                if (key.isAcceptable() && stopServer == false) {
                    logger.finest("Key is Acceptable");
                    serverChannel = (ServerSocketChannel) key.channel();
                    socketChannel = serverChannel.accept();

                    if (socketChannel == null) {
                        iterator.remove();
                        continue;
                    }

                    client = socketChannel.socket();

                    if (linger < 0) {
                        client.setSoLinger(false, 0);
                    } else {
                        client.setSoLinger(true, linger);
                    }

                    client.setTcpNoDelay(getBasicConfig().getAdvancedSettings().getClientSocketTcpNoDelay());

                    if (getBasicConfig().getAdvancedSettings().getClientSocketTrafficClass() != null) {
                        client.setTrafficClass(socketTrafficClass);//low delay=10
                    }

                    //logger.fine("ReceiveBufferSize: "+client.getReceiveBufferSize());

                    if (getBasicConfig().getAdvancedSettings().getClientSocketSendBufferSize() != 0) {
                        client.setSendBufferSize(
                                getBasicConfig().getAdvancedSettings().getClientSocketSendBufferSize());
                        //logger.fine("SendBufferSize: "+client.getSendBufferSize());
                    }

                    if (checkAccessConstraint(client) == false) {
                        iterator.remove();
                        continue;
                    }

                    socketChannel.configureBlocking(false);
                    theClient.setTrusted(getSkipValidation());
                    theClient.setSocket(socketChannel.socket());
                    theClient.setSocketChannel(socketChannel);

                    if (clientDataClass != null) {
                        if (getClientDataPool() == null) {
                            clientData = (ClientData) clientDataClass.newInstance();
                        } else {
                            //borrow a object from pool
                            clientData = (ClientData) getClientDataPool().borrowObject();
                        }
                        theClient.setClientData(clientData);
                    }

                    //Check if max connection has reached
                    if (getSkipValidation() != true && maxConnection != -1
                            && getClientHandlerPool().getNumActive() >= maxConnection) {
                        theClient.setClientEvent(ClientEvent.MAX_CON);
                    } else {
                        theClient.setClientEvent(ClientEvent.ACCEPT);
                    }

                    try {
                        _chPolled = (ClientHandler) getClientHandlerPool().borrowObject();
                        logger.finest("Asking " + _chPolled.getName() + " to handle.");
                        _chPolled.handleClient(theClient);
                    } catch (java.util.NoSuchElementException nsee) {
                        logger.warning("Could not borrow ClientHandler Object from pool. Error: " + nsee);
                        logger.warning("Closing SocketChannel [" + serverChannel.socket()
                                + "] since no ClientHandler available.");
                        socketChannel.close();
                    }

                    if (_chPolled != null) {
                        try {
                            getClientPool().addClient(_chPolled, true);
                        } catch (java.util.NoSuchElementException nsee) {
                            logger.warning("Could not borrow Thread from pool. Error: " + nsee);
                            //logger.warning("Closing SocketChannel ["+serverChannel.socket()+"] since no Thread available.");
                            //socketChannel.close();
                            //returnClientHandlerToPool(_chPolled);
                        }
                        _chPolled = null;
                    }
                    socketChannel = null;
                    client = null;

                    setSkipValidation(false);//reset it back
                } else if (key.isValid() && key.isReadable()) {
                    boolean addedEvent = false;
                    ClientHandler _ch = null;
                    try {
                        _ch = (ClientHandler) key.attachment();
                        logger.finest("Key is Readable, removing OP_READ from interestOps for " + _ch.getName());
                        key.interestOps(key.interestOps() & (~SelectionKey.OP_READ));
                        _ch.addEvent(ClientEvent.READ);
                        addedEvent = true;
                        //_ch.setSelectionKey(key);
                        getClientPool().addClient(_ch);
                    } catch (CancelledKeyException cke) {
                        logger.fine("Ignored Error - Key was Cancelled: " + cke);
                    } catch (java.util.NoSuchElementException nsee) {
                        logger.finest("NoSuchElementException: " + nsee);
                        if (addedEvent)
                            _ch.removeEvent(ClientEvent.READ);
                        continue;//no need to remove the key
                    }
                    _ch = null;
                } else if (key.isValid() && key.isWritable()) {
                    if (getClientPool().shouldNioWriteHappen() == false) {
                        continue; //no need to remove the key
                    }
                    boolean addedEvent = false;
                    ClientHandler _ch = null;
                    try {
                        _ch = (ClientHandler) key.attachment();
                        logger.finest("Key is Writable, removing OP_WRITE from interestOps for " + _ch.getName());
                        //remove OP_WRITE from interest set
                        key.interestOps(key.interestOps() & (~SelectionKey.OP_WRITE));
                        _ch.addEvent(ClientEvent.WRITE);
                        addedEvent = true;
                        //_ch.setSelectionKey(key);
                        getClientPool().addClient(_ch);
                    } catch (CancelledKeyException cke) {
                        logger.fine("Ignored Error - Key was Cancelled: " + cke);
                    } catch (java.util.NoSuchElementException nsee) {
                        logger.finest("NoSuchElementException: " + nsee);
                        if (addedEvent)
                            _ch.removeEvent(ClientEvent.WRITE);
                        continue;//no need to remove the key
                    }
                    _ch = null;
                } else if (stopServer == true && key.isAcceptable()) {
                    //we will not accept this key
                    setSkipValidation(false);//reset it back
                } else {
                    logger.warning("Unknown key got in SelectionKey: " + key);
                }
                iterator.remove(); //Remove key

                Thread.yield();
            } //end of iterator
            iterator = null;
        } //end of loop
    }

    private boolean checkAccessConstraint(Socket socket) {
        try {
            if (getAccessConstraintConfig() != null) {
                getAccessConstraintConfig().checkAccept(socket);
            }
            return true;
        } catch (SecurityException se) {
            logger.warning("SecurityException occurred accepting connection : " + se.getMessage());
            return false;
        }
    }

    /**
     * Register the given channel for the given operations. This adds the request
     * to a list and will be processed after selector select wakes up.
     * @return boolean flag to indicate if new entry was added to the list to register.
     * @since 1.4.5
     */
    public boolean registerChannel(SocketChannel channel, int ops, Object att)
            throws IOException, ClosedChannelException {
        if (getSelector() == null) {
            throw new IllegalStateException("Selector is not open!");
        }
        if (channel == null) {
            throw new IllegalArgumentException("Can't register a null channel!");
        }

        if (channel.isConnected() == false) {
            throw new ClosedChannelException();
        }

        RegisterChannelRequest req = new RegisterChannelRequest(channel, ops, att);
        RegisterChannelRequest reqOld = null;
        synchronized (registerChannelRequestMap) {
            reqOld = (RegisterChannelRequest) registerChannelRequestMap.get(channel);
            if (reqOld == null) {
                registerChannelRequestMap.put(channel, req);
                getSelector().wakeup();
                return true;
            } else {
                if (reqOld.equals(req) == false) {
                    reqOld.setOps(reqOld.getOps() | req.getOps());
                    reqOld.setAtt(req.getAtt());
                    return true;
                }
                return false;
            }
        }
        /*
        logger.warning("Before register...");
        channel.register(getSelector(), ops, att);
        logger.warning("Before wakeup and after register...");
        getSelector().wakeup();
        logger.warning("After wakeup...");
        */
    }

    /** 
     * Makes the pool of ByteBuffer
     * @since 1.4.5
     */
    private void makeByteBufferPool(PoolConfig opConfig) {
        logger.finer("Creating ByteBufferPool pool");

        int bufferSize = getBasicConfig().getAdvancedSettings().getByteBufferSize();
        boolean useDirectByteBuffer = getBasicConfig().getAdvancedSettings().getUseDirectByteBuffer();
        PoolableObjectFactory factory = new ByteBufferObjectFactory(bufferSize, useDirectByteBuffer);

        byteBufferPool = poolManager.makeByteBufferPool(factory, opConfig);
        poolManager.initPool(byteBufferPool, opConfig);
    }

    /**
     * Returns ObjectPool of java.nio.ByteBuffer class. 
     * @since 1.4.5
     */
    public ObjectPool getByteBufferPool() {
        return byteBufferPool;
    }

    /** 
     * Makes the pool of ByteBuffer
     * @since 1.4.5
     */
    private void makeClientPool(PoolConfig opConfig) throws Exception {
        logger.finer("Creating ClientThread pool");
        ThreadObjectFactory factory = new ThreadObjectFactory();
        ObjectPool objectPool = poolManager.makeClientPool(factory, opConfig);
        pool = new ClientPool(makeQSObjectPool(objectPool), opConfig);
        factory.setClientPool(pool);
        pool.setMaxThreadsForNioWrite(getBasicConfig().getAdvancedSettings().getMaxThreadsForNioWrite());
        poolManager.initPool(objectPool, opConfig);
    }

    /**
      * Sets the ClientWriteHandler class that interacts with 
     * client sockets to handle data write (only used in non-blocking mode).
     * @param handler object the fully qualified name of the class that 
     *  implements {@link ClientWriteHandler}
     * @see #getClientWriteHandler
     * @since 1.4.5
      */
    public void setClientWriteHandler(String handler) {
        clientWriteHandlerString = handler;
        logger.log(Level.FINEST, "Set to {0}", handler);
    }

    /**
      * Returns the ClientWriteHandler class that interacts with 
     * client sockets (only used in non-blocking mode).
     * @see #setClientWriteHandler
     * @since 1.4.5
      */
    public String getClientWriteHandler() {
        return clientWriteHandlerString;
    }

    /**
      * Returns the date/time when the server was last started.
     * @return last started time. Will be <code>null</code> if never started.
     * @since 1.4.5
      */
    public java.util.Date getLastStartTime() {
        return lastStartTime;
    }

    /**
     * Sets the debug flag to ByteBufferOutputStream and
     * ByteBufferInputStream class that are used in non-blcking mode
     * @since 1.4.5
     */
    public static void setDebugNonBlockingMode(boolean flag) {
        org.quickserver.util.io.ByteBufferOutputStream.setDebug(flag);
        org.quickserver.util.io.ByteBufferInputStream.setDebug(flag);
    }

    /**
     * Returns the implementation that is used to do Client Identification.
     * @since 1.4.5
     */
    public ClientIdentifier getClientIdentifier() {
        return clientIdentifier;
    }

    /**
     * Makes QSObjectPool from ObjectPool
     * @since 1.4.5
     */
    private QSObjectPool makeQSObjectPool(ObjectPool objectPool) throws Exception {
        return (QSObjectPool) qsObjectPoolMaker.getQSObjectPool(objectPool);
    }

    /**
     * Returns the current blocking mode of the server.
     * @since 1.4.6
     */
    public boolean getBlockingMode() {
        return blockingMode;
    }

    /**
     * Loads all the Business Logic class
     * @since 1.4.6
     */
    protected void loadBusinessLogic() throws Exception {
        if (clientCommandHandlerString == null && clientEventHandlerString == null) {
            logger.severe("ClientCommandHandler AND ClientEventHandler was not set.");
            throw new AppException("ClientCommandHandler AND ClientEventHandler was not set.");
        }

        clientCommandHandler = null;
        if (clientCommandHandlerString != null) {
            logger.finest("Loading ClientCommandHandler class..");
            Class clientCommandHandlerClass = getClass(clientCommandHandlerString, true);
            clientCommandHandler = (ClientCommandHandler) clientCommandHandlerClass.newInstance();
        }

        boolean setClientCommandHandlerLookup = false;
        clientEventHandler = null;
        if (clientEventHandlerString == null) {
            clientEventHandlerString = "org.quickserver.net.server.impl.DefaultClientEventHandler";
            setClientCommandHandlerLookup = true;
        }
        logger.finest("Loading ClientEventHandler class..");
        if (clientEventHandlerString.equals(clientCommandHandlerString)
                && ClientEventHandler.class.isInstance(clientCommandHandler)) {
            clientEventHandler = (ClientEventHandler) clientCommandHandler;
        } else {
            clientEventHandler = (ClientEventHandler) getClass(clientEventHandlerString, true).newInstance();
            if (setClientCommandHandlerLookup) {
                ((DefaultClientEventHandler) clientEventHandler).setClientCommandHandler(clientCommandHandler);
            }
        }

        clientExtendedEventHandler = null;
        if (clientExtendedEventHandlerString != null) {
            logger.finest("Loading ClientExtendedEventHandler class..");
            if (clientExtendedEventHandlerString.equals(clientCommandHandlerString)
                    && ClientExtendedEventHandler.class.isInstance(clientCommandHandler)) {
                clientExtendedEventHandler = (ClientExtendedEventHandler) clientCommandHandler;
            } else if (clientExtendedEventHandlerString.equals(clientEventHandlerString)
                    && ClientExtendedEventHandler.class.isInstance(clientEventHandler)) {
                clientExtendedEventHandler = (ClientExtendedEventHandler) clientEventHandler;
            } else {
                Class clientExtendedEventHandlerClass = getClass(clientExtendedEventHandlerString, true);
                clientExtendedEventHandler = (ClientExtendedEventHandler) clientExtendedEventHandlerClass
                        .newInstance();
            }
        }

        clientObjectHandler = null;
        if (clientObjectHandlerString != null) {
            logger.finest("Loading ClientObjectHandler class..");
            if (clientObjectHandlerString.equals(clientCommandHandlerString)
                    && ClientObjectHandler.class.isInstance(clientCommandHandler)) {
                clientObjectHandler = (ClientObjectHandler) clientCommandHandler;
            } else if (clientObjectHandlerString.equals(clientEventHandlerString)
                    && ClientObjectHandler.class.isInstance(clientEventHandler)) {
                clientObjectHandler = (ClientObjectHandler) clientEventHandler;
            } else if (clientObjectHandlerString.equals(clientExtendedEventHandlerString)
                    && ClientObjectHandler.class.isInstance(clientExtendedEventHandler)) {
                clientObjectHandler = (ClientObjectHandler) clientExtendedEventHandler;
            } else {
                clientObjectHandler = (ClientObjectHandler) getClass(clientObjectHandlerString, true).newInstance();
            }
        } //end of != null

        clientBinaryHandler = null;
        if (clientBinaryHandlerString != null) {
            logger.finest("Loading ClientBinaryHandler class..");
            if (clientBinaryHandlerString.equals(clientCommandHandlerString)
                    && ClientBinaryHandler.class.isInstance(clientCommandHandler)) {
                clientBinaryHandler = (ClientBinaryHandler) clientCommandHandler;
            } else if (clientBinaryHandlerString.equals(clientEventHandlerString)
                    && ClientBinaryHandler.class.isInstance(clientEventHandler)) {
                clientBinaryHandler = (ClientBinaryHandler) clientEventHandler;
            } else if (clientBinaryHandlerString.equals(clientExtendedEventHandlerString)
                    && ClientBinaryHandler.class.isInstance(clientExtendedEventHandler)) {
                clientBinaryHandler = (ClientBinaryHandler) clientExtendedEventHandler;
            } else if (clientBinaryHandlerString.equals(clientObjectHandlerString)
                    && ClientBinaryHandler.class.isInstance(clientObjectHandler)) {
                clientBinaryHandler = (ClientBinaryHandler) clientObjectHandler;
            } else {
                clientBinaryHandler = (ClientBinaryHandler) getClass(clientBinaryHandlerString, true).newInstance();
            }
        } //end of != null

        clientWriteHandler = null;
        if (clientWriteHandlerString != null) {
            logger.finest("Loading ClientWriteHandler class..");
            if (clientWriteHandlerString.equals(clientCommandHandlerString)
                    && ClientWriteHandler.class.isInstance(clientCommandHandler)) {
                clientWriteHandler = (ClientWriteHandler) clientCommandHandler;
            } else if (clientWriteHandlerString.equals(clientEventHandlerString)
                    && ClientWriteHandler.class.isInstance(clientEventHandler)) {
                clientWriteHandler = (ClientWriteHandler) clientEventHandler;
            } else if (clientWriteHandlerString.equals(clientExtendedEventHandlerString)
                    && ClientWriteHandler.class.isInstance(clientExtendedEventHandler)) {
                clientWriteHandler = (ClientWriteHandler) clientExtendedEventHandler;
            } else if (clientWriteHandlerString.equals(clientObjectHandlerString)
                    && ClientWriteHandler.class.isInstance(clientObjectHandler)) {
                clientWriteHandler = (ClientWriteHandler) clientObjectHandler;
            } else if (clientWriteHandlerString.equals(clientBinaryHandlerString)
                    && ClientWriteHandler.class.isInstance(clientBinaryHandler)) {
                clientWriteHandler = (ClientWriteHandler) clientBinaryHandler;
            } else {
                clientWriteHandler = (ClientWriteHandler) getClass(clientWriteHandlerString, true).newInstance();
            }
        } //end of != null

        Class authenticatorClass = null;
        if (clientAuthenticationHandlerString != null) {
            logger.finest("Loading ClientAuthenticationHandler class..");
            authenticatorClass = getClass(clientAuthenticationHandlerString, true);
        }

        if (authenticatorClass != null) {
            Object obj = authenticatorClass.newInstance();

            if (ClientAuthenticationHandler.class.isInstance(obj))
                clientAuthenticationHandler = (ClientAuthenticationHandler) obj;
            else
                authenticator = (Authenticator) obj;
        }

        clientDataClass = null;
        if (clientDataString != null) {
            logger.finest("Loading ClientData class..");
            clientDataClass = getClass(clientDataString, true);
        }

        Assertion.affirm(clientEventHandler != null, "ClientEventHandler was not loaded!");
    }

    /**
      * Sets the ClientEventHandler class that gets notified of 
     * client events.
     * @param handler the fully qualified name of the class that 
     *  implements {@link ClientEventHandler}
     * @see #getClientEventHandler
     * @since 1.4.6
      */
    public void setClientEventHandler(String handler) {
        clientEventHandlerString = handler;
        logger.finest("Set to " + handler);
    }

    /**
      * Returns the ClientEventHandler class that gets notified of 
     * client events.
     * @see #setClientEventHandler
     * @since 1.4.6
      */
    public String getClientEventHandler() {
        return clientEventHandlerString;
    }

    /**
     * Sets the default {@link DataMode} for the ClientHandler
     * @since 1.4.6
     */
    public void setDefaultDataMode(DataMode dataMode, DataType dataType) throws IOException {
        if (dataType == DataType.IN)
            this.defaultDataModeIN = dataMode;
        if (dataType == DataType.OUT)
            this.defaultDataModeOUT = dataMode;
    }

    /**
     * Sets the default {@link DataMode} for the ClientHandler
     * @since 1.4.6
     */
    public void setDefaultDataMode(DefaultDataMode defaultDataMode) throws IOException {
        defaultDataModeIN = defaultDataMode.getDataMode(DataType.IN);
        defaultDataModeOUT = defaultDataMode.getDataMode(DataType.OUT);
        ;
    }

    /**
     * Returns the default {@link DataMode} for the ClientHandler
     * @since 1.4.6
     */
    public DataMode getDefaultDataMode(DataType dataType) {
        if (dataType == DataType.IN)
            return defaultDataModeIN;
        if (dataType == DataType.OUT)
            return defaultDataModeOUT;
        else
            throw new IllegalArgumentException("Unknown DataType: " + dataType);
    }

    /**
      * Sets the ClientExtendedEventHandler class that gets notified of 
     * extended client events.
     * @param handler the fully qualified name of the class that 
     *  implements {@link ClientExtendedEventHandler}
     * @see #getClientExtendedEventHandler
     * @since 1.4.6
      */
    public void setClientExtendedEventHandler(String handler) {
        clientExtendedEventHandlerString = handler;
        logger.finest("Set to " + handler);
    }

    /**
      * Returns the ClientExtendedEventHandler class that gets notified of 
     * extended client events.
     * @see #setClientExtendedEventHandler
     * @since 1.4.6
      */
    public String getClientExtendedEventHandler() {
        return clientExtendedEventHandlerString;
    }

    /**
     * If Application Jar Path was set, load the jars
     * @since 1.4.6
     */
    private void loadApplicationClasses() throws Exception {
        if (getApplicationJarPath() != null && getClassLoader() == null) {
            setClassLoader(ClassUtil.getClassLoader(getApplicationJarPath()));
            //update qsadmin to use the same
            if (adminServer != null) {
                adminServer.getServer().setClassLoader(getClassLoader());
            }
        }
    }

    /** 
     * Returns PID of the JVM
     * @return PID of the JVM
     * @since 1.4.8
     */
    public static String getPID() {
        return pid;
    }

    public boolean isRawCommunicationLogging() {
        return rawCommunicationLogging;
    }

    public void setRawCommunicationLogging(boolean rawCommunicationLogging) {
        this.rawCommunicationLogging = rawCommunicationLogging;
    }

    public int getRawCommunicationMaxLength() {
        return rawCommunicationMaxLength;
    }

    public void setRawCommunicationMaxLength(int rawCommunicationMaxLength) {
        this.rawCommunicationMaxLength = rawCommunicationMaxLength;
    }

    public String getUptime() {
        Date lst = getLastStartTime();
        return JvmUtil.getUptime(lst);
    }
}