/******************************************************************************* * This file is part of OpenNMS(R). * * Copyright (C) 2006-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.netmgt.config; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.opennms.core.utils.ConfigFileConstants; import org.opennms.core.utils.ThreadCategory; import org.opennms.netmgt.collectd.Attr; import org.opennms.netmgt.config.collectd.jmx.Attrib; import org.opennms.netmgt.config.collectd.jmx.CompAttrib; import org.opennms.netmgt.config.collectd.jmx.CompMember; import org.opennms.netmgt.config.collectd.jmx.JmxCollection; import org.opennms.netmgt.config.collectd.jmx.JmxDatacollectionConfig; import org.opennms.netmgt.config.collectd.jmx.Mbean; import org.opennms.netmgt.config.collectd.jmx.Mbeans; import org.opennms.netmgt.model.RrdRepository; import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.InputStreamResource; import org.springframework.core.io.Resource; /** * This class is the main respository for JMX data collection configuration * information used by the an instance of the JMX service monitor. When this class is loaded it * reads the jmx data collection configuration into memory. * * <strong>Note: </strong>Users of this class should make sure the * <em>init()</em> is called before calling any other method to ensure the * config is loaded before accessing other convenience methods. * * @author <A HREF="mailto:mike@opennms.org">Mike Jamison </A> * @author <A HREF="http://www.opennms.org/">OpenNMS </A> * @author <A HREF="mailto:mike@opennms.org">Mike Jamison </A> * @author <A HREF="http://www.opennms.org/">OpenNMS </A> * @version $Id: $ */ public final class JMXDataCollectionConfigFactory { /** * The singleton instance of this factory */ private static JMXDataCollectionConfigFactory m_singleton = null; /** * The config class loaded from the config file */ private JmxDatacollectionConfig m_config; /** * This member is set to true if the configuration file has been loaded. */ private static boolean m_loaded = false; /** * Map of group maps indexed by SNMP collection name. */ private Map<String, Map<String, Mbean>> m_collectionGroupMap; /** * Map of JmxCollection objects indexed by data collection name */ private Map<String, JmxCollection> m_collectionMap; /** * <p>Constructor for JMXDataCollectionConfigFactory.</p> * * @param stream a {@link java.io.InputStream} object. * @throws org.exolab.castor.xml.MarshalException if any. * @throws org.exolab.castor.xml.ValidationException if any. */ public JMXDataCollectionConfigFactory(InputStream stream) { initialize(new InputStreamResource(stream)); } /** * Private constructor * * @exception java.io.IOException * Thrown if the specified config file cannot be read */ private JMXDataCollectionConfigFactory(String configFile) throws IOException { initialize(new FileSystemResource(configFile)); } private void initialize(Resource resource) { JMXDataCollectionConfigDao dao = new JMXDataCollectionConfigDao(); dao.setConfigResource(resource); dao.afterPropertiesSet(); m_config = dao.getConfig(); buildCollectionMap(); } private void buildCollectionMap() { // Build collection map which is a hash map of Collection // objects indexed by collection name...also build // collection group map which is a hash map indexed // by collection name with a hash map as the value // containing a map of the collections's group names // to the Group object containing all the information // for that group. So the associations are: // // CollectionMap // collectionName -> Collection // // CollectionGroupMap // collectionName -> groupMap // // GroupMap // groupMapName -> Group // // This is parsed and built at initialization for // faster processing at run-time. // m_collectionMap = new HashMap<String, JmxCollection>(); m_collectionGroupMap = new HashMap<String, Map<String, Mbean>>(); // BOZO isn't the collection name defined in the jmx-datacollection.xml file and // global to all the mbeans? Collection<JmxCollection> collections = m_config.getJmxCollectionCollection(); Iterator<JmxCollection> citer = collections.iterator(); while (citer.hasNext()) { JmxCollection collection = citer.next(); // Build group map for this collection Map<String, Mbean> groupMap = new HashMap<String, Mbean>(); Mbeans mbeans = collection.getMbeans(); Collection<Mbean> groupList = mbeans.getMbeanCollection(); Iterator<Mbean> giter = groupList.iterator(); while (giter.hasNext()) { Mbean mbean = giter.next(); groupMap.put(mbean.getName(), mbean); } m_collectionGroupMap.put(collection.getName(), groupMap); m_collectionMap.put(collection.getName(), collection); } } /** * Load the config from the default config file and create the singleton * instance of this factory. * * @exception java.io.IOException * Thrown if the specified config file cannot be read * @exception org.exolab.castor.xml.MarshalException * Thrown if the file does not conform to the schema. * @exception org.exolab.castor.xml.ValidationException * Thrown if the contents do not match the required schema. * @throws java.io.IOException if any. * @throws org.exolab.castor.xml.MarshalException if any. * @throws org.exolab.castor.xml.ValidationException if any. */ public static synchronized void init() throws IOException { if (m_loaded) { // init already called - return // to reload, reload() will need to be called return; } try { File cfgFile = ConfigFileConstants.getFile(ConfigFileConstants.JMX_DATA_COLLECTION_CONF_FILE_NAME); ThreadCategory.getInstance(JMXDataCollectionConfigFactory.class).debug("init: config file path: " + cfgFile.getPath()); m_singleton = new JMXDataCollectionConfigFactory(cfgFile.getPath()); } catch (IOException ioe) { log().error("Unable to open JMX data collection config file", ioe); throw ioe; } m_loaded = true; } /** * Reload the config from the default config file * * @exception java.io.IOException * Thrown if the specified config file cannot be read/loaded * @exception org.exolab.castor.xml.MarshalException * Thrown if the file does not conform to the schema. * @exception org.exolab.castor.xml.ValidationException * Thrown if the contents do not match the required schema. * @throws java.io.IOException if any. * @throws org.exolab.castor.xml.MarshalException if any. * @throws org.exolab.castor.xml.ValidationException if any. */ public static synchronized void reload() throws IOException { m_singleton = null; m_loaded = false; init(); } /** * Return the singleton instance of this factory. * * @return The current factory instance. * @throws java.lang.IllegalStateException * Thrown if the factory has not yet been initialized. */ public static synchronized JMXDataCollectionConfigFactory getInstance() { if (!m_loaded) throw new IllegalStateException("The factory has not been initialized"); return m_singleton; } /** * <p>setInstance</p> * * @param singleton a {@link org.opennms.netmgt.config.JMXDataCollectionConfigFactory} object. */ public static synchronized void setInstance(JMXDataCollectionConfigFactory singleton) { m_singleton = singleton; m_loaded = true; } /** * This method returns the list of MIB objects associated with a particular * system object id, IP address, and ifType for the specified collection. * * @param cName * name of the data collection from which to retrieve oid * information. * @param aSysoid * system object id to look up in the collection * @param anAddress * IP address to look up in the collection * @return a list of MIB objects */ public Map<String, List<Attrib>> getAttributeMap(String cName, String aSysoid, String anAddress) { ThreadCategory log = log(); Map<String, List<Attrib>> attributeMap = new HashMap<String, List<Attrib>>(); if (log.isDebugEnabled()) log.debug("getMibObjectList: collection: " + cName + " sysoid: " + aSysoid + " address: " + anAddress); if (aSysoid == null) { if (log.isDebugEnabled()) log.debug("getMibObjectList: aSysoid parameter is NULL..."); return attributeMap; } // Retrieve the appropriate Collection object // JmxCollection collection = m_collectionMap.get(cName); if (collection == null) { return attributeMap; } Mbeans beans = collection.getMbeans(); Enumeration<Mbean> en = beans.enumerateMbean(); while (en.hasMoreElements()) { List<Attrib> list = new ArrayList<Attrib>(); Mbean mbean = en.nextElement(); Attrib[] attributes = mbean.getAttrib(); for (int i = 0; i < attributes.length; i++) { list.add(attributes[i]); } CompAttrib[] compAttributes = mbean.getCompAttrib(); for (int i = 0; i < compAttributes.length; i++) { CompMember[] compMembers = compAttributes[i].getCompMember(); for (int j = 0; j < compMembers.length; j++) { Attrib compAttrib = new Attrib(); compAttrib.setName(compAttributes[i].getName() + "|" + compMembers[j].getName()); compAttrib.setAlias(compMembers[j].getAlias()); compAttrib.setType(compMembers[j].getType()); list.add(compAttrib); } } attributeMap.put(mbean.getObjectname(), list); } return attributeMap; } /** * <p>getMBeanInfo</p> * * @param cName a {@link java.lang.String} object. * @return a {@link java.util.Map} object. */ public Map<String, BeanInfo> getMBeanInfo(String cName) { Map<String, BeanInfo> map = new HashMap<String, BeanInfo>(); // Retrieve the appropriate Collection object // JmxCollection collection = m_collectionMap.get(cName); if (collection == null) { log().warn("no collection named '" + cName + "' was found"); } else { Mbeans beans = collection.getMbeans(); Enumeration<Mbean> en = beans.enumerateMbean(); while (en.hasMoreElements()) { BeanInfo beanInfo = new BeanInfo(); Mbean mbean = en.nextElement(); beanInfo.setMbeanName(mbean.getName()); beanInfo.setObjectName(mbean.getObjectname()); beanInfo.setKeyField(mbean.getKeyfield()); beanInfo.setExcludes(mbean.getExclude()); beanInfo.setKeyAlias(mbean.getKeyAlias()); Attrib[] attributes = mbean.getAttrib(); CompAttrib[] compositeAttributes = mbean.getCompAttrib(); List<String> attribNameList = new ArrayList<String>(); List<String> compAttribNameList = new ArrayList<String>(); for (Object ca : compositeAttributes) { CompAttrib myCa = (CompAttrib)ca; CompMember[] compositeMembers = myCa.getCompMember(); for (Object cm : compositeMembers) { CompMember myCm = (CompMember)cm; attribNameList.add(myCa.getName() + "|" + myCm.getName()); compAttribNameList.add(myCa.getName() + "|" + myCm.getName()); } } for (Object a : attributes) { Attrib myA = (Attrib)a; attribNameList.add(myA.getName()); } beanInfo.setAttributes(attribNameList); beanInfo.setCompositeAttributes(compAttribNameList); map.put(mbean.getObjectname(), beanInfo); } } return Collections.unmodifiableMap(map); } /** * <p>getMBeanInfo_save</p> * * @param cName a {@link java.lang.String} object. * @return a {@link java.util.Map} object. */ public Map<String, String[]> getMBeanInfo_save(String cName) { Map<String, String[]> map = new HashMap<String, String[]>(); // Retrieve the appropriate Collection object // JmxCollection collection = m_collectionMap.get(cName); Mbeans beans = collection.getMbeans(); Enumeration<Mbean> en = beans.enumerateMbean(); while (en.hasMoreElements()) { Mbean mbean = en.nextElement(); int count = mbean.getAttribCount(); String[] attribs = new String[count]; Attrib[] attributes = mbean.getAttrib(); for (int i = 0; i < attributes.length; i++) { attribs[i] = attributes[i].getName(); } map.put(mbean.getObjectname(), attribs); } return Collections.unmodifiableMap(map); } /** * Takes a list of castor generated MibObj objects iterates over them * creating corresponding MibObject objects and adding them to the supplied * MibObject list. * * @param objectList * List of MibObject objects parsed from * 'datacollection-config.xml' * @param mibObjectList * List of MibObject objects currently being built */ static void processObjectList(List<Attrib> objectList, List<Attr> mibObjectList) { //TODO: Make mibObjectList a Set //TODO: Delete this method, it is not referenced anywhere Iterator<Attrib>i = objectList.iterator(); while (i.hasNext()) { Attrib mibObj = i.next(); // Create a MibObject from the castor MibObj Attr aMibObject = new Attr(); aMibObject.setName(mibObj.getName()); aMibObject.setAlias(mibObj.getAlias()); aMibObject.setType(mibObj.getType()); aMibObject.setMaxval(mibObj.getMaxval()); aMibObject.setMinval(mibObj.getMinval()); // Add the MIB object provided it isn't already in the list if (!mibObjectList.contains(aMibObject)) { mibObjectList.add(aMibObject); } } } /** * Retrieves configured RRD step size. * * @param cName * Name of the data collection * @return RRD step size for the specified collection */ public int getStep(String cName) { JmxCollection collection = m_collectionMap.get(cName); if (collection != null) return collection.getRrd().getStep(); else return -1; } /** * Retrieves configured list of RoundRobin Archive statements. * * @param cName * Name of the data collection * @return list of RRA strings. */ public List<String> getRRAList(String cName) { JmxCollection collection = m_collectionMap.get(cName); if (collection != null) return collection.getRrd().getRraCollection(); else return null; } /** * Retrieves the configured path to the RRD file repository. * * @return RRD repository path. * @param collectionName a {@link java.lang.String} object. */ public RrdRepository getRrdRepository(String collectionName) { RrdRepository repo = new RrdRepository(); repo.setRrdBaseDir(new File(getRrdPath())); repo.setRraList(getRRAList(collectionName)); repo.setStep(getStep(collectionName)); repo.setHeartBeat((2 * getStep(collectionName))); return repo; //return m_config.getRrdRepository(); } /** * <p>getRrdPath</p> * * @return a {@link java.lang.String} object. */ public String getRrdPath() { String rrdPath = m_config.getRrdRepository(); if (rrdPath == null) { throw new RuntimeException("Configuration error, failed to " + "retrieve path to RRD repository."); } /* * TODO: make a path utils class that has the below in it strip the * File.separator char off of the end of the path. */ if (rrdPath.endsWith(File.separator)) { rrdPath = rrdPath.substring(0, (rrdPath.length() - File.separator.length())); } return rrdPath; } private static ThreadCategory log() { return ThreadCategory.getInstance(JMXDataCollectionConfigFactory.class); } }