Introducing MBeans

«« Previous
Next »»

This lesson introduces the fundamental concept of the JMX API, namely managed beans, or MBeans.

An MBean is a managed Java object, similar to a JavaBeans component, that follows the design patterns set forth in the JMX specification. An MBean can represent a device, an application, or any resource that needs to be managed. MBeans expose a management interface that consists of the following:

◈ A set of readable or writable attributes, or both.
◈ A set of invokable operations.
◈ A self-description.

The management interface does not change throughout the life of an MBean instance. MBeans can also emit notifications when certain predefined events occur.

The JMX specification defines five types of MBean:

◈ Standard MBeans
◈ Dynamic MBeans
◈ Open MBeans
◈ Model MBeans
◈ MXBeans

The examples in this trail demonstrate only the simplest types of MBean, namely standard MBeans and MXBeans.

Standard MBeans


This section presents an example of a straightforward, standard MBean.

A standard MBean is defined by writing a Java interface called SomethingMBean and a Java class called Something that implements that interface. Every method in the interface defines either an attribute or an operation in the MBean. By default, every method defines an operation. Attributes and operations are methods that follow certain design patterns. A standard MBean is composed of an MBean interface and a class. The MBean interface lists the methods for all exposed attributes and operations. The class implements this interface and provides the functionality of the instrumented resource.

The following sections examine an example of a standard MBean and a simple JMX technology-enabled agent (JMX agent) that manages the MBean.

MBean Interface

An example of a basic MBean interface, HelloMBean , follows:


package com.example;

public interface HelloMBean {

    public void sayHello();
    public int add(int x, int y);
 
    public String getName();
   
    public int getCacheSize();
    public void setCacheSize(int size);
}

By convention, an MBean interface takes the name of the Java class that implements it, with the suffix MBean added. In this case, the interface is called HelloMBean. The Hello class that implements this interface is described in the next section.

According to the JMX specification, an MBean interface consists of named and typed attributes that are readable and possibly writable, in addition to the named and typed operations that can be invoked by the applications that are managed by the MBean. The HelloMBean interface declares two operations: the Java methods add() and sayHello().

HelloMBean declares two attributes: Name is a read-only string, and CacheSize is an integer that can be both read and written. Getter and setter methods are declared to allow the managed application to access and possibly change the attribute values. As defined by the JMX specification, a getter is any public method that does not return void and whose name begins with get. A getter enables a manager to read the value of the attribute, whose type is that of the returned object. A setter is any public method that takes a single parameter and whose name begins with set. A setter enables a manager to write a new value in the attribute, whose type is the same as that of the parameter.

The implementation of these operations and attributes is shown in the following section.

MBean Implementation

The Hello Java class that follows implements the HelloMBean MBean interface:

package com.example;

public class Hello ...
    implements HelloMBean {
    public void sayHello() {
        System.out.println("hello, world");
    }
   
    public int add(int x, int y) {
        return x + y;
    }
   
    public String getName() {
        return this.name;
    }
   
    public int getCacheSize() {
        return this.cacheSize;
    }
   
    public synchronized void setCacheSize(int size) {
        ...
 
        this.cacheSize = size;
        System.out.println("Cache size now " + this.cacheSize);
    }
    ...
   
    private final String name = "Reginald";
    private int cacheSize = DEFAULT_CACHE_SIZE;
    private static final int
        DEFAULT_CACHE_SIZE = 200;
}

The straightforward Hello class provides the definitions of the operations and attributes that are declared by HelloMBean. The sayHello() and add() operations are extremely simple, but real-life operations can be as simple or as sophisticated as needed.

The methods to get the Name attribute and to get and set the CacheSize attribute are also defined. In this example, the Name attribute value never changes. However, in a real scenario this attribute might change as the managed resource runs. For example, the attribute might represent statistics such as uptime or memory usage. Here, the attribute is merely the name Reginald.

Calling the setCacheSize method enables you to alter the CacheSize attribute from its declared default value of 200. In a real scenario, changing the CacheSize attribute could require other operations to be performed, such as discarding entries or allocating new entries. This example merely prints a message to confirm that the cache size has changed. However, more sophisticated operations could be defined instead of the simple call to println().

