blob: 824c7b6d9682f028624fb6a5fb49b700ea89c786 [file] [log] [blame]
package org.apache.helix.tools.commandtools;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import java.io.FileWriter;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanServerConnection;
import javax.management.MBeanServerDelegate;
import javax.management.MBeanServerNotification;
import javax.management.Notification;
import javax.management.NotificationListener;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.relation.MBeanServerNotificationFilter;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class JmxDumper implements NotificationListener {
public static final String help = "help";
public static final String domain = "domain";
public static final String fields = "fields";
public static final String pattern = "pattern";
public static final String operations = "operations";
public static final String period = "period";
public static final String className = "className";
public static final String outputFile = "outputFile";
public static final String jmxUrl = "jmxUrl";
public static final String sampleCount = "sampleCount";
private static final Logger _logger = LoggerFactory.getLogger(JmxDumper.class);
String _domain;
MBeanServerConnection _mbeanServer;
String _beanClassName;
String _namePattern;
int _samplePeriod;
Map<ObjectName, ObjectName> _mbeanNames = new ConcurrentHashMap<ObjectName, ObjectName>();
Timer _timer;
String _outputFileName;
List<String> _outputFields = new ArrayList<String>();
Set<String> _operations = new HashSet<String>();
PrintWriter _outputFile;
int _samples = 0;
int _targetSamples = -1;
String _jmxUrl;
public JmxDumper(String jmxService, String domain, String beanClassName, String namePattern,
int samplePeriod, List<String> fields, List<String> operations, String outputfile,
int sampleCount) throws Exception {
_jmxUrl = jmxService;
_domain = domain;
_beanClassName = beanClassName;
_samplePeriod = samplePeriod;
_outputFields.addAll(fields);
_operations.addAll(operations);
_outputFileName = outputfile;
_namePattern = namePattern;
_targetSamples = sampleCount;
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://" + _jmxUrl + "/jmxrmi");
JMXConnector jmxc = JMXConnectorFactory.connect(url, null);
_mbeanServer = jmxc.getMBeanServerConnection();
MBeanServerNotificationFilter filter = new MBeanServerNotificationFilter();
filter.enableAllObjectNames();
_mbeanServer.addNotificationListener(MBeanServerDelegate.DELEGATE_NAME, this, filter, null);
init();
_timer = new Timer(true);
_timer.scheduleAtFixedRate(new SampleTask(), _samplePeriod, _samplePeriod);
}
class SampleTask extends TimerTask {
@Override
public void run() {
List<ObjectName> errorMBeans = new ArrayList<ObjectName>();
_logger.info("Sampling " + _mbeanNames.size() + " beans");
for (ObjectName beanName : _mbeanNames.keySet()) {
MBeanInfo info;
try {
info = _mbeanServer.getMBeanInfo(beanName);
} catch (Exception e) {
_logger.error(e.getMessage() + " removing it");
errorMBeans.add(beanName);
continue;
}
if (!info.getClassName().equals(_beanClassName)) {
_logger.warn("Skip: className " + info.getClassName() + " expected : " + _beanClassName);
continue;
}
StringBuffer line = new StringBuffer();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-hh:mm:ss:SSS");
String date = dateFormat.format(new Date());
line.append(date + " ");
line.append(beanName.toString() + " ");
MBeanAttributeInfo[] infos = info.getAttributes();
Map<String, MBeanAttributeInfo> infoMap = new HashMap<String, MBeanAttributeInfo>();
for (MBeanAttributeInfo infoItem : infos) {
infoMap.put(infoItem.getName(), infoItem);
}
for (String outputField : _outputFields) {
try {
if (infoMap.containsKey(outputField)) {
Object mbeanAttributeValue = _mbeanServer.getAttribute(beanName, outputField);
line.append(mbeanAttributeValue.toString() + " ");
} else {
_logger.warn(outputField + " not found");
line.append("null ");
}
} catch (Exception e) {
_logger.error("Error:", e);
line.append("null ");
continue;
}
}
MBeanOperationInfo[] operations = info.getOperations();
Map<String, MBeanOperationInfo> opeMap = new HashMap<String, MBeanOperationInfo>();
for (MBeanOperationInfo opeItem : operations) {
opeMap.put(opeItem.getName(), opeItem);
}
for (String ope : _operations) {
if (opeMap.containsKey(ope)) {
try {
_mbeanServer.invoke(beanName, ope, new Object[0], new String[0]);
// System.out.println(ope+" invoked");
} catch (Exception e) {
_logger.error("Error:", e);
continue;
}
}
}
_outputFile.println(line.toString());
// System.out.println(line);
}
for (ObjectName deadBean : errorMBeans) {
_mbeanNames.remove(deadBean);
}
_samples++;
// System.out.println("samples:"+_samples);
if (_samples == _targetSamples) {
synchronized (JmxDumper.this) {
_logger.info(_samples + " samples done, exiting...");
JmxDumper.this.notifyAll();
}
}
}
}
void init() throws Exception {
Set<ObjectInstance> existingInstances =
_mbeanServer.queryMBeans(new ObjectName(_namePattern), null);
_logger.info("Total " + existingInstances.size() + " mbeans matched " + _namePattern);
for (ObjectInstance instance : existingInstances) {
if (instance.getClassName().equals(_beanClassName)) {
_mbeanNames.put(instance.getObjectName(), instance.getObjectName());
_logger.info("Sampling " + instance.getObjectName());
}
}
FileWriter fos = new FileWriter(_outputFileName);
System.out.println(_outputFileName);
_outputFile = new PrintWriter(fos);
}
@Override
public void handleNotification(Notification notification, Object handback) {
MBeanServerNotification mbs = (MBeanServerNotification) notification;
if (MBeanServerNotification.REGISTRATION_NOTIFICATION.equals(mbs.getType())) {
// System.out.println("Adding mbean " + mbs.getMBeanName());
_logger.info("Adding mbean " + mbs.getMBeanName());
if (mbs.getMBeanName().getDomain().equalsIgnoreCase(_domain)) {
addMBean(mbs.getMBeanName());
}
} else if (MBeanServerNotification.UNREGISTRATION_NOTIFICATION.equals(mbs.getType())) {
// System.out.println("Removing mbean " + mbs.getMBeanName());
_logger.info("Removing mbean " + mbs.getMBeanName());
if (mbs.getMBeanName().getDomain().equalsIgnoreCase(_domain)) {
removeMBean(mbs.getMBeanName());
}
}
}
private void addMBean(ObjectName beanName) {
_mbeanNames.put(beanName, beanName);
}
private void removeMBean(ObjectName beanName) {
_mbeanNames.remove(beanName);
}
public static int processCommandLineArgs(String[] cliArgs) throws Exception {
CommandLineParser cliParser = new GnuParser();
Options cliOptions = constructCommandLineOptions();
CommandLine cmd = null;
try {
cmd = cliParser.parse(cliOptions, cliArgs);
} catch (ParseException pe) {
System.err.println("CommandLineClient: failed to parse command-line options: "
+ pe.toString());
printUsage(cliOptions);
System.exit(1);
}
boolean ret = checkOptionArgsNumber(cmd.getOptions());
if (ret == false) {
printUsage(cliOptions);
System.exit(1);
}
String portStr = cmd.getOptionValue(jmxUrl);
// int portVal = Integer.parseInt(portStr);
String periodStr = cmd.getOptionValue(period);
int periodVal = Integer.parseInt(periodStr);
String domainStr = cmd.getOptionValue(domain);
String classNameStr = cmd.getOptionValue(className);
String patternStr = cmd.getOptionValue(pattern);
String fieldsStr = cmd.getOptionValue(fields);
String operationsStr = cmd.getOptionValue(operations);
String resultFile = cmd.getOptionValue(outputFile);
String sampleCountStr = cmd.getOptionValue(sampleCount, "-1");
int sampleCount = Integer.parseInt(sampleCountStr);
List<String> fields = Arrays.asList(fieldsStr.split(","));
List<String> operations = Arrays.asList(operationsStr.split(","));
JmxDumper dumper = null;
try {
dumper =
new JmxDumper(portStr, domainStr, classNameStr, patternStr, periodVal, fields,
operations, resultFile, sampleCount);
synchronized (dumper) {
dumper.wait();
}
} finally {
if (dumper != null) {
dumper.flushFile();
}
}
return 0;
}
private void flushFile() {
if (_outputFile != null) {
_outputFile.flush();
_outputFile.close();
}
}
private static boolean checkOptionArgsNumber(Option[] options) {
for (Option option : options) {
int argNb = option.getArgs();
String[] args = option.getValues();
if (argNb == 0) {
if (args != null && args.length > 0) {
System.err.println(option.getArgName() + " shall have " + argNb + " arguments (was "
+ Arrays.toString(args) + ")");
return false;
}
} else {
if (args == null || args.length != argNb) {
System.err.println(option.getArgName() + " shall have " + argNb + " arguments (was "
+ Arrays.toString(args) + ")");
return false;
}
}
}
return true;
}
@SuppressWarnings("static-access")
private static Options constructCommandLineOptions() {
Option helpOption =
OptionBuilder.withLongOpt(help).withDescription("Prints command-line options info")
.create();
Option domainOption =
OptionBuilder.withLongOpt(domain).withDescription("Domain of the JMX bean").create();
domainOption.setArgs(1);
domainOption.setRequired(true);
Option fieldsOption =
OptionBuilder.withLongOpt(fields).withDescription("Fields of the JMX bean to sample")
.create();
fieldsOption.setArgs(1);
fieldsOption.setRequired(false);
Option operationOption =
OptionBuilder.withLongOpt(operations).withDescription("Operation to invoke").create();
operationOption.setArgs(1);
operationOption.setRequired(true);
Option periodOption =
OptionBuilder.withLongOpt(period).withDescription("Sampling period in MS").create();
periodOption.setArgs(1);
periodOption.setRequired(false);
Option classOption =
OptionBuilder.withLongOpt(className).withDescription("Classname of the MBean").create();
classOption.setArgs(1);
classOption.setRequired(true);
Option patternOption =
OptionBuilder.withLongOpt(pattern).withDescription("pattern of the MBean").create();
patternOption.setArgs(1);
patternOption.setRequired(true);
Option outputFileOption =
OptionBuilder.withLongOpt(outputFile).withDescription("outputFileName").create();
outputFileOption.setArgs(1);
outputFileOption.setRequired(false);
Option jmxUrlOption =
OptionBuilder.withLongOpt(jmxUrl).withDescription("jmx port to connect to").create();
jmxUrlOption.setArgs(1);
jmxUrlOption.setRequired(true);
Option sampleCountOption =
OptionBuilder.withLongOpt(sampleCount).withDescription("# of samples to take").create();
sampleCountOption.setArgs(1);
sampleCountOption.setRequired(false);
Options options = new Options();
options.addOption(helpOption);
options.addOption(domainOption);
options.addOption(fieldsOption);
options.addOption(operationOption);
options.addOption(classOption);
options.addOption(outputFileOption);
options.addOption(jmxUrlOption);
options.addOption(patternOption);
options.addOption(periodOption);
options.addOption(sampleCountOption);
return options;
}
public static void printUsage(Options cliOptions) {
HelpFormatter helpFormatter = new HelpFormatter();
helpFormatter.printHelp("java " + JmxDumper.class.getName(), cliOptions);
}
public static void main(String[] args) throws Exception {
/*
* List<String> fields = Arrays.asList(new
* String("AvgLatency,MaxLatency,MinLatency,PacketsReceived,PacketsSent").split(","));
* List<String> operations = Arrays.asList(new String("resetCounters").split(","));
* JmxDumper dumper = new JmxDumper(27961, "org.apache.zooKeeperService",
* "org.apache.zookeeper.server.ConnectionBean",
* "org.apache.ZooKeeperService:name0=*,name1=Connections,name2=*,name3=*", 1000, fields,
* operations, "/tmp/1.csv");
* Thread.currentThread().join();
*/
int ret = processCommandLineArgs(args);
System.exit(ret);
}
}