Generally, every MBean that is not a singleton, should be mapped (or modeled) to (as) a SNMP table. MBeans that contain lists of other MBeans or Java objects should be mapped to several SNMP tables that share a common base index (i.e., the index of the master MBean/table).
The following steps describe how a MBean can be descriptively mapped to a table:
First of all, you need a JMXTableSupport instance which is registered with the MBean server:
final MBeanServerConnection server = ManagementFactory.getPlatformMBeanServer(); final MBeanAttributeMOTableSupport tableSupport = new MBeanAttributeMOTableSupport(server);
Next you need to define how the index of the SNMP table is mapped from MBean data or autogenerated. The mapping is done bidirectional by mapping a MBean to a key uniquely identifying the MBean and an Index (OID) uniquely defining the SNMP table row(MOTableRow). The JMXIndexSupport knows about Key and Index and thus provides the mapping.
tableSupport.add(oidJvmThreadInstanceEntry, new MBeanAttributeMOTableInfo(onameJvmThreading, // The key provider retrieves all available keys (thus all available MBean instances: new MBeanInvokationKeyProvider(onameJvmThreading, new TypedAttribute("AllThreadIds", long.class), "getThreadInfo", true), ... new String[] { "ThreadId" }, // The JMX index support maps from rowIdentidier (= Key) to MBean objectname and ... new JMXIndexSupport() { public ObjectName mapToRowMBean(Object rowIdentifier) { return null; } // from rowIdentfier to index OID and ... public OID mapToIndex(Object rowIdentifier) { Long l = (Long)rowIdentifier; return OctetString.fromHexString(Long.toHexString(l)).toSubIndex(true); } // maps native row ID (key used by MBean) or index to rowIdentifier ... public Object getRowIdentifier(Object nativeRowId, int nativeIndex) { return nativeRowId; } // maps index OID to rowIdentifier (= Key) public Object mapToRowIdentifier(OID rowIndex) { if (rowIndex == null) { return null; } OctetString os = new OctetString(); os.fromSubIndex(rowIndex, true); String hexString = os.toHexString(); return Long.parseLong(hexString, 16); } }));
Next, each column of the SNMP table needs to be mapped to a MBean attribute or action:
tableSupport.add(oidJvmThreadInstanceEntry, new MBeanAttributeMOTableInfo(onameJvmThreading, new MBeanInvokationKeyProvider(onameJvmThreading, new TypedAttribute("AllThreadIds", long.class), "getThreadInfo", true), new TypedAttribute[] { new TypedCompositeDataAttribute(new TypedAttribute("threadId", Long.class)), new CombinedBitsType(new TypedAttribute[] { new EnumBitsType("threadState", Thread.State.class, Thread.State.values(), 3), new BooleanBitsType("inNative", 1), new BooleanBitsType("suspended", 2)}), new TypedCompositeDataAttribute(new TypedAttribute("blockedCount", Long.class)), new TypedCompositeDataAttribute(new TypedAttribute("blockedTime", Long.class)), new TypedCompositeDataAttribute(new TypedAttribute("waitedCount", Long.class)), new TypedCompositeDataAttribute(new TypedAttribute("waitedTime", Long.class)), new MBeanProxyType(server, onameJvmThreading, Long.class, "getThreadUserTime", new TypedCompositeDataAttribute(new TypedAttribute("threadId", long.class))) { public Object transformFromNative(Object nativeValue, ObjectName objectName) { Long result = (Long) super.transformFromNative(nativeValue, objectName); if ((result == null) || (result < 0)) { return 0L; } return result; } }, new TypedCompositeDataAttribute(new TypedAttribute("threadName", String.class)), new TypedCompositeDataAttribute(new TypedAttribute("lockOwnerName", String.class)), new TypedCompositeDataAttribute(new TypedAttribute("lockOwnerId", Long.class)) { public Object transformFromNative(Object nativeValue, ObjectName objectName) { Long result = (Long)super.transformFromNative(nativeValue, objectName); if ((result == null) || (result < 0)) { return "0.0"; } OID rowPointer = new OID(JvmManagementMib.oidJvmThreadInstanceEntry); rowPointer.append(JvmManagementMib.colJvmThreadInstId); String index = Long.toHexString(result); OctetString os = OctetString.fromHexString(index); rowPointer.append(os.toSubIndex(true)); return rowPointer.toString(); } }}, new String[] { "ThreadId" }, new JMXIndexSupport() { public ObjectName mapToRowMBean(Object rowIdentifier) { return null; } public Object getRowIdentifier(Object nativeRowId, int nativeIndex) { return nativeRowId; } public OID mapToIndex(Object rowIdentifier) { Long l = (Long)rowIdentifier; return OctetString.fromHexString(Long.toHexString(l)).toSubIndex(true); } public Object mapToRowIdentifier(OID rowIndex) { if (rowIndex == null) { return null; } OctetString os = new OctetString(); os.fromSubIndex(rowIndex, true); String hexString = os.toHexString(); return Long.parseLong(hexString, 16); } }));
Finally, the table model of the MOTable has been set to the JMXTableModel bound to the table support and table OID created in the previous steps:
JMXTableModel jvmMemMgrPoolRelEntryModel = JMXTableModel.getDefaultInstance(oidJvmMemMgrPoolRelEntry, tableSupport, super.getJvmMemMgrPoolRelEntry().getColumns()); ((MOTableJMX)super.getJvmMemMgrPoolRelEntry()). setModel(jvmMemMgrPoolRelEntryModel);