Interview Questions

SNMP Agent Using JDMK Api

In this article let’s discuss about creating a SNMP Agent using JDMK api.

Consider a scenario where you want to monitor your java application. You can do so by creating a SNMP agent and MIB. Now using the MIB you can query the SNMP agent to retrieve the status of your application. We can create the SNMP agent using JDMK (Java Dynamic Management Kit) api. We can also send SNMP traps using the same.
First let’s create a MIB and a sample SNMP agent using JDMK. Consider the following MIB.

Test-MIB.mib

Test-MIB DEFINITIONS ::= BEGIN
IMPORTS
    MODULE-IDENTITY, OBJECT-TYPE, NOTIFICATION-TYPE, Counter32, enterprises
        FROM SNMPv2-SMI
    DisplayString, TEXTUAL-CONVENTION
        FROM SNMPv2-TC
    MODULE-COMPLIANCE, OBJECT-GROUP, NOTIFICATION-GROUP
        FROM SNMPv2-CONF;

-- Module Identity
------------------
test-mib MODULE-IDENTITY
    LAST-UPDATED "201010240000Z"
    -- Format is "YYYYMMDDhhmmZ"
    ORGANIZATION "techdive.in"
    CONTACT-INFO "techdive.in
          <a href="http://techdive.in"
" title="http://techdive.in"
">http://techdive.in"
</a>    ::= { testAppMgt 1 }

-- Enterprise OIDs
------------------

    test        OBJECT IDENTIFIER ::= { enterprises 11 }
    testAppMgt  OBJECT IDENTIFIER ::= { test 19 }

-- Textual Conventions
----------------------

SysTimeMillis64TC ::= TEXTUAL-CONVENTION
    STATUS       current
    DESCRIPTION
          "An elapsed time, expressed in milli-seconds (ms).
           This type is based on Counter64.
      "
    SYNTAX Counter64

-- OBJECT-TYPE OID tree
-----------------------

testMgtMIBInfo
    OBJECT IDENTIFIER ::= { test-mib 1 }
testMgtMIBDBStatus
    OBJECT IDENTIFIER ::= { test-mib 2 }
testMgtMIBDBNotifs
    OBJECT IDENTIFIER ::= { test-mib 3 }

--
-- Server Informations
--
appVersion OBJECT-TYPE
   SYNTAX      DisplayString
   MAX-ACCESS  read-only
   STATUS      current
   DESCRIPTION
       "Sytem Version"
   ::= { testMgtMIBInfo 2 }

sysUpTime   OBJECT-TYPE
    SYNTAX  TimeTicks
    ACCESS  read-only
    STATUS  mandatory
    DESCRIPTION
          "The time since the
          network management portion of the system was last
          re-initialized."
    ::= { testMgtMIBInfo 3 }

userCount OBJECT-TYPE
    SYNTAX      Counter32
    MAX-ACCESS  read-only
    STATUS      current
    DESCRIPTION
        "Number of registered users in the system"
    ::= { testMgtMIBInfo 4 }

dbStatus OBJECT-TYPE
    SYNTAX      INTEGER { started(1), shutdown(2)}
    MAX-ACCESS  read-only
    STATUS      current
    DESCRIPTION
         " Database Status 1 - Started, 2 - Shutdown "
    ::= { testMgtMIBDBStatus 1 }

--
-- NOTIFICATIONS
--
dbStartNotification NOTIFICATION-TYPE
    OBJECTS { dbStatus}
    STATUS  current
    DESCRIPTION
    "DB Started"
    ::= { testMgtMIBDBNotifs 1 }

dbDownNotification NOTIFICATION-TYPE
    OBJECTS { dbStatus }
    STATUS  current
    DESCRIPTION
    "DB down"
    ::= { testMgtMIBDBNotifs 2 }

END

