Java tutorial
/* * 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> * Handles client events [Optional Class]. * <li>ClientCommandHandler [#]<br> * Handles client character/string commands. * <li>ClientObjectHandler [#]<br> * Handles client interaction - Object commands. * <li>ClientBinaryHandler [#]<br> * Handles client interaction - binary data. * <li>ClientWriteHandler [Optional Class]<br> * Handles client interaction - writing data (Only used in non-blocking mode). * <li>ClientAuthenticationHandler [Optional Class]<br> * Used to Authencatet a client. * <li>ClientData [Optional Class]<br> * Client data carrier (support class) * <li>ClientExtendedEventHandler [Optional Class]<br> * 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> <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); } }