org.opennms.features.jmxconfiggenerator.jmxconfig.JmxDatacollectionConfiggenerator.java Source code

Java tutorial

Introduction

Here is the source code for org.opennms.features.jmxconfiggenerator.jmxconfig.JmxDatacollectionConfiggenerator.java

Source

/*******************************************************************************
 * This file is part of OpenNMS(R).
 *
 * Copyright (C) 2012-2014 The OpenNMS Group, Inc.
 * OpenNMS(R) is Copyright (C) 1999-2014 The OpenNMS Group, Inc.
 *
 * OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc.
 *
 * OpenNMS(R) is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 *
 * OpenNMS(R) is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with OpenNMS(R).  If not, see:
 *      http://www.gnu.org/licenses/
 *
 * For more information contact:
 *     OpenNMS(R) Licensing <license@opennms.org>
 *     http://www.opennms.org/
 *     http://www.opennms.com/
 *******************************************************************************/

package org.opennms.features.jmxconfiggenerator.jmxconfig;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.management.InstanceNotFoundException;
import javax.management.IntrospectionException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanInfo;
import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import javax.management.openmbean.CompositeData;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import javax.xml.bind.JAXB;

import org.apache.commons.lang3.StringUtils;
import org.opennms.features.namecutter.NameCutter;
import org.opennms.xmlns.xsd.config.jmx_datacollection.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author Simon Walter <simon.walter@hp-factory.de>
 * @author Markus Neumann <markus@opennms.com>
 */
public class JmxDatacollectionConfiggenerator {

    private static Logger logger = LoggerFactory.getLogger(JmxDatacollectionConfiggenerator.class);

    private static ObjectFactory xmlObjectFactory = new ObjectFactory();

    private static List<String> standardVmBeans = new ArrayList<String>();

    private static List<String> ignores = new ArrayList<String>();

    private static List<String> numbers = new ArrayList<String>();

    private static List<String> rras = new ArrayList<String>();

    private static Map<String, Integer> aliasMap = new HashMap<String, Integer>();

    private static List<String> aliasList = new ArrayList<String>();

    private static Rrd rrd = new Rrd();
    private static NameCutter nameCutter = new NameCutter();

    static {
        // Domanis directly from JVMs
        standardVmBeans.add("JMImplementation");
        standardVmBeans.add("com.sun.management");
        standardVmBeans.add("java.lang");
        standardVmBeans.add("java.nio");
        standardVmBeans.add("java.util.logging");

        // valid numbertyps
        numbers.add("int");
        numbers.add("long");
        numbers.add("double");
        numbers.add("float");
        numbers.add("java.lang.Long");
        numbers.add("java.lang.Integer");

        // rrd setup
        rrd.setStep(300);
        rras.add("RRA:AVERAGE:0.5:1:2016");
        rras.add("RRA:AVERAGE:0.5:12:1488");
        rras.add("RRA:AVERAGE:0.5:288:366");
        rras.add("RRA:MAX:0.5:288:366");
        rras.add("RRA:MIN:0.5:288:366");
        rrd.getRra().addAll(rras);
    }

