Integrate NSClient4j with Java Management Extensions NSClient4j is a pure Java API that provides simple and quick access to Windows Performance Monitor (WPM) statistics. These statistics prove invaluable for monitoring your Windows servers and providing a baseline for activities such as capacity planning and trouble shooting. Additionally, access to low-level performance or operating statistics can be useful in your low-level code, such as the current rate of GETS against your IIS (Internet Information Server) Web server.In this article, the continuation of my series on WPM, I present a brief introduction to Java Management Extensions (JMX) and its benefits. I then explain how to implement JMX-based services for NSClient4j and describe different methods for implementing that technology.Changes in NSClient4j since Part 1As a brief aside, I want to enumerate some changes and enhancements that have been made in the NSClient4j package since Part 1 of this series: The main package name has migrated from com.marketwide.nagios to org.nsclient4j.Calls have been added to the NSClient4j class as convenience methods: getUsedDiskSpace(String diskVol)getFreeDiskSpace(String diskVol)getTotalDiskSpace(String diskVol)getUsedPercentDiskSpace(String diskVol)getFreePercentDiskSpace(String diskVol)To accommodate NSClient4j implementation in the Oracle 9i internal JVM, the source code is now fully supported under J2SE 1.3.Additional error handling identifies the cause of any errors emerging from the WPM engine (e.g., “Unrecognized pattern,” or “Counter not found”), and the NSClient4JException has been extended to differentiate between transport errors and WPM errors.New JMX-based classes provide JMX instrumentation of the WPM engine, which is this article’s focus.Recap of basic NSClient4j functionalityTo provide a quick review of the NSClient4j functionality, the code below shows a few lines of Java code connecting to an NSClient server on a Windows host and acquiring the system’s context-switch rate per second.Listing 1. Source for CLStat, a simple NSClient4j example public class CLStat { public static void main(String[] args) { try { NSClient4j client = new NSClient4j("192.168.1.4", 1248); System.out.println("Result:" + client.getPerfMonCounter("SystemContext Switches/sec")); } catch (NSClient4JException e) { System.err.println("Exception Geting Stat:" + e); } } } Listing 2. Run the CLStat example c:temp>set CLASSPATH=c:nsclient4j-rootnsclient4jdistnsclient4j.jar;%CLASSPATH%c:temp>java org.nsclient4j.CLStat Result:4120.66700 Now that we have reviewed the basics of NSClient4j, let’s look at JMX and how it can be implemented to support NSClient4j.NSClient4j and JMXThe Java Management Extensions technology is a Java standard for building management and monitoring components for applications. Though NSClient4j provides a satisfactory low-level API for accessing WPM statistics (see Figure 1), a monitoring layer implemented in JMX provides both a higher standard of integration and compliance while significantly extending NSClient4j’s functionality.Let’s consider some of the advantages of a JMX layer for NSClient4j: A standard API for accessing and monitoring target systems: Face it, there are enough APIs out there, and if we can implement an existing one to acquire and monitor performance data on Windows servers, we avoid API proliferation and gain a better integrated heterogeneous monitoring system. JMX instrumentation is widely implemented, and we can now add Windows servers to the list of systems and applications that JMX can monitor.JMX is not, admittedly, ubiquitous, but several supported mechanisms map JMX to other monitoring and management infrastructures such as SNMP (Simple Network Management Protocol) and CIM (Common Information Model).JMX provides a wide selection of additional services useful in a monitoring environment, such as gauges, events, notifications, and a series of connectivity options.On a more practical level, JMX provides a better infrastructure than a custom-built framework for making this information available, and provides separation of concerns between NSClient4j’s low-level data collection capabilities and JMX’s monitoring capabilities. Another issue is traffic against a critical Windows server for providing performance data to numerous interested parties. JMX provides a way to proxy this traffic and simultaneously gather current statistics through only one connected agent and supply data to many subscribers.Now, compare Figure 1 with Figure 2.In this article, I discuss the development and implementation of NSClient4j using different JMX options. The goal is create an NSClient4j JMX service that will scan a set of performance counters and expose functionality in JMX to query that data. To do that, we will:Create an MBean and define the lifecycle of the monitoring process.Implement a timer so scans can execute on a defined frequencyStore the results of the scans and expose them as JMX attributesIntroduction to JMXIf you are already familiar with JMX, you will not learn anything new here. However, instead of presenting a comprehensive JMX tutorial, I intend to give an idea of how JMX works. A full JMX tutorial can be found in Resources.The central structure in a JMX service is the agent, also referred to as the JMX agent. You can think of this as a sort of component bus. The service providing components are called MBeans (management beans), which are registered with the MBeanServer at the beginning of their lifecycles. The MBeanServer provides a set of services for the registered MBeans: Invocations of operations against MBeans are made to the MBeanServer, which in turn invokes against the specified registered MBeanThe MBeanServer retrieves attributes of the registered MBeansThe MBeanServer acquires extensive metadata about the registered MBeansThe MBeanServer creates and registers new MBeansFor the full MBeanServer API, see Resources.This architecture, as illustrated in Figure 3, excels at isolating components. For example, a client that must acquire data from a variety of different MBeans in the MBeanServer does not need any of the low-level proprietary classes in its classloader. All operations against an MBeanServer are designed to be highly abstracted to allow a wide variety of hosted MBeans. This abstraction, the rich metadata, and the isolation provided by MBeanServer creates a powerful mechanism; you will find that JMX-supported applications will integrate seamlessly with any type of MBean.Figure 3. A generalization of the JMX architecture. Click on thumbnail to view full-sized image.MBean identity and ObjectNamesAn MBean can be a simple class that does not necessarily have the more complex lifecycle and container relationship contract as an Enterprise JavaBeans (EJB) component. However, different types of MBeans require different types of support to be registered in an MBeanServer. In this article, we review two types of MBeans: standard MBeans and dynamic MBeans. All MBean services are exposed through the MBeanServer, so, to specifically target an MBean operation, a unique identifier is needed for each registered MBean. This identifier is an ObjectName (javax.management.ObjectName). Each MBean registered in the MBeanServer has a unique ObjectName. Figure 4 shows its basic structure.The components of an ObjectName are:A domain name: A simple dot-separated compound name (e.g., org.foo.bar).A set of key properties made up of name-value pairs (e.g., type=Foo, name=Bar). At least one key property is required.The domain is separated from the properties by a colon, and the key properties are separated by commas. An example of a full ObjectName is: org.foo.bar: type=Foo, name=Bar. See Resources for the ObjectName Javadoc.Exposing functionality and metadataWhen an object is registered as an MBean with an MBeanServer, the MBeanServer expects a mechanism to determine that object’s exposed functionality. The categories of metadata are as follows:Attributes: Single fields of data managed by the MBeanOperations: Functions with zero or more parameters implemented by the MBeanConstructors: Exposed constructors that the MBeanServer can use to construct new instancesNotifications: Notification callbacks emitted by the MBeanOur first example of an NSClient4j MBean is org.nsclient4j.NSClient4jService, a simple example of a standard MBean. Standard MBeans have static metadata defined completely by the interface the class implements. The interface must be in the package and adopt the naming convention <MBeanClassName>.MBean. So, in this instance: MBean class name: org.nsclient4j.NSClient4jServiceMBean interface name: org.nsclient4j.NSClient4jServiceMBeanThe MBeanServer exposes the functionality defined in the NSClient4jServiceMBean interface.NSClient4jService in detailIn the NSClient4jService example, I hard-coded an MBean class to track three statically defined Windows Performance Monitor counters:System thread countPercentage of memory in useCPU utilizationThe counters cannot be changed without recoding the MBean class and interface. This provides us a simple example to get started, but has obvious reuse and extensibility limitations. Figure 5 shows the class structure of the org.nsclient4j.NSClient4jServiceMBean interface and the concrete implementation of that interface in the class org.nsclient4j.NSClient4jService.The lifecycle of this simple MBean is as follows:The MBean class is constructed. There are two constructors, one is parameterless, and one is supplied all the runtime arguments required to run the MBean. These are: The host name of the target host to monitor.The frequency to poll the target host.The name of the scheduling MBean. (See “Scheduling Events” later in this article).The start() method is called. This creates a new NSClient4j instance for the target host and registers a scheduled callback from the JMX agent’s scheduling service. The callback invokes NSClient4jService‘s run() method.Every time the scheduling service invokes the callback() method, the NSClient4jService uses the NSClient4j instance to update the performance counters.The JMX agent responds to requests for counter values.The stop() method is called to shut down the scheduling service callbacks and close the NSClient4j instance.The NSClient4jService class implements the NSClient4jServiceMBean interface by convention to adopt that interface as the JMX instrumentation layer. The NSClient4jService class implements the javax.management.MBeanRegistration interface as a convenient way to get a handle to the MBeanServer, which is passed in on the preRegister() method. The NSClient4jService class implements the javax.management.NotificationListener interface so its instances can be registered as callback handlers for the scheduling service. Running the NSClient4jServiceAt this point, we have a fairly simple class that has the trappings of a JMX MBean, but does not serve as one until a JMX agent loads and registers it. For an MBean like this, some JMX agents will have different ways for loading and registering. We will review some proprietary approaches later, but for now, let’s examine two simple procedures for loading and running the NSClient4jService MBean using J2SE 1.5’s built-in JMX agent:A custom bootstrap classThe JMX m-let serviceCustom bootstrap classThe custom bootstrap class NSClient4jServiceBootStrap implements all the required steps to create, register, and run the MBean. The class must be run in J2SE 1.5+, which has a built-in JMX agent. Calling the class on the command line results in the following actions: NSClient4jServiceBootStrap instantiates an NSClient4jService object and initializes the host name to the single command line parameter. The frequency is hard-coded to 5,000 ms.A new object name is created for the MBean called org.nsclient4j:type=Monitor,name=<host name>.The MBeanServer is acquired using the javax.management.MBeanServerFactory class.The scheduling service MBean is registered to provide scheduling services. Your JMX agent provider may be preconfigured to register a scheduling MBean off the bat, but in this simplified example, we must do so ourselves.The NSClient4jService MBean is registered and then started.Listing 3 shows how the custom bootstrap class is invoked to monitor localhost:Listing 3. Run the simple bootstrap class C:jdk1.5.0bin>set NSCP=c:nsclient4j-rootnsclient4j distnsclient4j.jar;c:nsclient4j-rootnsclient4jliblog4j-1.2.8.jarC:jdk1.5.0bin>java –cp %NSCP% -showversion -Dcom.sun.management.jmxremote - Dcom.sun.management.jmxremote.port=8004 - Dcom.sun.management.jmxremote.authenticate= false -Dcom.sun.management.jmxremote.ssl=false org.nsclient4j.jmx.NSClient4jServiceBootStrap localhost java version "1.5.0" Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0-b64)Java HotSpot(TM) Client VM (build 1.5.0-b64, mixed mode, sharing)0 [main] INFO org.nsclient4j.jmx.NSClient4jService - Instantiated org.nsclient4j.jmx.NSClient4jService 0 [main] INFO org.nsclient4j.jmx.NSClient4jService - ============================================ Starting NSClient4jService ============================================ 30 [main] INFO org.nsclient4j.jmx.NSClient4jService - Connected to Host:localhost Up Since Mon Aug 22 09:13:33 EDT 2005 30 [main] INFO org.nsclient4j.jmx.NSClient4jService - ============================================ Started NSClient4jService ============================================ 1011 [Timer-1] DEBUG org.nsclient4j.jmx.NSClient4jService - CPU:9.0 1011 [Timer-1] DEBUG org.nsclient4j.jmx.NSClient4jService - Thread Count:612 1011 [Timer-1] DEBUG org.nsclient4j.jmx.NSClient4jService - Memory:29.10851 6019 [Timer-1] DEBUG org.nsclient4j.jmx.NSClient4jService - CPU:10.0 6019 [Timer-1] DEBUG org.nsclient4j.jmx.NSClient4jService - Thread Count:609 6019 [Timer-1] DEBUG org.nsclient4j.jmx.NSClient4jService - Memory:28.9902 11016 [Timer-1] DEBUG org.nsclient4j.jmx.NSClient4jService - CPU:9.0 11016 [Timer-1] DEBUG org.nsclient4j.jmx.NSClient4jService - Thread Count:609 11016 [Timer-1] DEBUG org.nsclient4j.jmx.NSClient4jService - Memory:28.97452 Observe the numerous -D options on the Java command line: -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=8004 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false These options are inserted to enable the JMX agent and to allow us to connect remotely to the JMX agent so we can monitor. See Resources for these documented options.M-let serviceThe second MBean load-and-run process for our NSClient4jService MBean is the JMX agent management applet (m-let) service. If we were to try to generalize the simple bootstrap class so it could load any MBean, and then modify the bootstrap class to externalize the configuration into a text file so we could customize the bootstrap without changing the bootstrap’s code, we would end up with something like the m-let service. The m-let service simplifies the bootstrap process. In the next example, we only need to specify the name of the m-let file’s URL; the service handles the rest. Of course, we must provide the file, which has the following format: <MLET CODE = class | OBJECT = serfile ARCHIVE = "archiveList" [CODEBASE = codebaseURL] [NAME = mbeanname] [VERSION = version]> [arglist] </MLET> See Resources for more on m-let.Our implementation of the file looks like this:Listing 4. nsclient4j-mlet.txt <MLET CODE="javax.management.timer.Timer" ARCHIVE="." NAME="org.nsclient4j:type=Timer"> </MLET> <MLET CODE="org.nsclient4j.jmx.NSClient4jService" ARCHIVE="nsclient4j.jar" NAME="org.nsclient4j:type=Monitor,host=localhost"> <ARG TYPE="java.lang.String" VALUE="localhost"> <ARG TYPE="long" VALUE="5000"> <ARG TYPE="java.lang.String" VALUE="org.nsclient4j:type=Timer"> </MLET> Note the following:Two MLET tags are defined, one for Timer and one for NSClient4jService.The Timer‘s archive is defined as a "." since the class is assumed to be in the JDK’s runtime classes. NSClient4jService‘s archive is specified as nsclient4j.jar.Both MBeans are assigned unique names in the ObjectName format.The NSClient4jService arguments are defined in the sequence and in the types specific to the NSClient4jService‘s parameterized constructor.The m-let service reads in this file, instantiates each of the MBean classes with the according parameters, and registers them with the JMX agent.For our basic command line examples, we still need a bootstrap class, but this class simply invokes the m-let service rather than the target MBeans directly. This m-let bootstrapper is SimpleMLetService. Listing 5 shows how to use it:Listing 5. Use the m-let service bootstrap C:jdk1.5.0bin>set NSCP=c:nsclient4j- rootnsclient4jdistnsclient4j.jar;c:nsclient4j-rootnsclient4jliblog4j-1.2.8.jarC:jdk1.5.0bin>java -cp %NSCP% -showversion -Dcom.sun.management.jmxremote - Dcom.sun.management.jmxremote.port=8004 - Dcom.sun.management.jmxremote.authenticate=false - Dcom.sun.management.jmxremote.ssl=false org.nsclient4j.jmx.SimpleMLetService file:/C:nsclient4j-rootnsclient4jconfignsclient4j-mlet.txt java version "1.5.0" Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0-b64) Java HotSpot(TM) Client VM (build 1.5.0-b64, mixed mode, sharing)MLet URL:file:/C:nsclient4j-rootnsclient4jconfignsclient4j-mlet.txt 0 [main] INFO org.nsclient4j.jmx.NSClient4jService - nstantiated org.nsclient4j.jmx.NSClient4jService 0 [main] INFO org.nsclient4j.jmx.NSClient4jService - ============================================ Starting NSClient4jService ============================================ 30 [main] INFO org.nsclient4j.jmx.NSClient4jService - Connected to Host:localhost Up Since Mon Aug 22 09:13:36 EDT 2005 30 [main] INFO org.nsclient4j.jmx.NSClient4jService - ============================================ Started NSClient4jService ============================================ 30 [Timer-1] DEBUG org.nsclient4j.jmx.NSClient4jService - CPU:20.0 30 [Timer-1] DEBUG org.nsclient4j.jmx.NSClient4jService - Thread Count:613 30 [Timer-1] DEBUG org.nsclient4j.jmx.NSClient4jService - Memory:31.15842 5037 [Timer-1] DEBUG org.nsclient4j.jmx.NSClient4jService - CPU:21.0 5037 [Timer-1] DEBUG org.nsclient4j.jmx.NSClient4jService - Thread Count:613 5037 [Timer-1] DEBUG org.nsclient4j.jmx.NSClient4jService - Memory:31.06562 Obviously, this approach is the preferred way to load and run MBeans, since no code level changes are required and most MBeans can be loaded this way. I will later briefly review an additional approach more in line with the more likely scenario of loading an MBean into a full application server, rather than a standalone JDK.Viewing the MBeanAt this point, we have our MBean running and monitoring one or more target NT hosts. It would be nice to actually look at some of these monitored values. One of the advantages of implementing JMX is the variety of tools available for interfacing with the management layer. Here’s a quick look at some of those.JConsole: A GUI JMX console shipped with J2SE 1.5MC4J: An open source GUI JMX consoleJMX console for JBoss: JBoss Application Server’s Web-based JMX consoleJBoss Web Console: JBoss Application Server’s GUI/Web-based JMX consoleJConsoleJConsole is a GUI console that can connect to local remote J2SE 1.5+ JVMs and provides extensive information on the JVM runtime data as well as basic MBean management operations. To run JConsole, call the jconsole executable in the J2SE 1.5 bin directory as follows: C:jdk1.5.0bin<jconsole localhost:8004.You should connect directly and see something like this:Click on the MBeans tab and navigate to org.nsclient4j. Locate your MBean. It will look like this:Figure 7. JConsole MBean attributes view. Click on thumbnail to view full-sized image.Now you can see the latest performance readings.MC4JMC4J is an open source JMX console. One of its advantages over JConsole includes its wide support for different JMX implementations. More than likely, MC4J will work with whatever JMX implementation you use. It also provides a nice graphing interface so you can visually monitor the statistics you are tracking in your MBean.Figure 8. MC4J JMX tree and graph. Click on thumbnail to view full-sized image.JBoss JMX consoleJBoss Application Server provides a Web-based JMX console that exposes MBean metadata, operations, and attributes.Figure 9. JBoss JMX console. Click on thumbnail to view full-sized image.JBoss Web ConsoleJBoss Application Server also provides an alternate Web-based JMX console with all the functionality of the JMX console and some additional features. Figure 10 shows how to set up a monitor that will track the target host’s number of processes.Figure 10. JBoss Web Console. Click on thumbnail to view full-sized image.Dynamic MBeansThe most obvious problem at this stage is that our MBean can monitor only three counters. We cannot change those counters, and we cannot add new ones without modifying the code. Ideally, we should be able to define the list of counters we want to monitor in some external file or resource so we can change the list of counters on the fly without recompiling. However, remembering that the standard MBean’s interface defines the attributes and operations available in an MBean, we’re still stuck with the same three attributes from the JMX interface’s perspective.This is where dynamic MBeans come into play. Dynamic MBeans are somewhat more complicated to develop than standard MBeans, but their flexibility makes them powerful. Standard MBeans have their management layer defined statically by the interface that they implement. In dynamic MBeans, the management interface is defined on the fly so new attributes and operations can be dynamically added.Without a statically defined Java interface to derive metadata from, a dynamic MBean requires a different mechanism for allowing the MBeanServer to manage its metadata and support its operations and attribute transfers. This mechanism is defined by the interface javax.management.DynamicMBean. It simply defines the operations that the MBean class must support while leaving the concrete underlying implementation more flexible than the standard MBean. The mandatory methods we must implement for a dynamic MBean are as follows:Object getAttribute(String name): Returns the value of a passed attribute.AttributeList getAttributes(String[] names): Returns the value of the passed attributes.MBeanInfo getMBeanInfo(): Provides information on the MBean’s supported attributes and operations (as the standard MBean’s MBean interface does) as well as information on the MBean’s supported constructors and notifications.Object invoke (String actionName, Object[] params, String[] signature): Handles the invocation of an operation against the dynamic MBean. Since the implementation of the MBean is not defined, the invocation of the operation may not be as simple as calling a method in the bean. This can be considered roughly equivalent to the invoke() method in the Java InvocationHandler interface.setAttribute(Attribute attribute): Sets the value of an attribute.setAttributes(AttributeList attributes): Sets the values of a list of attributes.Armed with this, we can now externalize all the counters we want to support into an external file and dynamically configure the MBean at runtime. This implementation is complete in org.nsclient4j.jmx.dynamic.NSClient4JDynamicService.Listing 6 illustrates the XML we can use to configure the MBean at load-time. I have added three additional counters.Listing 6. The nsclient4jconfig.xml file <nsclinet4jconfig> <counter name="ProcessorTime" code="CPU" type="java.lang.Integer" description="ProcessorTime is the percentage of elapsed time that all of process threads used the processor to execution instructions. An instruction is the basic unit of execution in a computer, a thread is the object that executes instructions, and a process is the object created when a program is run. Code executed to handle some hardware interrupts and trap conditions are included in this count."/> <counter name="Threads" code="SystemThreads" type="java.lang.Float" description="Threads is the number of threads in the computer at the time of data collection. This is an instantaneous count, not an average over the time interval. A thread is the basic executable entity that can execute instructions in a processor."/> <counter name="Processes" code="SystemProcesses" type="java.lang.Float" description="Processes is the number of processes in the computer at the time of data collection. This is an instantaneous count, not an average over the time interval. Each process represents the running of a program."/> <counter name="Interrupts" code="Processor(_Total)Interrupts/sec" type="java.lang.Float" description="Interrupts/sec is the average rate, in incidents per second, at which the processor received and serviced hardware interrupts. It does not include deferred procedure calls (DPCs), which are counted separately. This value is an indirect indicator of the activity of devices that generate interrupts, such as the system clock, the mouse, disk drivers, data communication lines, network interface cards, and other peripheral devices. These devices normally interrupt the processor when they have completed a task or require attention. Normal thread execution is suspended. The system clock typically interrupts the processor every 10 milliseconds, creating a background of interrupt activity. This counter displays the difference between the values observed in the last two samples, divided by the duration of the sample interval."/> <counter name="Datagrams" code="UDPDatagrams/sec" type="java.lang.Float" description="Datagrams/sec is the rate at which UDP datagrams are sent or received by the entity."/> <counter name="Segments" code="TCPSegments/sec" type="java.lang.Float" description="Segments/sec is the rate at which TCP segments are sent or received using the TCP protocol."/> </nsclinet4jconfig> The counter elements contain the following attributes:name: The name of the JMX attributecode: The NSClient4j counter name that will be polled from the target servertype: The Java type of the counter (usually one of the numeric classes)description: A description of the counter (exposed in the JMX metadata)The list below summarizes how the dynamic MBean code differs from the standard MBean code:Implements javax.management.DynamicMBean.Method setCounter(Element counterNodes) allows an XML element to be passed in to configure the WPM counters to be monitored.Values of the counters are stored in an array of org.nsclient4j.jmx.dynamic.Counters. Rather than store the raw value from the return of nsclient4j, the Counter class provides a container that stores additional metadata about the counter. This is rendered invisible externally by the JMX layer.Additional parameters in constructor.Figure 11 shows the class structure of the NSClient4JDynamicService and Counter classes.Figure 11. NSClient4JDynamicService and Counter classes. Click on thumbnail to view full-sized image.Running the dynamic MBeanThe codebase contains an NSClient4JDynamicService simple bootstrap class, but we will jump straight to the m-let boot strap. The following is the m-let file for running the dynamic MBean.Listing 7. nsclient4j-dynamic-mlet.txt <MLET CODE="javax.management.timer.Timer" ARCHIVE="." NAME="org.nsclient4j:type=Timer"> </MLET> <MLET CODE="org.nsclient4j.jmx.dynamic.NSClient4JDynamicService" ARCHIVE="nsclient4j.jar" NAME="org.nsclient4j:type=Monitor,host=localhost"> <ARG TYPE="java.lang.String" VALUE="localhost"> <ARG TYPE="long" VALUE="5000"> <ARG TYPE="java.lang.String" VALUE="file:/C:nsclient4j- rootnsclient4jconfignsclient4jconfig.xml"> <ARG TYPE="java.lang.String" VALUE="org.nsclient4j:type=Timer"> </MLET> Note the addition of the third parameter, which tells the MBean where to load the counter configuration from. Listing 8 shows how to run the m-let bootstrap for the dynamic service, which matches the code for the standard MBean (since we are using the m-let service), except for the m-let text file’s name.Listing 8. The dynamic MBean bootstrapped from the m-let service C:jdk1.5.0bin>set NSCP=c:nsclient4j- rootnsclient4jdistnsclient4j.jar;c:nsclient4j-rootnsclient4j liblog4j-1.2.8.jarC:jdk1.5.0bin>java -cp %NSCP% -showversion -Dcom.sun.management.jmxremote - Dcom.sun.management.jmxremote.port=8004 - Dcom.sun.management.jmxremote.authenticate=false - Dcom.sun.management.jmxremote.ssl=false org.nsclient4j.jmx.SimpleMLetService file:/C:nsclient4j-rootnsclient4jconfignsclient4j-dynamic-mlet.txt java version "1.5.0" Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0-b64) Java HotSpot(TM) Client VM (build 1.5.0-b64, mixed mode, sharing)MLet URL:file:/C:nsclient4j-rootnsclient4jconfignsclient4j-dynamic-mlet.txt 0 [main] INFO org.nsclient4j.jmx.dynamic.NSClient4JDynamicService - Instantiated org.nsclient4j.jmx.dynamic.NSClient4JDynamicService javax.management.ObjectInstance:javax.management.ObjectInstance@ab01df39 0 [main] INFO org.nsclient4j.jmx.dynamic.NSClient4JDynamicService - ============================================ Starting NSClient4jService ============================================ 20 [main] INFO org.nsclient4j.jmx.dynamic.NSClient4JDynamicService - Connected to Host:localhost Up Since Mon Aug 22 09:13:36 EDT 2005 30 [main] INFO org.nsclient4j.jmx.dynamic.NSClient4JDynamicService - ============================================ Started NSClient4jService ============================================ javax.management.ObjectInstance:javax.management.ObjectInstance@81b484c5 30 [Timer-1] DEBUG org.nsclient4j.jmx.dynamic.NSClient4JDynamicService - Read Counter UDPDatagrams/sec from localhost:3.80726 30 [Timer-1] DEBUG org.nsclient4j.jmx.dynamic.NSClient4JDynamicService - Read Counter Processor(_Total)Interrupts/sec from localhost:1201.2014 40 [Timer-1] DEBUG org.nsclient4j.jmx.dynamic.NSClient4JDynamicService - Read Counter TCPSegments/sec from localhost:67.36466 40 [Timer-1] DEBUG org.nsclient4j.jmx.dynamic.NSClient4JDynamicService - Read Counter SystemProcesses from localhost:88.0 40 [Timer-1] DEBUG org.nsclient4j.jmx.dynamic.NSClient4JDynamicService - Read Counter SystemThreads from localhost:647.0 40 [Timer-1] DEBUG org.nsclient4j.jmx.dynamic.NSClient4JDynamicService - Read Counter CPU from localhost:13 Implement NSClient4JDynamicService in JBossMost likely, you will be using an NSClient MBean in a full application server environment rather than the standalone JVM examples shown previously. Now let’s look at some minor additions to the load-and-run process for deploying the NSClient4JDynamicService in JBoss. There are no code changes, but JBoss provides a convenient and powerful XML-based deployment mechanism for MBeans. We will examine two options for deploying our MBeans in JBoss:Static class and XML service fileService Archive (SAR)JBoss service XML deployment filesJBoss implements an extended and more powerful version of the m-let service and uses a more descriptive XML tag set. Listing 9 gives an example of a JBoss XML deployment descriptor for an NSClient4JDynamicService MBean:Listing 9. An example JBoss service deployment descriptor <?xml version="1.0" encoding="UTF-8"?> <server> <mbean code="org.nsclient4j.jmx.dynamic.NSClient4JDynamicService" name="org.nsclient4j:service=WPM,host=localhost"> <constructor> <!-- Host Name --> <arg type="java.lang.String" value="localhost"/> <!-- Polling Frequency --> <arg type="long" value="5000"/> <!-- Timer MBean --> <arg type="java.lang.String" value= "jboss.jmx:name=SnmpAgent,service=timer,type=heartbeat"/> </constructor> <attribute name="Counter"> <nsclient4jconfig> <counter name="ProcessorTime" code="CPU" type="java.lang.Integer" description="ProcessorTime is the percentage of elapsed time that all of process threads used the processor to execution instructions. An instruction is the basic unit of execution in a computer, a thread is the object that executes instructions, and a process is the object created when a program is run. Code executed to handle some hardware interrupts and trap conditions are included in this count."/> <counter name="Threads" code="SystemThreads" type="java.lang.Float" description="Threads is the number of threads in the computer at the time of data collection. This is an instantaneous count, not an average over the time interval. A thread is the basic executable entity that can execute instructions in a processor."/> <counter name="Processes" code="SystemProcesses" type="java.lang.Float" description="Processes is the number of processes in the computer at the time of data collection. This is an instantaneous count, not an average over the time interval. Each process represents the running of a program."/> </nsclient4jconfig> </attribute> <depends>jboss.jmx:name=SnmpAgent,service=timer,type=heartbeat< /depends> </mbean> </server> There are a number of specific and conceptual differences to this bootstrapping (or deployment) process:JBoss starts its own deployment, classloading, and MBean initialization infrastructure in the application server at runtime. Consequently, a rich set of application server-provided services simplifies the process for deploying custom MBeans.One of these services is a hot deployer that scans a specific directory for files to deploy. Consequently, we can package our NSClient4JDynamicService MBean and drop it in the deployment directory, and JBoss handles the rest.The JBoss MBean deployer automatically calls the start() and stop() methods for us, so the entire MBean lifecycle can be left to JBoss.JBoss provides a proprietary extension to the JMX specification call dependencies. This allows us to specify that our MBean will not start until another MBean(s) have started. This extension proves useful in scenarios like the one above, where our MBean depends on the JMX agent’s timer service and we do not want to start our service until the timer service starts. This sort of dependency tracking can be placed on any MBean in the agent.Complex types can be passed to the MBean. JBoss implements property editors that automatically convert string-based values in our XML deployment descriptors to a series of complex types such as: JMX object namesPrimitive typesXML elementsPropertiesFilesThe configuration options available in a JBoss service deployment descriptor are extensive, so I limit our discussion here to the ones we have used:The master element is <server>. Any number of MBeans can be deployed within a server element. Additional elements can be used to influence classpath and classloader considerations.The <mbean> element specifies a new MBean deployment. In our example, there are two attributes:code: The MBean class namename: The MBean‘s JMX ObjectNameThe <constructor> element and its subsidiary <arg> elements make up the values passed to the MBean class instance on construction. It is optional, but can only appear once.The <attribute> element allows us to pass values to the MBean (as JMX attributes) after construction, but before start(). In this case, we pass one attribute called Counter, which consists of the same XML we used to initialize the NSClient4JDynamicService. A property editor creates the org.w3c.dom.Element so it can pass to the public void setCounter(Element counterNodes) method in the MBean. Any number of attributes can be defined, but remember, each one must be a valid JMX attribute.The <depends> element specifies that our MBean should not start until the object name specified has started. The object name in the example is the JMX timer service for the JBoss JMX agent.In our example, we have specified most of the MBean’s configuration factors through the constructor, but, typically, attributes are better for initializing runtime attributes. This allows for a more flexible set of runtime configuration specifications, such as the specification of the optional NSClient password and port. Also remember that the start() method is not called until the MBean has been constructed, all of its attributes set, and all dependencies satisfied.In our example of the dynamic service, I relied mostly on the constructor for passing runtime configuration for some simplicity because special code must be added to dynamic MBean method implementations. This code can be added later, giving the class superior flexibility at the cost of some complexity.Now that we have looked at the XML deployment descriptor syntax, let’s look at how we use the XML to deploy the MBean.Static class and XML service fileThis deployment technique delegates the issue of the MBean class’s location to the JBoss classloader. We simply create our JBoss XML deployment descriptor and give it a name under the format: <whatever name>-service.xml.We can call our file nsclient-service.xml. Then we simply copy the file to the current JBoss server’s deploy directory. If we are running the default server, this directory might be c:jboss-4.0.2serverdefaultdeploy. If the JBoss server is running, the service will be hot-deployed. If not, when it starts, it will start the service at initialization time.Of course, deployment will fail if JBoss cannot classload the specified MBean. The simplest way to get our classes into the JBoss classpath is to copy the nsclient4j.jar file into the server’s lib directory. In our example above, this would be c:jboss-4.0.2serverdefaultdeploy. This is static deployment, and the server must be restarted for the library to get into the classpath. In the next example, we look at how to build a Service Archive (SAR) that will hot-deploy both the service and the nsclient4j.jar file.Listing 10 shows how to prepare and start the JBoss server for the first time.Listing 10. Configure and start the default JBoss server C:jboss-4.0.2bin>xcopy c:nsclient4j-rootnsclient4jdistnsclient4j.jar c:jboss-4.0.2serverdefaultlibC:nsclient4j-rootnsclient4jdistnsclient4j.jar 1 File(s) copiedC:jboss-4.0.2bin>run.bat... ... ... 12:41:28,178 INFO [Server] JBoss (MX MicroKernel) [4.0.2 (build: CVSTag=JBoss_4_0_2 date=200505022023)] Started in 46s:116ms That last line indicates that the server has completed its startup. Now let’s deploy our nsclient-service.xml. In another command window, we copy the nsclient-service.xml file to the running server’s deploy directory:Listing 11. Hot-deploy the service deployment descriptor C:jboss-4.0.2bin>xcopy C:nsclient4j-rootnsclient4jconfignsclient- service.xml c:jboss-4.0.2serverdefaultdeploy C:nsclient4j-rootnsclient4jconfignsclient-service.xml 1 File(s) copied If we look back in the JBoss window, we will see that the service has deployed and started.Listing 12: Output from deployment 12:46:37,914 INFO [Server] JBoss (MX MicroKernel) [4.0.2 (build: CVSTag=JBoss_4_0_2 date=200505022023)] Started in 56s:31ms 12:50:37,919 INFO [NSClient4JDynamicService] Instantiated org.nsclient4j.jmx.dynamic.NSClient4JDynamicService 12:50:37,939 INFO [NSClient4JDynamicService] ============================================ Starting NSClient4jService ============================================ 12:50:37,969 INFO [NSClient4JDynamicService] Connected to Host:localhost Up Since Sun Aug 28 11:18:30 EDT 2005 12:50:37,969 INFO [NSClient4JDynamicService] ============================================ Started NSClient4jService ============================================ You may see some additional error messages, but these will not affect our bean. In some cases, the container incorrectly assumes our methods should be supported by our MBeans, which results in some harmless exception reporting. I will take care of that in a future code release.The nsclient4j.jar does not necessarily need to deploy in the JBoss server’s lib directory. We could specify the location of the jar in a classpath element in the nsclient-service.xml file.Package and deploy the MBean using a SARJBoss also supports hot-deployment of a service along with its archive of classes required for deployment. This can be achieved using a SAR. This file, when packaged, has the same format as a jar file, but the JBoss deployer treats it differently. The deployer basically:Scans the SAR for any embedded jar files and, when it finds them, loads the contents using the current deployment’s classloaderLooks in the META-INF subdirectory for a jboss-service.xml file and deploys it identically to the nsclient-service.xml file we used in the prior exampleWe can use any jar-creating tool like the JDK’s JAR utility or an Ant task to create a sar file that contains our service deployment descriptor and the nsclient4j.jar. The sar file looks like this:Listing 13. The contents of the nsclient4j.sar file C:nsclient4j-rootnsclient4jdist>jar -tvf nsclient4j.sar 0 Mon Aug 22 16:50:34 EDT 2005 META-INF/ 319 Mon Aug 22 16:50:32 EDT 2005 META-INF/MANIFEST.MF 28394 Mon Aug 22 16:50:22 EDT 2005 nsclient4j.jar 1592 Mon Aug 22 14:10:04 EDT 2005 META-INF/jboss-service.xml Now let’s go back to our JBoss server. We do not need the nsclient4j.jar file in the lib, so we can remove it. Then we can copy the new nsclient4j.sar to the deploy directory and it will automatically deploy. The output on the JBoss console will be the same as the prior example’s.Also note that, while a fully packaged sar file is cleaner to move around, you can also create a directory in the JBoss deploy directory called nsclient4j.sar and copy the contents of the sar file into it. The JBoss deployer will treat it the same as a sar file, which may offer a more convenient way to tweak the service deployment descriptor after deployment. If the timestamp on the descriptor changes, the JBoss deployer will redeploy the service.So far in this article, I have reviewed the changes in the NSClient4j API and explained how we can implement JMX to extend the usefulness of the API. I have really just scratched the surface of JMX. For anyone interested in further JMX coding, the following sections cover JMX attributes and operations and how to schedule events using the JMX timer.A note on JMX operations and attributesThe visible properties of an MBean registered with a JMX agent are attributes and operations. Within the scope of standard MBeans, you can consider any exposed method that has no parameters, is a getter or setter, and complies with the JavaBeans property signatures to be an attribute. For example:Getter: public String getFoo()Setter: public void setFoo(String s)Attribute: FooAny method exposed through an MBean in JMX that is not an attribute is referred to as an operation. For example:public String getBar(int i)public void start()The JMX agent provides access to each registered MBean’s attributes and operations through the MBeanServer. While the larger functionality of the MBeanServer and JMX in general is outside this article’s scope, these two functions of the MBeanServer prove useful. As mentioned before, each registered MBean has a unique ObjectName.Getting an MBean’s attribute is as simple as passing the MBeanServer your MBean’s ObjectName and the attribute’s name. getAttribute(ObjectName name, String attribute) gets the value of a named MBean’s specific attribute.This example retrieves the value of the attribute Foo:Listing 14. Acquire an MBean’s attribute MBeanServer mbeanServer = (MBeanServer)MBeanServerFactory.findMBeanServer(null).iterator().next(); ObjectName myObjectName = new ObjectName("my.jmx.domain:name=MyMBean"); Object myAttrValue = mbeanServer.getAttribute(myObjectName, "Foo"); Invoking an MBean’s operation is slightly more complicated due to the allowance for parameters and function overloading. Accordingly, we must pass the MBeanServer not just the name of the operation, but also the values of the parameters and the types of the arguments in the target operation’s signature.An example that invokes the operation String generateHTMLReport(String a, String b, Integer i):Listing 15. Invoke an MBean’s operation MBeanServer mbeanServer = (MBeanServer)MBeanServerFactory.findMBeanServer(null).iterator().next(); ObjectName myObjectName = new ObjectName("my.jmx.domain:name=MyMBean"); String[] argTypes = new String[]{"java.lang.String", "java.lang.String", "java.lang.Integer"}; Object[] args = new Object[]{"Logins", "Login Time", new Integer(5)}; String report = (String)mbeanServer.invoke(myObjectName, "generateHTMLReport", args, argTypes); Scheduling eventsThe JMX specification defines a set of services that are mandatory in any implementation of a JMX agent. Among those is a timer service, which allows MBeans to register for notifications on a specified date, time, or interval. While the JMX timer service may not define a timing functionality significantly more sophisticated than that of java.util.Timer, it does provide additional metadata and management operations. Moreover, it provides a standardized and central scheduling facility that avoids over-spawning of timing threads from individual MBeans implementing their own timing mechanisms. Other implementations may also offer more advanced features such as timer persistence.The standard implementation of the JMX timing service is javax.management.timer.Timer.For a given MBean to receive scheduled callbacks, there is a three step process:Issue a request (invocation) against the timer service for a scheduled notification, which will be emitted across the entire JMX agent with a unique name (type)Register the MBean with the MBeanServer to be a NotificationListener for the type of notifications just scheduledHandle the notifications when received from the MBeanServerListing 16 briefly illustrates this process. This code is assumed to be in a class that implements the NotificationListener interface.Listing 16. Schedule a notification ObjectName timerMBean = new ObjectName(timerName); Integer timerId = (Integer) server.invoke(timerMBean,"addNotification",new Object[] {"nsclient4j.poll.schedule." +objectName.toString(), "run",null,new java.util.Date(),new Long(frequency) }, new String[] {"java.lang.String","java.lang.String","java.lang.Object", "java.util.Date","long" });NotificationFilterSupport filter = new NotificationFilterSupport(); filter.enableType("nsclient4j.poll.schedule." + objectName.toString()); mbeanserver.addNotificationListener(timerMBean, this,filter,null); ... ...// Callbacks come here public void handleNotification(Notification notification, Object handback) { run(); } ConclusionAfter reading this article, you should have a better understanding of how NSClient4j can be used in applied practice and how to implement JMX services. I only scratched the surface of JMX, and much of the uncovered material may be useful to you. See the Resources to study additional features of JMX.Nicholas Whitehead is a Java architect in New Jersey. JavaSoftware DevelopmentTechnology IndustrySmall and Medium Business