Java Dynamic Management Kit 5.1 Tutorial

18.2.2 Customized Classes

As was the case for the simple SNMP table example, to be able to tie the code generated by mibgen to its proper instrumentation, you must extend the classes generated by mibgen in a series of custom classes. The customized classes are those provided in the examplesDir/current/Snmp/MBeanTable directory.

The customized extensions to the generated classes add the following functionality:

Aspects of the JmxMBeanServerImpl and JmxMBeanEntryImpl classes are examined in more detail in the following sections.

18.2.2.1 JmxMBeanServerImpl

As stated previously, JmxMBeanServerImpl extends the JmxMBeanServer empty skeleton class. JmxMBeanServerImpl also instantiates a subclass of TableJmxMBeanTable, to mirror the MBeans registered in an MBean server.


Example 18–4 Subclassing TableJmxMBeanTable

[...]
    private final void inittables() {

       JmxMBeanTable = new TableJmxMBeanTable(myMib,null) {
             public Object createJmxMBeanEntryMBean(SnmpMibSubRequest req,
                                                    SnmpOid rowOid, 
                                                    int depth, 
                                                    ObjectName entryObjName,
                                                    SnmpMibTable meta, 
                                                    Integer  aJmxMBeanIndex)
                 throws SnmpStatusException  {

                 return JmxMBeanServerImpl.this.
                    createJmxMBeanEntryMBean(req,rowOid,depth,entryObjName,
                                                 meta,aJmxMBeanIndex);
                }

                public void removeEntryCb(int pos, SnmpOid row, 
                                          ObjectName name,
                                          Object entry, SnmpMibTable meta)
                    throws SnmpStatusException {
                    super.removeEntryCb(pos,row,name,entry,meta);
                    final JmxMBeanEntryMBean e = 
                        (JmxMBeanEntryMBean) entry;
                    final EnumJmxMBeanRowStatus destroy =
                         new EnumJmxMBeanRowStatus(EnumRowStatus.destroy);
                    e.setJmxMBeanRowStatus(destroy);
                }
            };
	
        JmxMBeanTable.setCreationEnabled(true);
    }

[...]

In Example 18–4, JmxMBeanServerImpl defines a method, inittables, to instantiate and initialize its table objects. The inittables method creates a new instance of TableJmxMBeanTable, and overrides it so that its createJmxMBeanEntryMBean method returns a JmxMBeanEntryImpl object instead of the default JmxMBeanEntry object. This is done by defining a new createJmxMBeanEntryMBean method in the customized JmxMBeanEntryImpl class (see Example 18–5), and calling it on the parent JmxMBeanServerImpl object from the overridden TableJmxMBeanTable object, as shown in Example 18–4.

The removeEntryCb method is overridden, so that table entries representing MBeans also delete their associated entries in the jmxMBeanAtrrTable by calling setJmxMBeanRowStatus(destroy) when they are removed from the jmxMBeanTable.


Example 18–5 createJmxMBeanEntryMBean Method

[...]
    private Object createJmxMBeanEntryMBean(SnmpMibSubRequest req,
					                                SnmpOid rowOid, 
					                                int depth, 
					                                ObjectName entryObjName,
					                                SnmpMibTable meta, 
					                                Integer  aJmxMBeanIndex)
	         throws SnmpStatusException  {

		      SnmpVarBind rowStatusVb = req.getRowStatusVarBind();

	         if (rowStatusVb == null) 
	             throw new SnmpStatusException(
		                    SnmpStatusException.snmpRspNoCreation);

		      if (! isAvailable(aJmxMBeanIndex.intValue())) {
	             if (Boolean.getBoolean("info"))
                 System.out.println("Index is not suitable: " 
                     + aJmxMBeanIndex);
	             throw new SnmpStatusException(
		                    SnmpStatusException.snmpRspInconsistentValue);
	         }

		      JmxMBeanEntryImpl entry = 
	             new JmxMBeanEntryImpl(myMib,this);
	         entry.JmxMBeanIndex = aJmxMBeanIndex;

		      entry.createFromRemote(req,rowStatusVb);
	         return entry;
    }

[...]