With the Hello MBean and its interface thus defined, they can now be used to manage the resource they represent, as shown in the following section.

Creating a JMX Agent to Manage a Resource

Once a resource has been instrumented by MBeans, the management of that resource is performed by a JMX agent.

The core component of a JMX agent is the MBean server. An MBean server is a managed object server in which MBeans are registered. A JMX agent also includes a set of services to manage MBeans.

The Main class that follows represents a basic JMX agent:

package com.example;

import java.lang.management.*;
import javax.management.*;

public class Main {

    public static void main(String[] args)
        throws Exception {
   
        MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
        ObjectName name = new ObjectName("com.example:type=Hello");
        Hello mbean = new Hello();
        mbs.registerMBean(mbean, name);
       
        ...
   
        System.out.println("Waiting forever...");
        Thread.sleep(Long.MAX_VALUE);
    }
}

The JMX agent Main begins by obtaining an MBean server that has been created and initialized by the platform, by calling the getPlatformMBeanServer() method of the java.lang.management.ManagementFactory class. If no MBean server has been created by the platform already, then getPlatformMBeanServer() creates an MBean server automatically by calling the JMX method MBeanServerFactory.createMBeanServer(). The MBeanServer instance obtained by Main is named mbs.

Next, Main defines an object name for the MBean instance that it will create. Every JMX MBean must have an object name. The object name is an instance of the JMX class ObjectName and must conform to the syntax defined by the JMX specification. Namely, the object name must contain a domain and a list of key-properties. In the object name defined by Main, the domain is com.example (the package in which the example MBean is contained). In addition, the key-property declares that this object is of the type Hello.

An instance of a Hello object, named mbean, is created. The Hello object named mbean is then registered as an MBean in the MBean server mbs with the object name name, by passing the object and the object name into a call to the JMX method MBeanServer.registerMBean().

With the Hello MBean registered in the MBean server, Main simply waits for management operations to be performed on Hello. In this example, these management operations are invoking sayHello() and add(), and getting and setting the attribute values.

Running the Standard MBean Example

Having examined the example classes, you can now run the example. In this example, JConsole is used to interact with the MBean.

To run the example, follow these steps:

1. Save the bundle of JMX API sample classes, jmx_examples.zip, to your working directory, work_dir.

2. Unzip the bundle of sample classes by using the following command in a terminal window.

unzip jmx_examples.zip

3. Compile the example Java classes from within the work_dir directory.