The above MIB is contains details such as appVersion, sysUpTime, userCount which are to be provided by the SNMP agent when queried (SNMP get, SNMP getNext) by the SNMP manager. It is also provides details of what are all the notifications which are to be sent based on certain events. Whenever the database is started or shutdown the application can send SNMP traps to the configured trap receiver.
Now download JDMK api binary version from net. You will find a tool mibgen inside the downloaded package. We need to create corresponding Java classes for the above mib Test-MIB.mib. We can create those classes using the following command

mibgen  Test-MIB.mib

The following classes will be autogenerated

EnumDbStatus.java
Test_MIB.java
Test_MIBOidTable.java
TestMgtMIBDBStatus.java
TestMgtMIBDBStatusMBean.java
TestMgtMIBDBStatusMeta.java
TestMgtMIBInfo.java
TestMgtMIBInfoMBean.java
TestMgtMIBInfoMeta.java

Out of the above java classes we need to extend Test_MIB.java, TestMgtMIBDBStatus.java and TestMgtMIBInfo.java.

Consider the following class ApplMgtMIBInfoImpl.java which extends from TestMgtMIBInfo.

ApplMgtMIBInfoImpl.java

import javax.management.MBeanServer;
import com.sun.management.snmp.SnmpStatusException;
import com.sun.management.snmp.agent.SnmpMib;

public class ApplMgtMIBInfoImpl extends TestMgtMIBInfo
{
        /*
         * (non-Javadoc)
         * @see com.sn.TestMgtMIBInfo#getUserCount()
         */

        @Override
        public Long getUserCount() throws SnmpStatusException
        {
                return 20L;
        }

        /*
         * (non-Javadoc)
         * @see com.sn.TestMgtMIBInfo#getSysUpTime()
         */

        @Override
        public Long getSysUpTime() throws SnmpStatusException
        {
                return 5000L;
        }

        /*
         * (non-Javadoc)
         * @see com.sn.TestMgtMIBInfo#getAppVersion()
         */

        @Override
        public String getAppVersion() throws SnmpStatusException
        {
                return "2.0";
        }

        public ApplMgtMIBInfoImpl()
        {
                this(null);
        }

        /**
         * @param myMib
         */

        public ApplMgtMIBInfoImpl(SnmpMib myMib)
        {
                super(myMib);
        }

        /**
         * @param myMib
         * @param server
         */

        public ApplMgtMIBInfoImpl(SnmpMib myMib, MBeanServer server)
        {
                super(myMib, server);
        }
}

The above class provides customized implementation for appVersion, userCount and sysUptime SNMP variables.

Consider the class ApplMgtMIBDBStatusImpl as follows.

ApplMgtMIBDBStatusImpl.java

import java.net.InetAddress;
import javax.management.MBeanServer;
import com.sun.management.snmp.SnmpInt;
import com.sun.management.snmp.SnmpOid;
import com.sun.management.snmp.SnmpOidRecord;
import com.sun.management.snmp.SnmpStatusException;
import com.sun.management.snmp.SnmpVarBind;
import com.sun.management.snmp.SnmpVarBindList;
import com.sun.management.snmp.agent.SnmpMib;

public class ApplMgtMIBDBStatusImpl extends TestMgtMIBDBStatus
{

        private static final long       serialVersionUID        = -7094115679131109484L;
        private static boolean          dbFlag                          = false;
        private String                          inetAddress                     = "127.0.0.1";

        /**
         * @return the inetAddress
         */

        public String getInetAddress()
        {
                return inetAddress;
        }

        /**
         * @param inetAddress
         *            the inetAddress to set
         */

        public void setInetAddress(String inetAddress)
        {
                this.inetAddress = inetAddress;
        }

        public ApplMgtMIBDBStatusImpl()
        {
                this(null);
        }

        /**
         * @param myMib
         */

        public ApplMgtMIBDBStatusImpl(SnmpMib myMib)
        {
                super(myMib);
        }

        /**
         * @param myMib
         * @param server
         */

        public ApplMgtMIBDBStatusImpl(SnmpMib myMib, MBeanServer server)
        {
                super(myMib, server);
        }

