Dynamic MBean Loading Service





Dynamic MBean Loading Service

One of Java's great strengths as a software platform is its ability to dynamically load new classes. The ability to load secure, platform-independent code over the network at both the client (applets) and the server (servlets) has played a major role in the Java success story. It should come as no surprise, then, that JMX takes advantage of this same capability in the management domain; nor should it be hard to guess what the mechanism is called: MLets.

1 The MLET Tag

The MLet service is responsible for dynamically loading new MBeans into the MBeanServer. How does it know which MBeans to load and where to load them from? It reads one or more MLET tags from an MLet file. An MLet file is just a text file that contains a set of MLET tags. MLET tags are like the APPLET tags found in HTML documents that cause a browser to load Java applets. Here's an MLET tag that will cause the MLet service to load a SessionPool MBean:

sessionpool.mlet 
<MLET CLASS=net.jnjmx.ch3.todd.SessionPool

      ARCHIVE="todd.jar"
      CODEBASE=http://www.jnjmx.net/
      NAME=book:id=SessionPool>
              <ARG TYPE=int VALUE=16>
</MLET>

The interaction diagram in Figure illustrates the process that the MLet service uses to go from the MLET tag in sessionpool.mlet to having a SessionPool MBean loaded in the MBeanServer.

Figure. Processing an MLET Tag

graphics/07fig02.gif

The MLet service reads the MLET tag from sessionpool.mlet and then loads the net.jnjmx.ch3.todd.SessionPool class from http://www.jmx.net/todd.jar. Once the class has been loaded, the MLet service creates an instance of the class and registers it with the MBeanServer under the name book:id=SessionPool.

There are a couple of things worth noting even about this simple example. The first is that the SessionPool MBean class file lives on a Web server, not necessarily on the machine where the MBeanServer is running. This arrangement markedly simplifies software installation and upgrade; rather than installing .jar files, we just install appropriately configured .mlet files and use the MLet service to fetch the class files on demand. For upgrades, new .jar files are dropped on the Web server and the applications that use them are restarted; the application need not be restarted immediately because the applications will continue to run using the old class files. When they are eventually restarted, they will load the new ones. This allows management systems to "push down" manageability automatically, where it's needed, when it's needed.

The second thing to note is that the sessionpool.mlet file also does not have to be on the MBeanServer's machine. It can be read via a URL and therefore could also be remote. That means that an administrator could use a single .mlet file to bootstrap and configure a whole set of MBeanServers, as illustrated in Figure.

3. Centralized Configuration of Multiple MBeanServers Using the MLet Service

graphics/07fig03.gif

Finally, note that we can generalize this capability. As a management application discovers resources that it knows how to manage—a new device, a database instance, or a Web server, for example—it can dynamically load and configure the necessary MBeans. This approach minimizes the management application's size; it loads only the MBeans it needs, without constraining the range of resources the application can handle. This approach also allows resources that don't provide their own MBeans to be managed by management applications.

2 MLet Examples

In Chapter 6 we used the Apache MBean to explore the use of JMX monitors. The sample code for the ApacheMonitor application presented in that chapter used createMBean() to instantiate Apache MBean. In this section we will modify ApacheMonitor to use the MLet service to create instances of Apache MBean.

There are two ways to use the MLet service to create MBean instances:

  1. Directly, to create MBeans specified by a set of MLET tags

  2. Indirectly, by specifying a loader parameter in a call to createMBean()

The first approach moves the configuration of the management application's MBeanServer out of the application. To add some more MBeans to the management server, all an administrator has to do is change a text file on a Web server; no Java is required. The second approach allows developers to take advantage of Java's ability to load code dynamically from remote sources.

Let's examine the first approach first. Rather than calling createMBean(), the ApacheMonitor application will now use the MLet service's getMBeansFromURL() method. Here is the updated version of ApacheMonitor:

package net.jnjmx.ch7; 

import javax.management.*;
import javax.management.monitor.*;

