org.apache.helix.tools.JmxDumper.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.helix.tools.JmxDumper.java

Source

package org.apache.helix.tools;

/*
 * 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.apache.log4j.Logger;

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 = Logger.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 {
        try {
            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);
        } catch (Exception e) {
            _logger.error("fail to get all existing mbeans in " + _domain, e);
            throw e;
        }
    }

    @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);
    }
}