Coverage Report - net.sf.snmpadaptor4j.core.JmxListener
 
Classes in this File Line Coverage Branch Coverage Complexity
JmxListener
100 %
92/92
100 %
48/48
3,8
 
 1  
 package net.sf.snmpadaptor4j.core;
 2  
 
 3  
 import java.net.URL;
 4  
 import java.util.HashMap;
 5  
 import java.util.List;
 6  
 import java.util.Map;
 7  
 import java.util.Set;
 8  
 import javax.management.MBeanAttributeInfo;
 9  
 import javax.management.MBeanServer;
 10  
 import javax.management.MBeanServerDelegate;
 11  
 import javax.management.MBeanServerNotification;
 12  
 import javax.management.Notification;
 13  
 import javax.management.NotificationListener;
 14  
 import javax.management.ObjectName;
 15  
 import javax.management.relation.MBeanServerNotificationFilter;
 16  
 import net.sf.snmpadaptor4j.SnmpAppContext;
 17  
 import net.sf.snmpadaptor4j.core.mapping.MBeanAttributeMapping;
 18  
 import net.sf.snmpadaptor4j.core.mapping.SnmpTrapMapping;
 19  
 import net.sf.snmpadaptor4j.core.mapping.XmlMappingParser;
 20  
 import org.apache.log4j.Logger;
 21  
 
 22  
 /**
 23  
  * Object designed to respond to each registration or deregistration of MBeans.
 24  
  * @author <a href="http://fr.linkedin.com/in/jpminetti/">Jean-Philippe MINETTI</a>
 25  
  */
 26  
 public class JmxListener
 27  
                 implements NotificationListener {
 28  
 
 29  
         /**
 30  
          * Logger.
 31  
          */
 32  32
         protected final Logger logger = Logger.getLogger(JmxListener.class);
 33  
 
 34  
         /**
 35  
          * <b>M</b>anagement <b>I</b>nformation <b>B</b>ase (MIB) for access to JMX attributes.
 36  
          */
 37  
         private final JmxSnmpMib jmxSnmpMib;
 38  
 
 39  
         /**
 40  
          * Manager of JMX notifications.
 41  
          */
 42  
         private final JmxNotificationManager jmxNotificationManager;
 43  
 
 44  
         /**
 45  
          * Context of main application.
 46  
          */
 47  
         private final SnmpAppContext mainAppContext;
 48  
 
 49  
         /**
 50  
          * Application context map.
 51  
          */
 52  
         private final Map<ClassLoader, SnmpAppContext> appContextMap;
 53  
 
 54  
         /**
 55  
          * <code>TRUE</code> for handle only MBeans created by the same {@link ClassLoader} that the SNMP adapter. <code>FALSE</code> for handle all MBeans of the JVM.
 56  
          */
 57  
         private final boolean classLoaderScope;
 58  
 
 59  
         /**
 60  
          * JMX agent.
 61  
          */
 62  
         private MBeanServer jmxServer;
 63  
 
 64  
         /**
 65  
          * Constructor.
 66  
          * @param jmxSnmpMib <b>M</b>anagement <b>I</b>nformation <b>B</b>ase (MIB) for access to JMX attributes (must not be <code>NULL</code>).
 67  
          * @param jmxNotificationManager Manager of JMX notifications (must not be <code>NULL</code>).
 68  
          * @param mainAppContext Context of main application (must not be <code>NULL</code>).
 69  
          * @param appContextMap Application context map (must not be <code>NULL</code>).
 70  
          * @param classLoaderScope <code>TRUE</code> for handle only MBeans created by the same {@link ClassLoader} that the SNMP adapter. <code>FALSE</code> for handle
 71  
          *            all MBeans of the JVM.
 72  
          */
 73  
         public JmxListener (final JmxSnmpMib jmxSnmpMib, final JmxNotificationManager jmxNotificationManager, final SnmpAppContext mainAppContext,
 74  
                         final Map<ClassLoader, SnmpAppContext> appContextMap, final boolean classLoaderScope) {
 75  7
                 this(jmxSnmpMib, jmxNotificationManager, mainAppContext, appContextMap, classLoaderScope, null);
 76  7
         }
 77  
 
 78  
         /**
 79  
          * Constructor (used for tests).
 80  
          * @param jmxSnmpMib <b>M</b>anagement <b>I</b>nformation <b>B</b>ase (MIB) for access to JMX attributes (must not be <code>NULL</code>).
 81  
          * @param jmxNotificationManager Manager of JMX notifications.
 82  
          * @param mainAppContext Context of main application (must not be <code>NULL</code>).
 83  
          * @param appContextMap Application context map (must not be <code>NULL</code>).
 84  
          * @param classLoaderScope <code>TRUE</code> for handle only MBeans created by the same {@link ClassLoader} that the SNMP adapter. <code>FALSE</code> for handle
 85  
          *            all MBeans of the JVM.
 86  
          * @param jmxServer JMX agent.
 87  
          */
 88  
         protected JmxListener (final JmxSnmpMib jmxSnmpMib, final JmxNotificationManager jmxNotificationManager, final SnmpAppContext mainAppContext,
 89  
                         final Map<ClassLoader, SnmpAppContext> appContextMap, final boolean classLoaderScope, final MBeanServer jmxServer) {
 90  32
                 super();
 91  32
                 this.mainAppContext = mainAppContext;
 92  32
                 this.appContextMap = appContextMap;
 93  32
                 this.classLoaderScope = classLoaderScope;
 94  32
                 this.jmxSnmpMib = jmxSnmpMib;
 95  32
                 this.jmxNotificationManager = jmxNotificationManager;
 96  32
                 this.jmxServer = jmxServer;
 97  32
         }
 98  
 
 99  
         /**
 100  
          * Returns the JMX agent.
 101  
          * @return JMX agent.
 102  
          */
 103  
         protected final MBeanServer getJmxServer () {
 104  27
                 return this.jmxServer;
 105  
         }
 106  
 
 107  
         /**
 108  
          * Opens the connection with the JMX agent.
 109  
          * @param server JMX agent.
 110  
          * @throws Exception Exception if an error occurred.
 111  
          */
 112  
         public synchronized void open (final MBeanServer server) throws Exception {
 113  3
                 this.logger.trace("JMX opening...");
 114  3
                 if (this.jmxServer != null) {
 115  1
                         throw new Exception("Already connected to a JMX agent");
 116  
                 }
 117  
 
 118  
                 // JMX notification registering
 119  2
                 final MBeanServerNotificationFilter filter = new MBeanServerNotificationFilter();
 120  2
                 filter.enableAllObjectNames();
 121  2
                 server.addNotificationListener(MBeanServerDelegate.DELEGATE_NAME, this, filter, server);
 122  
 
 123  
                 // Existing MBean parsing
 124  2
                 final Set<ObjectName> mBeanNameList = server.queryNames(null, null);
 125  2
                 for (final ObjectName mBeanName : mBeanNameList) {
 126  40
                         register(server, mBeanName);
 127  
                 }
 128  
 
 129  2
                 this.jmxServer = server;
 130  2
                 this.logger.trace("JMX opened");
 131  2
         }
 132  
 
 133  
         /**
 134  
          * Closes the connection with the JMX agent.
 135  
          * @throws Exception Exception if an error occurred.
 136  
          */
 137  
         public synchronized void close () throws Exception {
 138  3
                 this.logger.trace("JMX closing...");
 139  3
                 if (this.jmxServer == null) {
 140  1
                         throw new Exception("Not connected to a JMX agent");
 141  
                 }
 142  
 
 143  
                 // JMX notification deregistering
 144  2
                 this.jmxServer.removeNotificationListener(MBeanServerDelegate.DELEGATE_NAME, this);
 145  
 
 146  
                 // Cleaning
 147  2
                 this.jmxNotificationManager.unregisterAll(this.jmxServer);
 148  2
                 this.jmxSnmpMib.unregisterAllAttributes();
 149  
 
 150  2
                 this.jmxServer = null;
 151  2
                 this.logger.trace("JMX closed");
 152  2
         }
 153  
 
 154  
         /*
 155  
          * {@inheritDoc}
 156  
          * @see javax.management.NotificationListener#handleNotification(javax.management.Notification, java.lang.Object)
 157  
          */
 158  
         public final synchronized void handleNotification (final Notification notification, final Object handback) {
 159  21
                 if ((handback instanceof MBeanServer) && (this.jmxServer == (MBeanServer) handback)) {
 160  19
                         if (notification instanceof MBeanServerNotification) {
 161  18
                                 final MBeanServerNotification serverNotification = (MBeanServerNotification) notification;
 162  18
                                 if (MBeanServerNotification.REGISTRATION_NOTIFICATION.equals(serverNotification.getType())) {
 163  
                                         try {
 164  14
                                                 register((MBeanServer) handback, serverNotification.getMBeanName());
 165  
                                         }
 166  1
                                         catch (final Throwable e) {
 167  1
                                                 this.logger.error(serverNotification.getMBeanName() + ": MBean not loaded in SNMP adapter", e);
 168  14
                                         }
 169  
                                 }
 170  4
                                 else if (MBeanServerNotification.UNREGISTRATION_NOTIFICATION.equals(serverNotification.getType())) {
 171  
                                         try {
 172  3
                                                 unregister((MBeanServer) handback, serverNotification.getMBeanName());
 173  
                                         }
 174  1
                                         catch (final Throwable e) {
 175  1
                                                 this.logger.error(serverNotification.getMBeanName() + ": MBean not unloaded in SNMP adapter", e);
 176  2
                                         }
 177  
                                 }
 178  
                         }
 179  
                 }
 180  21
         }
 181  
 
 182  
         /**
 183  
          * Registers a MBean to SNMP <b>M</b>anagement <b>I</b>nformation <b>B</b>ase (MIB).
 184  
          * @param server JMX agent.
 185  
          * @param mBeanName MBean name.
 186  
          * @throws Exception Exception if an error occurred.
 187  
          */
 188  
         private void register (final MBeanServer server, final ObjectName mBeanName) throws Exception {
 189  53
                 if (server.isRegistered(mBeanName)) {
 190  52
                         final ClassLoader classLoader = server.getClassLoaderFor(mBeanName);
 191  52
                         if (classLoader != null) {
 192  14
                                 if (!this.classLoaderScope || this.getClass().getClassLoader().equals(classLoader)) {
 193  12
                                         final Class<?> mBeanClass = classLoader.loadClass(server.getObjectInstance(mBeanName).getClassName());
 194  12
                                         final URL url = mBeanClass.getResource(mBeanClass.getSimpleName() + ".snmp.xml");
 195  12
                                         if (url != null) {
 196  10
                                                 if (this.logger.isDebugEnabled()) {
 197  5
                                                         this.logger.debug("SNMP mapping found at " + url);
 198  
                                                 }
 199  10
                                                 final XmlMappingParser parser = XmlMappingParser.newInstance(url);
 200  
 
 201  
                                                 // Base OID finding
 202  10
                                                 final SnmpAppContext ctx = findAppContext(classLoader);
 203  10
                                                 String baseOid = ctx.getMBeanOidMap().get(mBeanName);
 204  10
                                                 if (baseOid == null) {
 205  8
                                                         baseOid = parser.findBaseOid(mBeanName, ctx.getRootOidMap(), ctx.getDefaultRootOid(), this.mainAppContext.getDefaultRootOid());
 206  
                                                 }
 207  10
                                                 if (baseOid == null) {
 208  4
                                                         if (this.logger.isDebugEnabled()) {
 209  2
                                                                 this.logger.debug("None OID found for [" + mBeanName + "]");
 210  
                                                         }
 211  
                                                 }
 212  
                                                 else {
 213  
 
 214  
                                                         // Mapping loading
 215  6
                                                         final Map<String, MBeanAttributeInfo> mBeanAttributeInfoMap = new HashMap<String, MBeanAttributeInfo>();
 216  60
                                                         for (final MBeanAttributeInfo mBeanAttributeInfo : server.getMBeanInfo(mBeanName).getAttributes()) {
 217  54
                                                                 mBeanAttributeInfoMap.put(mBeanAttributeInfo.getName(), mBeanAttributeInfo);
 218  
                                                         }
 219  6
                                                         final List<MBeanAttributeMapping> mBeanAttributeMappingList = parser.newMBeanAttributeMappingList(mBeanAttributeInfoMap, classLoader,
 220  
                                                                         baseOid);
 221  6
                                                         this.jmxSnmpMib.registerAttributes(server, mBeanName, mBeanAttributeMappingList);
 222  6
                                                         final Map<String, SnmpTrapMapping> trapMappingMap = parser.newSnmpTrapMappingMap(baseOid);
 223  6
                                                         this.jmxNotificationManager.register(server, mBeanName, trapMappingMap);
 224  
 
 225  
                                                 }
 226  10
                                         }
 227  2
                                         else if (this.logger.isDebugEnabled()) {
 228  1
                                                 this.logger.debug("SNMP mapping missing for [" + mBeanName + "]");
 229  
                                         }
 230  12
                                 }
 231  2
                                 else if (this.logger.isTraceEnabled()) {
 232  1
                                         this.logger.trace("SNMP mapping ignored because classLoaderScope = TRUE for [" + mBeanName + "]");
 233  
                                 }
 234  
                         }
 235  38
                         else if (this.logger.isTraceEnabled()) {
 236  19
                                 this.logger.trace("SNMP mapping ignored because the MBean is not accessible for [" + mBeanName + "]");
 237  
                         }
 238  
                 }
 239  53
         }
 240  
 
 241  
         /**
 242  
          * Finds the application context by its class loader.
 243  
          * @param classLoader Class loader of application.
 244  
          * @return Application context found.
 245  
          */
 246  
         private SnmpAppContext findAppContext (final ClassLoader classLoader) {
 247  10
                 SnmpAppContext ctx = null;
 248  10
                 ClassLoader cl = classLoader;
 249  28
                 while ((ctx == null) && (cl != null)) {
 250  18
                         ctx = this.appContextMap.get(cl);
 251  18
                         cl = cl.getParent();
 252  
                 }
 253  10
                 if (ctx == null) {
 254  8
                         ctx = this.mainAppContext;
 255  
                 }
 256  10
                 return ctx;
 257  
         }
 258  
 
 259  
         /**
 260  
          * Unregisters a MBean of SNMP <b>M</b>anagement <b>I</b>nformation <b>B</b>ase (MIB).
 261  
          * @param server JMX agent.
 262  
          * @param mBeanName MBean name.
 263  
          */
 264  
         private void unregister (final MBeanServer server, final ObjectName mBeanName) {
 265  2
                 this.jmxSnmpMib.unregisterAttributes(server, mBeanName);
 266  2
                 this.jmxNotificationManager.unregister(server, mBeanName);
 267  2
         }
 268  
 
 269  
         /*
 270  
          * {@inheritDoc}
 271  
          * @see java.lang.Object#toString()
 272  
          */
 273  
         @Override
 274  
         public final String toString () {
 275  1
                 return "JmxListener[jmxSnmpMib=" + this.jmxSnmpMib + "; jmxNotificationManager=" + this.jmxNotificationManager + "; mainAppContext=" + this.mainAppContext
 276  
                                 + "; appContextMap=" + this.appContextMap + "; classLoaderScope=" + this.classLoaderScope + "]";
 277  
         }
 278  
 
 279  
 }