Using the Java Dynamic Management Kit, you can create an agent application that is both an SNMP agent and a normal JMX agent. SNMP MIBs can be represented as MBeans and the SNMP protocol adaptor exposes them to SNMP managers. Only MBeans derived from MIBs may be managed through the SNMP protocol adaptor, but other managers may view and access them through other protocols.
The mibgen tool provided generates the code for MBeans that represent a MIB. Groups and table entries are represented as MBean instances which expose the SNMP variables through their attributes. The mibgen tool creates skeleton getters and setters which only read or write internal variables. In order to expose the behavior of your host machine or device, you need to implement the code that will access the host- or device-specific functionality.
The SNMP protocol adaptor interacts with your customized MIB MBeans to implement a compatible SNMPv1 and SNMPv2c agent. It also provides the mechanisms for sending traps and implementing both community-based access control and message-level data security (see "Security Mechanisms in the SNMP Toolkit").
The program listings in this tutorial show only functional code: comments and output statements have been modified or removed for space considerations. However, all management functionality has been retained for the various demonstrations. The complete source code is available in the Snmp/Agent example directory located in the main examplesDir (see "Directories and Classpath" in the preface).
Contents:
"MIB Development Process" explains how MIBs are implemented as MBeans.
"The SNMP Protocol Adaptor" shows how to build an SNMP agent with the components of the Java Dynamic Management Kit.
"Sending Traps" demonstrates the SNMP trap mechanism in the SNMP adaptor.
"Stand-Alone SNMP Agents" demonstrates an alternative way of implementing an SNMP agent.
Here we describe the process for making MIBs manageable through the SNMP protocol adaptor of the Java Dynamic Management Kit. In our example, we demonstrate this process on a subset of the MIB-II defined by RFC 1213.
Once you have defined the MIB you want to manage in your SNMP agent you need to generate its MBean representation using the mibgen tool. This tool generates MBeans that represent the whole MIB, each of its groups and nested groups, and each of its table entries. This command-line tool and its output are fully described in the Java Dynamic Management Kit 4.1 Tools Reference guide.
The mibgen tool only generates the MBean structure to represent the MIB, it is up to the developer to implement the MIB functionality inside the generated classes. Our example will only give a simple implementation of the MIB-II for demonstration purposes. However, this will show you the way to extend the generated classes in order to provide your own implementation.
To run the mibgen tool for our example, go to the examplesDir/Snmp/Agent directory and enter the following command:
$ mibgen -d . mib_II_subset.txt |
This will generate the following files in the current directory:
The MBean (by inheritance) for the whole MIB: RFC1213_MIB.java
The MBean and its helper class for the Snmp group: Snmp.java, SnmpMBean.java, SnmpMeta.java
The MBean and its helper class for the System group: System.java, SystemMBean.java, SystemMeta.java
The MBean and its helper class for the Interfaces group: Interfaces.java, InterfacesMBean.java, InterfacesMeta.java
The class representing the Interfaces table, and the MBean representing entries in the table: TableIfTable.java, IfEntry.java, IfEntryMBean.java, IfEntryMeta.java
Classes representing enumerated types used in these groups: EnumSnmpEnableAuthenTraps.java, EnumIfOperStatus.java, EnumIfAdminStatus.java, EnumIfType.java
The OID table for SNMP managers wanting to access this MIB: RFC1213_MIBOidTable.java
The MBean with the name of the MIB is a central administrative class for managing the other MBeans that represent the MIB groups and table entries. All of the other MBeans contain the SNMP variables as attributes of their management interface. The mibgen tool generates standard MBeans for the MIB, so attributes are implemented with individual getter and setter methods.
These MBeans are just skeletons, meaning that the attribute implementations only return a default value. You must implement the getters and setters of the attributes to read and write data with the correct semantic meaning of the SNMP variable.
Since SNMP does not support actions in MIBs, the only operations in these MBeans are checkers associated with the SNMP "Set" request in writeable variables. Again, these are skeleton methods which you must implement to do the checks that you require before the corresponding "Set" operation. You may add operations and expose them in the MBean interface, but the SNMP manager will not be able to access them. However, other managers will be able to call these operations if they are connected through another protocol.
Our example only implements a fraction of the attributes, those that are used in this tutorial. The others are simply initialized with a plausible value. Using DEFVAL statements in our MIB, we could force mibgen to generate MBeans with user-defined default values for attributes. As this is not done in our example, mibgen provides a plausible default value according to the variable type.
Our implementations of MIB behavior are contained in the classes with the Impl suffix. These implementation classes extend those that are generated by mibgen so that we can regenerate them without overwriting our customizations.
Here is a summary of the implementation shown in the agent example:
InterfacesImpl.java - adds a notification listener to the IfTable object, then creates two table entries with plausible values and adds them to the table; this class is associated with:
TableEntryListenerImpl.java - the listener for table notifications when entries are added or removed: it prints out the values of a new table entry and prints a message when an entry is removed
IfEntryImpl.java - implements a table entry and provides an internal method for switching the OperStatus variable that triggers a trap (see Example 12-2); this method is not exposed in the MBean interface, so it is only available to the code of this agent application
SnmpImpl.java - initializes and implements variables of the Snmp group; many of these are state variables of the SNMP agent, so we call the getter methods of the SNMP adaptor object to return the information
SystemImpl.java - initializes the System group variables with realistic values
The SnmpImpl.java and SystemImpl.java files provide code that you may reuse when you need to implement these common SNMP groups.
The only class that we need to replace is RFC1213_MIB. Our implementation is located in patchfiles/RFC1213_MIB.java so that we can overwrite the one generated by mibgen. The main function of this MBean is to register the other MBeans of the MIB during its pre-registration phase. Our customization consists of specifying our *Impl classes when instantiating the group and table entry MBeans.
We replace the generated file with our implementation before compiling all of the classes in the examplesDir/Snmp/Agent directory. The classpath must contain the current directory (.):
$ cp -i patchfiles/RFC1213_MIB.java . cp: overwrite ./RFC1213_MIB.java (yes/no)? y $ javac -classpath classpath -d . *.java |
We are now ready to look at the implementation of an SNMP agent and run the example applications.
Once your MIBs are implemented as MBeans, your agent application needs an SNMP protocol adaptor in order to function as an SNMP agent. Since the SNMP adaptor is also an MBean, it can be created and started dynamically in your agent by a connected manager, or through the HTML adaptor. In our simple Agent example, we will launch it through the code of the agent application.
public class Agent { static SnmpAdaptorServer snmpAdaptor = null; public static void main(String args[]) { MBeanServer server; ObjectName htmlObjName; ObjectName snmpObjName; ObjectName mibObjName; ObjectName trapGeneratorObjName; int htmlPort = 8082; int snmpPort = 8085; // non-standard [...] try { server = MBeanServerFactory.createMBeanServer(); String domain = server.getDefaultDomain(); // Create and start the HTML adaptor. // htmlObjName = new ObjectName( domain + ":class=HtmlAdaptorServer,protocol=html,port=" + htmlPort); HtmlAdaptorServer htmlAdaptor = new HtmlAdaptorServer(htmlPort); server.registerMBean(htmlAdaptor, htmlObjName); htmlAdaptor.start(); // Create and start the SNMP adaptor. // snmpObjName = new ObjectName(domain + ":class=SnmpAdaptorServer,protocol=snmp,port=" + snmpPort); snmpAdaptor = new SnmpAdaptorServer(snmpPort); server.registerMBean(snmpAdaptor, snmpObjName); snmpAdaptor.start(); // The rest of the code is specific to our SNMP agent // Send a coldStart SNMP Trap (use port = snmpPort+1) // Trap communities are defined in the ACL file // snmpAdaptor.setTrapPort(new Integer(snmpPort+1)); snmpAdaptor.sendV1Trap(0, 0, null); // Create the MIB-II (RFC 1213) and add it to the MBean server. // mibObjName = new ObjectName("snmp:class=RFC1213_MIB"); RFC1213_MIB mib2 = new RFC1213_MIB(); // The MBean will register all group and table entry MBeans // during its pre-registration server.registerMBean(mib2, mibObjName); // Bind the SNMP adaptor to the MIB mib2.setSnmpAdaptorName(snmpObjName); [...] } catch (Exception e) { e.printStackTrace(); } } // Needed to get a reference on the SNMP adaptor object static public SnmpAdaptorServer getSnmpAdaptor() { return snmpAdaptor; } } |
We launch the SNMP adaptor in the same way that we launch the HTML adaptor. First we create a meaningful object name for its MBean, then we instantiate the class with a constructor allowing us to specify a non-default port, we register the MBean with the MBean server, and we start the adaptor to make it active.
By default, the SNMP protocol adaptor uses the standard SNMP port 161. Since other applications may be using this port on a machine, our simple agent uses port 8085. When we connect to this agent, our SNMP manager will need to specify this non-standard port number.
On certain platforms, applications also require super-user privileges to assign the default SNMP port 161. If your SNMP adaptor uses this port, its agent application will have to be launched with super-user privileges.
Our agent application creates and manages one MIB, our subset of MIB-II. To do so, it instantiates the corresponding RFC1213_MIB MBean that has been generated by the mibgen tool (see "MIB Development Process"). We give it a meaningful object name and then we register it in the MBean server.
The registration process lets the MBean instantiate and register other MBeans that represent the groups of the MIB and the entries of its tables. The set of all these MBeans at the end of registration makes up the MBean representation of the MIB. If an SNMP manager later adds entries to a table, the MIB implementation will register the new entry MBean into the MBean server as well.
If you do not wish to expose a MIB through the MBean server, you do not have to register it. However, you still need to create all of its other MBean objects so that the SNMP adaptor can access all of its groups and table entries. The generated code provides the init method in the main MBean of the MIB. Calling this method will create all necessary MBean objects without registering them in the MBean server.
The SNMP adaptor does not interact with MBeans in the same way as the other connectors and adaptors. Because the SNMP data model relies on MIBs, only MBeans representing MIBs can be managed through SNMP. The SNMP adaptor does not interact with MBeans of a MIB through the MBean server, they must be explicitly bound to the instance of the SNMP adaptor.
After a MIB is instantiated, you must set the SnmpAdaptorName attribute of its main MBean to bind it to the SNMP adaptor. You can either call its setSnmpAdaptorName method directly or, if the MIB's MBean was registered in the MBean server, another management application may set the attribute through the MBean's exposed interface.
In the binding process, the SNMP adaptor obtains the root OID of the MIB. The adaptor uses this OID to determine which variables are implemented in the MIB's corresponding MBeans. In order for the SNMP adaptor to resolve a request on a given OID, the root OID of all bound MIBs must not overlap. This implies that no root OID may be equal to another or be a substring of another.
Even though the SNMP adaptor may be registered in the MBean server, the adaptor only makes MIBs visible to SNMP managers. Other MBeans in the agent cannot be accessed or even represented in the SNMP protocol. The SNMP manager is limited by its protocol: it cannot take full advantage of a Java Dynamic Management agent through the basic MIBs, and it does not have access to any other MBeans. In an advanced management solution, you could write a special MIB and implement it so that operations on its variables actually interact with the MBean server. This is left as an exercise for the reader.
Once the MBean representing a MIB has been instantiated and bound to the SNMP adaptor, it is accessible through the SNMP adaptor. SNMP managers can send requests to operate on the contents of the MIB. The SNMP adaptor interprets the SNMP management requests, performs the operation on the corresponding MBean and returns the SNMP response to the manager. The SNMP protocol adaptor is compatible with SNMPv1 and SNMPv2c.
The advantage of having an SNMP agent "inside" a Java Dynamic Management agent is that you can use the other communications protocols to interact with MIBs and manage the SNMP adaptor. Since both the registered MIBs and the adaptor are MBeans, they are exposed for management. In our simple agent, the MIB was registered, and you can view its MBeans in a web browser through the HTML protocol adaptor.
If our agent included other connectors, management applications could connect to the agent and also manage the MIB and the SNMP adaptor. A non-SNMP manager could instantiate new MIB objects, bind them to the SNMP adaptor and operate on the exposed attributes and operations of the MIB.
Non-SNMP managers may operate on the variables of a MIB, getting and setting values, regardless of any SNMP manager that might also be accessing them through the SNMP adaptor. When dealing with a table, however, they may not create new table entry MBeans without adding them to the table. For example, in the InterfacesImpl.java class, we called the addEntry method of the IfTable object before registering the entry MBeans with the MBean server. This ensures that the new entries will be visible when an SNMP manager accesses the table.
In order for a non-SNMP manager to create a table entry, you must customize the table's group MBean to expose this functionality. Briefly, you would need to write a new method that instantiates and initializes the entry's MBean, adds the MBean to the table object, and registers the entry MBean in the MBean server. Advanced customization such as this is not covered in our example. In general, the designer of the agent and management applications is responsible for all coherency issues when accessing MIBs concurrently through different protocols and when adding table entries.
Non-SNMP managers can also control the SNMP agent through the MBean of the SNMP adaptor. Like the other communications MBeans, the port and other attributes can be modified when the SNMP adaptor is stopped. You can also get information about its state, and stop or restart it to control when it is on-line. These administrative attributes and operations are defined in the CommunicatorServerMBean interface.
The SNMP adaptor server also implements the SnmpAdaptorServerMBean interface to define its operating information. The SNMP protocol defines certain variables that SNMP agents must expose about their current state. For example, the SNMP adaptor provides methods for getSnmpInPkts and getSnmpOutBadValues. Non-SNMP managers can read these variables as attributes of the SNMP adaptor MBean.
The SNMP adaptor also exposes other operating information that is unavailable to SNMP managers. For example, the ActiveClientCount and ServedClientCount read-only attributes report on SNMP manager connections to this agent. The read-write BufferSize attribute lets you change the size of the message buffer, but only when the adaptor is not on-line. The adaptor MBean also exposes operations for sending traps or implementing your own security (see "Message-Level Security").
After building the example as described in "MIB Development Process", launch the simple agent with the following command:
$ java -classpath classpath Agent nbTraps |
For this run, set nbTraps to zero. You should see some initialization messages, including our notification listener giving information about the two table entries which are created. Access this agent's HTML adaptor by pointing a web browser to the following URL: http://localhost:8082/. Through the HTML adaptor, you can see the MBeans representing the MIB:
The class=RFC1213_MIB MBean in the snmp domain is the MBean representing the MIB; it contains a name and information about the SNMP adaptor to which the MIB is bound
The RFC1213_MIB domain contains the MBeans for each group; both name=Snmp and name=System contain variables with values provided by our customizations
The ifTable domain contains the entries of the Interfaces table
The trapGenerator domain contains the class that sends traps periodically, as part of our sample MIB implementation
In any of these MBeans, you could write new values into the text fields of exposed attributes and click the "Apply" button. This will set the corresponding SNMP variable, and thereafter, SNMP managers will see the new value. This is an example of managing a MIB through a protocol other than SNMP.
For any SNMP agent application, you can turn on trace messages for the SNMP adaptor by specifying the -DINFO_ADAPTOR_SNMP property on the command line. The tracing mechanism is covered in the Java Dynamic Management Kit 4.1 Tools Reference guide and in the Javadoc API of the Trace class.
Type <Control-C> when you are finished viewing the agent.
Agents can send unsolicited event reports to management application by using traps. The SNMP protocol adaptor can send both v1 and v2 traps, the difference being in the format of the corresponding PDU. Traps in the SNMP specification are not acknowledged by the management application, so agents do not know if traps are received.
Inform requests are acknowledged event reports, but they are sent by entities acting in a manager role, according to RFC 1905. Therefore, the SNMP inform requests are implemented in the SNMP manager API of the Java Management extensions: see "The Inform Request Example" for more information.
Our simple SNMP agent application also demonstrates how the agent can send traps. The customized class IfEntryImpl in the example directory extends the IfEntry class generated by mibgen in order to provide a method that switches the IfOperStatus variable and sends a trap. This is an example of customization of the generated code: an agent-side entity will switch the operation status, the MIB variable will be updated and a trap will be sent to SNMP managers.
The IfEntryImpl.java file provided in the example directory contains the code for sending the trap through the SNMP adaptor.
public void switchifOperStatus() { // implements the switch and then calls sendTrap indirectly [...] } // Method called after the variable has been switched // Should be called with generic==2 (up) or 3 (down or testing) public void sendTrap(int generic) { SnmpAdaptorServer snmpAdaptor = null; // Retrieve the reference of the SNMP protocol adaptor through // the static method of the Agent or StandAloneSnmpAgent class snmpAdaptor = Agent.getSnmpAdaptor(); if (snmpAdaptor == null) { snmpAdaptor = StandAloneSnmpAgent.getSnmpAdaptor(); } if (snmpAdaptor == null) { return; } // Create the variable bindings to send in the trap Vector varBindList = new Vector(); SnmpOid oid1 = new SnmpOid("1.3.6.1.2.1.2.2.1.1." + IfIndex); SnmpInt value1 = new SnmpInt(IfIndex); SnmpVarBind varBind1 = new SnmpVarBind(oid1, (SnmpValue) value1); varBindList.addElement(varBind1); try { snmpAdaptor.sendV1Trap(generic, 0, varBindList); } catch (Exception e) { e.printStackTrace(); } } |
As the sendTrap method runs in a different thread, it needs to get a reference to the SNMP adaptor instance. Here we call the static methods of our two possible agent implementations. This code is specific to these agents and is only an example of how to retrieve this information.
In order to simulate a live operation status, we invent the LinkTrapGenerator class which will switch the status periodically. It is an MBean which contains a thread which loops endlessly. The interval period between traps and the number of the table entry can be modified through the MBean's attributes.
public void run() { int remainingTraps = nbTraps; while ((nbTraps == -1) || (remainingTraps > 0)) { try { sleep(interval); } catch (Exception e) { e.printStackTrace(); } triggerTrap(); remainingTraps--; } } public void triggerTrap() { // get the entry whose status we will switch IfEntryImpl ifEntryImpl = InterfacesImpl.find(ifIndex); if (ifEntryImpl == null) { errors++; return; } ifEntryImpl.switchifOperStatus(); successes++; } |
To run the trap generator, the example application instantiates and registers a LinkTrapGenerator MBean. During its registration, this MBean starts the thread, sending a trap every two seconds by default.
// Create a LinkTrapGenerator (specify the ifIndex in the object name) // String trapGeneratorClass = "LinkTrapGenerator"; int ifIndex = 1; trapGeneratorObjName = new ObjectName("trapGenerator" + ":class=LinkTrapGenerator,ifIndex=" + ifIndex); LinkTrapGenerator trapGenerator = new LinkTrapGenerator(nbTraps); server.registerMBean(trapGenerator, trapGeneratorObjName); [...] trapGenerator.start(); |
There are several methods in the SNMP protocol adaptor for sending traps to remote managers. They differ in their method signatures, depending upon whether or not you need to specify the destination host. When no host is specified, the SNMP protocol adaptor relies on the trap group definition in access control lists (ACL), as described below.
In all cases, traps are sent to the port specified by the current value of the TrapPort attribute on the SnmpAdaptorServer MBean. In our simple agent, we set the trap port to 8086, but this can be changed at any time by a custom MIB implementation or a management application.
This is the method that was used in Example 12-2 to send traps, along with its v2 equivalent (see the Javadoc API for a description of the parameters):
sendV1Trap( int generic, int specific, java.util.Vector varBindList )
sendV2Trap( SnmpOid trapOid, java.util.Vector varBindList )
Using these methods, you must first define the trap group in an access control list. See "Access Control Lists (ACL)" for a formal definition of the trap group and instructions for defining the ACL file when starting the agent. By default, these lists are file-based, but you may implement other mechanisms, as described in "Custom Access Control".
In this example we provide the following template file:
acl = { ... } trap = { { trap-community = public hosts = yourmanager } } |
The trap group lists all of the hosts to which the SNMP protocol adaptor will send every trap. A community definition associates a community name with a list of hosts specified either by their hostname or by their IP address. All hosts in a community definition will receive the trap in a PDU identified by the community name.
Since access control and trap recipients share the same file, you must fully define the access control when you want to send traps using the ACL mechanism.
Given this definition, traps will be sent to a host called yourmanager, and the community string of the trap PDU would contain the value public. By adding community definitions to this file, you can specify all hosts which will receive traps along with the community string for each host or group of hosts.
If the ACL file is not defined, or if the trap group is empty, the default behavior of these methods is to send a trap only to the localhost.
The other two methods of the SNMP protocol adaptor, one for each trap version, let you send a trap to a specified recipient:
sendV1Trap( java.net.InetAddress address, java.lang.String cs, ... )
sendV2Trap( java.net.InetAddress address, java.lang.String cs, ... )
In both cases, these methods take an address and a community string, in addition to the version-specific trap information. The address is an InetAddress object which is usually instantiated by its static methods getLocalHost or getByName. The latter method returns a valid InetAddress object when given a string representing a hostname or IP address.
The cs parameter is the community string, a name that the agent and manager exchange to help identify one another. The string given will be used as the community when sending the trap PDU.
Either one of these methods sends a trap to a single manager using a single community string. The ACL trap group mechanism is better suited to sending traps to multiple managers, though it requires the setup of a trap group. Note that even if a trap group is in use, the two methods above only send one trap to the specified host address.
Before launching the SNMP agent again, edit the jdmk.acl file to replace the occurrences of yourmanager with the name of a host running an SNMP manager. You then have two options for launching the simple agent:
By first copying the ACL file to the configuration directory where it is automatically detected:
$ cp jdmk.acl installDir/SUNWjdmk/jdmk4.1/JDKversion/etc/conf $ java -classpath classpath Agent nbTraps |
Or by specifying the ACL file as a property when launching the agent
$ java -Djdmk.acl.file=examplesDir/Snmp/Agent/jdmk.acl \ -classpath classpath Agent nbTraps |
In these commands, nbTraps is the number of traps that the agent will send. Set it to a small integer to avoid too much output. If you omit this parameter, traps will be sent continuously.
If you don't have an SNMP manager or a second host, don't copy the ACL file or specify it as a property. In the absence of the trap-community definitions, the traps will be addressed to the trap port on the local host. And even if no manager is running, we can still see the agent sending the traps. See "SNMP Trap Handler" in the SNMP manager topic for details about receiving traps.
Access this agent's HTML adaptor by pointing a web browser to the following URL: http://localhost:8082/. Click on the class=LinkTrapGenerator,ifIndex=1 MBean in the trapGenerator domain.
Through the HTML adaptor, you can see the MBean representing the trap generator object. You can modify its attributes to change the table entry that it operates on and to change the interval between traps.
Change the trap interval to 10000 so that traps are sent every 10 seconds.
Go back to the agent view and click on the ifEntry.ifIndex=1 MBean in the ifTable domain. Set the reload period to 10, and click the "Reload" button.
You should see the effect of the trap generator which is to switch the value of the IfOperStatus variable. It is our implementation of the table entry which sends a trap when this status is changed.
Go back to the agent view and click on the name=Snmp MBean in the RFC1213_MIB domain. Scroll down to see the SnmpOutPkts and SnmpOutTraps variables.
These variables should be the only non zero values, if no manager has connected to the SNMP agent. The Snmp group shows information about the SNMP adaptor, and we can see how many traps have been sent since the agent was launched.
Type <Control-C> when you are finished interacting with the agent.
The LinkTrapGenerator MBean is not manageable through the SNMP adaptor because it is not part of any MIB. It is an example of another MBean providing some control of the SNMP agent, and this control can be exercised by other managers connecting through other protocols. This shows that designing an SNMP agent application involves both the implementation of the MIB functionality and, if desired, the implementation of other dynamic controls afforded by the JMX architecture and the services of the Java Dynamic Management Kit.
The design of the SNMP protocol adaptor and of the MBeans generated by mibgen give you the option of creating an SNMP agent that is not a Java Dynamic Management agent.
This stand-alone agent has no MBean server and thus no possibility of being managed other than through the SNMP protocol. The application must instantiate all MIBs that the SNMP agent will need, as it will be impossible to create them through another manager. The advantage of a stand-alone agent is the reduced size of the application, in terms of memory usage.
import com.sun.jdmk.Trace; import com.sun.jdmk.comm.SnmpAdaptorServer; public class StandAloneSnmpAgent { static SnmpAdaptorServer snmpAdaptor = null; public static void main(String args[]) { // Parse command line and enable tracing [...] try { // The agent is started on a non standard SNMP port: 8085 int port = 8085; snmpAdaptor = new SnmpAdaptorServer(port); // Start the adaptor snmpAdaptor.start(); // Send a coldStart SNMP Trap snmpAdaptor.sendV1Trap(0, 0, null); // Create the MIB you want in the agent (ours is MIB-II subset) RFC1213_MIB mib2 = new RFC1213_MIB(); // Initialize the MIB so it creates the associated MBeans mib2.init(); // Bind the MIB to the SNMP adaptor mib2.setSnmpAdaptor(snmpAdaptor); // Optional: create a LinkTrapGenerator int ifIndex = 1; LinkTrapGenerator trapGen = new LinkTrapGenerator(ifIndex); trapGen.start(); } catch (Exception e) { e.printStackTrace(); } } // Needed to get a reference on the SNMP adaptor object static public SnmpAdaptorServer getSnmpAdaptor() { return snmpAdaptor; } } |
As this example demonstrates, the stand-alone agent uses exactly the same MIB MBeans, with the same customization, as our other agents. However, instead of registering them in the MBean server, they are only instantiated. And whereas the registration process creates all subordinate MBeans of the MIB, now we must call its init method explicitly.
The init method performs the same function as the preRegister method, only it does not register the MBean with the MBean server. Each of the group MBeans then has two constructors, one with and one without a reference to the MBean server. When table entries are added dynamically, the corresponding object only registers the new entry's MBean if the MBean server reference is non-null; that is, only if the MBean is not instantiated in a stand-alone agent.
The mibgen tool automatically generates both the pre-registration methods and the init methods in the MIB MBeans. Therefore, no special action is necessary to use them in either a regular agent or a stand-alone agent. If you use a stand-alone agent for memory considerations, you can remove the registration process from the generated MBean and only customize the "init" process.
In our example, we have applied the customizations to both processes so that the MIB can be used by either agent. In the following code, customizations are noted with MODIF_ comments:
public class RFC1213_MIB extends SnmpMib implements Serializable { // Default constructor. Initialize the Mib tree public RFC1213_MIB() { mibName = "RFC1213_MIB"; } // Initialization of the MIB with no registration in the MBean server public void init() throws IllegalAccessException { // Allow only one initialization of the MIB if (isInitialized == true) { return ; } // Initialization of the "Snmp" group { SnmpMeta meta = new SnmpMeta((SnmpMib)this); // MODIF_BEGIN //meta.setInstance(new Snmp((SnmpMib)this)); meta.setInstance(new SnmpImpl((SnmpMib)this)); // MODIF_END root.registerNode("1.3.6.1.2.1.11", (SnmpMibNode)meta); } // Initialization of the other groups [...] isInitialized = true; } // Initialization of the MIB with AUTOMATIC REGISTRATION // in the MBean server public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception { // Allow only one initialization of the MIB if (isInitialized == true) { throw new InstanceAlreadyExistsException(); } // Initialize MBeanServer information this.server = server; // Initialization of the "Snmp" group { SnmpMeta meta = new SnmpMeta((SnmpMib)this); // MODIF_BEGIN //Snmp instance = new Snmp((SnmpMib)this, server); Snmp instance = new SnmpImpl((SnmpMib)this, server); // MODIF_END meta.setInstance(instance); root.registerNode("1.3.6.1.2.1.11", (SnmpMibNode)meta); server.registerMBean( instance, new ObjectName(mibName + ":name=Snmp")); } [...] // Initialization of the other groups isInitialized = true; return name; } private boolean isInitialized = false; } |
After the MIB is initialized, it only needs to be bound to the SNMP adaptor, as in the other agents; except that in the stand-alone case, we use the setSnmpAdaptor method which takes a direct reference to the SNMP adaptor instead of an object name. That is all you need to do when programing a stand-alone SNMP agent.
Launch the stand-alone agent with the following command:
$ java -classpath classpath StandAloneSnmpAgent nbTraps |
If you haven't copied the jdmk.acl file to the configuration directory, add the following property to your command line: -Djdmk.acl.file=examplesDir/Snmp/Agent/jdmk.acl
You should see the same initialization messages as with the simple agent. Then, you should see the agent sending out a trap every two seconds. If you have an SNMP manager application, you can send requests to the agent and receive the traps. See "Developing an SNMP Manager" for example applications you can use.
The only limitation of a stand-alone agent is that you cannot access or manage the SNMP adaptor and MIB MBeans in the dynamic management sense. However, the SNMP adaptor still relies on the ACL file for access control and traps, unless you have customized the ACL mechanism, and you can implement other security schemes as described in "Message-Level Security".
Type <Control-C> when you are finished running the stand-alone agent.