This chapter covers the more advanced implementations of the Simple Network Management Protocol (SNMP) that you can create using Java Dynamic Management Kit (Java DMK). These advanced implementations focus on SNMP tables, and are presented in three examples of increasing complexity in the following sections.
18.1 Simple SNMP Tables presents the most straightforward implementation of SNMP tables using Java DMK.
18.2 SNMP Table Instrumentation shows how to expose and manipulate the content of an MBean server using SNMP tables.
18.3 Virtual SNMP Tables shows how to expose and manipulate the content of an MBean server using virtual SNMP tables.
SNMP allows for the structuring of Management Information Base (MIB) variables into logical tables, in which the variables are organized into rows and columns. Java DMK allows for the dynamic addition of rows to the tables that conform to the RowStatus convention defined by SNMPv2. Java DMK also allows you to change the values of existing rows. An example of the use of tables in the Java DMK SNMP API is found in the directory examplesDir/current/Snmp/Rowstatus.
This example shows how to create rows remotely in an SNMP agent from an SNMP manager application, in tables that follow the RowStatus convention. It does this by enabling remote entry creation in the tables and by sending the appropriate requests to create or destroy rows in the table. The RowStatus example also shows how to replace the generated skeletons for SNMP groups, tables, and entries with your own customized classes, so that the customized entry class is instantiated when a remote SNMP manager requests the creation of a row in a table managed by a RowStatus variable.
This example is built around a small SNMP MIB called DEMO-MIB which contains one group, Demo, and one table, TableDemoTable. Before you can proceed with the example, you need to generate this MIB and its associated classes, by using the mibgen compiler supplied with Java DMK. For details of the mibgen compiler, see the Java Dynamic Management Kit 5.1 Tools Reference Guide.
The example MIB is contained in the configuration file mib_demo.txt, which is found in examplesDir/current/Snmp/Rowstatus. You should run the example from within this directory.
Ensure that your PATH environment variable knows where to find mibgen.
In a default installation, mibgen is found in installDir/bin.
Run the mibgen compiler.
$ mibgen -d . mib_demo.txt |
You see that mibgen has generated the following Java classes.
Demo, which implements the Demo group.
DemoEntry, which defines an MBean that provides variables to serve as entries in the table, and specifies the operations that can be performed on this MBean.
DemoEntryMBean, which represents the remote management interface for the DemoEntry MBean.
DemoEntryMeta, which constructs the SNMP metadata for the DemoEntry MBean.
DemoMBean, which represents the remote management interface for the Demo MBean.
DemoMeta, which constructs the SNMP metadata for the DemoEntry MBean.
DEMO_MIB, which defines the behavior of this SNMP MIB.
DEMO_MIBOidTable, which contains the metadata definitions for DEMO-MIB.
DemoTableMeta, which constructs the metadata definitions for the SNMP table.
EnumDemoTableRowStatus, which provides an enumerated integer value that informs you of the status of a table row.
TableDemoTable, which constructs the SNMP table, making each row from DemoEntry objects using SNMP SET operations, and then registers the rows in the Java DMK MBean server.
To be able to modify the classes dynamically, we must take the classes generated by the mibgen compiler and extend them accordingly in a series of custom classes. These customized classes are provided in the examplesDir/current/Snmp/Rowstatus directory.
public class TableDemoTableImpl extends TableDemoTable { public TableDemoTableImpl(SnmpMib myMib, DemoImpl myGroup) { super(myMib); this.myGroup = myGroup; } public TableDemoTableImpl(SnmpMib myMib, MBeanServer server, DemoImpl myGroup) { super(myMib,server); this.myGroup = myGroup; } public void removeEntryCb(int pos, SnmpOid row, ObjectName name, Object entry, SnmpMibTable meta) throws SnmpStatusException { super.removeEntryCb(pos,row,name,entry,meta); myGroup.removeRowCb(); } public void addEntryCb(int pos, SnmpOid row, ObjectName name, Object entry, SnmpMibTable meta) throws SnmpStatusException { super.addEntryCb(pos,row,name,entry,meta); myGroup.addRowCb(); } public Object createDemoEntryMBean(SnmpMibSubRequest req, SnmpOid rowOid, int depth, ObjectName entryObjName, SnmpMibTable meta, Integer aDemoTableIndex) throws SnmpStatusException { DemoEntryImpl entry = new DemoEntryImpl(theMib); entry.DemoTableIndex = aDemoTableIndex; return entry; } private DemoImpl myGroup; }
The TableDemoTableImpl class shown in Example 18–1 subclasses the TableDemoTable class generated by mibgen from the mib_demo.txt file. It customizes the SnmpTableSupport addEntryCb and removeEntryCb methods, adding new functionality not originally provided by TableDemoTable. The addEntryCb and removeEntryCb callback methods are called from the SNMP runtime when a row is added to or removed from the table. The removedRowCb and addRowCb methods are callback methods that maintain a local row counter, DemoInteger. However, the fact that they maintain a row counter is merely for the sake of this example. The addEntryCb and removeEntryCb callback methods could perform any operation the developer chooses to implement, such as emitting notifications, for example.
The most important method of the TableDemoTableImpl class is createDemoEntryMBean. The createDemoEntryMBean method inTableDemoTableImpl overrides the one defined by TableDemoTable to implement DemoEntryImpl instead of the skeletal DemoEntry.
The DemoImpl group, shown in Example 18–2, overrides the constructors defined by the Demo group, to allow remote creation of rows in a DemoTable.
public class DemoImpl extends Demo { public DemoImpl(SnmpMib myMib) { super(myMib); DemoTable = new TableDemoTableImpl(myMib, this); DemoTable.setCreationEnabled(true); } public DemoImpl(SnmpMib myMib, MBeanServer server) { super(myMib,server); DemoTable = new TableDemoTableImpl(myMib, server, this); DemoTable.setCreationEnabled(true); } public void addRowCb() { DemoInteger = new Integer(DemoInteger.intValue() + 1); } public void removeRowCb() { DemoInteger = new Integer(DemoInteger.intValue() - 1); } }
As you can see, as well as setting the SnmpTableSupport method setCreationEnabled value to true, DemoImpl defines the addRowCb and removeRowCb callback methods called by TableDemoTableImpl. For each row added or deleted, addRowCb and removeRowCb update the value of the DemoInteger counter accordingly. DemoInteger is defined by the Demo class, and counts the number of entries in the demoTable.
public class Manager { [...] try { SnmpOidTableSupport oidTable = new DEMO_MIBOidTable(); SnmpOid.setSnmpOidTable(oidTable); [..] EnumRowStatus createAndGo = new EnumRowStatus("createAndGo"); EnumRowStatus destroy = new EnumRowStatus("destroy"); SnmpVarBindList setList1 = new SnmpVarBindList("Manager SET varbind list"); SnmpVarBind stringRow1 = new SnmpVarBind(new SnmpOid("demoTableString.1"), new SnmpString("This is row 1")); SnmpVarBind statusRow1 = new SnmpVarBind(new SnmpOid("demoTableRowStatus.1"), createAndGo.toSnmpValue()); setList1.addVarBind(stringRow1); setList1.addVarBind(statusRow1); request = session.snmpSetRequest(null, setList1); completed = request.waitForCompletion(10000); [...] result = request.getResponseVarBindList(); SnmpVarBindList getList = new SnmpVarBindList("Manager GET varbind list"); getList.addVarBind("demoInteger.0"); getList.addVarBind("demoTableString.1"); getList.addVarBind("demoTableRowStatus.1"); request = session.snmpGetRequest(null, getList); completed = request.waitForCompletion(10000); [...] result = request.getResponseVarBindList(); SnmpVarBindList setList2 = new SnmpVarBindList("Manager SET varbind list"); SnmpVarBind statusRow2 = new SnmpVarBind(new SnmpOid("demoTableRowStatus.1"), destroy.toSnmpValue()); setList2.addVarBind(statusRow2); request = session.snmpSetRequest(null, setList2); completed = request.waitForCompletion(10000); [...] result = request.getResponseVarBindList(); request = session.snmpGetRequest(null, getList); completed = request.waitForCompletion(10000); result = request.getResponseVarBindList(); [...] session.destroySession(); java.lang.System.exit(0); } [...] }
The Manager begins by loading the DEMO_MIBOidTable table created by mibgen. After setting up the SNMP peer, session and parameters, the Manager defines the creation and deletion of rows. The table in this example supports the RowStatus variable. The values for columns can be changed by sending SNMP set requests to the index of the row that is to be added or removed. Setting the value of the column that contains the RowStatus variable to createAndGo creates and activates a row for which a value is provided by the set request. Setting the value of the RowStatus column of an existing row you want to delete to destroy, deletes that row.
As stated in To Generate the DEMO-MIB, you must have run mibgen on the mib_demo.txt file before proceeding with this example. Run the example inside the examplesDir/current/Snmp/Rowstatus directory.
Compile the Java classes.
$ javac -d . *.java |
Start the Agent.
Make sure that no agents are already running before you start the Agent.
$ java Agent |
You see confirmation of the creation of the HTML adaptor on port 8082 and the SNMP adaptor on port 8085, and the addition of the DEMO-MIB to the MBean server. You can perform management operations on the Agent using the HTML adaptor, by loading the URL http://localhost:8082/ in a browser.
In another terminal, start the Manager.
$ java Manager agent-hostname 8085 |
Where agent-hostname is the name of the machine where the Agent is running. 8085 represents the port number on which the SNMP adaptor is running.
When you start the Manager, you will be prompted to press Enter to perform the get and set operations to add and delete rows from the demoTable.
The example shown in 18.1 Simple SNMP Tables showed simply how to use Java DMK to add and remove rows from an SNMP table. The example presented in this section goes further, and demonstrates how to add instrumentation to an SNMP table.
This example is based on the classes in the examplesDir/current/Snmp/MBeanTable directory. It defines a MIB called JMX-MBEAN-SERVER-MIB that exposes the content of an MBean server through SNMP. To achieve this, JMX-MBEAN-SERVER-MIB defines two SNMP tables:
jmxMBeanTable, which has read-create access permission, and contains one row per MBean registered in the remote MBean server.
jmxMBeanAttrTable, which has read-only access permission, and extends the jmxMBeanTable. For each MBean mirrored in the jmxMBeanTable, it contains one additional row per attribute exposed by this MBean.
Before you can proceed with this example, you need to generate the JMX-MBEAN-SERVER-MIB and it's associated classes, by using the mibgen compiler supplied with Java DMK. For details of the mibgen compiler, see the Java Dynamic Management Kit 5.1 Tools Reference Guide.
The example MIB is contained in the configuration file JMX-MBEAN-SERVER-MIB.mib, which is found in examplesDir/current/Snmp/MBeanTable. You should run the example from within this directory.
Ensure that your PATH environment variable knows where to find mibgen.
In a default installation, mibgen is found in installDir/bin.
Create a new directory, generated, inside examplesDir/current/Snmp/MBeanTable.
$ mkdir generated |
This directory is where mibgen generates the classes associated with JMX-MBEAN-SERVER-MIB.mib.
Run the mibgen compiler.
$ mibgen -X:use-display-hint -d generated JMX-MBEAN-SERVER-MIB.mib |
The advanced mibgen option use-display-hint instructs mibgen to generate an attribute of type String for any object using a textual convention whose DISPLAY-HINT is 255a. This option is used because JMX-MBEAN-SERVER-MIB defines textual conventions (for example, JmxMBeanObjectNameTC) which must be translated into java.lang.String attributes, rather than the default Byte[] attributes.
The -d generated option sends the output of mibgen to the newly created generated directory
Within the generated directory, you see that mibgen has generated the following Java classes.
EnumJmxMBeanRowStatus, an enumerated type generated for the jmxMBeanRowStatus columnar variable defined in the MIB.
JmxMBeanAttrEntry, which implements the JmxMBeanAttrEntryMBean interface and is generated from the jmxMBeanAttrEntry conceptual row defined in the MIB. The JmxMBeanAttrEntry is an empty skeleton class that can be subclassed to implement a row in the jmxMBeanAttrTable defined in the MIB. This example shows how this generated class can be subclassed to provide an instrumented implementation, JmxMBeanAttrEntryImpl, that is linked to the actual resource exposed.
JmxMBeanAttrEntryMBean, which represents the public interface for the jmxMBeanAttrEntry conceptual row. An object implementing the JmxMBeanAttrEntryMBean interface represents a row in the jmxMBeanAttrTable, that is defined in the MIB.
JmxMBeanAttrEntryMeta, which constructs the SNMP metadata for the JmxMBeanAttrEntry conceptual row, that is defined in the MIB.
JmxMBeanAttrTableMeta, which constructs the SNMP metadata for the jmxMBeanAttrTable table, that is defined in the MIB.
JmxMBeanEntry, which implements the JmxMBeanEntryMBean interface and is generated from the jmxMBeanEntry conceptual row defined in the MIB. The JmxMBeanEntry is an empty skeleton class that can be subclassed to implement a row in the jmxMBeanTable defined in the MIB. This example shows how this generated class can be subclassed in order to provide an instrumented implementation, JmxMBeanEntryImpl. that is linked to the actual resource exposed.
JmxMBeanEntryMBean, which represents the public interface for the jmxMBeanEntry conceptual row. An object implementing the JmxMBeanEntryMBean interface represents a row in the jmxMBeanTable, that is defined in the MIB.
JmxMBeanEntryMeta, which constructs the SNMP metadata for the jmxMBeanEntry conceptual row defined in the MIB.
JmxMBeanServer, which represents the jmxMBeanServer group that is defined in the MIB. The JmxMBeanServer is an empty skeleton class that can be subclassed to implement the jmxMBeanServer group. This default implementation of the jmxMBeanServer group instantiates the generated default implementation for the two SNMP tables defined in that group:
TableJmxMBeanAttrTable for the jmxMBeanAttrTable.
TableJmxMBeanTable for the jmxMBeanTable.
This example shows how the generated class JmxMBeanServer, can be subclassed by JmxMBeanServerImpl, to instantiate specialized generated table classes that instantiate JmxMBeanAttrEntryImpl and JmxMBeanEntryImpl objects instead of the generated JmxMBeanAttrEntry and JmxMBeanEntry skeletons.
JmxMBeanServerMBean, which represents the public interface for the object that represents the jmxMBeanServer group.
JmxMBeanServerMeta, which constructs the SNMP metadata for the jmxMBeanServer group, that is defined in the MIB.
JMX_MBEAN_SERVER_MIB, which instantiates the metadata and instrumentation classes for all the objects defined in the JMX-MBEAN-SERVER-MIB module. This example shows how JMX_MBEAN_SERVER_MIB_Impl subclasses the generated JMX_MBEAN_SERVER_MIB, to instantiate the customized class JmxMBeanServerImpl instead of the generated skeletons.
JMX_MBEAN_SERVER_MIBOidTable, which is a convenience class generated by mibgen to make it possible to map symbolic OID definitions from the JMX-MBEAN-SERVER-MIB. For example, jmxMBeanAttrName is mapped to the corresponding OID value, 1.3.6.1.4.1.42.2.145.1.3.1.1.1.1.4.1.2.
JmxMBeanTableMeta, which constructs the metadata definitions for the jmxMBeanTable table defined in the MIB.
TableJmxMBeanAttrTable, which is the default implementation of the jmxMBeanAttrTable table defined in the MIB. By default, the table is empty.
TableJmxMBeanTable, which is the default implementation of the jmxMBeanTable table defined in the MIB. By default, the table is empty. If remote creation of rows through SNMP is enabled, the default implementation creates instances of the generated JmxMBeanEntry empty skeleton. The JmxMBeanServerImpl class provided in this example shows how to instantiate a specialized version of this class that creates customized JmxMBeanEntryImpl objects instead of the default implementation.
As was the case for the simple SNMP table example, to be able to tie the code generated by mibgen to its proper instrumentation, you must extend the classes generated by mibgen in a series of custom classes. The customized classes are those provided in the examplesDir/current/Snmp/MBeanTable directory.
The customized extensions to the generated classes add the following functionality:
JMX_MBEAN_SERVER_MIB_Impl extends the generated JMX_MBEAN_SERVER_MIB, instantiating JmxMBeanServerImpl instead of the generated JmxMBeanServer skeleton. In addition, JMX_MBEAN_SERVER_MIB_Impl adds a new method, start(), that when called starts mirroring the content of the MBean server into the MIB.
The start() method instructs the JmxMBeanServerImpl group to populate the jmxMBeanTable and jmxMBeanAttrTable according to the initial content of the MBean server mirrored by this MIB. The start() method also instructs the JmxMBeanServerImpl group to start listening for MBeanServerNotifications coming from the MBean server.
JmxMBeanServerImpl extends the JmxMBeanServer skeleton, and instantiates a subclass of TableJmxMBeanTable, in which the MBeans registered in the MBean server are mirrored. The TableJmxMBeanTable object is subclassed to support remote creation and deletion of entries in the jmxMBeanTable through SNMP.
An SNMP SET request that creates a new row in the jmxMBeanTable is transformed into a createMBean() call to the MBean server.
An SNMP SET request deleting an existing row in the jmxMBeanTable is transformed in an unregisterMBean() call to the MBean server.
The JmxMBeanServerImpl also listens for notifications of the type MBeanServerNotification to keep the jmxMBeanTable updated, and creates and deletes rows in the jmxMBeanAttrTable accordingly.
JmxMBeanAttrEntryImpl extends the generated JmxMBeanAttrEntry, and is used to mirror an attribute of an MBean that is itself mirrored in the jmxMBeanTable. A JmxMBeanAttrEntryImpl object adds instrumentation to a conceptual row in the jmxMBeanAttrTable. When a new row is added to jmxMBeanTable, the corresponding JmxMBeanEntryImpl updates the jmxMBeanAttrTable by adding one row per attribute exposed by the mirrored MBean. When a row is deleted from the jmxMBeanTable, the JmxMBeanEntryImpl deletes the corresponding rows from the jmxMBeanAttrTable. The jmxMBeanAttrTable is thus managed entirely internally by the agent. Its instances cannot be created or deleted by a remote SNMP manager, except as a side effect of the creation or deletion of a row in the jmxMBeanTable.
JmxMBeanEntryImpl extends the generated JmxMBeanEntry skeleton. JmxMBeanEntryImpl is used to mirror MBeans that are registered in the MBean server that is made accessible through SNMP by the JMX_MBEAN_SERVER_MIB_Impl MIB. A JmxMBeanEntryImpl object adds instrumentation to a conceptual row in the jmxMBeanTable. JmxMBeanEntryImpl objects can be created or deleted as a result of SNMP SET requests, or as a result of an MBean being locally registered or deregistered from the MBean server mirrored by the MIB. The JmxMBeanServerImpl object keeps the jmxMBeanTable updated, and thus manages the life cycle of the JmxMBeanEntryImpl objects. The JmxMBeanEntryImpl objects keep the jmxMBeanAttrTable updated, and thus manage the life cycle of the JmxMBeanAttrEntryImpl objects.
Aspects of the JmxMBeanServerImpl and JmxMBeanEntryImpl classes are examined in more detail in the following sections.
As stated previously, JmxMBeanServerImpl extends the JmxMBeanServer empty skeleton class. JmxMBeanServerImpl also instantiates a subclass of TableJmxMBeanTable, to mirror the MBeans registered in an MBean server.
[...] private final void inittables() { JmxMBeanTable = new TableJmxMBeanTable(myMib,null) { public Object createJmxMBeanEntryMBean(SnmpMibSubRequest req, SnmpOid rowOid, int depth, ObjectName entryObjName, SnmpMibTable meta, Integer aJmxMBeanIndex) throws SnmpStatusException { return JmxMBeanServerImpl.this. createJmxMBeanEntryMBean(req,rowOid,depth,entryObjName, meta,aJmxMBeanIndex); } public void removeEntryCb(int pos, SnmpOid row, ObjectName name, Object entry, SnmpMibTable meta) throws SnmpStatusException { super.removeEntryCb(pos,row,name,entry,meta); final JmxMBeanEntryMBean e = (JmxMBeanEntryMBean) entry; final EnumJmxMBeanRowStatus destroy = new EnumJmxMBeanRowStatus(EnumRowStatus.destroy); e.setJmxMBeanRowStatus(destroy); } }; JmxMBeanTable.setCreationEnabled(true); } [...]
In Example 18–4, JmxMBeanServerImpl defines a method, inittables, to instantiate and initialize its table objects. The inittables method creates a new instance of TableJmxMBeanTable, and overrides it so that its createJmxMBeanEntryMBean method returns a JmxMBeanEntryImpl object instead of the default JmxMBeanEntry object. This is done by defining a new createJmxMBeanEntryMBean method in the customized JmxMBeanEntryImpl class (see Example 18–5), and calling it on the parent JmxMBeanServerImpl object from the overridden TableJmxMBeanTable object, as shown in Example 18–4.
The removeEntryCb method is overridden, so that table entries representing MBeans also delete their associated entries in the jmxMBeanAtrrTable by calling setJmxMBeanRowStatus(destroy) when they are removed from the jmxMBeanTable.
[...] private Object createJmxMBeanEntryMBean(SnmpMibSubRequest req, SnmpOid rowOid, int depth, ObjectName entryObjName, SnmpMibTable meta, Integer aJmxMBeanIndex) throws SnmpStatusException { SnmpVarBind rowStatusVb = req.getRowStatusVarBind(); if (rowStatusVb == null) throw new SnmpStatusException( SnmpStatusException.snmpRspNoCreation); if (! isAvailable(aJmxMBeanIndex.intValue())) { if (Boolean.getBoolean("info")) System.out.println("Index is not suitable: " + aJmxMBeanIndex); throw new SnmpStatusException( SnmpStatusException.snmpRspInconsistentValue); } JmxMBeanEntryImpl entry = new JmxMBeanEntryImpl(myMib,this); entry.JmxMBeanIndex = aJmxMBeanIndex; entry.createFromRemote(req,rowStatusVb); return entry; } [...]
As shown in Example 18–4, the createJmxMBeanEntryMBean method is called when a remote SNMP Manager creates a new row in a jmxMBeanTable. Example 18–5 shows how the createJmxMBeanEntryMBean method is overridden by the JmxMBeanServerImpl class, to add new entries in the jmxMBeanAttrTable as the rows are created in the table.
Of the parameters the customized version of createJmxMBeanEntryMBean takes when it is started, the following are the most significant..
req, the SnmpMibSubRequest containing the VarBind list pertaining to this new row.
rowOid, the index of this new row's OID.
meta, the SnmpMibTable metadata object of the jmxMBeanTable.
aJmxMBeanIndex, the value of the new entry's JmxMBeanIndex index variable.
The createJmxMBeanEntryMBean method checks first of all whether the VarBind list found when req calls getRowStatusVarBind() is valid. It then checks whether the row's OID index is available. Once it has established that both the VarBind and the index are viable, it proceeds to create a JmxMBeanEntryImpl instance, entry. Calling createFromRemote() at this point ensures that createMBean() will be called when the new entry is eventually activated.
As mentioned previously, JmxMBeanServerImpl is also configured to listen for notifications of the type MBeanServerNotifications coming from the MBean server.
[...] public synchronized void start() { started = true; try { final ObjectName delegate = new ObjectName("JMImplementation:type=MBeanServerDelegate"); myMib.getMibServer(). addNotificationListener(delegate,mbsListener,null,null); try { Set mbeans = myMib.getMibServer().queryNames(null,null); for (final Iterator it = mbeans.iterator(); it.hasNext();) { final ObjectName name = (ObjectName) it.next(); if (mustShow(name)) showMBean("start",name); } } catch (Exception x) { try { myMib.getMibServer(). removeNotificationListener(delegate,mbsListener); } catch (Exception e) { /* OK */ } throw x; } } catch (Exception x) { started = false; System.err.println("Failed to start MBean Table: " + x); if (Boolean.getBoolean("debug")) x.printStackTrace(); } } [...]
The start() method shown in Example 18–5 starts a notification listener in the MIB server that listens out for notifications from the MBeanServerDelegate. It also populates the jmxMBeanTable and jmxMBeanAttrTable with the initial content of the MBean server. These tables are then updated as and when notifications are received, as shown in Example 18–7.
[...] private void handleMBSNotification(Notification notif, Object handback) { synchronized(this) { if (!started) return; } if (notif instanceof MBeanServerNotification) { final MBeanServerNotification n = (MBeanServerNotification) notif; final String nt = n.getType(); final ObjectName mbeanName = n.getMBeanName(); if (MBeanServerNotification.REGISTRATION_NOTIFICATION. equals(nt)) { synchronized (this) { if (mustShow(mbeanName)) { showMBean(nt,mbeanName); } } } else if (MBeanServerNotification.UNREGISTRATION_NOTIFICATION. equals(nt)) { synchronized (this) { if (mustHide(mbeanName)) { hideMBean(nt,mbeanName); } } } } } [...]
The handleMBSNotification method that JmxMBeanServerImpl defines begins by checking that the correct type of notification has been received. If the notification is of the type MBeanServerNotification.REGISTRATION_NOTIFICATION, then a method named showMBean() is called. Otherwise, if MBeanServerNotification.UNREGISTRATION_NOTIFICATION is received, then the MBean is hidden with a call to hideMBean. The showMBean() method is shown in Example 18–8.
[...] private void showMBean(String operation,ObjectName name,int index) { try { JmxMBeanEntry entry = new JmxMBeanEntryImpl(myMib,this); entry.JmxMBeanIndex = new Integer(index); entry.setJmxMBeanObjectName(name.toString()); JmxMBeanTable.addEntry(entry); names.put(name, entry); entry.setJmxMBeanRowStatus( new EnumJmxMBeanRowStatus(EnumRowStatus.active)); if (Boolean.getBoolean("info")) System.out.println("ADDED: JmxMBeanTable["+index+"]="+name); } catch (Exception x) { System.err.println("Failed to add MBean entry: " + name); if (Boolean.getBoolean("debug")) x.printStackTrace(); } } [...]
The showMBean method adds the MBeans that handleMBSNotification learns about to the SNMP table JmxMBeanTable as JmxMBeanEntryImpl row entries. The status of the row is set to active. The MBean discovered by the notification listener is thus entered in the SNMP table as a row entry.
As mentioned previously, the JmxMBeanEntryImpl objects keep the jmxMBeanAttrTable updated. Example 18–9 and Example 18–10 show how JmxMBeanEntryImpl changes the status of rows, activates rows, and destroys them.
[...] public synchronized void setJmxMBeanRowStatus(EnumJmxMBeanRowStatus x) throws SnmpStatusException { switch (x.intValue()) { case EnumRowStatus.active: if (! (JmxMBeanRowStatus.intValue() == EnumRowStatus.active)) activate(); break; case EnumRowStatus.notReady: break; case EnumRowStatus.notInService: super.setJmxMBeanRowStatus(x); break; case EnumRowStatus.destroy: destroy(); break; default: throw new SnmpStatusException(SnmpStatusException. snmpRspInconsistentValue); } } [...]
The setJmxMBeanRowStatus method shown above is called by the SNMP runtime when the row is created remotely. It is also called explicitly by JmxMBeanEntryImpl when the row is created after receiving an MBean server notification.
private void activate() throws SnmpStatusException { if ((remoteCreation) && (mbean == null || name == null)) throw new SnmpStatusException(SnmpStatusException.snmpRspGenErr); try { if (remoteCreation) { myGroup.registerMBean(mbean,name,this); initJmxMBean(name); remoteCreation = false; mbean = null; } exposedAttrCount = myGroup.addAttributes(name,this); if (Boolean.getBoolean("info")) System.out.println(name.toString()+ ": added " + exposedAttrCount + " attribute(s)."); JmxMBeanRowStatus = new EnumJmxMBeanRowStatus(EnumRowStatus.active); } catch (Exception x) { SnmpStatusException sn = new SnmpStatusException(SnmpStatusException.snmpRspGenErr); sn.initCause(x); throw sn; } } private void destroy() throws SnmpStatusException { try { JmxMBeanRowStatus = new EnumJmxMBeanRowStatus(EnumRowStatus.notInService); if (name != null && remoteDeletion) myGroup.unregisterMBean(name,this); remoteCreation = false; mbean = null; if (name != null) myGroup.removeAttributes(name,this); attrList = null; } catch (Exception x) { SnmpStatusException sn = new SnmpStatusException(SnmpStatusException.snmpRspGenErr); sn.initCause(x); throw sn; } }
Example 18–10 shows the activate and destroy methods defined by JmxMBeanEntryImpl. The activate method verifies first of all whether the row was created remotely or locally, and acts differently depending on the answer. If the row was created remotely by calling the createFromRemote method, the row entry registers the requested MBean in the MBean. If the row is created locally, this means that the request to create the row was made by an existing registered MBean, so there is no need to register it again in the MBean server.
The destroy method is created when the row is removed, either locally by handleMBeanServerNotification or remotely. The destroy method is called by removeEntryCb, which itself is called when the row is removed from the table, whether remotely or locally.
In this example, the table instrumentation operations shown in the preceding sections are all demonstrated by a single class, the Agent. The fact that this Agent class creates MBeans, and registers them in an MBean server so that they can be mirrored by the SNMP MIB, is irrelevant to this example. The real purpose of the Agent is to demonstrate how to add instrumentation to the SNMP tables using Java DMK technology, not to demonstrate what the example actually does.
public class Agent { public interface SimpleMBean { public String getName(); public int getCount(); public void reset(); } public static class Simple implements SimpleMBean { public Simple() { this(null); } // Define MBean operations [...] } public static MBeanServer getPlatformMBeanServer() { final MBeanServer test = MBeanServerFactory.createMBeanServer(); final MBeanServer first = (MBeanServer) MBeanServerFactory.findMBeanServer(null).get(0); if (test != first) MBeanServerFactory.releaseMBeanServer(test); return first; } public int populate(MBeanServer server, int mbeanCount) { int count = 0; for (int i=0; i<mbeanCount; i++) { try { final SimpleMBean simple = new Simple(); final ObjectName name = new ObjectName("Example:type=Simple,name="+ simple.getName()); server.registerMBean(simple,name); count ++; System.out.println("Registered MBean: " + name); } catch (Exception x) { System.err.println("Failed to register MBean: " + x); debug(x); } } return count; } public int depopulate(MBeanServer server, int mbeanCount) { int count = 0; while (true) { try { final ObjectName pattern = new ObjectName("Example:type=Simple,*"); Set mbeans = server.queryNames(pattern,null); if (mbeans.isEmpty()) break; for (Iterator it=mbeans.iterator(); it.hasNext() ; ) { if (count == mbeanCount) return count; final ObjectName next = (ObjectName) it.next(); try { server.unregisterMBean(next); count++; System.out.println("Deregistered MBean: " + next); } catch (InstanceNotFoundException x) { continue; } } if (count >= mbeanCount) break; } catch (Exception x) { System.err.println("Unexpected exception: " + x); debug(x); break; } } return count; } [...]
The Agent class shown in Example 18–11 defines a basic MBean called SimpleMBean, that performs very basic MBean operations. The getPlatformMBeanServer method is used to obtain the first MBean server that has been created in this session by the MBeanServerFactory.
[...] public void startmib(MBeanServer server,int port) throws IOException { final SnmpV3AdaptorServer adaptor = new SnmpV3AdaptorServer((InetAddressAcl)null,port); adaptor.enableSnmpV1V2SetRequest(); adaptor.start(); do { adaptor.waitState(CommunicatorServer.ONLINE,1000); } while (adaptor.getState() == CommunicatorServer.STARTING); final int state = adaptor.getState(); if (state != CommunicatorServer.ONLINE) { try { adaptor.stop(); } catch (Exception x) { /* OK */ } throw new IOException("Can't start adaptor: " + adaptor.getStateString()); } JMX_MBEAN_SERVER_MIB_Impl mib = new JMX_MBEAN_SERVER_MIB_Impl(server); try { mib.init(); mib.setSnmpAdaptor(adaptor); server.registerMBean(adaptor,null); mib.start(); } catch (Exception x) { System.err.println("Failed to register SnmpAdaptor: " + x); try { adaptor.stop(); } catch (Exception e) { /* OK */ } final IOException io = new IOException("Failed to register SnmpAdaptor"); io.initCause(x); throw io; } System.out.println("SnmpAdaptor ready at port: " + adaptor.getPort()); } [...]
The Agent class then defines a method, startmib, to instantiate an SNMPv3 adaptor server named adaptor, and creates an instance of the JMX_MBEAN_SERVER_MIB_Impl MIB, named mib. The mib is constructed around an MBean server named server, the content of which is mirrored in this MIB. The MIB is then initialized by a call to the init() method defined by JMX_MBEAN_SERVER_MIB_Impl. The reference to the SNMP protocol adaptor through which the MIB is accessible, in this case adaptor, is set by calling the SnmpMibAgent method setSnmpAdaptor, and this adaptor is then registered as an MBean in the MBean server server. The start() method defined by JMX_MBEAN_SERVER_MIB_Impl is then called. The start() method starts the mbeanServerGroup, which itself is an instance of JmxMBeanServerImpl, and which activates the mirroring of the MBean server in the MIB. See the JMX_MBEAN_SERVER_MIB_Impl.java file in the generated directory for the full implementation of the start() method.
[...] public void start(int port, String[] args) throws IOException { final MBeanServer server = getPlatformMBeanServer(); final JMXServiceURL[] urls = new JMXServiceURL[args.length]; for (int i=0;i<args.length;i++) { try { urls[i] = new JMXServiceURL(args[i]); } catch (MalformedURLException x) { throw x; } } for (int i=0;i<urls.length;i++) { try { final JMXConnectorServer s = JMXConnectorServerFactory. newJMXConnectorServer(urls[i], null,null); final ObjectName name = new ObjectName("Connector:type= "+s.getClass().getName()+ ",instance="+i); server.registerMBean(s,name); s.start(); } catch (Exception x) { final String msg = "Failed to start connector: " + args[i]; System.err.println(msg); final IOException io = new IOException(msg); io.initCause(x); throw io; } } populate(server,5); startmib(server,port); final Timer timer = new Timer(); final long period= 2000; final long now=0; final int max=4; for (int i=0;i<max;i++) { final TimerTask taskAdd = new TimerTask() { public void run() { final MBeanServer server = getPlatformMBeanServer(); populate(server,1); } }; final TimerTask taskRem = new TimerTask() { public void run() { final MBeanServer server = getPlatformMBeanServer(); depopulate(server,1); } }; timer.schedule(taskAdd,now+period*i,2*period*max); timer.schedule(taskRem,now+period*max+period*i,2*period*max); } } public static void initOidTable() { final SnmpOidTable orig = SnmpOid.getSnmpOidTable(); SnmpOidDatabaseSupport mibs = new SnmpOidDatabaseSupport(); mibs.add(new JMX_MBEAN_SERVER_MIBOidTable()); if (orig != null) mibs.add(orig); SnmpOid.setSnmpOidTable(mibs); } [...]
Agent now defines a start() method of its own. The start method firstly obtains an MBean server instance, server, by calling the getPlatformMBeanServer method defined earlier in the class. It also retrieves any JMX service URLs that might be passed to Agent as arguments when it is launched. If any JMX service URLs are provided at start-time, the Agent.start() method uses them to create JMXConnectorServer instances that it registers in the MBean server server as MBeans.
The MBean server instance server is populated with dummy MBeans by calling the populate() method. These MBeans are mirrored in the MIB, when the mirroring is activated.
The SNMP adaptor and the MIB are started by calling startmib, that was defined in Example 18–12.
Once the startmib method has been called, timer tasks are defined that periodically add and remove MBeans from the MBean server. These periodic tasks serve merely to test and demonstrate that the jmxMBeanTable and jmxMBeanAttrTable are updated accordingly in the MIB.
JMX_MBEAN_SERVER_MIBOidTable metadata definitions for the MIBs are loaded into the global SNMP OID table, to enable the use of symbolic OID names.
[...] public static void main(String[] args) { Agent agent = new Agent(); try { initOidTable(); final String portStr = System.getProperty("snmp.port", "16161"); final int port; try { port = Integer.parseInt(portStr); } catch (Exception x) { System.err.println("Invalid value specified for snmp.port: " + portStr); System.err.println("Error is: " + x); throw x; } System.out.println("Using SNMP port " + port); agent.start(port,args); // Wait forever... Thread.sleep(Long.MAX_VALUE); } catch (Exception x) { System.err.println("Failed to start agent: " + x); debug(x); System.exit(1); } } }
Finally, an instance of Agent, agent, is started on the appropriate SNMP port by calling the start method defined in Example 18–13.
After you have run mibgen to generate JMX_MBEAN_SERVER_MIB and its associated classes, as explained in 18.2.1 Classes Generated by mibgen, you can run the example.
Run the example from the examplesDir/current/Snmp/MBeanTable directory.
Compile the Java classes in the generated directory.
$ cd generated $ javac -d .. *.java |
This creates the compiled *.class files into the examplesDir/current/Snmp/MBeanTable directory, rather than in generated, so that they are accessible to the other classes used in the example.
Compile the Java classes in the examplesDir/current/Snmp/MBeanTable directory.
$ cd .. $ javac -d . *.java |
Start the Agent.
You have several options when starting the Agent class.
To start the Agent on the default SNMP port, 16161:
$ java Agent service:jmx:jmxmp:// |
To start the Agent on a port of your choice:
$ java -Dsnmp.port=port_number Agent service:jmx:jmxmp:// |
To start the Agent with a connector of your choosing, you can specify a different service URL and connector port. For example:
$ java Agent JMX_service_URL |
In any of the above cases, you see confirmation of the SNMP port used, confirmation of the creation of the connector and then the alternating registration and deregistration of sets of five MBeans.
You can now perform management operations on the Agent.
Use a JMX management console of your choice to examine the content of the MBean server through a JMXMP connector.
Use the SNMP management application, easymanager, that is supplied with Java DMK to examine the JMX-MBEAN-SERVER-MIB through SNMP. You can find the easymanager application in the installDir/contributions/easymanager directory.
Add the JMX-MBEAN-SERVER-MIBOidTable.class to your classpath.
Type the following command if you are using a UNIX platform:
$ export CLASSPATH=$CLASSPATH:examplesDir/current/Snmp/MBeanTable/ |
Type the following command if you are using a Windows platform:
set classpath=%classpath%;examplesDir/current/Snmp/MBeanTable/ |
Open installDir/contributions/easymanager/bin
Start easymanager.
If the SNMP agent has been started with the default example port, type the following command.
$ easymanager.sh -port 16161 |
You can now use easymanager to perform the following operations.
Pop up a MIB discovery window.
discovermib -p JDMK_STD_V2_profile |
Create a new MBean.
set:2 -w private :<<jmxMBeanObjectName.998,4,Test:name=test1>, <jmxMBeanClassName.998,4,Agent$Simple>, <jmxMBeanRowStatus.998,2,4>> |
Click on resynch in the MIB discovery window to observer the changes in the MIB.
Destroy the MBean you created.
set:2 -w private :<<jmxMBeanRowStatus.998,2,6>> |
This example shows how to implement virtual SNMP tables when instrumenting a MIB in Java DMK. It is based on the same MIB as the one used in the MBean Table Instrumentation example, so you should make sure you have run that example before running this one. However, the fundamental difference between this example and the SNMP Table Instrumentation example is that the tables implemented remain entirely virtual. The tables are calculated whenever they are needed by an SNMP request, rather than residing permanently in the SNMP session. The virtual table is also created with a limited period of validity. Once this period has expired, if the request for which the table was calculated has been executed, the table can be garbage-collected. There are thus two main advantages to operating with virtual tables rather than with tables that are permanently present.
Coherency: A table snapshot is calculated when a request is received, unless there is already a valid table snapshot present. Every snapshot is an immutable object. Consequently, the table snapshot will remain coherent with the context of the request for the entire duration of the execution of that request.
Resource usage: Unused and expired tables can be garbage-collected. Consequently, as long as there are no requests arriving, there is no redundant table consuming resources unnecessarily, and no need to continue updating the table when no managers are making requests.
The virtual SNMP table example is based on the classes in the examplesDir/current/Snmp/MBeanVirtualTable directory. It defines the MIB called JMX-MBEAN-SERVER-MIB that exposes the content of an MBean server through SNMP. This MIB is the same as the one defined in the previous example. To achieve this, JMX-MBEAN-SERVER-MIB defines the same two SNMP tables as before:
jmxMBeanTable, which has read-create access permission, and contains one row per MBean registered in the remote MBean server.
jmxMBeanAttrTable, which has read-only access permission, and extends the jmxMBeanTable. For each MBean mirrored in the jmxMBeanTable, it contains one additional row per attribute exposed by this MBean.
Before you can proceed with this example, you need to generate the JMX-MBEAN-SERVER-MIB and it's associated classes, by using the mibgen compiler supplied with Java DMK. For details of the mibgen compiler, see the Java Dynamic Management Kit 5.1 Tools Reference Guide.
The example MIB is contained in the configuration file JMX-MBEAN-SERVER-MIB.mib, which is found in examplesDir/current/Snmp/MBeanVirtualTable. You should run the example from within this directory.
Ensure that your PATH environment variable knows where to find mibgen.
In a default installation, mibgen is found in installDir/bin.
Create a new directory, generated, inside examplesDir/current/Snmp/MBeanVirtualTable.
$ mkdir generated |
This directory is where mibgen generates the classes associated with JMX-MBEAN-SERVER-MIB.mib.
Run the mibgen compiler.
$ mibgen -X:use-display-hint -X:no-table-access -X:abstract-mib \ -d generated JMX-MBEAN-SERVER-MIB.mib |
The advanced mibgen option use-display-hint instructs mibgen to generate an attribute of type String for any object using a textual convention whose DISPLAY-HINT is 255a. This option is used because JMX-MBEAN-SERVER-MIB defines textual conventions (for example, JmxMBeanObjectNameTC) which must be translated into java.lang.String attributes, rather than the default Byte[] attributes.
The -X:no-table-access option instructs mibgen not to generate a table accessor in the group MBean interfaces. The effect of this is that the accessors used to return the TableJmxMBeanTable and TableJmxMBeanAttrTable table objects are not created.
The -X:abstract-mib option, as the name suggests, generates an abstract MIB. In this case, the MIB class is an abstract class, in which the MBean factory methods are also abstract.
The -d generated option sends the output of mibgen to the newly created generated directory
Using the first two options makes it possible to get rid of any references to the default JmxMBeanServer class, as well as to its associated Table* objects. These classes are still generated, but our customer implementation no longer references them.
The MIB used in this example is the same as the one used in the SNMP table instrumentation example. Consequently, mibgen generates the same files in the generated directory as were generated in the table instrumentation example. The differences between this example and the instrumentation example are found in the customized classes, that are used to extend the generated classes.
The customized classes extend the functionality of the classes of the generated MIB. As was the case in the previous example, the JMX_MBEAN_SERVER_MIB_Impl extends the JMX-MBEAN-SERVER-MIB generated by mibgen, to instantiate the instrumented JmxMBeanServerImpl class. However, the extended behavior that the customized classes bring to the MIB in this example differs from that provided by the SNMP table instrumentation example. Most significantly, whereas the JmxMBeanServerImpl class was the principal author of the customized behavior of the MIB in the previous example, in this example the bulk of the work is done by JmxMBeanTableMetaImpl.
The following sections examine each of the customized classes, describing their purpose, and where necessary, highlighting their differences with the corresponding classes in the SNMP table instrumentation example.
JmxMBeanServerImpl instruments the jmxMBeanServer group defined in the MIB. It implements the JmxMBeanServerMBean interface that is generated by mibgen, but unlike the previous example, it does not extend the generated JmxMBeanServer skeleton. As a result of not extending the JmxMBeanServer object, and also because the MIB was generated with the -X:no-table-access option, the generated TableJmxMBeanTable and TableJmxMBeanAttrTable objects are not instantiated. Although they are still generated by mibgen, these classes are not used in this example.
The jmxMBeanTable and jmxMBeanAttrTable tables are implemented as virtual tables. They are computed on the fly when an SNMP request needs to access them. Furthermore, the jmxMBeanTable and jmxMBeanAttrTable tables are managed by their respective customized JmxMBeanTableMetaImpl and JmxMBeanAttrTableMetaImpl meta objects. In the table instrumentation example, the tables were managed by the JmxMBeanServerImpl object.
JmxMBeanServerMetaImpl extends the generated JmxMBeanServerMeta class, that is used to represent SNMP metadata for the JmxMBeanServer group.
JmxMBeanServerMetaImpl overrides the table-meta factory methods in order to instantiate customized implementations of the JmxMBeanTableMeta and JmxMBeanAttrTableMeta meta classes. These meta classes are customized in the JmxMBeanTableMetaImpl and JmxMBeanAttrTableMetaImpl meta classes, that are shown in 18.3.2.3 JmxMBeanTableMetaImpl and 18.3.2.4 JmxMBeanAttrTableMetaImpl respectively.
JmxMBeanTableMetaImpl extends the generated JmxMBeanTableMeta class, and overrides the following JmxMBeanTableMeta methods.
getEntry(SnmpOid rowOid), so that it returns a JmxMBeanEntryImpl object for the indicated row instead of a JmxMBeanEntry table entry.
contains(SnmpOid oid, Object userData), so that it tells whether the indicated row is present in the table.
getNextOid(Object userData), so that it returns the row index, the rowOid, of the first row in the table.
getNextOid(SnmpOid rowOid,Object userData), so that it returns the index of the row that immediately follows the indicated row in the table.
createNewEntry(SnmpMibSubRequest req, SnmpOid rowOid, int depth), so that it makes it possible to create new rows from SNMP SET requests. Overriding this method is not necessary when the table is read-only.
removeTableRow(SnmpMibSubRequest req, SnmpOid rowOid, int depth), so that it makes it possible to delete rows from SNMP SET requests. Overriding this method is not necessary when the table is read-only.
In addition to overriding the above methods, it is the JmxMBeanTableMetaImpl class that implements the caching mechanism. An SnmpUserData factory is used to create a transient request-contextual cache, that will remain active for the duration of the incoming request. After the request has completed, the contextual-cache is garbage-collected.
JmxMBeanTableMetaImpl defines the following subclasses, that implement the caching mechanism.
The JmxMBeanTableData is a snapshot of the table index. It is computed on demand, and cached in a JmxMBeanTableCache object which holds it through a WeakReference. The JmxMBeanTableData includes a time-stamp which is used to determine whether the cached data is still valid, or if it needs to be recomputed.
The JmxMBeanTableCache is a weak cache used to avoid constant recomputation of the JmxMBeanTableData. It holds the last computed JmxMBeanTableData in a weak reference, so that the garbage collector might reclaim it at any time, provided that no request is currently working on that most-recently computed copy. When an SNMP request that needs to access the jmxMBeanTable comes in, the JmxMBeanTableData is looked up from the JmxMBeanTableCache, and recomputed if needed. The JmxMBeanTableData object is then put in the request-contextual cache where it remains until the request is completed. The request will always consult that particular copy, thus ensuring the consistency of the returned data. If in the meantime a concurrent request comes in, it might obtain the same JmxMBeanTableData copy as the previous request, or a newly computed object, if the previous one was found to be obsolete. Consequently, the two requests might work on the same, or on different snapshots of the table. The snapshot obtained by a given request never changes.
The JmxMBeanTableDataItem is a simple class that wraps an ObjectName and a JmxMBeanEntry. The JmxMBeanEntry is lazy-evaluated. The JmxMBeanTableData contains an array of JmxMBeanTableDataItem objects.
JmxMBeanAttrTableMetaImpl extends the generated JmxMBeanAttrTableMeta class, and overrides the following methods.
getEntry(SnmpOid rowOid), so that it returns an JmxMBeanAttrEntryImpl object for the indicated row.
contains(SnmpOid oid, Object userData), so that it tells whether the indicated row is present int the table.
getNextOid(Object userData), so that it returns the row index, the rowOid, of the first row in the table.
getNextOid(SnmpOid rowOid,Object userData), so that it returns the index of the row that immediately follows the indicated row in the table.
The methods createNewEntry(SnmpMibSubRequest req, SnmpOid rowOid, int depth), and removeTableRow(SnmpMibSubRequest req, SnmpOid rowOid, int depth) are not overriden. The table is read-only, so these methods will never be called.
This class does not use any specific caching mechanism. Since the jmxMBeanAttrTable is an extension of the jmxMBeanTable it simply relies on the cache established for the jmxMBeanTable. The list of attributes pertaining to a specific MBean is cached, if necessary, in the corresponding JmxMBeanEntryImpl object, which can be reclaimed at the end of the request. Note that attribute values are not cached, but rather they are obtained when they are needed.
JmxMBeanContextFactory implements the com.sun.management.snmp.agent.SnmpUserDataFactory interface. The userData allocated by this factory is a java.util.Map that serves as a request-contextual cache. When the MIB instrumentation needs to access a piece of data, it proceeds as follows.
Firstly, it looks for it in the request-contextual cache, namely, in the userData.
If the required data is not found,the MIB instrumentation computes its value, puts it in the request-contextual cache, and then returns it.
This mechanism ensures the coherency of the data returned in the response. Once a piece of data has been loaded into the request-contextual cache, it is consistently reused for the whole duration of the request. This makes sure that the same snapshot is always used during the request, even if the underlying managed object constantly changes its value.
JmxMBeanEntryImpl extends theJmxMBeanEntry skeleton that is generated by mibgen, and adds instrumentation to the entries of the jmxMBeanTable table defined in the MIB. Each JmxMBeanEntry represents an MBean registered in the exposed MBean server. The JmxMBeanEntry objects are created on the fly when needed. They are temporary objects which are created when an SNMP request needs access to the instrumented MBean. Once created, JmxMBeanEntry objects are put in the request-contextual cache, where they remain until the request completes.
JmxMBeanAttrEntryImpl extends theJmxMBeanAttrEntry skeleton that is generated by mibgen, and adds instrumentation to the entries of the jmxMBeanAttrTable table defined in the MIB. Like JmxMBeanEntry objects, JmxMBeanAttrEntry objects are created on the fly when an SNMP request needs access to the instrumented MBean attributed.
JmxMBeanAttrEntry objects are stored in their corresponding JmxMBeanEntryImpl objects, which are themselves cached in the request-contextual cache and in the JmxMBeanTableCache.
After you have run mibgen to generate JMX_MBEAN_SERVER_MIB and its associated classes, as explained in 18.2.1 Classes Generated by mibgen, you can run the example.
Run the example from the examplesDir/current/Snmp/MBeanVirtualTable directory.
Compile the Java classes in the generated directory.
$ cd generated $ javac -d .. *.java |
This creates the compiled *.class files into the examplesDir/current/Snmp/MBeanVirtualTable directory, rather than in generated, so that they are accessible to the other classes used in the example.
Compile the Java classes in the examplesDir/current/Snmp/MBeanVirtualTable directory.
$ cd .. $ javac -d . *.java |
Start the Agent.
You have several options when starting the Agent class.
To start the Agent on the default SNMP port, 16161:
$ java Agent service:jmx:jmxmp:// |
To start the Agent on a port of your choice:
$ java -Dsnmp.port=port_number Agent service:jmx:jmxmp:// |
To start the Agent with a connector of your choosing, you can specify a different service URL and connector port. For example:
$ java Agent JMX_service_URL |
In any of the above cases, you see confirmation of the SNMP port used, confirmation of the creation of the connector and then the alternating registration and deregistration of sets of five MBeans.
You can now perform management operations on the Agent.
Use a JMX management console of your choice to examine the content of the MBean server through a JMXMP connector.
Use the SNMP management application, easymanager, that is supplied with Java DMK to examine the JMX-MBEAN-SERVER-MIB through SNMP. You can find the easymanager application in the installDir/contributions/easymanager directory.
Add the JMX-MBEAN-SERVER-MIBOidTable.class to your classpath.
Type the following command if you are using a UNIX platform:
$ export CLASSPATH=$CLASSPATH:examplesDir/current/Snmp/MBeanVirtualTable/ |
Type the following command if you are using a Windows platform:
set classpath=%classpath%;examplesDir/current/Snmp/MBeanVirtualTable/ |
Open installDir/contributions/easymanager/bin
Start easymanager.
If the SNMP agent has been started with the default example port, type the following command.
$ easymanager.sh -port 16161 |
You can now use easymanager to perform the following operations.
Pop up a MIB discovery window.
discovermib -p JDMK_STD_V2_profile |
Create a new MBean.
set:2 -w private :<<jmxMBeanObjectName.998,4,Test:name=test1>, <jmxMBeanClassName.998,4,Agent$Simple>, <jmxMBeanRowStatus.998,2,4>> |
Click on resynch in the MIB discovery window to observer the changes in the MIB.
Destroy the MBean you created.
set:2 -w private :<<jmxMBeanRowStatus.998,2,6>> |