Java Dynamic Management Kit 5.0 Tutorial

Chapter 19 Developing an SNMP Manager

The Java Management extensions specify the SNMP manager API for implementing an SNMP manager application in the Java programming language. This API is covered in the Javadoc API provided with the Java Dynamic Management Kit (DMK) (see “Related Books” in the Preface for more information). In this chapter, we explain the example applications that use this API.

The SNMP manager API can be used to access any SNMP agent, not just those developed with the Java DMK. It is compatible with SNMPv1, SNMPv2c and SNMPv3, and it includes mechanisms for handling traps. It lets you program both synchronous managers that block while waiting for responses and multi-threaded asynchronous managers that do not. Managers can also communicate with other managers using inform requests and responses.

The complete source code for these applications is available in the Snmp/Manager and Snmp/Inform example directories located in the main examplesDir (see “Directories and Classpath” in the Preface).

This chapter covers the following topics:

Synchronous Managers

The synchronous SNMP manager is the simplest to program: the manager sends a request to an agent (peer) and waits for the answer. During the wait, the manager is blocked until either a response is received or the timeout period expires.

The SNMP manager API allows two ways of referring to variables when issuing requests:

Referring directly to OIDs requires no configuration but makes code less flexible. The advantages of using variable names are simplified coding and the independence of manager code when custom MIBs are modified. The SNMP manager API supports variable names by storing a description of the MIB it accesses in the SnmpOid object.

To refer to variable names, the manager needs to initialize this description with an OID table object. The OID table is instantiated from a subclass of the SnmpOidTableSupport class generated by the mibgen tool when compiling the MIB. Because this support class is regenerated whenever the MIB is recompiled, the new MIB definition is automatically loaded into the manager when it is started (see the Example 19–1).

The SNMP manager API specifies the SnmpPeer object for describing an agent, and the SnmpParameters object for describing its read-write communities and its protocol version (SNMPv1 or SNMPv2). The objects SnmpUsmPeer and SnmpUsmParameters perform these roles under SNMPv3, and handle user-based security parameters. The SnmpSession is an object for sending requests. The session instance has an SnmpOptions field that we can use to set multiplexing and error fixing behavior.


Note –

The objects specified by the SNMP manager API are not MBeans and cannot be registered in an MBean server to create a manager that can be controlled remotely. However, you can write an MBean that uses these classes to retrieve and expose information from SNMP agents.


A manager can contain any number of peers, one for each agent it accesses, and any number of sessions, one for each type of behavior it implements. Once the peers and the sessions are initialized, the manager can build lists of variables and send session requests to operate on them. The session returns a request object, and the manager calls its waitForCompletion method with the desired timeout delay.

Finally, the manager analyzes the result of the request, first to see if there were any errors, then to extract the data returned by the request.

Synchronous SNMPv1 and SNMPv2 Managers

Example 19–1 shows the code of the main method of the SyncManager application for SNMPv1 and SNMPv2. It applies all the steps described in the previous section to execute a very simple management operation.


Example 19–1 SNMPv1 and SNMPv2 SyncManager Example

The SyncManager example is found in the examplesDir/Snmp/Manager directory.

// read the command line parameters
String host = argv[0];
String port = argv[1];

// Specify the OidTable containing all the MIB II knowledge
// Use the OidTable generated by mibgen when compiling MIB II
//
SnmpOidTableSupport oidTable = new RFC1213_MIBOidTable();
SnmpOid.setSnmpOidTable(oidTable);
       
SnmpPeer agent = new SnmpPeer(host, Integer.parseInt(port));
     
// When creating the parameter object, you can specify the
// read and write community to be used when querying the agent.
SnmpParameters params = new SnmpParameters("public", "private");
agent.setSnmpParam(params);

SnmpSession session = new SnmpSession("SyncManager session");

// When invoking a service provided by the SnmpSession, it
// will use the default peer if none is specified explicitly
session.setDefaultPeer(agent);

// Create a listener and dispatcher for SNMP traps:
// SnmpEventReportDispatcher will run as a thread and
// listens for traps in UDP port = agent port + 1
SnmpEventReportDispatcher trapAgent =
    new SnmpEventReportDispatcher(Integer.parseInt(port)+1);
// TrapListenerImpl will receive a callback
// when a valid trap PDU is received.
trapAgent.addTrapListener(new TrapListenerImpl()); 
new Thread(trapAgent).start();

// Build the list of variables you want to query.
// For debugging, you can associate a name to your list.
SnmpVarbindList list= new SnmpVarbindList(
    "SyncManager varbind list");
     
// We want to read the "sysDescr" variable.
list.addVariable("sysDescr.0");
    
// Make the SNMP get request and wait for the result.
SnmpRequest request = session.snmpGet(null, list);
boolean completed = request.waitForCompletion(10000);

// Check for a timeout of the request.
if (completed == false) {
    java.lang.System.out.println(
"Request timed out. Check if agent can be reached");
    java.lang.System.exit(0);
}

// Check if the response contains an error.
int errorStatus = request.getErrorStatus();
if (errorStatus != SnmpDefinitions.snmpRspNoError) {
    java.lang.System.out.println("Error status = " + 
        SnmpRequest.snmpErrorToString(errorStatus));
    java.lang.System.out.println("Error index = " +
        request.getErrorIndex());
    java.lang.System.exit(0);
}

// Now we can extract the content of the result.
SnmpVarbindList result = request.getResponseVbList();
java.lang.System.out.println("Result: \n" + result);

[...] // Wait for user to type enter. Traps will be handled.

// End the session properly and we're done
session.destroySession();
java.lang.System.exit(0);

In this SNMP manager application, we demonstrate how to implement and enable a trap listener for the traps sent by the agent. First we need to instantiate an SnmpEventReportDispatcher object. Then we add our listener implementation through its addTrapListener method, and finally we start its thread. Trap listeners can be implemented in any manager using the SNMP manager API, not only synchronous managers.

Configuring SNMPv3 Security for Managers

