Managing components with Modeler

Easily configure JMX with the Jakarta Commons Modeler component

Imagine that you just built the next killer Java application, and it's selling like hotcakes. However, even though (being a good developer), you built in enough debug and tracing information to monitor the state of your live application units, you're missing the ability to monitor each and every class and their attributes. Although you could build monitoring services as part of your application, it takes the focus away from your core business, the application itself. It would be nice if you could monitor your application for the state of its classes, attributes, and operations. Java Management Extensions (JMX) lets you do just that. However, this article isn't about JMX, it's about how to easily configure JMX using the Jakarta Commons Modeler component. Specifically, it's about how to use the Modeler component to create model MBeans, which are used to monitor resources in your application.

Say hello to Modeler

The Modeler component was created from source code taken from Tomcat 4 development. Recognizing that creating metadata information about managed resources in code is a tedious process, the Tomcat developers extracted this information from code to an external XML-based file. They further realized that this extraction could easily be made useful for other managed components—not only server-based projects like Tomcat but any place that needed to use model MBeans. Therefore, they created the Modeler package to make this service available across the board.

As an application developer, it's nice to know at a glance the services that Modeler provides. The following list shows these services:

  • It provides a service for reading and parsing XML-based configuration information for model MBeans.
  • It provides a central registry for storing this configuration information, which also forms the basis for easily creating ModelMBeanInfo objects from this information.
  • It provides an alternate to the RequiredModelMBean object, which is a default implementation of the ModelMBean interface in JMX. This alternate is called BaseModelMBean. However, this alternate only supports management of objects that are of type objectReference (as opposed to RequiredModelMBean, which supports other types such as EJBHandle and RMIReference).
  • It provides a Java Naming and Directory Interface (JNDI) context for storing information about MBeans. This is, in effect, an alternate view of the registry and provides a logging mechanism for changes made to managed beans.
  • It provides a set of custom Ant tasks, which allow the creation of MBeans and reading of descriptor files from within an Ant build script.

Let's see these services in action with the help of some examples. We'll start with the transfer of ModelMBeanInfo information from code to XML file.

Creating metadata in XML

Modeler makes it easy to manage application components by combining the flexibility of model MBeans with the ease of writing XML configuration information about modeled components. The information about modeled components, which we also call metadata, describes these components for the model MBeans that are registered in the MBeanServer. This allows management of these components via JMX. Information from JMX agents passes to the model MBeans and on to the actual managed components through the mapping provided by this metadata information. This information can get very complex if done in code.

Before we discuss how to create this information via an external file, let's look at the registry provided by Modeler. The registry is the central component of Modeler, and it manages interaction with the MBeanServer for management components.

The central registry

A registry is a central place for managing components in Modeler. It's used to load, register, and manage MBeans. Its primary function is to load metadata information from an XML file, which by default is called mbeans-descriptors.xml, and then transfer this information from the XML syntax into ModelMBeanInfo for the model MBeans and application components that it will be mapped to. The registry relies on classes in the org.apache.commons.modeler.modules package for reading this information.

To use the registry, you use the factory method getRegistry(Object key, Object guard). It creates a default registry if one doesn't exist for the key that you specify. This implies that there can be several registries in Modeler, differentiated by a key. This is true, and it gives you greater flexibility in using the same environment for separating multiple application components based on this key. However, before you can use this feature, you must enable it by calling the static method setUseContextClassLoader(boolean enable) and passing in a true value for the enable parameter. Doing so creates a HashMap for storing registries based on keys. If you haven't enabled multiple registries and wish to use the single default registry, you need to pass a value of null to the key parameter of the getRegistry() method.

The second parameter for the getRegistry() method accepts an object called guard and is used to prevent unauthorized access to the registry. If you want to restrict access to a registry of your application components, use the following procedure:

  1. Create the registry for the first time by calling Registry registry = getRegistry(null, null) or Registry registry = getRegistry(yourKey, null), as the case may be.
  2. Decide on an object that you wish to become the guard for your registry. It can be as simple as a String passphrase, or a more complex object. For the purposes of this example, let's use the String passphrase "Jupiter."
  3. Set this passphrase to be the guard for your registry by calling registry.setGuard("Jupiter");.
  4. The next time you want to access your registry, you'll need to use getRegistry(null, "Jupiter") or getRegistry(yourKey, "Jupiter").
  5. Any component trying to call your registry without supplying the correct value of the guard will get a null value.

Once a registry has been created, it can be used to load metadata information and associate it with model MBeans. Let's look at how this is done in the next section.

Loading and registering metadata

The registry provides several methods for loading the information from the mbeans-descriptors file. The default, and the most convenient method, is to read the data from an XML file. As we said before, the default XML file is called mbeans-descriptors.xml. You can load this file either as a URL object, as a File object, or as an InputStream, and then call loadMetadata(Object source) to pass any of these three objects in as a parameter. The method figures out the type of the object and tries to retrieve information from it accordingly.

It isn't necessary that this file be an XML file (or that it be called mbeans-descriptors). You can use serialized versions of your metadata information, but the file loaded via a URL, File, or InputStream must end with a .ser extension.

Listing 1 shows an XML file that contains the metadata information for our TimerApplication example. It contains only basic information about the attributes delay and message.