As shown in Example 18–4, the createJmxMBeanEntryMBean method is called when a remote SNMP Manager creates a new row in a jmxMBeanTable. Example 18–5 shows how the createJmxMBeanEntryMBean method is overridden by the JmxMBeanServerImpl class, to add new entries in the jmxMBeanAttrTable as the rows are created in the table.

Of the parameters the customized version of createJmxMBeanEntryMBean takes when it is started, the following are the most significant..

The createJmxMBeanEntryMBean method checks first of all whether the VarBind list found when req calls getRowStatusVarBind() is valid. It then checks whether the row's OID index is available. Once it has established that both the VarBind and the index are viable, it proceeds to create a JmxMBeanEntryImpl instance, entry. Calling createFromRemote() at this point ensures that createMBean() will be called when the new entry is eventually activated.

As mentioned previously, JmxMBeanServerImpl is also configured to listen for notifications of the type MBeanServerNotifications coming from the MBean server.


Example 18–6 Listening for Notifications

[...]
public synchronized void start() {

	  started = true;
	  try {
	    	 final ObjectName delegate = 
		       new ObjectName("JMImplementation:type=MBeanServerDelegate");
	       myMib.getMibServer().
		       addNotificationListener(delegate,mbsListener,null,null);

	       try {
				   Set mbeans = myMib.getMibServer().queryNames(null,null);
            for (final Iterator it = mbeans.iterator(); 
                it.hasNext();) {
		             final ObjectName name = (ObjectName) it.next();
		             if (mustShow(name)) showMBean("start",name);
		         }
	        } catch (Exception x) {
		         try {
		             myMib.getMibServer().
			              removeNotificationListener(delegate,mbsListener);
		         } catch (Exception e) { /* OK */ }
		         throw x;
	        }
	    } catch (Exception x) {
	        started = false;	    
	        System.err.println("Failed to start MBean Table: " + x);
	        if (Boolean.getBoolean("debug")) x.printStackTrace();
	    }
}

[...]

The start() method shown in Example 18–5 starts a notification listener in the MIB server that listens out for notifications from the MBeanServerDelegate. It also populates the jmxMBeanTable and jmxMBeanAttrTable with the initial content of the MBean server. These tables are then updated as and when notifications are received, as shown in Example 18–7.


Example 18–7 Handling Notifications

[...]

private void handleMBSNotification(Notification notif, Object handback) {
	
	  synchronized(this) { if (!started) return; }

  	  if (notif instanceof MBeanServerNotification) {
	        final MBeanServerNotification n = 
		          (MBeanServerNotification) notif;
	        final String nt = n.getType();
	        final ObjectName mbeanName = n.getMBeanName();
	    
	        if (MBeanServerNotification.REGISTRATION_NOTIFICATION.
		         equals(nt)) {

             synchronized (this) {
		         if (mustShow(mbeanName)) { 
			          showMBean(nt,mbeanName);
		         }
   		  } 
	     } else if (MBeanServerNotification.UNREGISTRATION_NOTIFICATION.
   		       equals(nt)) {

     		synchronized (this) {
		        if (mustHide(mbeanName)) {
			         hideMBean(nt,mbeanName);
		        }
  		  }
	     }
	  }
}

[...]

The handleMBSNotification method that JmxMBeanServerImpl defines begins by checking that the correct type of notification has been received. If the notification is of the type MBeanServerNotification.REGISTRATION_NOTIFICATION, then a method named showMBean() is called. Otherwise, if MBeanServerNotification.UNREGISTRATION_NOTIFICATION is received, then the MBean is hidden with a call to hideMBean. The showMBean() method is shown in Example 18–8.


Example 18–8 Exposing MBeans through the SNMP Tables

[...]

private void showMBean(String operation,ObjectName name,int index) {
	   try {
	       JmxMBeanEntry entry = new JmxMBeanEntryImpl(myMib,this);
	       entry.JmxMBeanIndex = new Integer(index);
	       entry.setJmxMBeanObjectName(name.toString());
	       JmxMBeanTable.addEntry(entry);
	       names.put(name, entry);
	       entry.setJmxMBeanRowStatus(
		           new EnumJmxMBeanRowStatus(EnumRowStatus.active));
	       if (Boolean.getBoolean("info")) 
		        System.out.println("ADDED: JmxMBeanTable["+index+"]="+name);
	   } catch (Exception x) {
	       System.err.println("Failed to add MBean entry: " + name);
	       if (Boolean.getBoolean("debug")) x.printStackTrace();
	   }
}