Before you run the SNMPv3 manager examples, you require some information about how SNMPv3 user-based model (USM) security is configured. Below is a brief description of the SNMPv3 security mechanism that provides you with the information you need to run the SNMPv3 examples in this chapter. Full descriptions of the SNMPv3 security mechanisms are given in SNMPv3 User-Based Security Model.

An SNMPv3 manager requires a security file, in the same way as an SNMPv3 agent does. The jdmk.security file for an SNMPv3 manager differs slightly from that of an SNMPv3 agent, as shown in the following example.


Example 19–2 A jdmk.security File for an SNMPv3 Manager

#Authentication only.
userEntry=0x8000002a05819dcb6e00001f95,defaultUser,,
usmHMACMD5AuthProtocol,mypasswd

# #####APPENDED PROPERTY####
localEngineBoots=5

# #####APPENDED PROPERTY####
localEngineID=0x8000002a05000000ec4c49ded9

In a manager's security file, there is more emphasis on the engine ID than in an agent's security file. The userEntry provides all the security information the manager needs to communicate with a particular authoritative agent, as follows:

0x8000002a05819dcb6e00001f95

This is the engine ID of the agent with which the manager will communicate

defaultUser

The authorized user for that agent

usmHMACMD5AuthProtocol

The authentication algorithm; in this case, HMAC MD5

mypasswd

The privacy password

In this example, the information in the userEntry corresponds to the security information provided in the AgentV3 example's jdmk.security file, in Example 18–3. Therefore, this manager can communicate with that agent.

The remaining information pertains to the manager itself:

localEngineBoots

Sets how many times the local engine will boot

localEngineID

Represents the ID of the engine associated to the SNMP session in which the manager is running

Synchronous SNMPv3 Managers

The example synchronous manager application created for SNMPv3 is similar to the SNMPv1/v2 manager, except that it implements SNMPv3 user-based USM mechanisms before making requests.


Example 19–3 SNMPv3 SyncManagerV3 Example