    public JmxDatacollectionConfig generateJmxConfigModel(MBeanServerConnection mBeanServerConnection,
            String serviceName, Boolean runStandardVmBeans, Boolean runWritableMBeans,
            Map<String, String> dictionary) {

        logger.debug("Startup values: \n serviceName: " + serviceName + "\n runStandardVmBeans: "
                + runStandardVmBeans + "\n runWritableMBeans: " + runWritableMBeans + "\n dictionary" + dictionary);
        nameCutter.setDictionary(dictionary);
        JmxDatacollectionConfig xmlJmxDatacollectionConfig = xmlObjectFactory.createJmxDatacollectionConfig();
        JmxCollection xmlJmxCollection = xmlObjectFactory.createJmxCollection();

        xmlJmxCollection.setName("JSR160-" + serviceName);
        xmlJmxCollection.setRrd(rrd);
        xmlJmxDatacollectionConfig.getJmxCollection().add(xmlJmxCollection);
        xmlJmxCollection.setMbeans(xmlObjectFactory.createMbeans());

        if (runStandardVmBeans) {
            ignores.clear();
        } else {
            ignores.addAll(standardVmBeans);
        }

        try {
            String[] domains = mBeanServerConnection.getDomains();
            logger.info("Found " + domains.length + " domains");
            logger.info("domains: " + Arrays.toString(domains));
            for (String domainName : domains) {

                // just domains that are relevant for the service
                if (!ignores.contains(domainName)) {
                    logger.info("domain: " + domainName);

                    // for all mBeans of the actual domain
                    for (ObjectInstance jmxObjectInstance : mBeanServerConnection
                            .queryMBeans(new ObjectName(domainName + ":*"), null)) {
                        Mbean xmlMbean = xmlObjectFactory.createMbean();
                        xmlMbean.setObjectname(jmxObjectInstance.getObjectName().toString());
                        String typeAndOthers = StringUtils
                                .substringAfterLast(jmxObjectInstance.getObjectName().getCanonicalName(), "=");
                        xmlMbean.setName(domainName + "." + typeAndOthers);

                        logger.debug("\t" + jmxObjectInstance.getObjectName());

                        MBeanInfo jmxMbeanInfo;
                        try {
                            jmxMbeanInfo = mBeanServerConnection.getMBeanInfo(jmxObjectInstance.getObjectName());
                        } catch (InstanceNotFoundException e) {
                            logger.error("InstanceNotFoundException skipping MBean '{}' message: '{}'",
                                    jmxObjectInstance.getObjectName(), e.getMessage());
                            continue;
                        } catch (IntrospectionException e) {
                            logger.error("IntrospectionException skipping MBean '{}' message: '{}'",
                                    jmxObjectInstance.getObjectName(), e.getMessage());
                            continue;
                        } catch (ReflectionException e) {
                            logger.error("ReflectionException skipping MBean '{}' message: '{}'",
                                    jmxObjectInstance.getObjectName(), e.getMessage());
                            continue;
                        } catch (Throwable e) {
                            logger.error(
                                    "problem during remote call to get MBeanInfo for '{}' skipping this MBean. Message '{}'",
                                    jmxObjectInstance.getObjectName(), e.getMessage());
                            continue;
                        }

                        logger.debug("--- Attributes for " + jmxObjectInstance.getObjectName());

                        for (MBeanAttributeInfo jmxBeanAttributeInfo : jmxMbeanInfo.getAttributes()) {

                            // process just readable mbeans
                            if (jmxBeanAttributeInfo.isReadable()) {
                                // precess writable mbeans if run writable
                                // mbeans is set
                                if (!jmxBeanAttributeInfo.isWritable() || runWritableMBeans) {

                                    logger.debug("Check mBean: '{}', attribute: '{}'",
                                            jmxObjectInstance.getObjectName().toString(),
                                            jmxBeanAttributeInfo.getName());
                                    logger.debug("isWritable: '{}', type: '{}'", jmxBeanAttributeInfo.isWritable(),
                                            jmxBeanAttributeInfo.getType());

                                    // check for CompositeData
                                    if ("javax.management.openmbean.CompositeData"
                                            .equals(jmxBeanAttributeInfo.getType())) {
                                        logger.error("actual mBean: '{}'", jmxObjectInstance.getObjectName());
                                        CompAttrib compAttrib = createCompAttrib(mBeanServerConnection,
                                                jmxObjectInstance, jmxBeanAttributeInfo);
                                        if (compAttrib != null) {
                                            logger.debug("xmlMbean got CompAttrib");
                                            xmlMbean.getCompAttrib().add(compAttrib);
                                        }
                                    }

                                    if (numbers.contains(jmxBeanAttributeInfo.getType())) {
                                        Attrib xmlJmxAttribute = createAttr(jmxBeanAttributeInfo);
                                        logger.debug("Added MBean: '{}' Added attribute: '{}'",
                                                xmlMbean.getObjectname(),
                                                xmlJmxAttribute.getName() + " as " + xmlJmxAttribute.getAlias());
                                        xmlMbean.getAttrib().add(xmlJmxAttribute);
                                    }
                                }
                            }
                        }

                        if (xmlMbean.getAttrib().size() > 0 || xmlMbean.getCompAttrib().size() > 0) {
                            xmlJmxCollection.getMbeans().getMbean().add(xmlMbean);
                        } else {
                            logger.debug("mbean: " + xmlMbean.getName() + " has no relavant attributes.");
                        }
                    }
                } else {
                    logger.debug("ignored: " + domainName);
                }
            }

        } catch (MalformedObjectNameException e) {
            logger.error("MalformedObjectNameException '{}'", e.getMessage());
        } catch (IOException e) {
            logger.error("IOException '{}'", e.getMessage());
        }
        logger.debug("finish collection!");
        return xmlJmxDatacollectionConfig;
    }

    public MBeanServerConnection createMBeanServerConnection(JMXConnector jmxConnector) throws IOException {
        MBeanServerConnection jmxServerConnection = jmxConnector.getMBeanServerConnection();
        logger.debug("jmxServerConnection: '{}'", jmxServerConnection);
        logger.debug("count: " + jmxServerConnection.getMBeanCount());
        return jmxServerConnection;
    }

    /**
     * This method gets the JmxConnector to connect with the given
     * jmxServiceURL.
     * 
     * @param username
     *            may be null
     * @param password
     *            may be null
     * @param jmxServiceURL
     *            should not be null!
     * @return a jmxConnector
     * @throws IOException
     *             if the connection to the given jmxServiceURL fails (e.g.
     *             authentication failure or not reachable)
     */
    public JMXConnector getJmxConnector(String username, String password, JMXServiceURL jmxServiceURL)
            throws IOException {
        JMXConnector jmxConnector;
        HashMap<String, String[]> env = new HashMap<String, String[]>();

        if (username != null && password != null) {
            String[] credentials = new String[] { username, password };
            env.put("jmx.remote.credentials", credentials);
        }

        jmxConnector = JMXConnectorFactory.connect(jmxServiceURL, env);
        jmxConnector.connect();

        return jmxConnector;
    }