        /*
         * (non-Javadoc)
         * @see com.sn.TestMgtMIBDBStatus#getDbStatus()
         */

        @Override
        public EnumDbStatus getDbStatus() throws SnmpStatusException
        {
                if (dbFlag == false)
                {
                        dbFlag = true;
                        return new EnumDbStatus(2);
                }
                else
                {
                        dbFlag = false;
                        return new EnumDbStatus(1);
                }
        }

        /**
         * This method should be called to send notification snmp trap for DB startup
         */

        public void sendTrapDBStarted()
        {
                System.out.println("Sending SNMP Trap to indicate DB startup");
                sendTrap("dbStartNotification", new EnumDbStatus(1));
        }

        /**
         * This method should be called to send notification snmp trap for DB Shutdown
         */

        public void sendTrapDBDown()
        {
                System.out.println("Sending SNMP Trap to indicate DB Shutdown");
                sendTrap("dbDownNotification", new EnumDbStatus(2));
        }

        /**
         * Send SNMP v2 traps with the generic number of the trap
         */

        private void sendTrap(String snmpVarName, EnumDbStatus snmpVarValue)
        {

                if (TestSNMPAgent.getSnmpAdaptor() == null)
                {
                        return;
                }

                SnmpVarBindList varBindList = new SnmpVarBindList();
                Test_MIBOidTable oidTable = new Test_MIBOidTable();
                SnmpOidRecord oid;
                try
                {
                        oid = oidTable.resolveVarName(snmpVarName);

                        SnmpOid oid1 = new SnmpOid(oid.getOid());
                        SnmpVarBind varBind1 = new SnmpVarBind(oid1, new SnmpInt(snmpVarValue));
                        varBindList.addVarBind(varBind1);
                        try
                        {

                                System.out.println("Calling TestSNMPAgent for sending Trap to " + inetAddress + " with "
                                                + varBindList.toString());

                                // sending snmpV2 trap
                                TestSNMPAgent.getSnmpAdaptor().snmpV2Trap(InetAddress.getByName(inetAddress), "public", oid1,
                                                varBindList);
                        }
                        catch (Exception e3)
                        {
                                System.out.println("Error sending SNMP v2 trap." + e3);
                        }
                }
                catch (SnmpStatusException e)
                {
                        System.out.println("Error : SnmpStatusException " + e);
                }
        }
}

It contains customized implementation for providing DB status and also methods to send SNMP trap notifications whenever there is DB startup and shutdown event.

Now let’s see the custom implementation for Test_MIB.java, using the following class Test_MIBImpl.

Test_MIBImpl.java

import javax.management.MBeanServer;
import javax.management.ObjectName;

public class Test_MibImpl extends Test_MIB
{
        private ApplMgtMIBDBStatusImpl  applMgtMIBDBStatusImpl;

        private ApplMgtMIBInfoImpl              applMgtMIBInfoImpl;

        public Test_MibImpl()
        {
               
        }

        public Test_MibImpl(ApplMgtMIBDBStatusImpl applMgtMIBDBStatusImpl, ApplMgtMIBInfoImpl applMgtMIBInfoImpl)
        {
                super();
                this.applMgtMIBDBStatusImpl = applMgtMIBDBStatusImpl;
                this.applMgtMIBInfoImpl = applMgtMIBInfoImpl;
        }

        protected Object createTestMgtMIBDBStatusMBean(String groupName, String groupOid, ObjectName groupObjname,
                        MBeanServer server)
        {
                return applMgtMIBDBStatusImpl;

        }

        protected Object createTestMgtMIBInfoMBean(String groupName, String groupOid, ObjectName groupObjname,
                        MBeanServer server)
        {
                return applMgtMIBInfoImpl;
        }
}

The above class overrides api methods to return custom class instances which we have implemented like ApplMgtMIBInfoImpl and ApplMgtMIBDBStatusImpl. Now its time to create the SNMP agent using the auto generated classes by JDMK api and the above classes implemented by us.

TestSNMPAgent.java