public ApacheMonitor {
  public static class StateNotificationListener implements 
        NotificationListener {
    public void handleNotification(Notification notification, 
        Object handback) {
      // Take some action when a notification is received from
      // the State attribute monitor
    }

  public static void main(String[] args) throws Exception {
    if (args.length < 1) {
      System.err.println( 
	    "Usage: java net.jnjmx.ch7.ApacheMonitor <mlet url>");
      System.exit(-1);
    }

    // Create the MBean to be monitored
    MBeanServer mbs = MBeanServerFactory.createMBeanServer();
    ObjectName loader = new ObjectName("loaders:name=Apache");
    mbs.createMBean("javax.management.loading.MLet", loader);

    // Load Apache MBean from the MLet file specified on the
    // command line
    mbs.invoke(loader,
               "getMBeansFromURL",
               new Object[] {args[0] },
               new String[] {"java.lang.String" });

    // remainder of the ApacheMonitor application: set up
    // monitors, notification listeners, etc.
    ...
  }
...
}

The first difference is the creation of the MLet service. Once we have an MLet instance in the MBeanServer, we can invoke its methods. That's the second difference; we call the MLet service's getMBeansFromURL() method via the MBeanServer and pass in a String object representing the URL of the .mlet file. That's all there is to it. The getMBeansFromURL() method will read the .mlet file, interpret the MLET tags it contains, and create the specified MBeans. To complete the picture, here is an .mlet file that specifies an Apache MBean:

apache.mlet 
<MLET CLASS=net.jnjmx.ch3.Apache
      ARCHIVE="apache.jar"
      CODEBASE=http://www.jnjmx.net/jars
      NAME=resources:type=Apache,url=www.jnjmx.net>
<ARG TYPE=String VALUE=www.jnjmx.net>
</MLET>

Now for the createMBean() alternative. Recall from Chapter 5 that two of the MBeanServer's four createMBean() methods take loader parameters. It turns out that a loader is the object name of an MLet service instance, and createMBean() uses that MLet service to load the desired MBean's class rather than using the system ClassLoader class. Because MLet extends URLClassLoader, everything works out rather nicely. Here is a version of ApacheMonitor that uses this technique:

package net.jnjmx.ch7; 

import javax.management.*;
import javax.management.monitor.*;

public ApacheMonitor {
  public static class StateNotificationListener implements 
	  NotificationListener {
    public void handleNotification(Notification notification, 
        Object handback) {
      // Take some action when a notification is received from
      // the State attribute monitor
    }

  public static void main(String[] args) throws Exception {
    // Create the MBean to be monitored
    MBeanServer mbs = MBeanServerFactory.createMBeanServer();
    ObjectName loader = new ObjectName("loaders:name=Apache");
    mbs.createMBean("javax.management.loading.MLet", loader);

    // Add the URL for the ApacheMBean classes to the MLet's
    // search path
    mbs.invoke(loader,
          "addURL",
          new Object[] {"http://www.jnjmx.net/jars/apache.jar" },
          new String[] {"java.lang.String" });

    ObjectName server = new ObjectName(
	  "resources.http:type=Apache,url=www.jnjmx.net");
    mbs.createMBean("net.jmx.ch3.Apache",
                    server,
                    loader,
                    new Object[] {"www.jnjmx.net" },
                    new String[] {"java.lang.String" });

    // remainder of the ApacheMonitor application: set up
    // monitors, notification listeners, etc.
    ...
  }
...
}

After creating the MLet instance, we invoke its addURL() method. Every MLet instance maintains an array of URLs that it will search for classes and resources during the loading process; addURL() adds an element to that array and thereby extends the search path. The invocation here adds the .jar file that contains the Apache MBean class file on the www.jnjmx.net server. Finally, we call createMBean() as before, except we include the object name of the MLet service that we just configured. Now createMBean() will use the MLet instance to load the Apache MBean class.

3 The MLet API

The MLet class implements the management interface defined by the MLetMBean interface. MLet also extends the URLClassLoader class and so is a full-fledged class loader. That is a useful fact to keep in mind if your application happens to have direct access to an MLet object reference. As usual, however, management applications that access an MLet via the MBeanServer interface may use only the attributes and operations defined by the MLetMBean interface.

3.1 MLet Constructors

There are four MLet constructors to choose from:

public MLet() 
public MLet(URL[] urls)
public MLet(URL[] urls, ClassLoader parent)
public MLet(URL[] urls, ClassLoader parent, 
    URLStreamHandlerFactory factory)

In each of the constructors the urls parameter specifies a set of URLs to search for classes; think of it as a classpath. So what good is the first, no-args, constructor? It turns out that because the MLetMBean interface defines addURL() methods that add URLs to the search path, even the no-args version provides some value.

The third and fourth versions allow us to specify an alternative delegation parent class loader. The final version allows us to include an instance of URLStreamHandlerFactory. If the array of URLs includes URLs for nonstandard protocols (e.g., a URL beginning with https), the MLet instance uses the specified URLStreamHandlerFactory to deal with those URLs.

What's an alternative delegation parent class loader, and why would you want to specify one? Recall that beginning in Java 2—that is, version 1.2—the set of class loaders in a JVM is arranged in a tree rooted at the "bootstrap," or "primordial," class loader. When asked to load a class, a class loader first delegates the task to its parent, which recursively delegates to its parent, and so on, until the process reaches the root of the tree. Only if the parent fails to load the class does the original class loader attempt to load it. By default, an MLet instance's parent class loader is the one that loaded the MLet class itself. Often the default is fine. There are, however, occasions when an application wants a different parent class loader—for example, if it wants to bypass the system class loader and load only classes from its own search path. On those occasions the application will use one of the MLet constructors that takes a ClassLoader parameter.

3.2 MLetBean Attributes

The MLetMBean interface defines two attributes, as shown in Figure.

3.3 MLet Operations

The MLet operations break down into three categories: search path extension, MBean loading, and resource access. As mentioned in Section 7.2.3.1, two methods are provided to add additional URLs to MLet's search path:

Figure MLetBean Attributes

Attribute

Type

Description

URLs

Read-only

A URL array–valued attribute that contains the current search path

LibraryDirectory

Read/write

A String-valued attribute that indicates where any native libraries required by classes that the MLet loads should be placed prior to loading those libraries

public void addURL(String url) 
public void addURL(URL url)

The responsibility for reading an .mlet file, loading the MBeans specified by the MLET tags it contains, and registering them with the MBeanServer falls to the getMBeansFromURL() methods:

public Set getMBeansFromURL(String url) 
public Set getMBeansFromURL(URL url)

The url parameter has nothing to do with the MLet instance's search path. Instead it tells the MLet where to find the .mlet file. The return value is a set of object instances, one for each of the MBeans that the call caused to be registered.

Finally, three operations provide access to resources found in MLet's search path:

public Enumeration getResources(String name) 
public URL getResource(String name)
public InputStream getResourceAsStream(String name)

The first two operations return URLs for the named resource, not the resource itself. The getResources() method returns an Enumeration of all the URLs for the specified resource. The getResource() method returns a single URL to the specified String resource. Unlike the getResource() methods, getResourceAsStream() returns an InputStream instance from which the contents of the named resource may be read.


     Python   SQL   Java   php   Perl 
     game development   web development   internet   *nix   graphics   hardware 
     telecommunications   C++ 
     Flash   Active Directory   Windows