Chapter 7. Instrumentation

Table of Contents

General MBean Coding Gotchas
Set up a JMX Dev Environment
Create a Standard MBean
Create a Dynamic MBean
Create a Model MBean
Create a Dynamic MBean by subclassing AbstractDynamicMBean
Wrappers for existing Java classes
Using AbstractDynamicMBean for MBeans that subclass something else

Definitely read the Introduction to MBeans chapter before you read this chapter.

I am giving instructions as if you are the JMX implementation in the 1.5 Java JDK. A Java developer shouldn't have any difficulty figuring out what classpaths should be with some other JMX implementation.

Instrumentation means implementation of MBeans themselves (as opposed to classes that use MBeans).

I don't detail implementation of either Model MBeans or Open MBeans. There are limited situations where Model MBeans are really called for. I don't cover them here because it is a difficult undertaking which you should avoid if you can. If I take it upon myself to simplify them with a template system or something in the future, I'll add a section about them here. Open MBeans are simply Dynamic MBeans that follow certain conventions, so, if you need to work with them, just learn how to make Dynamic MBeans here, then take some No-Doze and read the Open MBean section of the JMX Spec.

General MBean Coding Gotchas

I've had problems with is() method behavior with Sun's JMX implementation. They work as advertised with MX4J. (UPDATE: I have still not tested this with Java 1.5).

Jmx has lots of prohibitions and warnings against overloading. Anybody can code up a class where the base name alpha is used for a getter, setter, is, and multiple other methods, like

  1. getAlpha();

  2. getAlpha(arg1);

  3. getAlpha(arg1, arg2);

  4. setAlpha(anInt);

  5. setAlpha(aString);

  6. setAlpha(arg1, arg2);

  7. isAlpha();

  8. alpha();

  9. alpha(aString);

  10. alpha(anInt);

  11. alpha(arg1, arg2);

That's fine as long as you don't want to publish them with JMX. To publish with JMX, for each base name (alpha in this case), you are allowed only

  1. One getter with form getAlpha() OR one getter with form boolean isAlpha() OR one operation of any form.

  2. One setter with one argument (i.e. setAlpha(x)).

Some of these rules are enforced and some are not. To minimize changes of obsolescence, follow all of these rules.

Set up a JMX Dev Environment

Read the Required Software chapter if you haven't already.

If you are just learning to create MBeans, then you will need an Agent server to test them with. Download admcjmx-adaptors.jar from http://admc.com/dist/admcjmx-adaptors.jar. This has my generic JMX Adaptors, including the command-line Adaptor that we're going to use now.

    export CLASSPATH
    CLASSPATH=".:/path/to/admcjmx-adaptors.jar"
    java com.admc.jmx.JmxStreamAdaptor
                

from a Bourne-compatible shell (which includes "bash").

Enter the command "list" to see a list of the MBean names for all the registered MBeans. (I talk about MBean name queries in the JMX Client Implementation chapter. Since you haven't registered any MBeans yet, all of the MBeans listed are part of the Jmx Implementation itself or the Adaptor. Leave this Adaptor running and use another window to get another shell in the same directory. (We're going to code up some MBean classes in the other window, then use this window to load and inspect them).

Create a Standard MBean

Standard MBeans publish all of their public constructors automatically. You publish attribute and operation methods by putting them into your MBean Interface file. JMX Clients that look at your MBean class will see the same public constructors whether they use normal Java introspection or use JMX interrogation facilities for an existing MBean instance. JMX Clients can instantiate instances of your MBean using any of its public constructors. (The reasons behind these constructor idiosyncracies are explained in the Introduction to MBeans chapter).

Download TrivialStd.java or cut and paste this to a file named TrivialStd.java in the same directory.

public class TrivialStd implements TrivialStdMBean {
    public String speak() {
        return "Hello world";
    }
}

This is your target implementation class. Note that I coded no constructor so Java will create a no-arg public constructor for me by default. Like I said, JMX will automatically publish all public constructor, and this includes the default one.

Download TrivialStdMBean.java or cut and paste this to a file named TrivialStd.java in the same directory.

public interface TrivialStdMBean {
    public String speak();
}

This is your MBean Interface.

In your second terminal window, run

    javac Trivial*.java
            

