/**
* *****************************************************************************
* This file is part of OpenNMS(R).
*
* Copyright (C) 2007-2011 The OpenNMS Group, Inc. OpenNMS(R) is Copyright (C)
* 1999-2011 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 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 General Public License for more details.
*
* You should have received a copy of the GNU 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.tools.jmxconfiggenerator.jmxconfig;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;
import javax.management.*;
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.tools.jmxconfiggenerator.helper.NameTools;
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 ArrayList<String> standardVmBeans = new ArrayList<String>();
private static ArrayList<String> ignores = new ArrayList<String>();
private static ArrayList<String> numbers = new ArrayList<String>();
private static ArrayList<String> rras = new ArrayList<String>();
private static HashMap<String, Integer> aliasMap = new HashMap<String, Integer>();
private static ArrayList<String> aliasList = new ArrayList<String>();
private static Rrd rrd = new Rrd();
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) {
logger.debug("Startup values: \n serviceName: " + serviceName + "\n runStandardVmBeans: " + runStandardVmBeans + "\n runWritableMBeans: " + runWritableMBeans);
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 {
for (String domainName : mBeanServerConnection.getDomains()) {
// just domains that are relevant for the service
if (!ignores.contains(domainName)) {
logger.debug("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());
e.printStackTrace();
continue;
} catch (IntrospectionException e) {
logger.error("IntrospectionException skipping MBean '{}' message: '{}'", jmxObjectInstance.getObjectName(), e.getMessage());
e.printStackTrace();
continue;
} catch (ReflectionException e) {
logger.error("ReflectionException skipping MBean '{}' message: '{}'", jmxObjectInstance.getObjectName(), e.getMessage());
e.printStackTrace();
continue;
} catch (Throwable e) {
logger.error("problem during remote call to get MBeanInfo for '{}' skipping this MBean. Message '{}'", jmxObjectInstance.getObjectName(), e.getMessage());
e.printStackTrace();
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.info("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());
e.printStackTrace();
} catch (IOException e) {
logger.error("IOException '{}'", e.getMessage());
e.printStackTrace();
}
return xmlJmxDatacollectionConfig;
}
public MBeanServerConnection createMBeanServerConnection(String hostName, String port, String username, String password, Boolean ssl, Boolean jmxmp) {
JMXConnector jmxConnector;
JMXServiceURL jmxServiceURL;
MBeanServerConnection jmxServerConnection = null;
try {
if (jmxmp) {
jmxServiceURL = new JMXServiceURL("service:jmx:jmxmp://" + hostName + ":" + port);
} else {
jmxServiceURL = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://" + hostName + ":" + port + "/jmxrmi");
}
if (username != null && password != null) {
jmxConnector = JMXConnectorFactory.newJMXConnector(jmxServiceURL, null);
HashMap<String, String[]> env = new HashMap<String, String[]>();
String[] credentials = new String[]{username, password};
env.put("jmx.remote.credentials", credentials);
jmxConnector.connect(env);
} else {
jmxConnector = JMXConnectorFactory.connect(jmxServiceURL);
jmxConnector.connect();
}
logger.debug("jmxServerConnection: '{}'", jmxServerConnection);
jmxServerConnection = jmxConnector.getMBeanServerConnection();
logger.debug("count: " + jmxServerConnection.getMBeanCount());
} catch (MalformedURLException e) {
logger.error("MalformedURLException '{}'", e.getMessage());
e.printStackTrace();
} catch (IOException e) {
logger.error("IOException '{}'", e.getMessage());
e.printStackTrace();
}
return jmxServerConnection;
}
public void writeJmxConfigFile(JmxDatacollectionConfig jmxDatacollectionConfigModel, String outFile) {
JAXB.marshal(jmxDatacollectionConfigModel, new File(outFile));
}
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());
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 = NameTools.trimByDictionary(jmxMBeanAttributeInfo.getName() + StringUtils.capitalize(key));
alias = createAndRegisterUniceAlias(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());
e.printStackTrace();
}
if (contentAdded) {
logger.error("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 = NameTools.trimByDictionary(jmxMBeanAttributeInfo.getName());
alias = createAndRegisterUniceAlias(alias);
xmlJmxAttribute.setAlias(alias);
return xmlJmxAttribute;
}
private String createAndRegisterUniceAlias(String originalAlias) {
String uniceAlias = originalAlias;
if (!aliasMap.containsKey(originalAlias)) {
aliasMap.put(originalAlias, 0);
uniceAlias = 0 + uniceAlias;
} else {
aliasMap.put(originalAlias, aliasMap.get(originalAlias) + 1);
uniceAlias = aliasMap.get(originalAlias).toString() + originalAlias;
}
//find alias crashes caused by cuting down alias length to 19 chars
if (aliasList.contains(NameTools.trimByCamelCase(uniceAlias, 19))) {
logger.error("ALIAS CRASH AT :" + uniceAlias + "\t as: " + NameTools.trimByCamelCase(uniceAlias, 19));
uniceAlias = uniceAlias + "_NAME_CRASH_AS_19_CHAR_VALUE";
} else {
uniceAlias = NameTools.trimByCamelCase(uniceAlias, 19);
aliasList.add(uniceAlias);
}
return uniceAlias;
}
}