Skip to content

Instantly share code, notes, and snippets.

@stalep
Created September 7, 2017 13:03
Show Gist options
  • Save stalep/3689c960ea7ed911a6906f79af095d7a to your computer and use it in GitHub Desktop.
Save stalep/3689c960ea7ed911a6906f79af095d7a to your computer and use it in GitHub Desktop.
package org.jboss.performance.monitor;
import org.jboss.aesh.cl.CommandDefinition;
import org.jboss.aesh.cl.Option;
import org.jboss.aesh.cl.OptionList;
import org.jboss.aesh.cl.completer.FileOptionCompleter;
import org.jboss.aesh.cl.converter.CLConverter;
import org.jboss.aesh.cl.validator.CommandValidator;
import org.jboss.aesh.cl.validator.CommandValidatorException;
import org.jboss.aesh.cl.validator.OptionValidator;
import org.jboss.aesh.cl.validator.OptionValidatorException;
import org.jboss.aesh.console.command.Command;
import org.jboss.aesh.console.command.CommandOperation;
import org.jboss.aesh.console.command.CommandResult;
import org.jboss.aesh.console.command.ConsoleCommand;
import org.jboss.aesh.console.command.invocation.CommandInvocation;
import org.jboss.aesh.terminal.Key;
import org.jboss.aesh.terminal.Shell;
import org.jboss.as.controller.client.ModelControllerClient;
import org.xml.sax.SAXException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.jar.JarFile;
/**
* @author <a href="mailto:stale.pedersen@jboss.org">Ståle W. Pedersen</a>
*/
@CommandDefinition(name = "monitor",
description = "Monitor JBoss resources",
validator = MonitorResourcesCommand.MonitorValidator.class)
public class MonitorResourcesCommand implements Command, ConsoleCommand {
private static org.jboss.logging.Logger logger = org.jboss.logging.Logger.getLogger(MonitorResourcesCommand.class);
private static final String[] deploymentSubsystemValues =
new String[]{"ejb3", "jpa", "web", "webservices", "all", "none"};
private static final String[] subsystemValues =
new String[]{"ejb3", "datasources", "web", "transactions", "messaging", "jca", "all", "none"};
@Option(shortName = 'h', hasValue = true,
converter = InetConverter.class,
description = "Host name of the server to be monitored (defaults to localhost). ")
private InetAddress host;
@Option(shortName = 'p', hasValue = true, defaultValue = "9999",
validator = PortValidator.class,
description = "Port of the management interface, if not the default port. "
+ "Could be the port of the native interface of the domain controller.")
private int port;
@Option(shortName = 'f', hasValue = true, required = true,
completer = FileOptionCompleter.class,
converter = PrintWriterConverter.class,
defaultValue = "Filename to log usage statistics to.")
private PrintWriter file;
@Option(shortName = 'i', hasValue = true, defaultValue = "1000",
description = "Interval in milliseconds to gather usage statistics (defaults to once per second).")
private long interval;
@Option(shortName = 't', hasValue = true, description = "Timestamp date format.", defaultValue = "HH:mm:ss:SSS")
private String timestamp;
@Option(shortName = 'c', hasValue = true, description = "Configuration file for the standalone server you want to monitor.")
private File config;
@OptionList(name = "domain-config", shortName = 'd', valueSeparator = ',',
description = "Give the two configuration files, starting with the host configuration (e.g. host.xml), and then the domain.")
private List<File> domainConfig;
@OptionList(shortName = 's', valueSeparator = ',',
defaultValue = {"ejb3", "datasources", "web", "transactions", "messaging", "jca", "all", "none"},
validator = SubsystemsValidator.class,
description = "List of Subsystems you would like to monitor - valid values are threads, ejb3, datasources, web, transactions, "
+ "messaging, jca and all or none - defaults to all (none is for when you want to do monitoring of deployments only)")
private List<String> subsystems;
@OptionList(name = "domain-server-names", shortName = 'n', valueSeparator = ',',
description = "List of server names in a domain to monitor - values are the server names listed in host.xml, default to all "
+ "(based on what it finds in host.xml).")
private List<String> domainServerNames;
@OptionList(shortName = 'a', valueSeparator = ',',
converter = JarFileConverter.class,
description = "List of deployments (or applications) to monitor - values are the names of the deployments such as <name>.ear, "
+ "<name>.war, with the path information to read the files.")
private List<JarFile> deployments;
@OptionList(name = "deployment-subsystems", shortName = 'b', valueSeparator = ',',
defaultValue = {"ejb3", "jpa", "web", "webservices", "all", "none"},
validator = DeploymentSubsystemsValidator.class,
description = "List of subsystems, specific to your deployments, you would like to monitor - valid values are ejb3, jpa, web, webservices, "
+ "all or none - defaults to all (none is not valid if none is specified for subsystems)")
private List<String> deploymentSubsystems;
@Option(name = "help", hasValue = false)
private boolean help;
private boolean domainMode = false;
private boolean deploymentsToMonitor = false;
private String username;
private String password;
private boolean attached = false;
private Status status = Status.DEFAULT;
private ModelControllerClient client;
private Monitor[] monitors;
private ServerConfiguration serverConfiguration = null;
private Shell shell;
public MonitorResourcesCommand() {
}
public List<String> getSubsystems() {
return subsystems;
}
public List<String> getDomainServerNames() {
return domainServerNames;
}
public List<JarFile> getDeployments() {
return deployments;
}
public List<String> getDeploymentSubsystems() {
return deploymentSubsystems;
}
public File getConfig() {
return config;
}
public List<File> getDomainConfig() {
return domainConfig;
}
@Override
public CommandResult execute(CommandInvocation commandInvocation) {
if(help)
commandInvocation.getShell().out().println(commandInvocation.getHelpInfo("monitor"));
else {
this.shell = commandInvocation.getShell();
attached = true;
commandInvocation.attachConsoleCommand(this);
initClient(commandInvocation);
setupMonitoring();
startMonitoring();
}
return CommandResult.SUCCESS;
}
private void connectWithPassword() {
client = NativeClient.getInstance(host, port, username, password);
}
private void setupMonitoring() {
// Setup for monitoring the specified subsystems and/or deployments
if(serverConfiguration != null)
monitors = serverConfiguration.createMonitors(client, subsystems, deployments, deploymentSubsystems);
else
shell.out().println("ServerConfiguration is null, cannot start monitors");
}
private void initClient(CommandInvocation ci) {
// Try connecting to the server without authentication first
ModelControllerClient unauthenticatedClient = NativeClient.getInstance(host, port);
if (NativeClient.needsToAuthenticate(unauthenticatedClient)) {
// Failed to connect, must authenticate
if (username == null && password == null) {
ci.getShell().out().println("Connection failed, so you must authenticate!");
ci.getShell().out().print("Enter username: ");
ci.getShell().out().flush();
status = Status.USERNAME;
}
try {
unauthenticatedClient.close();
} catch (IOException e) {
logger.info("Tried to close the unauthenticated client, because we needed to authenticate, but it failed!");
}
}
else {
client = unauthenticatedClient;
}
}
private void startMonitoring() {
MonitorResourcesRunner runner = new MonitorResourcesRunner(logger, monitors, client, file, interval, timestamp);
try {
runner.startMonitoring();
}
catch (InitialiseMonitorResourcesException e) {
shell.out().println("MonitorResources failed to start with the following error message: "+e.getMessage());
}
}
public void shutdown() {
if(client != null)
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private void setServerConfiguration(ServerConfiguration serverConfiguration) {
this.serverConfiguration = serverConfiguration;
}
@Override
public void processOperation(CommandOperation commandOperation) throws IOException {
if(status == Status.DEFAULT) {
}
else if(status == Status.USERNAME) {
if(commandOperation.getInputKey() == Key.ENTER) {
status = Status.PASSWORD;
shell.out().print("Enter password: ");
shell.out().flush();
}
else
username = username + commandOperation.getInputKey().getAsChar();
}
else if(status == Status.PASSWORD) {
if(commandOperation.getInputKey() == Key.ENTER) {
connectWithPassword();
status = Status.PASSWORD;
}
else
password = password + commandOperation.getInputKey().getAsChar();
}
}
@Override
public boolean isAttached() {
return attached;
}
public static class PortValidator implements OptionValidator<Integer> {
@Override
public void validate(Integer port) throws OptionValidatorException {
if(port < 0 || port > 65535)
throw new OptionValidatorException("Hey, looks like you specified an invalid port number: "+port);
}
}
public static class InetConverter implements CLConverter<InetAddress> {
@Override
public InetAddress convert(String host) throws OptionValidatorException {
try {
return InetAddress.getByName(host);
}
catch (UnknownHostException e) {
throw new OptionValidatorException("Hey, looks like the host is invalid: " + e.getMessage());
}
}
}
public static class JarFileConverter implements CLConverter<JarFile> {
@Override
public JarFile convert(String deployment) throws OptionValidatorException {
try {
return new JarFile(deployment);
}
catch (IOException e) {
throw new OptionValidatorException(
"The deployment file you specified was invalid: "+e.getMessage());
}
}
}
public static class PrintWriterConverter implements CLConverter<PrintWriter> {
@Override
public PrintWriter convert(String file) throws OptionValidatorException {
try {
return new PrintWriter(new File(file));
}
catch (IOException e) {
throw new OptionValidatorException("Could not open file: "+file+", "+e.getMessage());
}
}
}
public static class DeploymentSubsystemsValidator implements OptionValidator<String> {
@Override
public void validate(String value) throws OptionValidatorException {
for(String subsystem : deploymentSubsystemValues)
if(subsystem.equals(value))
return;
throw new OptionValidatorException("Value "+value+" is not accepted, must one of: "+ Arrays.toString(deploymentSubsystemValues));
}
}
public static class SubsystemsValidator implements OptionValidator<String> {
@Override
public void validate(String value) throws OptionValidatorException {
for(String subsystem : subsystemValues)
if(subsystem.equals(value))
return;
throw new OptionValidatorException("Value "+value+" is not accepted, must one of: "+ Arrays.toString(deploymentSubsystemValues));
}
}
public class MonitorValidator implements CommandValidator<MonitorResourcesCommand> {
@Override
public void validate(MonitorResourcesCommand command) throws CommandValidatorException {
if(command.getConfig() == null || !command.getConfig().isFile()) {
if(command.getDomainConfig() == null || command.getDomainConfig().size() == 0)
throw new CommandValidatorException("You did not specify a server configuration file (either domain or standalone) to parse for subsystem information!");
if(command.getDomainConfig().size() != 2)
throw new CommandValidatorException("You did not specify the correct number of arguments for domain-config option. You should specify two files, the host and domain xml files.");
}
if(command.getSubsystems() == null || command.getSubsystems().size() == 0) {
command.subsystems = ServerConfiguration.subsystemsToMonitor();
}
else {
if (command.subsystems.size() == 1 && (command.subsystems.get(0).equals("all"))) {
command.subsystems = ServerConfiguration.subsystemsToMonitor();
}
else if (command.subsystems.size() == 1 && (command.subsystems.get(0).equals("none"))) {
command.subsystems = null;
}
else {
boolean subsystemsAreValid = false;
List<String> possibleSubsystemsToMonitor = ServerConfiguration.subsystemsToMonitor();
if (possibleSubsystemsToMonitor.containsAll(command.subsystems)) {
subsystemsAreValid = true;
}
if (!subsystemsAreValid) {
throw new CommandValidatorException(
"The subsystems you specified on the command line are not completely valid." +
"Valid subsystems are " + ServerConfiguration.subsystemsToMonitor() + " or all or none.");
}
}
}
if(command.getDeployments() != null) {
command.deploymentsToMonitor = true;
// So we are doing deployment monitoring, so let's see if the deployment-subsystems are valid
if (command.deploymentSubsystems == null) {
command.deploymentSubsystems = ServerConfiguration.deploymentSubsystemsToMonitor();
}
else {
if (command.deploymentSubsystems.size() == 1 &&
(command.deploymentSubsystems.get(0).equals("all"))) {
command.deploymentSubsystems = ServerConfiguration.deploymentSubsystemsToMonitor();
}
else if (command.deploymentSubsystems.size() == 1 &&
(command.deploymentSubsystems.get(0).equals("none"))) {
if (command.subsystems == null) {
// Specified no monitoring at all, with none on subsystems, and none on deployment subsystems
throw new CommandValidatorException(
"You must specify a subsystem to monitor either for general subsystems, or deployment specific subsystems, but both were specified as none." +
"Valid subsystems are " + ServerConfiguration.subsystemsToMonitor() +
"Valid deloyment-subsystems are " + ServerConfiguration.deploymentSubsystemsToMonitor());
}
command.deploymentSubsystems = null;
}
else {
boolean deploymentSubsystemsAreValid = false;
List<String> possibleDeploymentSubsystemsToMonitor = ServerConfiguration.deploymentSubsystemsToMonitor();
if (possibleDeploymentSubsystemsToMonitor.containsAll(command.deploymentSubsystems)) {
deploymentSubsystemsAreValid = true;
}
if (!deploymentSubsystemsAreValid) {
throw new CommandValidatorException(
"The deployment-subsystems you specified on the command line are not completely valid." +
"Valid deployment-subsystems are " + ServerConfiguration.deploymentSubsystemsToMonitor() + "or all or none.");
}
}
}
}
parseServerConfigFile(command);
}
private void parseServerConfigFile(MonitorResourcesCommand command) throws CommandValidatorException {
// Parse the servers configuration file
SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
ServerConfiguration serverConfiguration = null;
serverConfiguration = ServerConfigurationFactory.createServerConfiguration(
command.domainMode, command.deploymentsToMonitor, command.deployments);
try {
SAXParser saxParser = saxParserFactory.newSAXParser();
if (command.domainMode) {
saxParser.parse(command.domainConfig.get(0), serverConfiguration);
saxParser.parse(command.domainConfig.get(1), serverConfiguration);
}
else {
saxParser.parse(command.config, serverConfiguration);
}
}
catch (SAXException e) {
throw new CommandValidatorException("Configuration file may not be valid: "+e.getMessage());
}
catch (ParserConfigurationException e) {
throw new CommandValidatorException("Parser configuration error: "+e.getMessage());
}
catch (IOException e) {
throw new CommandValidatorException("Looks like the file name may not be correct: "+e.getMessage());
}
// Check if the host and port passed on the command line - or the defaults, match the configuration file
if (!serverConfiguration.getManagementInterfaceHostName().equalsIgnoreCase(command.host.getHostName()) &&
!serverConfiguration.getManagementInterfaceHostName().equalsIgnoreCase(command.host.getHostAddress())) {
throw new CommandValidatorException(
"Specified and/or default host does not match the host specified in the configuration file!" +
"Specified and/or default host is : " + command.host +
"Host in the configuration file is: " + serverConfiguration.getManagementInterfaceHostName());
}
if (serverConfiguration.getManagementInterfacePort() != command.port) {
throw new CommandValidatorException(
"Specified and/or default port does not match the port specified in the configuration file!" +
"Specified and/or default port is : " + command.port +
"Port in the configuration file is: " + serverConfiguration.getManagementInterfacePort());
}
//todo: this doesnt do much atm, serverNames is not used
if (command.domainMode) {
List<String> serverNames;
if (command.domainServerNames == null) {
serverNames = serverConfiguration.getServerNames();
} else {
serverNames = command.domainServerNames;
if (serverNames.size() == 1 && (serverNames.get(0).equals("all"))) {
serverNames = serverConfiguration.getServerNames();
} else {
boolean serverNamesAreValid = false;
List<String> possibleServerNames = serverConfiguration.getServerNames();
if (possibleServerNames.containsAll(serverNames)) {
serverNamesAreValid = true;
}
if (!serverNamesAreValid) {
throw new CommandValidatorException(
"The server names you specified on the command line are not valid." +
"Valid server names from your configuration are " + serverConfiguration.getServerNames() + " or all");
}
}
}
}
// Check to see that the XML configuration contained information for address for all the subsystems specified for monitoring
if (command.subsystems != null) {
List<String> removedSubsystems = new ArrayList<String>();
for (String subsystem : command.subsystems) {
if (!serverConfiguration.subsystemHasAddressInConfiguration(subsystem)) {
logger.warn("Missing configuration needed for monitoring of subsystem " + subsystem + "!");
logger.warn("Removing " + subsystem + " from monitoring.");
removedSubsystems.add(subsystem);
}
}
command.subsystems.removeAll(removedSubsystems);
if (command.subsystems.isEmpty()) {
throw new CommandValidatorException(
"Due to previous warnings, there are no valid subsystems to monitor!" +
"Exiting due to previous errors. You will need to configure your server correctly for monitoring.");
}
}
command.setServerConfiguration(serverConfiguration);
}
}
enum Status {
USERNAME,
PASSWORD,
DEFAULT;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment