/*
* 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.tools;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.Properties;
import com.slamd.client.ClientException;
import com.slamd.client.ClientMessageWriter;
import com.slamd.common.Constants;
import com.slamd.common.SLAMDException;
import com.slamd.resourcemonitor.ResourceMonitorClient;
/**
* This class defines a command-line application that may serve as a resource
* monitor client for use with SLAMD. All of the configuration is done through
* command-line options.
*
*
* @author Neil A. Wilson
*/
public class CommandLineResourceMonitorClient
implements ClientMessageWriter
{
/**
* The name of the configuration property that specifies the address of the
* SLAMD server.
*/
public static final String PROPERTY_SLAMD_ADDRESS = "SLAMD_ADDRESS";
/**
* The name of the configuration property that specifies the resource monitor
* client port for the SLAMD server.
*/
public static final String PROPERTY_SLAMD_MONITOR_PORT = "SLAMD_MONITOR_PORT";
/**
* The name of the configuration property that specifies the stat port for the
* SLAMD server.
*/
public static final String PROPERTY_SLAMD_STAT_PORT = "SLAMD_STAT_PORT";
/**
* The name of the configuration property that specifies the client address.
*/
public static final String PROPERTY_CLIENT_ADDRESS = "CLIENT_ADDRESS";
/**
* The name of the configuration property that specifies whether to enable
* real-time statistics tracking.
*/
public static final String PROPERTY_ENABLE_RT = "ENABLE_REAL_TIME_STATS";
/**
* The name of the configuration property that specifies the interval for
* reporting real-time statistics.
*/
public static final String PROPERTY_RT_INTERVAL = "REAL_TIME_REPORT_INTERVAL";
/**
* The name of the configuration property that indicates whether to
* automatically reconnect to the server if the connection is lost.
*/
public static final String PROPERTY_AUTO_RECONNECT = "AUTO_RECONNECT";
/**
* The name of the configuration property that specifies the authentication
* ID.
*/
public static final String PROPERTY_AUTH_ID = "AUTH_ID";
/**
* The name of the configuration property that specifies the authentication
* password.
*/
public static final String PROPERTY_AUTH_PW = "AUTH_PASS";
/**
* The name of the configuration property that specifies whether to use SSL.
*/
public static final String PROPERTY_USE_SSL = "USE_SSL";
/**
* The name of the configuration property that specifies whether to blindly
* trust any certificate.
*/
public static final String PROPERTY_BLIND_TRUST = "BLIND_TRUST";
/**
* The name of the configuration property that specifies the path to the SSL
* keystore.
*/
public static final String PROPERTY_KEY_STORE = "SSL_KEY_STORE";
/**
* The name of the configuration property that specifies the password for the
* SSL keystore.
*/
public static final String PROPERTY_KEY_PASS = "SSL_KEY_PASS";
/**
* The name of the configuration property that specifies the path to the SSL
* trust store.
*/
public static final String PROPERTY_TRUST_STORE = "SSL_TRUST_PASS";
/**
* The name of the configuration property that specifies the password for the
* SSL trust store.
*/
public static final String PROPERTY_TRUST_PASS = "SSL_TRUST_PASS";
/**
* The name of the configuration property that specifies whether to enable
* verbose mode.
*/
public static final String PROPERTY_VERBOSE = "VERBOSE_MODE";
/**
* The length of time that the monitor client should sleep between attempts to
* reconnect to the SLAMD server if it is supposed to automatically reconnect.
*/
public static final int SERVER_DOWN_RECONNECT_TIME = 30000;
// Indicates whether the client should automatically attempt to reconnect to
// the SLAMD server after it has disconnected.
private boolean autoReconnect = false;
// Indicates whether the client should enable reporting statistics to the
// SLAMD server while a job is in progress.
private boolean enableRealTimeStats = false;
// Indicates whether the client should blindly trust the server's SSL cert.
private boolean blindTrust = false;
// Indicates whether the client should use SSL to communicate with the SLAMD
// server.
private boolean useSSL = false;
// Indicates whether the client should use time synchronization with the SLAMD
// server.
private boolean useTimeSync = true;
// Indicates whether this client is operating in verbose mode.
private boolean verboseMode = false;
// The type of authentication to use.
private int authType = Constants.AUTH_TYPE_NONE;
// The port number to use when communicating with the SLAMD server.
private int slamdPort = Constants.DEFAULT_MONITOR_LISTENER_PORT_NUMBER;
// The port number to use when communicating with the SLAMD server's stat
// listener.
private int slamdStatPort = Constants.DEFAULT_STAT_LISTENER_PORT_NUMBER;
// The interval to use when reporting real-time statistics to the SLAMD
// server.
private int statReportInterval = Constants.DEFAULT_STAT_REPORT_INTERVAL;
// The authentication ID to use when connecting to the SLAMD server.
private String authID = null;
// The password to use when connecting to the SLAMD server.
private String authPW = null;
// The local address to use for the client.
private String clientAddress = null;
// The path to the directory containing the resource monitor configuration
// files.
private String configDirectory = "config";
// The address of the SLAMD server.
private String slamdHost = "127.0.0.1";
// The location of the JSSE key store that will be used if the communication
// between the client and the server is SSL-based.
private String sslKeyStore = null;
// The password needed to access the JSSE key store.
private String sslKeyStorePassword = null;
// The location of the JSSE trust store that will be used if the communication
// between the client and the server is SSL-based.
private String sslTrustStore = null;
// The password needed to access the JSSE trust store.
private String sslTrustStorePassword = null;
/**
* Passes off all the work to the constructor so that we can pass in a
* reference to this class to the client.
*
* @param args The command-line arguments provided to this application.
*/
public static void main(String[] args)
{
new CommandLineResourceMonitorClient(args);
}
/**
* Parses the command line parameters and connects to the SLAMD server to
* accept and process resource monitor requests.
*
* @param args The command-line arguments provided to this application.
*/
public CommandLineResourceMonitorClient(String[] args)
{
// See if a configuration file was specified. If so, then use it to
// initialize the client settings.
for (int i=0; i < args.length; i++)
{
if (args[i].equals("-f"))
{
processConfigFile(args[++i]);
break;
}
}
// Parse the command-line parameters
for (int i=0; i < args.length; i++)
{
if (args[i].equals("-h"))
{
slamdHost = args[++i];
}
else if (args[i].equals("-p"))
{
slamdPort = Integer.parseInt(args[++i]);
}
else if (args[i].equals("-P"))
{
slamdStatPort = Integer.parseInt(args[++i]);
}
else if (args[i].equals("-C"))
{
clientAddress = args[++i];
}
else if (args[i].equals("-D"))
{
authID = args[++i];
}
else if (args[i].equals("-w"))
{
authPW = args[++i];
}
else if (args[i].equals("-c"))
{
configDirectory = args[++i];
}
else if (args[i].equals("-S"))
{
useSSL = true;
}
else if (args[i].equals("-B"))
{
blindTrust = true;
}
else if (args[i].equals("-k"))
{
sslKeyStore = args[++i];
}
else if (args[i].equals("-K"))
{
sslKeyStorePassword = args[++i];
}
else if (args[i].equals("-t"))
{
sslTrustStore = args[++i];
}
else if (args[i].equals("-T"))
{
sslTrustStorePassword = args[++i];
}
else if (args[i].equals("-r"))
{
autoReconnect = true;
}
else if (args[i].equals("-s"))
{
enableRealTimeStats = true;
}
else if (args[i].equals("-I"))
{
statReportInterval = Integer.parseInt(args[++i]);
}
else if (args[i].equals("-Y"))
{
useTimeSync = false;
}
else if (args[i].equals("-v"))
{
verboseMode = true;
}
else if (args[i].equals("-H"))
{
displayUsage();
System.exit(0);
}
else if (args[i].equals("-f"))
{
// Already handled this.
i++;
}
else
{
System.err.println("ERROR: Unrecognized argument \"" + args[i] + '"');
displayUsage();
System.exit(1);
}
}
// Make sure that the configuration directory was specified.
if (configDirectory == null)
{
System.err.println("ERROR: No configuration directory provided (use " +
"-c)");
displayUsage();
System.exit(1);
}
// Determine if we should use authentication.
if ((authID != null) && (authPW != null))
{
authType = Constants.AUTH_TYPE_SIMPLE;
}
else if (authID != null)
{
System.err.println("WARNING: Auth ID provided but no password");
System.err.println(" No authentication will be performed.");
}
else if (authPW != null)
{
System.err.println("WARNING: Auth password provided but no auth ID");
System.err.println(" No authentication will be performed.");
}
// Instantiate the resource monitor client and use it to process requests.
ResourceMonitorClient monitorClient = null;
while (true)
{
try
{
monitorClient = new ResourceMonitorClient(clientAddress, slamdHost,
slamdPort, useSSL, blindTrust, sslKeyStore, sslKeyStorePassword,
sslTrustStore, sslTrustStorePassword, authType, authID, authPW,
useTimeSync, this, configDirectory, enableRealTimeStats,
statReportInterval, slamdStatPort);
monitorClient.handleRequests();
}
catch (ClientException ce)
{
System.err.println(ce.getMessage());
if (! ce.stillAvailable())
{
System.err.println("This resource monitor client will shut down.");
break;
}
}
catch (SLAMDException se)
{
System.err.println(se.getMessage());
}
System.err.println("Disconnected from the SLAMD server.");
if (autoReconnect)
{
System.err.println("Sleeping before attempt to reconnect...");
try
{
Thread.sleep(SERVER_DOWN_RECONNECT_TIME);
} catch (InterruptedException ie) {}
continue;
}
else
{
break;
}
}
}
/**
* Processes the contents of the specified config file.
*
* @param configFile The path to the configuration file to process.
*/
public void processConfigFile(String configFile)
{
Properties properties = new Properties();
try
{
properties.load(new FileInputStream(configFile));
}
catch (IOException ioe)
{
System.err.println("ERROR: Unable to load properties file \"" +
configFile + "\": " + ioe);
System.exit(1);
}
Iterator keys = properties.keySet().iterator();
while (keys.hasNext())
{
String name = (String) keys.next();
String value = properties.getProperty(name, null);
if (value == null)
{
continue;
}
if (name.equals(PROPERTY_SLAMD_ADDRESS))
{
slamdHost = value;
}
else if (name.equals(PROPERTY_SLAMD_MONITOR_PORT))
{
slamdPort = Integer.parseInt(value);
}
else if (name.equals(PROPERTY_SLAMD_STAT_PORT))
{
slamdStatPort = Integer.parseInt(value);
}
else if (name.equals(PROPERTY_CLIENT_ADDRESS))
{
clientAddress = value;
}
else if (name.equals(PROPERTY_ENABLE_RT))
{
if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("yes") ||
value.equalsIgnoreCase("on") || value.equalsIgnoreCase("1"))
{
enableRealTimeStats = true;
}
else if (value.equalsIgnoreCase("false") ||
value.equalsIgnoreCase("no") ||
value.equalsIgnoreCase("off") || value.equalsIgnoreCase("0"))
{
enableRealTimeStats = false;
}
else
{
System.err.println("ERROR: Cannot interpret the value of the " +
PROPERTY_ENABLE_RT + " property as a Boolean.");
System.exit(1);
}
}
else if (name.equals(PROPERTY_RT_INTERVAL))
{
statReportInterval = Integer.parseInt(value);
}
else if (name.equals(PROPERTY_AUTO_RECONNECT))
{
if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("yes") ||
value.equalsIgnoreCase("on") || value.equalsIgnoreCase("1"))
{
autoReconnect = true;
}
else if (value.equalsIgnoreCase("false") ||
value.equalsIgnoreCase("no") ||
value.equalsIgnoreCase("off") || value.equalsIgnoreCase("0"))
{
autoReconnect = false;
}
else
{
System.err.println("ERROR: Cannot interpret the value of the " +
PROPERTY_AUTO_RECONNECT +
" property as a Boolean.");
System.exit(1);
}
}
else if (name.equals(PROPERTY_AUTH_ID))
{
authID = value;
}
else if (name.equals(PROPERTY_AUTH_PW))
{
authPW = value;
}
else if (name.equals(PROPERTY_USE_SSL))
{
if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("yes") ||
value.equalsIgnoreCase("on") || value.equalsIgnoreCase("1"))
{
useSSL = true;
}
else if (value.equalsIgnoreCase("false") ||
value.equalsIgnoreCase("no") ||
value.equalsIgnoreCase("off") || value.equalsIgnoreCase("0"))
{
useSSL = false;
}
else
{
System.err.println("ERROR: Cannot interpret the value of the " +
PROPERTY_USE_SSL + " property as a Boolean.");
System.exit(1);
}
}
else if (name.equals(PROPERTY_BLIND_TRUST))
{
if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("yes") ||
value.equalsIgnoreCase("on") || value.equalsIgnoreCase("1"))
{
blindTrust = true;
}
else if (value.equalsIgnoreCase("false") ||
value.equalsIgnoreCase("no") ||
value.equalsIgnoreCase("off") || value.equalsIgnoreCase("0"))
{
blindTrust = false;
}
else
{
System.err.println("ERROR: Cannot interpret the value of the " +
PROPERTY_BLIND_TRUST + " property as a Boolean.");
System.exit(1);
}
}
else if (name.equals(PROPERTY_KEY_STORE))
{
sslKeyStore = value;
}
else if (name.equals(PROPERTY_KEY_PASS))
{
sslKeyStorePassword = value;
}
else if (name.equals(PROPERTY_TRUST_STORE))
{
sslTrustStore = value;
}
else if (name.equals(PROPERTY_TRUST_PASS))
{
sslTrustStorePassword = value;
}
else if (name.equals(PROPERTY_VERBOSE))
{
if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("yes") ||
value.equalsIgnoreCase("on") || value.equalsIgnoreCase("1"))
{
verboseMode = true;
}
else if (value.equalsIgnoreCase("false") ||
value.equalsIgnoreCase("no") ||
value.equalsIgnoreCase("off") || value.equalsIgnoreCase("0"))
{
verboseMode = false;
}
else
{
System.err.println("ERROR: Cannot interpret the value of the " +
PROPERTY_VERBOSE + " property as a Boolean.");
System.exit(1);
}
}
}
}
/**
* Displays usage information for this program.
*/
public void displayUsage()
{
String eol = Constants.EOL;
System.err.println(
"USAGE: java " + getClass().getName() + " [options]" + eol +
" where [options] include:" + eol +
"-f {file} -- The path to the monitor client configuration file" + eol +
"-h {host} -- The address of the SLAMD server" + eol +
"-p {port} -- The port number of the SLAMD server" + eol +
"-P {port} -- The port number that the SLAMD server uses for " + eol +
" collecting real-time statistics. " + eol +
"-C {addr} -- The local source address to use for the client." + eol +
"-D {authid} -- The ID to use to authenticate to the SLAMD server" + eol +
"-w {authpw} -- The password for the authentication ID" + eol +
"-c {dir} -- The name of the directory containing the monitor " + eol +
" configuration files." + eol +
"-S -- Indicates that the client should communicate with the" + eol +
" SLAMD server over SSL." + eol +
"-B -- Indicates that the client blindly trust any SSL" + eol +
" certificate presented by the SLAMD server." + eol +
"-k {file} -- The location of the JSSE key store." + eol +
"-K {pw} -- The password needed to access the JSSE key store." + eol +
"-t {file} -- The location of the JSSE trust store." + eol +
"-T {pw} -- The password needed to access the JSSE trust store." + eol +
"-r -- Automatically attempt to reconnect to the SLAMD " + eol +
" server if the connection is closed due to shutdown" + eol +
"-s -- Indicates that the client should enable real-time " + eol +
" statistics reporting to the SLAMD server." + eol +
"-I {value} -- Specifies the interval (in seconds) to use when " + eol +
" reporting real-time stats to the SLAMD server." + eol +
"-L -- Disable time synchronization with the SLAMD server" + eol +
"-v -- Operate in verbose mode" + eol +
"-H -- Show this usage information" + eol
);
}
/**
* Writes the specified message to standard output.
*
* @param message The message to be written.
*/
public void writeMessage(String message)
{
System.out.println(message);
}
/**
* Writes the specified message to standard output if verbose mode is enabled.
*
* @param message The message to be written.
*/
public void writeVerbose(String message)
{
if (verboseMode)
{
System.out.println(message);
}
}
/**
* Indicates whether the message writer is using verbose mode and therefore
* will display messages written with the <CODE>writeVerbose</CODE> method.
*
* @return <CODE>true</CODE> if the message writer is using verbose mode, or
* <CODE>false</CODE> if not.
*/
public boolean usingVerboseMode()
{
return verboseMode;
}
}