    /**
     * determines the jmxServiceUrl depending on jmxmp.
     * 
     * @param jmxmp
     * @param hostName
     * @param port
     * @return
     * @throws MalformedURLException
     */
    public JMXServiceURL getJmxServiceURL(Boolean jmxmp, String hostName, String port)
            throws MalformedURLException {
        if (jmxmp) {
            return new JMXServiceURL("service:jmx:jmxmp://" + hostName + ":" + port);
        }
        return new JMXServiceURL("service:jmx:rmi:///jndi/rmi://" + hostName + ":" + port + "/jmxrmi");
    }

    public void writeJmxConfigFile(JmxDatacollectionConfig jmxDatacollectionConfigModel, String outFile) {
        logger.debug("start marshalling");
        JAXB.marshal(jmxDatacollectionConfigModel, new File(outFile));
        logger.debug("finished marshalling");
    }

    private CompAttrib createCompAttrib(MBeanServerConnection jmxServerConnection, ObjectInstance jmxObjectInstance,
            MBeanAttributeInfo jmxMBeanAttributeInfo) {
        Boolean contentAdded = false;

        CompAttrib xmlCompAttrib = xmlObjectFactory.createCompAttrib();
        xmlCompAttrib.setName(jmxMBeanAttributeInfo.getName());
        xmlCompAttrib.setType("Composite");
        xmlCompAttrib.setAlias(jmxMBeanAttributeInfo.getName());

        CompositeData compositeData;
        try {
            logger.debug("Try to get composite data");
            compositeData = (CompositeData) jmxServerConnection.getAttribute(jmxObjectInstance.getObjectName(),
                    jmxMBeanAttributeInfo.getName());
            if (compositeData == null) {
                logger.warn(
                        "compositeData is null. jmxObjectInstance.getObjectName: '{}', jmxMBeanAttributeInfo.getName: '{}'");
            }
            if (compositeData != null) {
                logger.debug("compositeData.getCompositeType: '{}'", compositeData.getCompositeType());
                Set<String> keys = compositeData.getCompositeType().keySet();
                for (String key : keys) {
                    Object compositeEntry = compositeData.get(key);
                    if (numbers.contains(compositeEntry.getClass().getName())) {
                        contentAdded = true;
                        CompMember xmlCompMember = xmlObjectFactory.createCompMember();
                        xmlCompMember.setName(key);

                        logger.debug("composite member pure alias: '{}'",
                                jmxMBeanAttributeInfo.getName() + StringUtils.capitalize(key));
                        String alias = nameCutter
                                .trimByDictionary(jmxMBeanAttributeInfo.getName() + StringUtils.capitalize(key));
                        alias = createAndRegisterUniqueAlias(alias);
                        xmlCompMember.setAlias(alias);
                        logger.debug("composite member trimmed alias: '{}'", alias);

                        xmlCompMember.setType("gauge");
                        xmlCompAttrib.getCompMember().add(xmlCompMember);

                    } else {
                        logger.debug("composite member key '{}' object's class '{}' was not a number.", key,
                                compositeEntry.getClass().getName());
                    }
                }
            }
        } catch (Exception e) {
            logger.error("killed in action: '{}'", e.getMessage());
        }

        if (contentAdded) {
            logger.debug("xmlCompAttrib returned by createCompAttrib it's '{}'", xmlCompAttrib);
            return xmlCompAttrib;
        }
        return null;
    }

    private Attrib createAttr(MBeanAttributeInfo jmxMBeanAttributeInfo) {
        Attrib xmlJmxAttribute = xmlObjectFactory.createAttrib();

        xmlJmxAttribute.setType("gauge");
        xmlJmxAttribute.setName(jmxMBeanAttributeInfo.getName());
        String alias = nameCutter.trimByDictionary(jmxMBeanAttributeInfo.getName());
        alias = createAndRegisterUniqueAlias(alias);
        xmlJmxAttribute.setAlias(alias);

        return xmlJmxAttribute;
    }

    private String createAndRegisterUniqueAlias(String originalAlias) {
        String uniqueAlias = originalAlias;
        if (!aliasMap.containsKey(originalAlias)) {
            aliasMap.put(originalAlias, 0);
            uniqueAlias = 0 + uniqueAlias;
        } else {
            aliasMap.put(originalAlias, aliasMap.get(originalAlias) + 1);
            uniqueAlias = aliasMap.get(originalAlias).toString() + originalAlias;
        }
        //find alias crashes caused by cuting down alias length to 19 chars
        if (aliasList.contains(nameCutter.trimByCamelCase(uniqueAlias, 19))) {
            logger.error(
                    "ALIAS CRASH AT :" + uniqueAlias + "\t as: " + nameCutter.trimByCamelCase(uniqueAlias, 19));
            uniqueAlias = uniqueAlias + "_NAME_CRASH_AS_19_CHAR_VALUE";
        } else {
            uniqueAlias = nameCutter.trimByCamelCase(uniqueAlias, 19);
            aliasList.add(uniqueAlias);
        }
        return uniqueAlias;
    }

}