javac com/example/*.java

4. If you are running the Java Development Kit (JDK) version 6, start the Main application with the following command.

java com.example.Main

If you are running a JDK version that is older than version 6, you will need to start the Main application with the following option specified, to expose the application for monitoring and management.

java -Dcom.sun.management.jmxremote example.Main

A confirmation that Main is waiting for something to happen is displayed.

5. Start JConsole in a different terminal window on the same machine.

jconsole

The New Connection dialog box is displayed, presenting a list of running JMX agents that you can connect to.

6. In the New Connection dialog box, select com.example.Main from the list and click Connect.

A summary of your platform's current activity is displayed.

7. Click the MBeans tab.

This panel shows all the MBeans that are currently registered in the MBean server.

8. In the left frame, expand the com.example node in the MBean tree.

You see the example MBean Hello that was created and registered by Main. If you click Hello, you see its associated Attributes and Operations nodes in the MBean tree.

9. Expand the Attributes node of the Hello MBean in the MBean tree.

The MBean attributes that were defined by the Hello class are displayed.

10. Change the value of the CacheSize attribute to 150.

In the terminal window in which you started Main, a confirmation of this attribute change is generated.

11. Expand the Operations node of the Hello MBean in the MBean tree.

The two operations declared by the Hello MBean, sayHello() and add(), are visible.

12. Invoke the sayHello() operation by clicking the sayHello button.

A JConsole dialog box informs you that the method was invoked successfully. The message "hello, world" is generated in the terminal window in which Main is running.

13. Provide two integers for the add() operation to add and click the add button.

The answer is displayed in a JConsole dialog box.

14. To close JConsole, select Connection -> Exit.

MXBeans


This section explains a special type of MBean, called MXBeans.

An MXBean is a type of MBean that references only a predefined set of data types. In this way, you can be sure that your MBean will be usable by any client, including remote clients, without any requirement that the client have access to model-specific classes representing the types of your MBeans. MXBeans provide a convenient way to bundle related values together, without requiring clients to be specially configured to handle the bundles.

In the same way as for standard MBeans, an MXBean is defined by writing a Java interface called SomethingMXBean and a Java class that implements that interface. However, unlike standard MBeans, MXBeans do not require the Java class to be called Something. Every method in the interface defines either an attribute or an operation in the MXBean. The annotation @MXBean can be also used to annotate the Java interface, instead of requiring the interface's name to be followed by the MXBean suffix.

MXBeans existed in the Java 2 Platform, Standard Edition (J2SE) 5.0 software, in the package java.lang.management. However, users can now define their own MXBeans, in addition to the standard set that is defined in java.lang.management.

The main idea behind MXBeans is that types such as java.lang.management.MemoryUsage that are referenced in the MXBean interface, java.lang.management.MemoryMXBean in this case, are mapped into a standard set of types, the so-called Open Types that are defined in the package javax.management.openmbean. The exact mapping rules appear in the MXBean specification. However, the general principle is for simple types such as int or String to remain unchanged, while complex types such as MemoryUsage get mapped to the standard type CompositeDataSupport.

The MXBean example consists of the following files, which are found in jmx_examples.zip:

◈ QueueSamplerMXBean interface
◈ QueueSampler class that implements the MXBean interface
◈ QueueSample Java type returned by the getQueueSample() method in the MXBean interface
◈ Main, the program that sets up and runs the example

The MXBean example uses these classes to perform the following actions:

◈ Defines a simple MXBean that manages a resource of type Queue<String>
◈ Declares a getter, getQueueSample, in the MXBean that takes a snapshot of the queue when invoked and returns a Java class QueueSample that bundles the following values together:
    ◈ The time the snapshot was taken
    ◈ The queue size
    ◈ The head of the queue at that given time
◈ Registers the MXBean in an MBean server

MXBean Interface

The following code shows the example QueueSamplerMXBean MXBean interface:

package com.example;

public interface QueueSamplerMXBean {
    public QueueSample getQueueSample();
    public void clearQueue();
}

Note that you declare an MXBean interface in exactly the same way as you declare a standard MBean interface. The QueueSamplerMXBean interface declares a getter, getQueueSample and an operation, clearQueue.

Defining MXBean Operations

The MXBean operations are declared in the QueueSampler example class, as follows:

package com.example;

import java.util.Date;
import java.util.Queue;

public class QueueSampler
                implements QueueSamplerMXBean {
   
    private Queue<String> queue;
       
    public QueueSampler (Queue<String> queue) {
        this.queue = queue;
    }
       
    public QueueSample getQueueSample() {
        synchronized (queue) {
            return new QueueSample(new Date(),
                           queue.size(), queue.peek());
        }
    }
       
    public void clearQueue() {
        synchronized (queue) {
            queue.clear();
        }
    }
}

QueueSampler defines the getQueueSample() getter and clearQueue() operation that were declared by the MXBean interface. The getQueueSample() operation returns an instance of the QueueSample Java type which was created with the values returned by the java.util.Queue methods peek() and size(), and an instance of java.util.Date.

Defining the Java Type Returned by the MXBean Interface

The QueueSample instance returned by QueueSampler is defined in the QueueSample class, as follows:

package com.example;

import java.beans.ConstructorProperties;
import java.util.Date;

public class QueueSample {
   
    private final Date date;
    private final int size;
    private final String head;
       
    @ConstructorProperties({"date", "size", "head"})
    public QueueSample(Date date, int size,
                        String head) {
        this.date = date;
        this.size = size;
        this.head = head;
    }
       
    public Date getDate() {
        return date;
    }
       
    public int getSize() {
        return size;
    }
       
    public String getHead() {
        return head;
    }


In the QueueSample class, the MXBean framework calls all the getters in QueueSample to convert the given instance into a CompositeData instance and uses the @ConstructorProperties annotation to reconstruct a QueueSample instance from a CompositeData instance.

Creating and Registering the MXBean in the MBean Server

So far, the following have been defined: an MXBean interface and the class that implements it, as well as the Java type that is returned. Next, the MXBean must be created and registered in an MBean server. These actions are performed by the same Main example JMX agent that was used in the standard MBean example, but the relevant code was not shown in the Standard MBean lesson.

package com.example;

import java.lang.management.ManagementFactory;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;
import javax.management.MBeanServer;
import javax.management.ObjectName;

public class Main {

    public static void main(String[] args) throws Exception {
        MBeanServer mbs =
            ManagementFactory.getPlatformMBeanServer();
             
        ...
        ObjectName mxbeanName = new ObjectName("com.example:type=QueueSampler");
     
        Queue<String> queue = new ArrayBlockingQueue<String>(10);
        queue.add("Request-1");
        queue.add("Request-2");
        queue.add("Request-3");
        QueueSampler mxbean = new QueueSampler(queue);
     
        mbs.registerMBean(mxbean, mxbeanName);
               
        System.out.println("Waiting...");
        Thread.sleep(Long.MAX_VALUE);
    }
}


The Main class performs the following actions:

◈ Gets the platform MBean server.
◈ Creates an object name for the MXBean QueueSampler.
◈ Creates a Queue instance for the QueueSampler MXBean to process.
◈ Feeds the Queue instance to a newly created QueueSampler MXBean.
◈ Registers the MXBean in the MBean server in exactly the same way as a standard MBean.

Running the MXBean Example

The MXBean example uses classes from the jmx_examples.zip bundle that you used in the Standard MBeans section. This example requires version 6 of the Java SE platform. To run the MXBeans example follow these steps:

1. If you have not done so already, save jmx_examples.zip into your work_dir directory.

2. Unzip the bundle of sample classes by using the following command in a terminal window.

unzip jmx_examples.zip

3. Compile the example Java classes from within the work_dir directory.

javac com/example/*.java

4. Start the Main application. A confirmation that Main is waiting for something to happen is generated.

java com.example.Main

5. Start JConsole in a different terminal window on the same machine. The New Connection dialog box is displayed, presenting a list of running JMX agents that you can connect to.

jconsole

6. In the New Connection dialog box, select com.example.Main from the list and click Connect.

A summary of your platform's current activity is displayed.

7. Click the MBeans tab.

This panel shows all the MBeans that are currently registered in the MBean server.

8. In the left frame, expand the com.example node in the MBean tree.

You see the example MBean QueueSampler that was created and registered by Main. If you click QueueSampler, you see its associated Attributes and Operations nodes in the MBean tree.

9. Expand the Attributes node.

You see the QueueSample attribute appear in the right pane, with its value of javax.management.openmbean.CompositeDataSupport.

10. Double-click the CompositeDataSupport value.

You see the QueueSample values date, head, and size because the MXBean framework has converted the QueueSample instance into CompositeData. If you had defined QueueSampler as a standard MBean rather than as an MXBean, JConsole would not have found the QueueSample class because it would not be in its class path. If QueueSampler had been a standard MBean, you would have received a ClassNotFoundException message when retrieving the QueueSample attribute value. The fact that JConsole finds QueueSampler demonstrates the usefulness of using MXBeans when connecting to JMX agents through generic JMX clients such as JConsole.

11. Expand the Operations node.

A button to invoke the clearQueue operation is displayed.

12. Click the clearQueue button.

A confirmation that the method was invoked successfully is displayed.

13. Expand the Attributes node again, and double click on the CompositeDataSupport value.

The head and size values have been reset.

14. To close JConsole, select Connection -> Exit.

«« Previous
Next »»