Back in your JmxStreamAdaptor session, enter the following. (Don't type # or what follows on each line-- those are comments).

    instantiate TrivialStd dom:k=v  # Creates MBean of given class and name
    list dom:*                      # List MBean names with domain "dom"
    dump dom:k=v                    # Displays MBean of given name
    invoke dom:k=v speak              # Invokes given operation of given MBean
            

The descriptions that the Adaptor shows (for anything-- class, attributes, operations, constructors) are just dummy values. Standard MBeans do not support descriptions, so no JMX Client can obtain descriptions for them.

Here's what your output should look like.

Created MBeanServer with ID: 6cd7d5:fa884d34b0:-8000:onella.icfconsulting.com:1
Available commands (you can re-execute but not edit previous commands):
    classConstructors CLASS_NAME   (lists them)
    constructors MBEAN_NAME        (lists JMX published cons)
    dump MBEAN_NAME                (displays details of MBean)
    list [MBEAN_NAME_WILDCARD]     (lists MBean names)
    history                        (displays command history)
    invoke MBEAN_NAME OP [argType1, argType2...]
    instantiate CLASS_NAME MBEAN_NAME [argType1, argtype2...]
    attribute MBEAN_NAME ATT_NAME NEW_VALUE
    -                              (re-execute previous command)
    -X                             (re-execute Xth previous command)
    spool [/SPOOL/FILE.txt]        (no file turns off spooling)
EOD (like Ctrl-D/Z) to exit.

> instantiate TrivialStd dom:k=v
> list dom:*
{
    dom:k=v
}
> dump dom:k=v
DESCRIPTION: Manageable Bean

JMX-Declared CONSTRUCTORS:
Constructor exposed for management
    ()

ATTRIBUTES:
OPERATIONS:
java.lang.String: speak [Operation exposed for management]
    ()
> invoke dom:k=v speak
Hello world
> 
            

Now that you're not shaking in fear any more, make a subdirectory named "tst" and download this more substantial Standard MBean to that directory. tst/SampleStd.java and tst/SampleStdMBean.java. While more substantial than the previous MBean, it's still pretty simple. Do study the code and see how specific selected attributes and operations are published. Compile the classes into the tst subdirectory, then use your JmxStreamAdaptor to instantiate a few instances, invoke setters and operations, and dump the target MBean to see the results. You will definitley need a scrollbar on your window to see the dump. (You can also use the "spool" command to send output to a file).

Use the "classConstructors" and "constructors" commands of the Adaptor. Notice that you can only run "constructors" on an existing MBean instance whereas you can run classConstructors on any class, MBean or not, instantiated or not. Even though the constructor descriptions are dummy values, notice that you can only see the descriptions with the "constructors" command.

Notice that to instantiate an MBean, you only need to know the class name and arguments-- nothing JMX-specific. That solves the chicken-egg problem, but it means that you can't make use of any JMX publishing information (including descriptions, which will become useful once we move on to Dynamic MBeans) until after you instantiate an MBean.

Up to this point, we have used the command-line adaptor. I wanted to use the simplest adaptor possible in order to avoid a problem of Sun's JMX tutorial. When first being introduced to MBeans, we want to spend our time learning about JMX and MBeans, not learning how to operate a specific management tool. The command-line tool is perfect for working with little MBeans like TrivialStd.

However, at this point you should have a firm grasp on what MBeans are and what you can do with them. A graphical management tool makes it much easier to view MBeans with many constructors/attributes/operations, like SampleStdMBean. If you want to switch to a graphical tool, you can use my demo site at http://admc.com:8111/, or run your own instance. On my site you can instantiate and use any of the test.* classes discussed here. To start up your own instance, just run

    java com.admc.jmx.JmxHtmlAdaptor 1289
            

instead of

    java com.admc.jmx.JmxStreamAdaptor
            

This will run a http service on port 1289 of IPADDR_ANY. Run java com.admc.jmx.JmxHtmlAdaptor -h if you're interested in using other settings (like TLS). You then use a browser to go to http://localhost:1289/ (or whatever port you specified.

The home page consists of an entry field for narrowing down the MBean list (it defaults to listing all MBeans in the repository); a list of MBean names; and a button to display the public constructors for the MBean class name that you enter.

HTML-over-HTTP Adaptor's MBean Listing page.

If you click on an MBean Name, that MBean will be displayed for you. The adaptor can handle Strings and primitives, plus it can read (but not input) Collections of these types. If a constructor, operator or getter has an argument of a type outside of this, then you can only view that item. The constructors on the MBean dump page are the JMX-published constructors. If you use them, you will create an additional MBean instance with the MBean Name that you supply.

HTML-over-HTTP Adaptor's MBean Dump page.

If you Display Constructors from the main page, you will be able to create new MBeans. Since this function just uses Java reflection, you can list the public constructors for any Java class in the classpath, but instantiation will fail if you try to construct a class that is not an MBean.

Create a Dynamic MBean

You can use Dynamic MBeans with any Adaptor the same way that you work with your Standard MBean. The only difference you will see is that the Adaptor will show meaningful description for Dynamic MBean classes and methods. (But see the next paragraph regarding constructor descriptions).

As explained in the Introduction to MBeans chapter, JMX Clients have access to all of the public constructors of your target MBean class. JMX Clients can use normal Java reflection to find all of the public constructors. However, if JMX Clients use JMX facilities to look at your constructors, they will only see the ones that you tell JMX about (to do this manually, you define entries in the MBeanInfo structure returned by one of your methods-- but hopefully you won't do it manually). This can only be done by the JMX Client if it already has an instance of your MBean. To make sure that all JMX Clients get a consistent view of your MBean, you should tell JMX about all of your public constructors. (This wasn't a consideration for Standard MBeans since they auto-publish their constructors). In addition, JMX Clients can only see descriptions of constructors if they interrogate the constructors using an existing MBean instance and if you have told JMX about this constructor, as described above.

To create a Dynamic MBean, you start with the target class that you want to expose, and implement the DynamicMBean interface according to the API (for javax.management.DynamicMBean). This is neither easy nor enjoyable.

Tip

I highly recommend that you try to avoid coding DynamicMBeans directly.

The Dynamic MBean design is bad, unnecessarily complex, and inherently difficult to maintain. You have to implement a bunch of methods which all return static information (unless you want to sabotage your application by changing the MBean interface at runtime). You have to assemble complex, nested structures containing redundant information.

If you have Sun's RI, then study Sun's example at JMX_HOME /examples/DynamicMBean/SimpleDynamic.java. To appreciate the extraordinary complexity and mess, search through there for "state" (do a case insensitive search). SimpleDynamic has a simple MBean Attribute (= getter/setter) for "state". Of course you must implement the get and set methods, but take a look at how much additional coding you need to write and maintain to expose these methods as an MBean Attribute.

If you are going to be coding Dynamic, Open or Model MBeans manually, you need to learn how to use the following classes (as well as many member classes used within these).

  • javax.management.MBeanInfo

  • javax.management.MBeanOperationInfo

  • javax.management.MBeanAttributeInfo

Create a Model MBean

You have to assemble a MBeanInfo object for Model MBeans, just like for Dynamic MBeans, but there are some major differences. You don't touch the target (non-JMX) class to be exposed. You make a new JMX Client class (or use an existing one) to instantiate a ModelMBean (normally as implemented by RequiredModelMBean) which you use to expose any methods available to you (in this or other classes).

    mBeanInfo = ...
    RequiredModelMBean mb = new RequiredModelMBean(mBeanInfo);
    mb.setManagedResource(targetObject, "targetReferenceString");

See JMX_HOME examples/ModelMBean/ModelAgent.java for an example.

Create a Dynamic MBean by subclassing AbstractDynamicMBean

Just follow the commented example of tst/SampleADM.java.

Wrappers for existing Java classes

If, for any reason at all, you want to expose methods of an existing class, but don't want to modify that class itself, make an MBean wrapper for it. Maybe you want to isolate the JMX portions, or maybe you don't have the source code for the target class, or maybe you want to expose a Java "interface" instead of a real Java "class".

If you don't need description capabilities, then you can make a Standard MBean which is a subclass of the original class. (When I say target class, I mean the original class that you want to expose). Say Oclass is the target class, then you could make Mclass, a subclass of Oclass, for your Standard MBean class. Mclass would have the MBean interface MclassMBean.

For a DynamicMBean wrapper, I highly recommend that you use AbstractDynamicMBean instead of doing it manually. Just follow the commented example of tst/SampleWrapper.java.

Notice that you have to code your MBean constructors. Pay attention to the comments.

Using AbstractDynamicMBean for MBeans that subclass something else

It isn't that difficult to take an existing (or new) class that is a subclass of something else and make it into a DynamicMBean without losing the convenience of AbstractDynamicMBean.

Your modify your target class to implement the DynamicMBean interface, but the DynamicMBean methods will just delegate to calls to a nested AbstractDynamicMBean object that you will code.

Note

Here we are discussing a delegate for instrumentation-side target class. This is different from MX4J delegation stubs, which are JMX Client-side stubs for MBeans.

The first part, implementing DynamicMBean, is trivial if you name your nested AbstractDynamicMBean object "delegate". Just modify your class declarationline to "implement AbstractDynamicMBean", and copy this boilerplate into your class: ADMDelegation.txt.

Then you need to code up your AbstractDynamicMBean delegate object and take care a few other details. Study the example closely. tst/DelegatedADM.java.