/* * Sun Public License * * The contents of this file are subject to the Sun Public License Version * 1.0 (the "License"). You may not use this file except in compliance with * the License. A copy of the License is available at http://www.sun.com/ * * The Original Code is the SLAMD Distributed Load Generation Engine. * The Initial Developer of the Original Code is Neil A. Wilson. * Portions created by Neil A. Wilson are Copyright (C) 2004-2010. * Some preexisting portions Copyright (C) 2002-2006 Sun Microsystems, Inc. * All Rights Reserved. * * Contributor(s): Neil A. Wilson */ package com.slamd.server; import java.io.*; import java.net.*; import java.util.*; import javax.net.ssl.*; import com.slamd.common.*; import com.slamd.db.*; import com.slamd.job.*; import com.slamd.parameter.*; /** * This class defines a thread that will listen for connections from client * managers. * * * @author Neil A. Wilson */ public class ClientManagerListener extends Thread implements ConfigSubscriber { // Indicates whether the listener has actually stopped listening. boolean hasStopped; // Indicates whether the listener should keep listening. boolean keepListening; // Indicates whether the listener should use SSL. boolean useSSL; // The port number on which this listener should listen. int listenPort; // The maximum length of time in seconds that the SLAMD server will wait for a // response for a request sent to a client manager. int maxResponseWaitTime; // The mutex used to provide threadsafe access to the list of client managers. final Object clientManagerMutex; // The server socket used to accept new connections from client managers. ServerSocket serverSocket; // The config database for the SLAMD server. SLAMDDB configDB; // The SLAMD server with which this listener is associated. SLAMDServer slamdServer; // The set of client manager connections that are currently established. ArrayList<ClientManagerConnection> clientManagers; /** * Creates a new listener that will listen for connections from client * managers. * * @param slamdServer The SLAMD server with which this client manager * listener is associated. */ public ClientManagerListener(SLAMDServer slamdServer) { setName("Client Manager Listener"); this.slamdServer = slamdServer; // Read the appropriate information from the configuration configDB= slamdServer.getConfigDB(); configDB.registerAsSubscriber(this); listenPort = Constants.DEFAULT_CLIENT_MANAGER_LISTENER_PORT_NUMBER; String listenPortStr = configDB.getConfigParameter( Constants.PARAM_CLIENT_MANAGER_LISTENER_PORT); if (listenPortStr != null) { try { listenPort = Integer.parseInt(listenPortStr); } catch (NumberFormatException e) {} } maxResponseWaitTime = Constants.DEFAULT_CLIENT_MANAGER_MAX_WAIT_TIME; String maxWaitTimeStr = configDB.getConfigParameter( Constants.PARAM_CLIENT_MANAGER_MAX_WAIT_TIME); if (maxWaitTimeStr != null) { try { maxResponseWaitTime = Integer.parseInt(maxWaitTimeStr); } catch (Exception e) {} } useSSL = Constants.DEFAULT_LISTENER_USE_SSL; String sslStr = configDB.getConfigParameter(Constants.PARAM_LISTENER_USE_SSL); if ((sslStr != null) && (sslStr.length() > 0)) { useSSL = sslStr.equals(Constants.CONFIG_VALUE_TRUE); if (useSSL) { String keyStore = slamdServer.getSSLKeyStore(); if ((keyStore != null) && (keyStore.length() > 0)) { System.setProperty(Constants.SSL_KEY_STORE_PROPERTY, keyStore); } String keyStorePassword = slamdServer.getSSLKeyStorePassword(); if ((keyStorePassword != null) && (keyStorePassword.length() > 0)) { System.setProperty(Constants.SSL_KEY_PASSWORD_PROPERTY, keyStorePassword); } String trustStore = slamdServer.getSSLTrustStore(); if ((trustStore != null) && (trustStore.length() > 0)) { System.setProperty(Constants.SSL_TRUST_STORE_PROPERTY, trustStore); } String trustStorePassword = slamdServer.getSSLTrustStorePassword(); if ((trustStorePassword != null) && (trustStorePassword.length() > 0)) { System.setProperty(Constants.SSL_TRUST_PASSWORD_PROPERTY, trustStorePassword); } } } clientManagers = new ArrayList<ClientManagerConnection>(); clientManagerMutex = new Object(); keepListening = true; hasStopped = false; } /** * Creates the server socket that will be used to accept new connections, then * listens for new connections until the SLAMD server is shut down. */ @Override() public void run() { slamdServer.logMessage(Constants.LOG_LEVEL_TRACE, "In ClientManagerLister.run()"); slamdServer.logMessage(Constants.LOG_LEVEL_CLIENT_DEBUG, "In ClientManagerListener.run()"); hasStopped = false; if (useSSL) { try { SSLServerSocketFactory socketFactory = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); serverSocket = socketFactory.createServerSocket(listenPort); slamdServer.logMessage(Constants.LOG_LEVEL_ANY, "Listening for SSL-based client connections " + "on port " + listenPort); } catch (Exception e) { e.printStackTrace(); slamdServer.logMessage(Constants.LOG_LEVEL_EXCEPTION_DEBUG, JobClass.stackTraceToString(e)); slamdServer.logMessage(Constants.LOG_LEVEL_ANY, "Unable to create SSL server socket: " + e); hasStopped = true; return; } } else { try { serverSocket = new ServerSocket(listenPort); slamdServer.logMessage(Constants.LOG_LEVEL_ANY, "Listening for client manager connections on " + "port " + listenPort); } catch (IOException ioe) { ioe.printStackTrace(); slamdServer.logMessage(Constants.LOG_LEVEL_EXCEPTION_DEBUG, JobClass.stackTraceToString(ioe)); slamdServer.logMessage(Constants.LOG_LEVEL_ANY, "Unable to create client manager server " + "socket: " + ioe); hasStopped = true; return; } } while (keepListening) { Socket clientSocket = null; try { clientSocket = serverSocket.accept(); ClientManagerConnection conn = new ClientManagerConnection(slamdServer, this, clientSocket); synchronized (clientManagerMutex) { clientManagers.add(conn); } conn.start(); } catch (SLAMDException se) { slamdServer.logMessage(Constants.LOG_LEVEL_CLIENT, "Unable to create a new client manager " + "connection -- " + se.getMessage()); slamdServer.logMessage(Constants.LOG_LEVEL_EXCEPTION_DEBUG, JobClass.stackTraceToString(se)); try { clientSocket.close(); } catch (Exception e) {} } catch (IOException ioe) { ioe.printStackTrace(); slamdServer.logMessage(Constants.LOG_LEVEL_EXCEPTION_DEBUG, JobClass.stackTraceToString(ioe)); slamdServer.logMessage(Constants.LOG_LEVEL_ANY, "I/O error accepting client manager listener " + "connection: " + ioe + " -- disabling the listener."); keepListening = false; } } hasStopped = true; } /** * Indicates that the listener should start listening for client manager * connections. */ public void startListening() { slamdServer.logMessage(Constants.LOG_LEVEL_TRACE, "In ClientManagerListener.startListening()"); slamdServer.logMessage(Constants.LOG_LEVEL_CLIENT_DEBUG, "In ClientManagerListener.startListening()"); keepListening = true; start(); } /** * Indicates that the listener should stop listening for client connections. * It will also notify all connected clients that the listener is shutting * down. */ public void stopListening() { slamdServer.logMessage(Constants.LOG_LEVEL_TRACE, "In ClientListener.stopListening()"); slamdServer.logMessage(Constants.LOG_LEVEL_CLIENT_DEBUG, "In ClientListener.stopListening()"); // Set the flag that indicates the server should no longer be listening keepListening = false; // Close the server socket so that no more connections will be accepted try { serverSocket.close(); } catch (Exception e) {} // Iterate through all the connections and inform them that the server is // shutting down. The sendServerShutdownMessage() method will also close // the connections. synchronized (clientManagerMutex) { for (int i=0; i < clientManagers.size(); i++) { ClientManagerConnection clientConnection = clientManagers.get(i); clientConnection.disconnect(true); } clientManagers.clear(); } } /** * This method will not return until the listener has actually stopped. Note * that it does not stop the listener -- you should first call the * <CODE>stopListening</CODE> method to signal the listener that it needs to * stop. */ public void waitForStop() { while (! hasStopped) { try { Thread.sleep(Constants.THREAD_BLOCK_SLEEP_TIME); } catch (InterruptedException ie) {} } } /** * Retrieves the list of client manager connections associated with the client * managers that have connected to the SLAMD server. * * @return The list of client manager connections associated with the client * managers that have connected to the SLAMD server. */ public ClientManagerConnection[] getClientManagers() { synchronized (clientManagerMutex) { ClientManagerConnection[] managerArray = new ClientManagerConnection[clientManagers.size()]; clientManagers.toArray(managerArray); return managerArray; } } /** * Retrieves the list of client manager connections associated with the client * managers that have connected to the SLAMD server, sorted by client ID. * * @return The list of client manager connections associated with the client * managers that have connected to the SLAMD server, sorted by client * ID. */ public ClientManagerConnection[] getSortedClientManagers() { synchronized (clientManagerMutex) { ClientManagerConnection[] managerArray = new ClientManagerConnection[clientManagers.size()]; clientManagers.toArray(managerArray); Arrays.sort(managerArray); return managerArray; } } /** * Retrieves a connection to the requested client manager. * * @param clientID The client ID associated with the client manager for * which to retrieve the connection. * * @return The requested client manager connection, or <CODE>null</CODE> if * no client manager is connected with the specified client ID. */ public ClientManagerConnection getClientManager(String clientID) { synchronized (clientManagerMutex) { for (int i=0; i < clientManagers.size(); i++) { ClientManagerConnection conn = clientManagers.get(i); if (conn.getClientID().equals(clientID)) { return conn; } } } return null; } /** * Indicates that the connection to the provided client manager has been lost * and that it should be removed from the list of available client managers. * * @param clientManagerConnection The client manager to which the connection * was lost. */ public void connectionLost(ClientManagerConnection clientManagerConnection) { // For now, just remove the manager from the list of current connections. synchronized (clientManagerMutex) { clientManagers.remove(clientManagerConnection); } } /** * Indicates that the specified client has disconnected from the SLAMD server * and that the client managers should be polled to determine if that client * might have been associated with a client manager. If so, then the * associated client manager will be updated to reflect the lost connection. * * @param clientConnection The client connection that has disconnected from * the SLAMD server. */ public void clientConnectionLost(ClientConnection clientConnection) { String clientIP = clientConnection.getClientIPAddress(); synchronized (clientManagerMutex) { for (int i=0; i < clientManagers.size(); i++) { ClientManagerConnection conn = clientManagers.get(i); if (conn.getClientIPAddress().equals(clientIP)) { conn.clientConnectionLost(); return; } } } } /** * Retrieves the maximum length of time in seconds that the SLAMD server will * wait for a response to a request issued to a client manager. * * @return The maximum length of time in seconds that the SLAMD server will * wait for a response to a request issued to a client manager. */ public int getMaxResponseWaitTime() { return maxResponseWaitTime; } /** * Retrieves the name that the client manager listener uses to subscribe to * the configuration handler in order to be notified of configuration changes. * * @return The name that the client manager listener uses to subscribe to the * configuration handler in order to be notified of configuration * changes. */ public String getSubscriberName() { return "Client Manager Listener"; } /** * Retrieves the set of configuration parameters associated with this * configuration subscriber. * * @return The set of configuration parameters associated with this * configuration subscriber. */ public ParameterList getSubscriberParameters() { slamdServer.logMessage(Constants.LOG_LEVEL_TRACE, "In ClientManagerListener.getParameters()"); IntegerParameter maxWaitTimeParameter = new IntegerParameter(Constants.PARAM_CLIENT_MANAGER_MAX_WAIT_TIME, "Maximum Wait Time", "The maximum length of time (in seconds) that " + "the SLAMD server will wait for a response " + "to a request issued to a client manager.", true, maxResponseWaitTime, true, 0, false, 0); IntegerParameter portParameter = new IntegerParameter(Constants.PARAM_CLIENT_MANAGER_LISTENER_PORT, "Client Manager Listener Port", "The port on which the SLAMD server listens " + "for connections from client managers.", true, listenPort, true, 1, true, 65535); Parameter[] params = new Parameter[] { portParameter, maxWaitTimeParameter }; return new ParameterList(params); } /** * Re-reads all configuration information used by the client listener manager. * In this case, only the maximum wait time will be read because the port * number is not dynamically reconfigurable. * * @throws SLAMDServerException If there is a problem reading or applying * the changes. */ public void refreshSubscriberConfiguration() throws SLAMDServerException { slamdServer.logMessage(Constants.LOG_LEVEL_TRACE, "In ClientManagerListener.refreshConfiguration()"); slamdServer.logMessage(Constants.LOG_LEVEL_CLIENT_DEBUG, "In ClientManagerListener.refreshConfiguration()"); maxResponseWaitTime = Constants.DEFAULT_CLIENT_MANAGER_MAX_WAIT_TIME; String maxWaitTimeStr = configDB.getConfigParameter( Constants.PARAM_CLIENT_MANAGER_MAX_WAIT_TIME); if (maxWaitTimeStr != null) { try { maxResponseWaitTime = Integer.parseInt(maxWaitTimeStr); } catch (Exception e) {} } } /** * Re-reads the configuration for the specified parameter if the parameter is * applicable to the client manager listener. In this case, only the maximum * wait time will be read because the port number is not dynamically * reconfigurable. * * @param parameterName The name of the parameter for which to reread the * configuration. * * @throws SLAMDServerException If there is a problem reading or applying * the specified change. */ public void refreshSubscriberConfiguration(String parameterName) throws SLAMDServerException { slamdServer.logMessage(Constants.LOG_LEVEL_TRACE, "In ClientManagerListener.refreshConfiguration(" + parameterName + ')'); slamdServer.logMessage(Constants.LOG_LEVEL_CLIENT_DEBUG, "In ClientManagerListener.refreshConfiguration(" + parameterName + ')'); if (parameterName.equalsIgnoreCase( Constants.PARAM_CLIENT_MANAGER_MAX_WAIT_TIME)) { maxResponseWaitTime = Constants.DEFAULT_CLIENT_MANAGER_MAX_WAIT_TIME; String maxWaitTimeStr = configDB.getConfigParameter( Constants.PARAM_CLIENT_MANAGER_MAX_WAIT_TIME); if (maxWaitTimeStr != null) { try { maxResponseWaitTime = Integer.parseInt(maxWaitTimeStr); } catch (Exception e) {} } } } }