The SyncManagerV3 example is in the examplesDir/Snmp/Manager directory.

			//Read the command line parameters
        final String host = argv[0];
        final String port = argv[1];

        // Initialize trace property. 
        [...]
  
        try {
            
     		 // Initialize the SNMP Manager API.
            final SnmpOidTableSupport oidTable = new RFC1213_MIBOidTable();
            SnmpOid.setSnmpOidTable(oidTable);
       
      		// Build the session.
            //
			    try {
						session= new SnmpSession("SyncManagerV3 session");
	   			 }catch(SnmpStatusException e) {
						println(e.getMessage());
						java.lang.System.exit(0);
	    		}
	    		catch(IllegalArgumentException e) {
				// If the engine configuration is faulty 
				println(e.getMessage());
				java.lang.System.exit(0);
	    		}
	
	    		// Access the SNMPv3 engine using getEngine
	    		//
	   			final SnmpEngine engine = session.getEngine();
	    
	    		// Create an SnmpUsmPeer object
 				//
			   final SnmpUsmPeer agent = 
				new SnmpUsmPeer(engine, host, Integer.parseInt(port));
	    
	    		// Create USM parameters
 				//
			   final SnmpUsmParameters p = 
				new SnmpUsmParameters(engine, "defaultUser");
	    
	    		// Set the security level 
				//
			 	p.setSecurityLevel(SnmpDefinitions.authNoPriv);

	    		// Contextualize the send request
	 		   //
	   			 p.setContextName("TEST-CONTEXT".getBytes());

	    		// Set the contextEngineId discovered by the peer upon 
				// creation
	   			 p.setContextEngineId(agent.getEngineId().getBytes());
	    
	    		// Associate the parameter with the agent.
	    		//
	    		agent.setParams(p);
	
	    
	    		// Discover time of creation and boot
			   //
			   agent.processUsmTimelinessDiscovery();
	    
	    		// Associate a default peer (agent) to an SnmpSession.
	    		// 
			   session.setDefaultPeer(agent);

	    		// Create a taskServer for processing traps (optional)
	    	    final DaemonTaskServer taskServer = new DaemonTaskServer();
	    		taskServer.start(Thread.NORM_PRIORITY);
	    
	    		// Create a listener and dispatcher for SNMP traps 
	    	   //
	    		final SnmpEventReportDispatcher trapAgent =
				new SnmpEventReportDispatcher(engine, 
					      Integer.parseInt(port) + 1, 
					      taskServer, null);
	    
	    		trapAgent.addTrapListener(new TrapListenerImpl());
           	final Thread trapThread = new Thread(trapAgent);
	    		trapThread.setPriority(Thread.MAX_PRIORITY);
			   	trapThread.start();	    
	    
	    		// Build the list of variables you want to query
	    	    //
	    		final SnmpVarBindList list = 
				new SnmpVarBindList("SyncManagerV3 varbind list");
	    
	    		// Read the "sysDescr" variable
			   //
           list.addVarBind("sysDescr.0");
	    
	    		// Make the SNMP get request and wait for the result
	    		//
	    		final SnmpRequest request = 
				session.snmpGetRequest(null, list);
	   			println("SyncManagerV3::main:" +
				    "Send get request to SNMP agent on " + host + 
				    " at port " + port);
			   final boolean completed = request.waitForCompletion(10000);
	    
	    		// Check for a timeout
	    		//
            if (completed == false) {
                println("SyncManagerV3::main:" + 
						" Request timed out. Check if agent  
						  can be reached");
		
                // Print request.
                //
                println("Request: " + request.toString());
                java.lang.System.exit(0);
            }
	    
            // Check the response for errors 
	    		//
            final int errorStatus = request.getErrorStatus();
            if (errorStatus != SnmpDefinitions.snmpRspNoError) {
                println("Error status = " + 
					 SnmpRequest.snmpErrorToString(errorStatus));
                println("Error index = " + 
					 request.getErrorIndex());
                java.lang.System.exit(0);
            }
       
            // Display the result.
            //
            final SnmpVarBindList result = request.getResponseVarBindList();
            println("Result: \n" + result);
       
            [...]

            // End the session
            //
            session.destroySession();
       
	    	    trapAgent.close();
	    		 taskServer.terminate();
            java.lang.System.exit(0);
     
				[...]
}

The first instantiated session creates an engine. This engine can be accessed using the getEngine method. To avoid excessive engine creation for each instantiated session, the first engine can be shared between SNMP session objects. Whilst sharing is possible, it should be avoided. It represents an unnecessary increase in overhead and limits the security possibilities because only one security file can be associated with an engine.

The engine is used by all the other classes in the application programming interface (API) to access the USM configuration, contained in the jdmk.security file associated with that session. In Example 19–3, when the peer p is created, it discovers its engineId, and then uses it as the SNMPv3 ContextEngineId. When the request is sent, this engine ID is included as a parameter by setContextEngineId.

In this example, the level of security is set as authentication without privacy. Consequently, this level of security is applied to all the requests between this manager and the peers associated with it, via the security parameters. This level of security must match the level specified in the engine's jdmk.security file.

It is also possible to access MIBs that have been registered in the scope of a context (see Binding the MIB MBeans for details of contextualized MIBs). In this example, the context TEST-CONTEXT is used, and is set as a parameter in the request by setContextName.

Finally, before sending any requests, if authentication is activated, the timeliness parameters of the request are discovered, using processUsmTimelinessDiscovery.

SNMP Trap Handler

A trap handler for the SNMP manager is an object that implements the SnmpTrapListener interface in the javax.management.snmp.manager package. When this object is bound as a listener of an SnmpEventReportDispatcher object, its methods are called to handle trap PDUs.

A trap listener is not a notification listener because the dispatcher is not a notification broadcaster. The listener has callback methods that work in the same manner, but they are given objects that represent traps, not instances of the Notification class.

The interface defines three methods, one for processing SNMPv1 traps, another for SNMPv2 traps and a third for SNMPv3 traps. Trap PDU packets have already been decoded by the dispatcher, and these methods handle an object representation of the trap: SnmpPduTrap objects for SNMPv1,SnmpPduRequest objects for SNMPv2 and SnmpScopedPduRequest for SNMPv3.


Example 19–4 SnmpTrapListener Implementation

public class TrapListenerImpl implements SnmpTrapListener {

		public void processSnmpTrapV1(SnmpPduTrap trap) {
        println("NOTE: TrapListenerImpl received trap :");
        println("\tGeneric " + trap.genericTrap);
        println("\tSpecific " + trap.specificTrap);
        println("\tTimeStamp " + trap.timeStamp);
        println("\tAgent adress " + trap.agentAddr.stringValue());
    }

    public void processSnmpTrapV2(SnmpPduRequest trap) {
        println("NOTE: Trap V2 not of interest !!!");
    }

    public void processSnmpTrapV3(SnmpScopedPduRequest trap) {
			println("NOTE: TrapListenerImpl received trap V3:");
			println("\tContextEngineId : " + 
				SnmpEngineId.createEngineId(trap.contextEngineId));
			println("\tContextName : " + new String(trap.contextName));
			println("\tVarBind list :");
			for(int i = 0; i < trap.varBindList.length; i++)
	    		println("oid : " + trap.varBindList[i].oid + " val : " + 
		    		trap.varBindList[i].value);
	
    }

 }

To Run the SyncManager Example
  1. In the examplesDir/Snmp/Manager directory, generate the OID table description of MIB-II that our manager will access.


    $ mibgen -mo mib_II.txt
    
  2. Compile the example classes.

    To set up your environment, see “Directories and Classpath” in the Preface.


    $ javac -classpath classpath -d . *.java
    
  3. Make sure that no other agent is running on port 8085, and start the simple SNMP agent in examplesDir/Snmp/Agent.

    See To Run the SNMPv1/v2 Agent Example if you have not already built and run this example.

    Type the following command to start the Agent example:


    $ cd examplesDir/Snmp/Agent
    $ java -classpath classpath Agent nbTraps
    

    If you are also running the asynchronous manager example with this agent, omit the nbTraps parameter. The agent then sends traps continuously and they can be seen in both managers. Otherwise, specify the number of traps to be sent to the manager. Wait until the manager is started to send the traps.

  4. Start the manager application in another window to connect to this agent.

    If you want to run the manager on a different host, replace localhost with the name of the host where you started the agent.


    $ cd examplesDir/Snmp/Manager
    $ java -classpath classpath SyncManager localhost 8085
    SyncManager::main: Send get request to SNMP agent on localhost at port 8085
    Result: 
    [Object ID : 1.3.6.1.2.1.1.1.0  (Syntax : String)
    Value : SunOS sparc 5.7]

    Here we see the output of the SNMP request, it is the value of the sysDescr variable on the agent.

  5. Press Enter in the agent's window.

    You should see the manager receiving the traps it sends. Leave the agent running if you are going on to the next example, otherwise remember to stop it by pressing Control-C.

To Run the SyncManagerV3 Example
  1. In the examplesDir/Snmp/Manager directory, generate the OID table description of MIB-II that our manager will access.


    $ mibgen -mo mib_II.txt
    
  2. Compile the example classes.

    To set up your environment, see “Directories and Classpath” in the Preface.


    $ javac -classpath classpath -d . *.java
    
  3. Make sure that no other agent is running on port 8085, and start the simple SNMPv3 agent, AgentV3, in examplesDir/Snmp/Agent.

    See To Run the SMNPv3 AgentV3 Example if you have not already built and run this example.

    Type the following commands to start the AgentV3 example:


    $ cd examplesDir/Snmp/Agent
    $ java -classpath classpath -Djdmk.security.file=./jdmk.security AgentV3
    
  4. Start the manager application in another window to connect to this agent.

    If you want to run the manager on a different host, replace localhost with the name of the host where you started the agent.


    $ cd examplesDir/Snmp/Manager
    $ java -classpath classpath -Djdmk.security.file=./jdmk.security 
    SyncManagerV3 localhost 8085
    

    Be sure to run the manager in its directory, otherwise it cannot find its jdmk.security file.

  5. Press Enter in the agent's window.

    You should see the manager receiving the traps it sends. Stop the manager by typing Control-C.

Synchronous Managers Accessing Several Agents

The SNMP API enables you to configure a synchronous SNMPv3 manager that can access several agents. The SyncManagerMultiV3 example demonstrates a simple SNMPv3 manager API that makes requests on two SNMPv3 agents. For the sake of simplicity, this manager does not listen for traps.


Example 19–5 SNMPv3 SyncManagerMultiV3 Example

	//Check host and port arguments
	//
        final String host1 = argv[0];
        final String port1 = argv[1];
			final String host2 = argv[2];
        final String port2 = argv[3];

        // Initialize the trace property. 

				[...]


            // Initialize the SNMP Manager API.
            // 
            final SnmpOidTableSupport oidTable = new RFC1213_MIBOidTable();
            SnmpOid.setSnmpOidTable(oidTable);
       
            [...]
	  
			    // Build the session. 
				//
	  			  try {
				// When instantiating a session, a new SNMP V3 engine is 
				// instantiated.
				//
					session= new SnmpSession("SyncManagerMultiV3 session");
	    		}catch(SnmpStatusException e) {
					println(e.getMessage());
					java.lang.System.exit(0);
			   }
	    		catch(IllegalArgumentException e) {
				// If the engine configuration is faulty
				//
				println(e.getMessage());
				java.lang.System.exit(0);
	    }
	
	    		// Get the SnmpEngine. 
			    //
			    final SnmpEngine engine = session.getEngine();
	    
	   			 // Create a SnmpPeer object for representing the first  
	    		 // entity to communicate with. 
	    	    //
	    		 final SnmpUsmPeer agent1 = 
				 new SnmpUsmPeer(engine, host1, Integer.parseInt(port1));
	    
	    		 //Create the second peer.
	    		 //
	   			final SnmpUsmPeer agent2 = 
				new SnmpUsmPeer(engine, host2, Integer.parseInt(port2));
	    
	    		// Create parameters to associate to the entity to  
	    		// communicate with.
	     	   //
	    		final SnmpUsmParameters p = 
				new SnmpUsmParameters(engine, "defaultUser");
	    
			   // Set security level to authNoPriv. 
			   //
		    	p.setSecurityLevel(SnmpDefinitions.authNoPriv);

	    		// Contextualize the send request
	    	   //
	    		p.setContextName("TEST-CONTEXT".getBytes());

	    		// Specify a contextEngineId 
		   		//
	    		p.setContextEngineId(agent1.getEngineId().getBytes());
	    
	    		// Associate the newly created parameter to the agent
		    	//
		    	agent1.setParams(p);
	
	    
	    		// Discover timeliness and boot
	    		agent1.processUsmTimelinessDiscovery();
	    
	    
	    		// Build the list of variables you want to query
	    		//
	    		final SnmpVarBindList list = 
				new SnmpVarBindList("SyncManagerMultiV3 varbind list");
	    
	    		// Read the "sysDescr" variable
	    		//
			   list.addVarBind("sysDescr.0");
	    
	    		// Make the SNMP get request on the first agent agent1 
	    		// and wait for the result
	    		//
	    		SnmpRequest request = 
				session.snmpGetRequest(agent1, null, list);
	    		println("SyncManagerMultiV3::main:" +
		    		" Send get request to SNMP agent on " + 
		    		host1 + " at port " + port1);
	    		boolean completed = request.waitForCompletion(10000);
	    
	    		// Check for a timeout
	    		//
            if (completed == false) {
                println("SyncManagerMultiV3::main:" +
						" Request timed out. Check if agent can 
						be reached");
		
                // Print request
                //
                println("Request: " + request.toString());
                java.lang.System.exit(0);
            }
	    
            // Check if the response contains an error 
	    		 //
            int errorStatus = request.getErrorStatus();
            if (errorStatus != SnmpDefinitions.snmpRspNoError) {
                println("Error status = " + 
						SnmpRequest.snmpErrorToString(errorStatus));
                println("Error index = " + 
						request.getErrorIndex());
                java.lang.System.exit(0);
            }
       
            // Display the content of the result
            //
            SnmpVarBindList result = request.getResponseVarBindList();
            println("Result: \n" + result);
       
            println("\n>> Press Enter if you want to send the request" +
		    " on the second agent.\n");
            java.lang.System.in.read();
            

	      		
			   // Repeat the process for the second agent agent2.
			   //
	    		SnmpUsmParameters p2 = 
				new SnmpUsmParameters(engine, "defaultUser");
	    
	    		p2.setSecurityLevel(SnmpDefinitions.authNoPriv);

	   			p2.setContextName("TEST-CONTEXT".getBytes());
	    
			   p2.setContextEngineId(agent2.getEngineId().getBytes());
	    
			   // Associate the updated parameters with agent2.
			   //
		      agent2.setParams(p2);
	    	    
	    		// Discover timeliness and boot
	    	   //
	    		agent2.processUsmTimelinessDiscovery();
	    
			  	// Make the request with agent2
			   //
	    		request = session.snmpGetRequest(agent2, null, list);
			    println("SyncManagerMultiV3::main:" +
		   			 " Send get request to SNMP agent on " + 
				    host2 + " at port " + port2);
				  completed = request.waitForCompletion(10000);
	    
	    		// Check for a timeout
	    		//
            if (completed == false) {
                println("SyncManagerMultiV3::main:" +
					" Request timed out. Check  if agent can be 
					reached");
		
                // Print request.
                //
                println("Request: " + request.toString());
                java.lang.System.exit(0);
            }
	    
           // Check if the response contains an error
	    		// 
	    		errorStatus = request.getErrorStatus();
           		 if (errorStatus != SnmpDefinitions.snmpRspNoError) {
               	 println("Error status = " + 
					SnmpRequest.snmpErrorToString(errorStatus));
               	 println("Error index = " + 
				request.getErrorIndex());
                java.lang.System.exit(0);
            }
       
            // Display the content of the result
            //
	    		result = request.getResponseVarBindList();
            println("Result: \n" + result);
       
            println("\n>> Press Enter if you want to stop " +
		    		"this manager.\n");
            java.lang.System.in.read();
	    
            // End the session
            //
            session.destroySession();
       
            
}

The SyncManagerMultiV3 example essentially performs the same actions as the SyncManagerV3 example, except it creates two SNMP USM peers rather than just one. The two peer agents are each created in exactly the same way as in the single peer example.

Both the peer agents are accessed using a common set of parameters, which are kept in the jdmk.security file for the session. This jdmk.security file contains two rows, one for each SNMP USM peer agent, as shown in the following example.


Example 19–6 jdmk.security File for the SyncManagerMultiV3 Example

#Authentication only.
userEntry=0x8000002a05819dcb6e00001f95,defaultUser,,usmHMACMD5AuthProtocol,
mypasswd
userEntry=0x8000002a05819dcb6e00001f96,defaultUser,,usmHMACMD5AuthProtocol,
mypasswd

# #####APPENDED PROPERTY####
localEngineBoots=5

# #####APPENDED PROPERTY####
localEngineID=0x8000002a05000000ec4c49ded9

If you configure your manager to create a greater number of peers, then its associated jdmk.security file must contain a corresponding number of entries, one for each authoritative engine with which the manager will communicate.

In this example, the only difference between the two userEntry rows is between the engine ID numbers. These engine IDs correspond to the engines of the two SNMP adaptor servers created by the MultipleAgentV3 example in Multiple Agents.

To Run the SyncManagerMultiV3 Example
  1. Before running the example, you must have run mibgen and compiled the Java classes in the examplesDir/Snmp/Manager directory.

    See To Run the SyncManager Example for instructions if you have not already done this.

  2. Make sure that no other agent is running, and start the multiple SNMPv3 agent, MultipleAgentV3, in examplesDir/Snmp/Agent.

    See Running the SNMPv3 MultipleAgentV3 Example if you have not already built and run this example.

    Type the following commands to start the MultipleAgentV3 example:


    $ cd examplesDir/Snmp/Agent
    $ java -classpath classpath -Djdmk.security.file=./jdmk.security 
    MultipleAgentV3
    

    The MultipleAgentV3 example simulates two SNMPv3 agents in one process. We shall make these agents peers of SyncManagerMultiV3.

    Be sure to run the multiple agent in its directory, otherwise it cannot find its jdmk.security and jdmk2.security files.

  3. Start the manager application in another window to connect to these two agents.

    If you want to run the manager on a different host from the one where the agents are running, replace localhost with the name of the host where you started the agents. Both the agents in the MultipleAgentV3 example run on the same host.


    $ cd examplesDir/Snmp/Manager
    $ java -classpath classpath -Djdmk.security.file=./jdmk.security 
    SyncManagerMultiV3 localhost 8085 localhost 8087
    

    Be sure to run the manager in its directory, otherwise it cannot find its jdmk.security file.

  4. You should see sending a request to the first agent. Press Enter to send a request to the second agent.

    You will now see the manager sending a second request to the agent on port 8087.

  5. Press Enter to stop the manager

Asynchronous Managers

The asynchronous SNMP manager lets you handle more requests in the same amount of time because the manager is not blocked waiting for responses. Instead, it creates a request handler object that runs as a separate thread and processes several responses concurrently. Otherwise, the initialization of peers, parameters, sessions, options, and dispatcher is identical to that of a synchronous manager. This applies to all three versions of SNMP.


Example 19–7 The AsyncManager Example

// read the command line parameters
String host = argv[0];
String port = argv[1];
   
// Use the OidTable generated by mibgen when compiling MIB-II.
SnmpOidTableSupport oidTable = new RFC1213_MIBOidTable();
       
// Sample use of the OidTable.
SnmpOidRecord record = oidTable.resolveVarName("udpLocalPort");
java.lang.System.out.println(
    "AsyncManager::main: variable = " + record.getName() + 
        " oid = " + record.getOid() + " type = " + record.getType());

// Initialize the SNMP Manager API.
SnmpOid.setSnmpOidTable(oidTable);
  
// Create an SnmpPeer object for representing the agent 
SnmpPeer agent = new SnmpPeer(host, Integer.parseInt(port));
     
// Create parameters for communicating with the agent
SnmpParameters params = new SnmpParameters("public", "private");
agent.setSnmpParam(params);
     
// Build the session and assign its default peer
SnmpSession session = new SnmpSession("AsyncManager session");
session.setDefaultPeer(agent);

// Same dispatcher and trap listener as in SyncManager example
SnmpEventReportDispatcher trapAgent =
    new SnmpEventReportDispatcher(Integer.parseInt(port)+1);
trapAgent.addTrapListener(new TrapListenerImpl()); 
new Thread(trapAgent).start();

// Build the list of variables to query
SnmpVarbindList list = new SnmpVarbindList("AsyncManager varbind list");
list.addVariable("sysDescr.0");
    
// Create a simple implementation of an SnmpHandler. 
AsyncRspHandler handler = new AsyncRspHandler();
       
// Make the SNMP walk request with our handler
SnmpRequest request = session.snmpWalkUntil(
    handler, list, new  SnmpOid("sysServices"));
       
// Here you could do whatever processing you need.
// In the context of the example, we are just going to wait
// 4 seconds while the response handler displays the result.
Thread.sleep(4000);

[...] // Wait for user to type enter. Traps will be handled.
       
// End the session properly and we're done.
//
session.destroySession();
java.lang.System.exit(0);

The trap mechanism in this application is identical to the one presented in the SyncManager example. The event report dispatcher receives traps and calls the corresponding method of our SnmpTrapListener class.

In this example, the manager performs an snmpWalkUntil request that gives a response for each variable that it gets. The response handler is called to process each of these responses.

Response Handler

A response handler for an asynchronous manager is an implementation of the SnmpHandler interface. When a handler object is associated with a request, its methods are called when the agent returns an answer or fails to return an answer. In these methods, you implement whatever actions you want for processing the responses to a request. Typically, these methods extracts the result of each request or the reason for its failure.

The timeout used by the request handler is the one specified by the SnmpPeer object representing the agent. The handler is also called to process any errors caused by the request in the session. This ensures that the manager application is never interrupted after issuing a request.


Example 19–8 The SnmpHandler Implementation

public class AsyncRspHandler implements SnmpHandler {
   
    // Empty constructor
    public AsyncRspHandler() {
    }

    // Called when the agent responds to a request
    public void processSnmpPollData( SnmpRequest request,
        int errStatus, int errIndex, SnmpVarbindList vblist) {

        java.lang.System.out.println(
            "Processing response: " + request.toString());
        java.lang.System.out.println(
            "errStatus = " + SnmpRequest.snmpErrorToString(errStatus) +
            " errIndex = " + errIndex);

        // Check if a result is available.
        if (request.getRequestStatus() ==
            SnmpRequest.stResultsAvailable) {

            // Extract the result for display
            SnmpVarbindList result = request.getResponseVbList();
            java.lang.System.out.println(
                "Result = " + result.vbListToString());
        }
    }

    // Called when the agent fails to respond to a request
    public void processSnmpPollTimeout(SnmpRequest request) {
        
        java.lang.System.out.println(
			"Request timed out: " + request.toString());
        
        if (request.getRequestStatus() == 
            SnmpRequest.stResultsAvailable) {

            // The result is empty and will display an error message
            SnmpVarbindList result = request.getResponseVbList();
            java.lang.System.out.println(
                "Result = " + result.vbListToString());
        }
    }

    // Called when there is an error in the session
    public void processSnmpInternalError(SnmpRequest request,
        String errmsg) {
        
        java.lang.System.out.println(
            "Session error: " + request.toString());
        java.lang.System.out.println("Error is: " + errmsg);
    }
}

To Run the AsyncManager Example
  1. If you have not done so already, start the simple SNMP agent in examplesDir/Snmp/Agent, after making sure that no other agent is running on port 8085.

    This manager also uses the OID table description (the SnmpOidTableSupport class) that we generated from the MIB for the synchronous manager. If you have not already done so, see To Run the SyncManager Example for instructions on how to do this.

  2. If you do not have an SNMP agent still running, make sure that no other agent is running on port 8085 and start one with the following command:


    $ cd examplesDir/Snmp/Agent
    $ java -classpath classpath Agent nbTraps
    
  3. Specify the number of traps to be sent to the manager in the nbTraps parameter.

    Wait until the manager is started to send the traps.

  4. In another terminal window, start the manager application to connect to this agent.

    If you want to run the manager on a different host, replace localhost with the name of the host where you started the agent.


    $ cd examplesDir/Snmp/Manager
    $ java -classpath classpath AsyncManager localhost 8085
    

    You should then see the output of the SnmpWalkUntil request: the response handler method is called for each variable that is returned.

  5. Press Enter in the agent's window to send traps and see the trap reports as they are received in the manager.

    When you have finished with the agent, do not forget to stop it by typing Control-C in its terminal window.

Inform Requests

The inform request is specified in SNMPv2 and SNMPv3 as a mechanism for sending a report and receiving a response.

Because SNMP managers both send and receive inform requests, the SNMP manager API includes the mechanisms for doing both. Roughly, inform requests are sent in the same way as other requests, and they are received in the same way as traps. Both of these mechanisms are explained in the following sections.

There are simple examples, one for SMNPv2 and one for SNMPv3. Each example has two manager applications, one of which sends an inform request, and the other which listens for and replies to this request. No SNMP agents are involved in these exchanges.

Sending an Inform Request (SNMPv2)

Like the other types of requests, the manager sends an inform request through a session. The only difference is that the peer object associated with the request should be an SNMP manager able to receive and reply to InformRequest PDUs.

You can associate a peer with a session by making it the default peer object. This is how we do it in this example. This means that if we do not specify a peer when sending requests, they are automatically addressed to our manager peer. Because sessions often have agent peers as a default, you can specify the manager peer as a parameter to the snmpInform method of the session object.


Example 19–9 Sending an SNMPv2 Inform Request in SimpleManager1

// When calling the program, you must specify the hostname
// of the SNMP manager you want to send the inform to.
//
String host = argv[0];
        
// Initialize the port number to send inform PDUs on port 8085.
//
int port = 8085;
   
try {
    // Create an SnmpPeer object that represents the entity to
    // communicate with. 
    //
    SnmpPeer peer = new SnmpPeer(host, port);
     
    // Create parameters to associate to the peer.
    // When creating the parameter object, you can specify the
    // read and write community to be used when sending an
    // inform request.
    // 
    SnmpParameters params = new SnmpParameters(
        "public", "private", "public");
       
    // The newly created parameter must be associated to the peer.
    //
    peer.setSnmpParam(params);
     
    // Build the session. A session creates, controls and manages
    // one or more requests.
    //
    SnmpSession session = new SnmpSession("SimpleManager1 session");
    session.setDefaultPeer(peer);
     
    // Make the SNMP inform request and wait for the result.
    //
    SnmpRequest request = session.snmpInform(
        null, new SnmpOid("1.2.3.4"), null);
    java.lang.System.out.println(
        "NOTE: Inform request sent to SNMP manager on " +
        host + " at port " + port);
    boolean completed = request.waitForCompletion(10000);
       
    // Check for a timeout of the request.
    //
    if (completed == false) {
        java.lang.System.out.println(
            "\nSimpleManager1::main: Request timed out. " +
            "Check if agent can be reached");
         
        // Print request.
        //
        java.lang.System.out.println("Request: " + request.toString());
        java.lang.System.exit(0);
    }
     
    // Now we have a response. Check if the response contains an error.
    //
    int errorStatus = request.getErrorStatus();
    if (errorStatus != SnmpDefinitions.snmpRspNoError) {
        java.lang.System.out.println("Error status = " +
            SnmpRequest.snmpErrorToString(errorStatus));
        java.lang.System.out.println("Error index = " +
            request.getErrorIndex());
        java.lang.System.exit(0);
    }
       
    // Now we shall display the content of the result.
    //
    SnmpVarbindList result = request.getResponseVbList();
    java.lang.System.out.println("\nNOTE: Response received:\n" + result);
       
    // Stop the session properly before exiting
    session.destroySession();
    java.lang.System.exit(0);
     
} catch(Exception e) {
    java.lang.System.err.println(
        "SimpleManager1::main: Exception occurred:" + e );
    e.printStackTrace();
}

Before sending the request, the snmpInform method automatically adds two variables to the head of the varbind list that is passed in as the last parameter. These are sysUpTime.0 and snmpTrapOid.0, in the order they appear in the list. These variables are mandated by RFC 1905 and added systematically so that the calling process does not need to add them.

Like all other requests in a session, inform requests can be handled either synchronously or asynchronously in the sender. In our example, we process the inform request synchronously: the manager blocks the session while waiting for the completion of the request. In an asynchronous manager, you would need to implement a response handler as explained in Response Handler, and then use it to process responses, as shown in Example 19–7.

Sending an Inform Request (SNMPv3)

This example shows how to build an SNMPv3 manager that sends and/or receives SNMPv3 requests. It initializes an SNMPv3 USM peer and an SNMP session, and then sends an inform request to a second SNMP manager.


Example 19–10 Sending an SNMPv3 Inform Request in SimpleManager1V3

			//	When calling the program, specify the hostname of the  
			// SNMP manager to send the inform to
			//
			final String host = argv[0];
        
        // Initialize the port number to send inform PDUs on port 8085.
        //
        final int port = 8085;
   
        	[...]

	    	// Create an SnmpUSMPeer object for representing the entity 
	    	// to communicate with. 
        //
        final SnmpUsmPeer peer = new SnmpUsmPeer(session.getEngine(),
						    host, 
						    port);
     
        // Create parameters to associate to the entity to 
	      // communicate with.
       // When creating the parameter object, you specify the necessary 
	      // security related parameters.
        // 
        final SnmpUsmParameters params = 
			new SnmpUsmParameters(session.getEngine());

	    	// Set the parameters
		   //
	    
	   		// First a principal
	    	//
		   params.setPrincipal("defaultUser");
	    
	      // A security level. Authentication is applied.
	      // 
	      params.setSecurityLevel(SnmpDefinitions.authNoPriv);
	    
	      // Add a contextEngineId. The context engine Id is the 
	      // manager's engine ID and not the adaptor's.
	      //
	      params.setContextEngineId(peer.getEngineId().getBytes());

	      // Add a context name.
	      //
	      params.setContextName("TEST-CONTEXT".getBytes());
	    
       // The newly created parameter must be associated to the peer.
       //
       peer.setParams(params);
     

	      // The inform is authenticated, so the timeliness 
	      // discovery must be processed.
	      //
	      peer.processUsmTimelinessDiscovery();
	    


       // A default peer (listener manager) can be associated to an 
	      // SnmpSession. When invoking a service provided by the 
	      // SnmpSession, if the peer is not specified, the session 
	      // will perform the service using the default one as the 
	      // target of the service
       //
       session.setDefaultPeer(peer);
	    
       println("\nNOTE: SNMP V3 simple manager 1 initialized");
            
       // Make the SNMP inform request and wait for the result.
       //
       println("\n>> Press Enter to send the inform request on " + 
		  host + " at port " + port + "...");
        try {
                System.in.read();
            } catch (IOException e) {
                e.printStackTrace();
            }

            final SnmpRequest request = 
		 session.snmpInformRequest(null, new SnmpOid("1.2.3.4"), 
					  null);

            println("NOTE: Inform request sent to SNMP manager on " + 
		    host + " at port " + port);

            final boolean completed = request.waitForCompletion(10000);
       
         // Check for a timeout of the request.
         //
         if (completed == false) {
                println("\nSimpleManager1::main: Request timed out." +
			" Check reachability of agent");
	 
                // Print request.
                //
                println("Request: " + request.toString());
                java.lang.System.exit(0);
            }
     
         // Check if the response contains an error
         //
         final int errorStatus = request.getErrorStatus();
         if (errorStatus != SnmpDefinitions.snmpRspNoError) {
                println("Error status = " +
 			SnmpRequest.snmpErrorToString(errorStatus));
                println("Error index = " + request.getErrorIndex());
                java.lang.System.exit(0);
            }
       
            // Display the content of the result.
            //
            final SnmpVarBindList result = request.getResponseVarBindList();
            println("\nNOTE: Response received:\n" + result);
       
            // End the session
            //
            println("\nNOTE: SNMP V3 simple manager 1 stopped...");
            session.destroySession();
       

}

As you can see, once the SNMPv3 session has been instantiated and the SNMPv3 USM peer has been created and configured, the inform request is sent in exactly the same way as under SNMPv2. The only difference is the additional SNMPv3 security.


Receiving Inform Requests

Managers receive inform requests as they do traps: they are unsolicited events that must be received by a dispatcher object. Unlike traps, an inform request requires a response PDU that, according to the SNMP specification, must contain the same variable bindings. Therefore, immediately after an inform request is successfully received and decoded, the SnmpEventReportDispatcher class automatically constructs and sends the inform response back to the originating host.

The manager application then retrieves the data in the inform request through a listener on the dispatcher. Inform request listeners are registered with the dispatcher object in the same way as trap listeners. The receiving manager in our example is very simple, because its only function is to create the dispatcher and the listener for inform requests. The receiving manager SimpleManager2 is the same for both the SimpleManager1 and SimpleManager1V3 examples.


Example 19–11 Receiving Inform Requests in SimpleManager2

// Initialize the port number to listen for incoming inform PDUs on 
// port 8085.
int port = 8085;

try {
    
    // Create a dispatcher for SNMP event reports 
	   // (SnmpEventReportDispatcher).
    // SnmpEventReportDispatcher is run as a thread and listens for informs
    // on the specified port.
    // Add our InformListenerImpl class as an SnmpInformListener.
    // InformListenerImpl will receive a callback when a valid trap
    // PDU is received.
    //
    SnmpEventReportDispatcher informDispatcher =
        new SnmpEventReportDispatcher(port,taskServer,null,null);
    informDispatcher.addInformListener(new InformListenerImpl());
	   final Thread informThread = new Thread(informDispatcher);
	   informThread.setPriority(Thread.MAX_PRIORITY);    
     informDispatcher.start();
		println("\nNOTE: SNMP simple manager 2 initialized");

    // Note that you can use the same SnmpEventReportDispatcher object
    // for both incoming traps and informs.
    // Just add your trap listener to the same dispatcher, for example:
    //    informDispatcher.addTrapListener(new TrapListenerImpl());

    // Here we are just going to wait for inform PDUs.
    //
    java.lang.System.out.println("\nNOTE: Event report listener 
	   initialized");
    java.lang.System.out.println(
      "      and listening for incoming inform PDUs on port " + port 
		+ "...");

} catch(Exception e) {
    java.lang.System.err.println(
        "SimpleManager2::main: Exception occurred:" + e );
    e.printStackTrace();
}

The remaining step is to program the behavior we want upon receiving an inform request. To do this, we must write the InformListenerImpl class that we registered as an inform request listener in the previous code sample. This class implements the SnmpInformListener interface and its processSnmpInform and processSnmpInformV3 methods handle the incoming inform requests.

Because the dispatcher automatically sends the inform response back to the originating host, the SnmpInformListener implementation does not need to do this. Usually this method extracts the variable bindings and takes whatever action is necessary upon receiving an inform request. In our example, we simply print out the source and the contents of the inform request.


Example 19–12 The InformListenerImpl Class

import java.io.IOException;
import javax.management.snmp.SnmpPduRequest;
import javax.management.snmp.manager.SnmpInformListener;

/**
 * This class implements the SnmpInformListener interface.
 * The callback method processSnmpInform is called when a
 * valid inform PDU is received.
 */

public class InformListenerImpl implements SnmpInformListener {

    public void processSnmpInform(SnmpPduRequest inform) {
        
        // Display the received PDU.
        //
        java.lang.System.out.println("\nNOTE: Inform request received:\n");
        java.lang.System.out.println("\tType
			 = " + inform.pduTypeToString(inform.type));
        java.lang.System.out.println("\tVersion   = " + inform.version);
        java.lang.System.out.println("\tRequestId = " + inform.requestId);
        java.lang.System.out.println("\tAddress   = " + inform.address);
        java.lang.System.out.println("\tPort      = " + inform.port);
        java.lang.System.out.println("\tCommunity = " +
            new String(inform.community));
        java.lang.System.out.println("\tVB list   = ");

        for (int i = 0; i < inform.varBindList.length; i++) {
            java.lang.System.out.println("\t\t" + inform.varBindList[i]);
        }


			public void processSnmpInformV3(SnmpScopedPduRequest inform) {
				println("\nNOTE: Inform request V3 received:\n");
       	 	println("\tType      = 
				" + inform.pduTypeToString(inform.type));
       		println("\tVersion   = " + inform.version);
		      	println("\tRequestId = " + inform.requestId);
     	  	println("\tAddress   = " + inform.address);
        		println("\tPort      = " + inform.port);
        		println("\tContext = " + new String(inform.contextName));
        		println("\tVB list   = ");
        		for (int i = 0; i < inform.varBindList.length; i++) {
           		 println("\t\t" + inform.varBindList[i]);
        }
    }
    
        // Our listener stops the manager after receiving its first
        // inform request
        java.lang.System.out.println(
            "\nNOTE: SNMP simple manager 2 stopped...");
        java.lang.System.exit(0);
    }
}

To Run the SNMPv2 Inform Request Example

The examplesDir/Snmp/Inform directory contains all of the files for the two manager applications, along with the InformListenerImpl class.

  1. Compile all files in this directory with the javac command.

    For example, on the Solaris platform with the Korn shell, you would type:


    $ cd examplesDir/Snmp/Inform/
    $ javac -classpath classpath *.java
    
  2. To run the example, start the inform request receiver with the following command.


    $ java -classpath classpath SimpleManager2
    

    You can start the application in another terminal window or on another host.

  3. Wait for this manager to be initialized, then start the other manager with the following command.

    The hostname is the name of the host where you started the receiving manager, or localhost.


    $ java -classpath classpath SimpleManager1 hostname
    
  4. When the sender is ready, press Enter to send the inform request.

    You should see the contents of the request displayed by the receiving manager. Immediately afterwards, the sender receives the inform response containing the same variable bindings and displays them. Both manager applications then exit automatically.

To Run the SNMPv3 Inform Request Example
  1. Before running the example, you must compile the Java classes in the examplesDir/Snmp/Inform directory.

    Type the following commands in your working directory:


    $ javac -d . SimpleManager1V3.java SimpleManager2.java 
    InformListenerImpl.java
    
  2. Start the receiving manager, SimpleManager2:


    $ java -classpath classpath -Djdmk.security.file=./jdmk.security 
    SimpleManager2
    

    This manager binds to port 8085.

  3. Wait for this manager to be initialized, then start the other manager with the following command, specifying the hostname and port number of the receiving manager.

    In this case, the host name is the localhost


    $ java -classpath classpath -Djdmk.security.file=./sender.security 
    SimpleManager1V3 localhost
    
  4. When the sender is ready, press Enter to send the inform request.

    You should see the contents of the request displayed by the receiving manager. Immediately afterwards, the sender receives the inform response containing the same variable bindings and displays them. Both manager applications then exit automatically.