Listing 1. XML descriptor for TimerApplication

 

<?xml version="1.0"?> <!DOCTYPE mbeans-descriptors PUBLIC "-//Apache Software Foundation//DTD Model MBeans Configuration File" "http://jakarta.apache.org/commons/dtds/mbeans-descriptors.dtd">

<mbeans-descriptors>

<mbean name="TimerApplication"description="Prints Time and message after a delay" type="com.manning.commons.chapter11.TimerApplicationOriginal">

<attribute name="delay"description="Delay in Milli Seconds"type="int"/> <attribute name="message"description="Message to Print"type="java.lang.String"/>

</mbean>

</mbeans-descriptors>

The root element, <mbeans-descriptor>, clearly states that this is a file containing MBeans information. Next, information about individual MBeans is encapsulated in individual <mbean> elements. The only MBean defined in the above XML file is TimerApplication, and it contains information about it in three attributes.

The allowed attributes for the <mbean> element are listed here:

  • name: Class name of the MBean, without package information, or a unique name for it within the MBeanServer.
  • description: Human-readable description of the MBean.
  • type: Fully qualified type of the Mbean-managed component.
  • domain: Domain in which this MBean should be registered in the MBeanServer.
  • group: Name of the group this MBean belongs to.
  • className: Fully qualified class name of the model MBean implementation to use. The default is RequiredModelMBean, supplied by JMX.

Note that none of the attributes are essential. This might seem strange, but it's plausible because Modeler uses reflection on the managed component to try to figure out the best values for the attributes, if they aren't provided in the XML descriptor file.

We define two attribute elements, which correspond to the delay and message attributes of TimerApplication. Each attribute element contains three attributes, like the attributes for the mbean element, giving more information about each attribute. However, you can define several more attributes for the attribute element, as follows:

  • name: Name of the property in your application component.
  • description: Human-readable description of the attribute.
  • type: Fully qualified type of the attribute. Primitive types can be used as is; Modeler converts them into corresponding wrapper classes.
  • displayName: Name to use for this attribute if you want it to be different from the name attribute.
  • getMethod: Name of the method used to retrieve the value of this property. However, it's used only if the property doesn't follow JavaBeans get/set conventions.
  • setMethod: Name of the method used to set the value of this property. Again, use it only if the property doesn't follow JavaBeans get/set conventions.
  • is: Specifies whether this property represents a Boolean value and, if it does, whether it uses isXXX as the method for getting the value of this property. You can use this attribute and indicate a true condition by setting it to true or yes.
  • readable: Indicates that this property is readable by JMX agents if set to true or yes.
  • writable: Indicates that this property is writable by JMX Agent agents if set to true or yes.

The two element attributes <mbean> and <attribute> and the surrounding <mbean> element information are sufficient to describe our TimerApplication. Now we need to know how to register this information with the Modeler registry.

In Listing 2, we'll use the Modeler registry to load the information for the TimerApplication from an XML file.

Before you run this code, make sure that you have commons-modeler.jar and commons-logging.jar libraries in your CLASSPATH in addition to jmxri.jar and jmxtools.jar.

Listing 2. Using Modeler to build ModelMBeanInfo for TimerApplication

 

package com.manning.commons.chapter11;

import java.net.URL;

import javax.management.ObjectName;

import org.apache.commons.modeler.Registry;

import com.sun.jdmk.comm.HtmlAdaptorServer;

public class TimerApplicationModelerAgent {

public TimerApplicationModelerAgent() { URL url=this.getClass().getResource("/timer_app_11.6.xml");

HtmlAdaptorServer adaptor = new HtmlAdaptorServer(); TimerApplicationOriginal timerApp = new TimerApplicationOriginal(15000, "The time is now: ");

ObjectName adaptorName = null; ObjectName timerAppName = null;

try {

Registry registry = Registry.getRegistry(null, null); registry.loadMetadata(url);

timerAppName = new ObjectName("TimerDomain:name=timerApp");

registry.registerComponent(timerApp,timerAppName, "com.manning.commons.chapter11.TimerApplicationOriginal");

adaptorName = new ObjectName("TimerDomain:name=adaptor, port=8082"); adaptor.setPort(8082);

registry.registerComponent(adaptor,adaptorName, "com.sun.jdmk.comm.HtmlAdaptorServer");

adaptor.start(); } catch(Exception e) { System.err.println(e); } }

public static void main(String[] args) { TimerApplicationModelerAgent agent =new TimerApplicationModelerAgent(); } }

Using Modeler transforms the code required to create ModelMBeanInfo dramatically. Once the registry has been created and loaded with basic metadata information about application components (using information from a file we've called timer_app_11.6.xml instead of the default mbeans-descriptors.xml), it's a breeze to register components on it by calling the registerComponent method. Note that we also tried to register the HtmlAdaptorServer component with the registry. This doesn't work completely because the timer_app_11.6.xml doesn't contain metadata information about the HtmlAdaptorServer. At runtime, Modeler tries its best by guessing information about the component using reflection; you only get a list of attributes and other reflexive operations when you look at its details from the browser by navigating to localhost:8082. Information about the current state of the attributes is unavailable because Modeler doesn't know how to extract it.

1 2 3 Page 1
Page 1 of 3