JMX (Java Management eXtensions) is an API for managing, among other things, J2EE applications. JOnAS (version 4 and above) integrates the MX4J open-source JMX server and registers a number of MBeans. The web-based JonasAdmin application acts as a JMX client, enabling viewing and manipulation of these MBeans.
It maybe desirable for an application to expose its own MBeans via the JMX server to allow application management (using, for example, MC4J). JOnAS currently does not provide a prebuilt method for registering MBeans with its JMX server. This chapter illustrates one method of registering application specific MBeans with the JOnAS JMX server based on the m-let service.
The basic task of registering an MBean with the JOnAS JMX server is accomplished by the following implementation of the ServletContextListener interface (http://java.sun.com/j2ee/1.4/docs/api/javax/servlet/ServletContextListener.html). This implementation reads a number of MLet files, which specify the MBeans to be registered, and attempts to register those beans during the web application context initialization.
import java.net.MalformedURLException; import java.net.URL; import java.util.Iterator; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.StringTokenizer; import javax.management.InstanceAlreadyExistsException; import javax.management.InstanceNotFoundException; import javax.management.ReflectionException; import javax.management.MBeanServer; import javax.management.MBeanException; import javax.management.MBeanRegistrationException; import javax.management.MBeanServerFactory; import javax.management.MalformedObjectNameException; import javax.management.NotCompliantMBeanException; import javax.management.ObjectInstance; import javax.management.ObjectName; import javax.management.loading.MLet; import javax.management.loading.MLetMBean; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContext; import javax.servlet.ServletContextListener; import org.apache.log4j.Logger; /** * ServletContextListener designed to register JMX MBeans into * an existing JMX server when the application starts up. The * MBeans to be loaded are specified in the MLet files whose * names are given in the servlet context parameter with the name * mletFiles, in a semi-colon delimited list (This is not really * needed as multiple mlets can be specified in one file, it might * be useful). Note that the filename are relative to the WEB-INF * directory of the servlet context being listened to. * * Note, that this listener should precede (in web.xml) any other * that depend on the MBeans being registered. * * Note that the MBean registration is sensitive to classloader * issues. For example, when registering the MBeans in the JMX server * provided by the Jonas application server any libraries required by * the MBeans need to be in the central lib directory (lib/ext). * * * @author Jonny Wray * */ public class MBeanRegistrationListener implements ServletContextListener { private static final String MLET_DOMAIN = "MBeanRegistrationListener"; private static final String MLET_BEAN_NAME = MLET_DOMAIN+":Name=MLet"; private static final String MLETFILE_INITPARAM_NAME = "mletFiles"; private static final Logger log = Logger.getLogger(MBeanRegistrationListener.class); private MBeanServer lookForExistingServer(){ List mbeanServers = MBeanServerFactory.findMBeanServer(null); if(mbeanServers != null && mbeanServers.size() > 0){ return (MBeanServer)mbeanServers.get(0); } return null; } private MBeanServer getMBeanServer(){ MBeanServer server = lookForExistingServer(); if(server == null){ server = MBeanServerFactory.createMBeanServer(MLET_DOMAIN); } return server; } public void contextDestroyed(ServletContextEvent arg0) { log.info("Destroy event"); // Anything that needs to be done here on deregistering // of the web application? } private List getMLetURLs(String filenames){ List urls = new ArrayList(); StringTokenizer tok = new StringTokenizer(filenames, ";"); while(tok.hasMoreTokens()){String filename = tok.nextToken(); URL configURL = Thread.currentThread().getContextClassLoader().getResource(filename); if(configURL == null){ log.error("Cound not load MLet file resource from "+filename+" using current thread context classloader"); } else{ urls.add(configURL); } } return urls; } private List getMLetURLs(ServletContext context, String filenames){ List urls = new ArrayList(); StringTokenizer tok = new StringTokenizer(filenames, ";"); while(tok.hasMoreTokens()){ String filename = "/WEB-INF/"+tok.nextToken(); URL configURL = null; try { configURL = context.getResource(filename); } catch (MalformedURLException e) { log.error("URL for "+filename+" is malformed", e); } if(configURL == null){ log.error("Cound not find MLet file resource from "+filename+" in servlet context"); } else{ urls.add(configURL); } } return urls; } /** * Dynamically register the MBeans specified in the list * of MLet files (relative to /WEB-INF/) specified in servlet * context parameter mletFiles as a semi-colon deliminated list * of file names. * * The algorithm looks for already running JMX servers and uses * the first it comes across. If no servers are running, then * it creates one. * * Note, the interface does not define any exceptions to be * thrown. Currently, any exceptions thrown during registration * are logged at error level and then ignored. This seems * reasonable, as these may or may not be a fatal event. In * this way the registration process reports its failure and * the application context initialization continues. */ public void contextInitialized(ServletContextEvent arg0) { log.info("Initializing event"); String filenames = arg0.getServletContext().getInitParameter(MLETFILE_INITPARAM_NAME); if(filenames != null && filenames.length() > 0){ MBeanServer server = getMBeanServer(); if(server != null){ try{ ObjectName name = new ObjectName(MLET_BEAN_NAME); if(!server.isRegistered(name)){ log.info("Creating new MLetMBean for dynamic registration"); MLetMBean mletService = new MLet(); server.registerMBean(mletService, name); } List urls = getMLetURLs(arg0.getServletContext(), filenames); for(int i=0;i < urls.size();i++){ URL url = (URL)urls.get(i); try { log.info("Registering MBeans from MLet file "+url); Set loadedMBeans = (Set)server.invoke(name, "getMBeansFromURL", new Object[]{url}, new String[]{URL.class.getName()}); processRegisteredMBeans(loadedMBeans); } catch (InstanceNotFoundException e) { log.error("Unable to register MBeans from MLet file "+url, e); } catch (MBeanException e) { log.error("Unable to register MBeans from MLet file "+url, e); } catch (ReflectionException e) { log.error("Unable to register MBeans from MLet file "+url, e); } } } catch(MalformedObjectNameException e){ log.error("Unable to register the MLetMBean", e); } catch(NotCompliantMBeanException e){ log.error("Unable to register the MLetMBean", e); } catch(MBeanRegistrationException e){ log.error("Unable to register the MLetMBean", e); } catch(InstanceAlreadyExistsException e){ log.error("Unable to register the MLetMBean", e); } } else{ log.error("MBeanServer not found and could not be created. Not registering MBeans."); } } else{ log.error("No mletFiles servlet context parameter found."); } } private void processRegisteredMBeans(Set loadedMBeans) { log.debug("Loaded beans: "+loadedMBeans.size()); Iterator it = loadedMBeans.iterator(); while(it.hasNext()){ Object o = it.next(); if(o instanceof ObjectInstance){ ObjectInstance inst = (ObjectInstance)o; log.info("Registered: "+inst.getObjectName()); } else if(o instanceof Throwable){ Throwable err = (Throwable)o; log.error("Error registering MBeans", err); } } } } |