[...]

The showMBean method adds the MBeans that handleMBSNotification learns about to the SNMP table JmxMBeanTable as JmxMBeanEntryImpl row entries. The status of the row is set to active. The MBean discovered by the notification listener is thus entered in the SNMP table as a row entry.

18.2.2.2 JmxMBeanEntryImpl

As mentioned previously, the JmxMBeanEntryImpl objects keep the jmxMBeanAttrTable updated. Example 18–9 and Example 18–10 show how JmxMBeanEntryImpl changes the status of rows, activates rows, and destroys them.


Example 18–9 Changing the Row Status

[...]

public synchronized void setJmxMBeanRowStatus(EnumJmxMBeanRowStatus x) 
	   throws SnmpStatusException {
	   switch (x.intValue()) {
	   case EnumRowStatus.active:
	       if (! (JmxMBeanRowStatus.intValue() == EnumRowStatus.active))
		        activate();
	       break;
	   case EnumRowStatus.notReady:
	       break;
	   case EnumRowStatus.notInService:
	       super.setJmxMBeanRowStatus(x);
	       break;
	   case EnumRowStatus.destroy:
	       destroy();
	       break;
	   default:
	       throw new SnmpStatusException(SnmpStatusException.
					                          snmpRspInconsistentValue);
	   }
}

[...]

The setJmxMBeanRowStatus method shown above is called by the SNMP runtime when the row is created remotely. It is also called explicitly by JmxMBeanEntryImpl when the row is created after receiving an MBean server notification.


Example 18–10 Activating and Destroying Rows

private void activate() throws SnmpStatusException {
	   if ((remoteCreation) && (mbean == null || name == null))
	       throw new SnmpStatusException(SnmpStatusException.snmpRspGenErr);
	   try {

	        if (remoteCreation) {
		         myGroup.registerMBean(mbean,name,this);
		         initJmxMBean(name);
		         remoteCreation = false;
		         mbean = null;
	        } 

	        exposedAttrCount = myGroup.addAttributes(name,this);
	        if (Boolean.getBoolean("info")) 
		         System.out.println(name.toString()+ ": added " + 
				                      exposedAttrCount
				                      + " attribute(s).");
	        JmxMBeanRowStatus = 
		         new EnumJmxMBeanRowStatus(EnumRowStatus.active);
	   } catch (Exception x) {
	        SnmpStatusException sn = 
		         new SnmpStatusException(SnmpStatusException.snmpRspGenErr);
	        sn.initCause(x);
	        throw sn;
	   }
}

private void destroy() throws SnmpStatusException {
  	try {
	       JmxMBeanRowStatus = 
		        new EnumJmxMBeanRowStatus(EnumRowStatus.notInService);
	       if (name != null && remoteDeletion)
		        myGroup.unregisterMBean(name,this);
	       remoteCreation = false;
	       mbean = null;	
	       if (name != null) 
		        myGroup.removeAttributes(name,this);
	       attrList = null;
	   } catch (Exception x) {
	       SnmpStatusException sn = 
		        new SnmpStatusException(SnmpStatusException.snmpRspGenErr);
	       sn.initCause(x);
	       throw sn;
	   }
}

Example 18–10 shows the activate and destroy methods defined by JmxMBeanEntryImpl. The activate method verifies first of all whether the row was created remotely or locally, and acts differently depending on the answer. If the row was created remotely by calling the createFromRemote method, the row entry registers the requested MBean in the MBean. If the row is created locally, this means that the request to create the row was made by an existing registered MBean, so there is no need to register it again in the MBean server.

The destroy method is created when the row is removed, either locally by handleMBeanServerNotification or remotely. The destroy method is called by removeEntryCb, which itself is called when the row is removed from the table, whether remotely or locally.