/* * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code 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 * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.management.snmp.jvminstr; // java imports // import java.util.Hashtable; import java.util.List; import java.util.ArrayList; import java.util.Iterator; import java.lang.ref.WeakReference; // jmx imports // import javax.management.MBeanServer; import javax.management.ObjectName; import javax.management.InstanceAlreadyExistsException; import javax.management.NotificationEmitter; import javax.management.NotificationListener; import javax.management.Notification; import javax.management.ListenerNotFoundException; import javax.management.openmbean.CompositeData; // jdmk imports // import com.sun.jmx.snmp.agent.SnmpMib; import com.sun.jmx.snmp.daemon.SnmpAdaptorServer; import com.sun.jmx.snmp.SnmpPeer; import com.sun.jmx.snmp.SnmpParameters; import com.sun.jmx.snmp.SnmpOidTable; import com.sun.jmx.snmp.SnmpOid; import com.sun.jmx.snmp.SnmpVarBindList; import com.sun.jmx.snmp.SnmpVarBind; import com.sun.jmx.snmp.SnmpCounter; import com.sun.jmx.snmp.SnmpCounter64; import com.sun.jmx.snmp.SnmpString; import com.sun.jmx.snmp.SnmpInt; import com.sun.jmx.snmp.Enumerated; import com.sun.jmx.snmp.agent.SnmpMibTable; import sun.management.snmp.jvmmib.JVM_MANAGEMENT_MIBOidTable; import sun.management.snmp.jvmmib.JVM_MANAGEMENT_MIB; import sun.management.snmp.jvmmib.JvmMemoryMeta; import sun.management.snmp.jvmmib.JvmThreadingMeta; import sun.management.snmp.jvmmib.JvmRuntimeMeta; import sun.management.snmp.jvmmib.JvmClassLoadingMeta; import sun.management.snmp.jvmmib.JvmCompilationMeta; import sun.management.snmp.util.MibLogger; import sun.management.snmp.util.SnmpCachedData; import sun.management.snmp.util.SnmpTableHandler; //java management imports import java.lang.management.ManagementFactory; import java.lang.management.MemoryPoolMXBean; import java.lang.management.MemoryNotificationInfo; import java.lang.management.MemoryType; public class JVM_MANAGEMENT_MIB_IMPL extends JVM_MANAGEMENT_MIB { private static final long serialVersionUID = -8104825586888859831L; private static final MibLogger log = new MibLogger(JVM_MANAGEMENT_MIB_IMPL.class); private static WeakReference<SnmpOidTable> tableRef; public static SnmpOidTable getOidTable() { SnmpOidTable table = null; if(tableRef == null) { table = new JVM_MANAGEMENT_MIBOidTable(); tableRef = new WeakReference<SnmpOidTable>(table); return table; } table = tableRef.get(); if(table == null) { table = new JVM_MANAGEMENT_MIBOidTable(); tableRef = new WeakReference<SnmpOidTable>(table); } return table; } /** * Handler waiting for memory <CODE>Notification</CODE>. * Translate each JMX notification in SNMP trap. */ private class NotificationHandler implements NotificationListener { public void handleNotification(Notification notification, Object handback) { log.debug("handleNotification", "Received notification [ " + notification.getType() + "]"); String type = notification.getType(); if (type.equals(MemoryNotificationInfo.MEMORY_THRESHOLD_EXCEEDED) || type.equals(MemoryNotificationInfo. MEMORY_COLLECTION_THRESHOLD_EXCEEDED)) { MemoryNotificationInfo minfo = MemoryNotificationInfo. from((CompositeData) notification.getUserData()); SnmpCounter64 count = new SnmpCounter64(minfo.getCount()); SnmpCounter64 used = new SnmpCounter64(minfo.getUsage().getUsed()); SnmpString poolName = new SnmpString(minfo.getPoolName()); SnmpOid entryIndex = getJvmMemPoolEntryIndex(minfo.getPoolName()); if (entryIndex == null) { log.error("handleNotification", "Error: Can't find entry index for Memory Pool: " + minfo.getPoolName() +": " + "No trap emitted for " + type); return; } SnmpOid trap = null; final SnmpOidTable mibTable = getOidTable(); try { SnmpOid usedOid = null; SnmpOid countOid = null; if (type.equals(MemoryNotificationInfo. MEMORY_THRESHOLD_EXCEEDED)) { trap = new SnmpOid(mibTable. resolveVarName("jvmLowMemoryPoolUsageNotif").getOid()); usedOid = new SnmpOid(mibTable. resolveVarName("jvmMemPoolUsed").getOid() + "." + entryIndex); countOid = new SnmpOid(mibTable. resolveVarName("jvmMemPoolThreshdCount").getOid() + "." + entryIndex); } else if (type.equals(MemoryNotificationInfo. MEMORY_COLLECTION_THRESHOLD_EXCEEDED)) { trap = new SnmpOid(mibTable. resolveVarName("jvmLowMemoryPoolCollectNotif"). getOid()); usedOid = new SnmpOid(mibTable. resolveVarName("jvmMemPoolCollectUsed").getOid() + "." + entryIndex); countOid = new SnmpOid(mibTable. resolveVarName("jvmMemPoolCollectThreshdCount"). getOid() + "." + entryIndex); } //Datas SnmpVarBindList list = new SnmpVarBindList(); SnmpOid poolNameOid = new SnmpOid(mibTable. resolveVarName("jvmMemPoolName").getOid() + "." + entryIndex); SnmpVarBind varCount = new SnmpVarBind(countOid, count); SnmpVarBind varUsed = new SnmpVarBind(usedOid, used); SnmpVarBind varPoolName = new SnmpVarBind(poolNameOid, poolName); list.add(varPoolName); list.add(varCount); list.add(varUsed); sendTrap(trap, list); }catch(Exception e) { log.error("handleNotification", "Exception occured : " + e); } } } } /** * List of notification targets. */ private ArrayList<NotificationTarget> notificationTargets = new ArrayList<NotificationTarget>(); private final NotificationEmitter emitter; private final NotificationHandler handler; /** * Instantiate a JVM MIB intrusmentation. * A <CODE>NotificationListener</CODE> is added to the <CODE>MemoryMXBean</CODE> * <CODE>NotificationEmitter</CODE> */ public JVM_MANAGEMENT_MIB_IMPL() { handler = new NotificationHandler(); emitter = (NotificationEmitter) ManagementFactory.getMemoryMXBean(); emitter.addNotificationListener(handler, null, null); } private synchronized void sendTrap(SnmpOid trap, SnmpVarBindList list) { final Iterator iterator = notificationTargets.iterator(); final SnmpAdaptorServer adaptor = (SnmpAdaptorServer) getSnmpAdaptor(); if (adaptor == null) { log.error("sendTrap", "Cannot send trap: adaptor is null."); return; } if (!adaptor.isActive()) { log.config("sendTrap", "Adaptor is not active: trap not sent."); return; } while(iterator.hasNext()) { NotificationTarget target = null; try { target = (NotificationTarget) iterator.next(); SnmpPeer peer = new SnmpPeer(target.getAddress(), target.getPort()); SnmpParameters p = new SnmpParameters(); p.setRdCommunity(target.getCommunity()); peer.setParams(p); log.debug("handleNotification", "Sending trap to " + target.getAddress() + ":" + target.getPort()); adaptor.snmpV2Trap(peer, trap, list, null); }catch(Exception e) { log.error("sendTrap", "Exception occured while sending trap to [" + target + "]. Exception : " + e); log.debug("sendTrap",e); } } } /** * Add a notification target. * @param target The target to add * @throws IllegalArgumentException If target parameter is null. */ public synchronized void addTarget(NotificationTarget target) throws IllegalArgumentException { if(target == null) throw new IllegalArgumentException("Target is null"); notificationTargets.add(target); } /** * Remove notification listener. */ public void terminate() { try { emitter.removeNotificationListener(handler); }catch(ListenerNotFoundException e) { log.error("terminate", "Listener Not found : " + e); } } /** * Add notification targets. * @param targets A list of * <CODE>sun.management.snmp.jvminstr.NotificationTarget</CODE> * @throws IllegalArgumentException If targets parameter is null. */ public synchronized void addTargets(List<NotificationTarget> targets) throws IllegalArgumentException { if(targets == null) throw new IllegalArgumentException("Target list is null"); notificationTargets.addAll(targets); } /** * Factory method for "JvmMemory" group MBean. * * You can redefine this method if you need to replace the default * generated MBean class with your own customized class. * * @param groupName Name of the group ("JvmMemory") * @param groupOid OID of this group * @param groupObjname ObjectName for this group (may be null) * @param server MBeanServer for this group (may be null) * * @return An instance of the MBean class generated for the * "JvmMemory" group (JvmMemory) * * Note that when using standard metadata, * the returned object must implement the "JvmMemoryMBean" * interface. **/ protected Object createJvmMemoryMBean(String groupName, String groupOid, ObjectName groupObjname, MBeanServer server) { // Note that when using standard metadata, // the returned object must implement the "JvmMemoryMBean" // interface. // if (server != null) return new JvmMemoryImpl(this,server); else return new JvmMemoryImpl(this); } /** * Factory method for "JvmMemory" group metadata class. * * You can redefine this method if you need to replace the default * generated metadata class with your own customized class. * * @param groupName Name of the group ("JvmMemory") * @param groupOid OID of this group * @param groupObjname ObjectName for this group (may be null) * @param server MBeanServer for this group (may be null) * * @return An instance of the metadata class generated for the * "JvmMemory" group (JvmMemoryMeta) * **/ protected JvmMemoryMeta createJvmMemoryMetaNode(String groupName, String groupOid, ObjectName groupObjname, MBeanServer server) { return new JvmMemoryMetaImpl(this, objectserver); } /** * Factory method for "JvmThreading" group metadata class. * * You can redefine this method if you need to replace the default * generated metadata class with your own customized class. * * @param groupName Name of the group ("JvmThreading") * @param groupOid OID of this group * @param groupObjname ObjectName for this group (may be null) * @param server MBeanServer for this group (may be null) * * @return An instance of the metadata class generated for the * "JvmThreading" group (JvmThreadingMeta) * **/ protected JvmThreadingMeta createJvmThreadingMetaNode(String groupName, String groupOid, ObjectName groupObjname, MBeanServer server) { return new JvmThreadingMetaImpl(this, objectserver); } /** * Factory method for "JvmThreading" group MBean. * * You can redefine this method if you need to replace the default * generated MBean class with your own customized class. * * @param groupName Name of the group ("JvmThreading") * @param groupOid OID of this group * @param groupObjname ObjectName for this group (may be null) * @param server MBeanServer for this group (may be null) * * @return An instance of the MBean class generated for the * "JvmThreading" group (JvmThreading) * * Note that when using standard metadata, * the returned object must implement the "JvmThreadingMBean" * interface. **/ protected Object createJvmThreadingMBean(String groupName, String groupOid, ObjectName groupObjname, MBeanServer server) { // Note that when using standard metadata, // the returned object must implement the "JvmThreadingMBean" // interface. // if (server != null) return new JvmThreadingImpl(this,server); else return new JvmThreadingImpl(this); } /** * Factory method for "JvmRuntime" group metadata class. * * You can redefine this method if you need to replace the default * generated metadata class with your own customized class. * * @param groupName Name of the group ("JvmRuntime") * @param groupOid OID of this group * @param groupObjname ObjectName for this group (may be null) * @param server MBeanServer for this group (may be null) * * @return An instance of the metadata class generated for the * "JvmRuntime" group (JvmRuntimeMeta) * **/ protected JvmRuntimeMeta createJvmRuntimeMetaNode(String groupName, String groupOid, ObjectName groupObjname, MBeanServer server) { return new JvmRuntimeMetaImpl(this, objectserver); } /** * Factory method for "JvmRuntime" group MBean. * * You can redefine this method if you need to replace the default * generated MBean class with your own customized class. * * @param groupName Name of the group ("JvmRuntime") * @param groupOid OID of this group * @param groupObjname ObjectName for this group (may be null) * @param server MBeanServer for this group (may be null) * * @return An instance of the MBean class generated for the * "JvmRuntime" group (JvmRuntime) * * Note that when using standard metadata, * the returned object must implement the "JvmRuntimeMBean" * interface. **/ protected Object createJvmRuntimeMBean(String groupName, String groupOid, ObjectName groupObjname, MBeanServer server) { // Note that when using standard metadata, // the returned object must implement the "JvmRuntimeMBean" // interface. // if (server != null) return new JvmRuntimeImpl(this,server); else return new JvmRuntimeImpl(this); } /** * Factory method for "JvmCompilation" group metadata class. * * You can redefine this method if you need to replace the default * generated metadata class with your own customized class. * * @param groupName Name of the group ("JvmCompilation") * @param groupOid OID of this group * @param groupObjname ObjectName for this group (may be null) * @param server MBeanServer for this group (may be null) * * @return An instance of the metadata class generated for the * "JvmCompilation" group (JvmCompilationMeta) * **/ protected JvmCompilationMeta createJvmCompilationMetaNode(String groupName, String groupOid, ObjectName groupObjname, MBeanServer server) { // If there is no compilation system, the jvmCompilation will not // be instantiated. // if (ManagementFactory.getCompilationMXBean() == null) return null; return super.createJvmCompilationMetaNode(groupName,groupOid, groupObjname,server); } /** * Factory method for "JvmCompilation" group MBean. * * You can redefine this method if you need to replace the default * generated MBean class with your own customized class. * * @param groupName Name of the group ("JvmCompilation") * @param groupOid OID of this group * @param groupObjname ObjectName for this group (may be null) * @param server MBeanServer for this group (may be null) * * @return An instance of the MBean class generated for the * "JvmCompilation" group (JvmCompilation) * * Note that when using standard metadata, * the returned object must implement the "JvmCompilationMBean" * interface. **/ protected Object createJvmCompilationMBean(String groupName, String groupOid, ObjectName groupObjname, MBeanServer server) { // Note that when using standard metadata, // the returned object must implement the "JvmCompilationMBean" // interface. // if (server != null) return new JvmCompilationImpl(this,server); else return new JvmCompilationImpl(this); } /** * Factory method for "JvmOS" group MBean. * * You can redefine this method if you need to replace the default * generated MBean class with your own customized class. * * @param groupName Name of the group ("JvmOS") * @param groupOid OID of this group * @param groupObjname ObjectName for this group (may be null) * @param server MBeanServer for this group (may be null) * * @return An instance of the MBean class generated for the * "JvmOS" group (JvmOS) * * Note that when using standard metadata, * the returned object must implement the "JvmOSMBean" * interface. **/ protected Object createJvmOSMBean(String groupName, String groupOid, ObjectName groupObjname, MBeanServer server) { // Note that when using standard metadata, // the returned object must implement the "JvmOSMBean" // interface. // if (server != null) return new JvmOSImpl(this,server); else return new JvmOSImpl(this); } /** * Factory method for "JvmClassLoading" group MBean. * * You can redefine this method if you need to replace the default * generated MBean class with your own customized class. * * @param groupName Name of the group ("JvmClassLoading") * @param groupOid OID of this group * @param groupObjname ObjectName for this group (may be null) * @param server MBeanServer for this group (may be null) * * @return An instance of the MBean class generated for the * "JvmClassLoading" group (JvmClassLoading) * * Note that when using standard metadata, * the returned object must implement the "JvmClassLoadingMBean" * interface. **/ protected Object createJvmClassLoadingMBean(String groupName, String groupOid, ObjectName groupObjname, MBeanServer server) { // Note that when using standard metadata, // the returned object must implement the "JvmClassLoadingMBean" // interface. // if (server != null) return new JvmClassLoadingImpl(this,server); else return new JvmClassLoadingImpl(this); } static String validDisplayStringTC(String str) { if(str == null) return ""; if(str.length() > DISPLAY_STRING_MAX_LENGTH) { return str.substring(0, DISPLAY_STRING_MAX_LENGTH); } else return str; } static String validJavaObjectNameTC(String str) { if(str == null) return ""; if(str.length() > JAVA_OBJECT_NAME_MAX_LENGTH) { return str.substring(0, JAVA_OBJECT_NAME_MAX_LENGTH); } else return str; } static String validPathElementTC(String str) { if(str == null) return ""; if(str.length() > PATH_ELEMENT_MAX_LENGTH) { return str.substring(0, PATH_ELEMENT_MAX_LENGTH); } else return str; } static String validArgValueTC(String str) { if(str == null) return ""; if(str.length() > ARG_VALUE_MAX_LENGTH) { return str.substring(0, ARG_VALUE_MAX_LENGTH); } else return str; } /** * WARNING: This should probably be moved to JvmMemPoolTableMetaImpl **/ private SnmpTableHandler getJvmMemPoolTableHandler(Object userData) { final SnmpMibTable meta = getRegisteredTableMeta("JvmMemPoolTable"); if (! (meta instanceof JvmMemPoolTableMetaImpl)) { final String err = ((meta==null)?"No metadata for JvmMemPoolTable": "Bad metadata class for JvmMemPoolTable: " + meta.getClass().getName()); log.error("getJvmMemPoolTableHandler", err); return null; } final JvmMemPoolTableMetaImpl memPoolTable = (JvmMemPoolTableMetaImpl) meta; return memPoolTable.getHandler(userData); } /** * WARNING: This should probably be moved to JvmMemPoolTableMetaImpl **/ private int findInCache(SnmpTableHandler handler, String poolName) { if (!(handler instanceof SnmpCachedData)) { if (handler != null) { final String err = "Bad class for JvmMemPoolTable datas: " + handler.getClass().getName(); log.error("getJvmMemPoolEntry", err); } return -1; } final SnmpCachedData data = (SnmpCachedData)handler; final int len = data.datas.length; for (int i=0; i < data.datas.length ; i++) { final MemoryPoolMXBean pool = (MemoryPoolMXBean) data.datas[i]; if (poolName.equals(pool.getName())) return i; } return -1; } /** * WARNING: This should probably be moved to JvmMemPoolTableMetaImpl **/ private SnmpOid getJvmMemPoolEntryIndex(SnmpTableHandler handler, String poolName) { final int index = findInCache(handler,poolName); if (index < 0) return null; return ((SnmpCachedData)handler).indexes[index]; } private SnmpOid getJvmMemPoolEntryIndex(String poolName) { return getJvmMemPoolEntryIndex(getJvmMemPoolTableHandler(null), poolName); } // cache validity // // Should we define a property for this? Should we have different // cache validity periods depending on which table we cache? // public long validity() { return DEFAULT_CACHE_VALIDITY_PERIOD; } // Defined in RFC 2579 private final static int DISPLAY_STRING_MAX_LENGTH=255; private final static int JAVA_OBJECT_NAME_MAX_LENGTH=1023; private final static int PATH_ELEMENT_MAX_LENGTH=1023; private final static int ARG_VALUE_MAX_LENGTH=1023; private final static int DEFAULT_CACHE_VALIDITY_PERIOD=1000; }