package com.sn;

import java.sql.Connection;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.ObjectName;

import com.sun.management.comm.SnmpAdaptorServer;

public class TestSNMPAgent
{

        static SnmpAdaptorServer        snmpAdaptor     = null;

        /**
         * @return the snmpAdaptor
         */

        public static SnmpAdaptorServer getSnmpAdaptor()
        {
                return snmpAdaptor;
        }

        /**
         * @param snmpAdaptor
         *            the snmpAdaptor to set
         */

        public static void setSnmpAdaptor(SnmpAdaptorServer snmpAdaptor)
        {
                TestSNMPAgent.snmpAdaptor = snmpAdaptor;
        }

        private Test_MIB        test_mib;

        /**
         * @return the test_mib
         */

        public Test_MIB getTest_mib()
        {
                return test_mib;
        }

        /**
         * @param test_mib
         *            the test_mib to set
         */

        public void setTest_mib(Test_MIB test_mib)
        {
                this.test_mib = test_mib;
        }

        public TestSNMPAgent()
        {

        }

        public TestSNMPAgent(Test_MIB test_mib)
        {
                this.test_mib = test_mib;
        }

        /**
         * @param args
         */

        public void init()
        {
                System.out.println("into snmpagent");
                final MBeanServer server;
                final ObjectName snmpObjName;
                final ObjectName mibObjName;

                int snmpPort = 161;
                try
                {
                        server = MBeanServerFactory.createMBeanServer();
                        String domain = server.getDefaultDomain();

                        // 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.snmpV1Trap(0, 0, null);

                        // Create the Test_MIB and add it to the MBean server.
                        mibObjName = new ObjectName("snmp:class=Test_MIB");
                        Test_MIB mib2 = new Test_MIB();

                        // The MBean will register all group and table entry MBeans
                        // during its pre-registration
                        server.registerMBean(test_mib, mibObjName);

                        // Bind the SNMP adaptor to the MIB
                        // snmpAdaptor.addMib(test_mib);
                        test_mib.setSnmpAdaptor(snmpAdaptor);

                }
                catch (Exception e)
                {
                        e.printStackTrace();
                }
        }
}

In the above class, consider the init method, it creates a snmpAdaptor and registers it with MBeanServer. Then it starts the SNMP adaptor, sends a SNMP trap to the trap receiver as a part of initialization. It then creates an instance of Test_MIB and registers the same with MBeanServer.
Since snmpdaptor is registered with MBeanServer it can be accessed through JMX. Ok its time to start the SNMP agent, so that it can be accessed externally from an SNMP manager.

ApplMonitor.java

package com.sn;

public class ApplMonitor
{

        public ApplMonitor()
        {

        }

        /**
         * @param args
         */

        public static void main(String[] args)
        {
                ApplMgtMIBDBStatusImpl applMgtMIBDBStatusImpl =
                                       new ApplMgtMIBDBStatusImpl();
                ApplMgtMIBInfoImpl applMgtMIBInfoImpl = new ApplMgtMIBInfoImpl();

                TestSNMPAgent testSn =    
                new TestSNMPAgent
                (new Test_MibImpl(applMgtMIBDBStatusImpl,applMgtMIBInfoImpl));

                testSn.init();

                sleep();

                applMgtMIBDBStatusImpl.sendTrapDBStarted();

                sleep();

                applMgtMIBDBStatusImpl.sendTrapDBDown();
        }

        public static void sleep()
        {
                try
                {
                        Thread.sleep(5000);
                }
                catch (InterruptedException e)
                {
                        e.printStackTrace();
                }
        }
}

The main method in the above class initiates the SNMP agent using the Test_MibImpl.init() method. It also calls sendTrapDBStarted() and sendTrapDBDown() methods in ApplMgtMIBDBStatusImpl class to send SNMP trap notifications for db startup and shutdown events.

You can use any MIB browser by downloading from net to do SNMP operations for the given MIB. Also download any trap receiver tool to receive traps for db events.