Skip navigation

The consistently excellent Brian Goetz has written a new article in his Java Theory and Practice series entitled "Be a good (event) listener". Since listeners are an important part of the JMX API, here's how his advice applies there.

There are really two sets of recommendations, one for eventgenerators and one for event listeners.

In the JMX API, events are instances of Notification, event generators are instances of NotificationEmitter (or its ancestorNotificationBroadcaster), and event listeners are instances of NotificationListener.

The advice for event generators (NotificationEmitters) can be summarized as:

  • Be careful about concurrency between, on the one hand, sending an event to the list of listeners, and on the other hand, changing this list.
  • Ensure that if a listener throws aRuntimeException, that doesn't stop other listeners from getting the event.

Fortunately, the standard implementation class NotificationBroadcasterSupport already follows this advice. It uses a CopyOnWriteArrayList to manage the list of listeners. It catches a RuntimeException from any listener and does not let it affect later listeners or the sender of the event.

If you have an MBean that sends notifications, it is almost always best to use NotificationBroadcasterSupport. In the simplest case, you can simply extend this class. Since the Java language doesn't support multiple inheritance, sometimes you can't do this because you are already extending another class. In that case you can still use delegation, like this:

public class Something extends SomethingElse
        implements SomethingMBean, NotificationEmitter {

    private final NotificationBroadcasterSupport nbs =
        new NotificationBroadcasterSupport();
    private static final MBeanNotificationInfo[] notificationInfo = {...};

    public void addNotificationListener(NotificationListener nl,
                                        NotificationFilter nf,
                                        Object handback) {
        nbs.addNotificationListener(nl, nf, handback);
    }

    public MBeanNotificationInfo[] getNotificationInfo() {
        return notificationInfo.clone();
    }

    ...the two removeNotificationListener methods delegate like
       addNotificationListener...

    ...code that wants to send a notification does:
        Notification n = ...;
        nbs.sendNotification(n);
}

In Java SE 6 (Mustang),NotificationBroadcasterSupport will acquire a new constructor that allows you to specify theMBeanNotificationInfo[] array directly, so you can delegate the getNotificationInfo() method too.

Concerning event listeners, Brian Goetz has four pieces of advice:

  1. The listener shouldn't add itself from its own constructor.
  2. Ensure that you always remove listeners when they are no longer relevant.
  3. Listeners usually run in a separate thread so pay attention to thread safety.
  4. Listeners should not perform blocking operations.

I haven't noticed people doing the first thing in this list, but if you have, now you know you should stop.

The second is very good advice, and in particular you should pay attention to exceptions. You should be sure that everyaddNotificationListener is always matched by aremoveNotificationListener no matter what execution path the code follows.

Paying attention to thread safety is just as important for listeners in the JMX API as elsewhere.

Finally, listeners should not block. If a local listener blocks, meaning a listener that is in the same Java Virtual Machine as the MBean sending the notification, then that will usually cause the sending MBean to block too until the listener completes. If a remote listener blocks, it won't hold up the sending MBean, but it will prevent the delivery of any other notifications from the same connection until it finishes.

If you need to do a blocking operation, you won't want to use the invokeLater facility mentioned by Brian Goetz unless you are in a graphical application. The facilities from java.util.concurrent provide a good alternative. For example, you could change this listener...

class BlockingListener implements NotificationListener {
    public void handleNotification(Notification n, Object handback) {
        blockingOperation();
    }
}

...into this one...

class NonBlockingListener implements NotificationListener {
    private final Executor executor =
        Executors.newSingleThreadExecutor();

    public void handleNotification(Notification n, Object handback) {
        executor.execute(new Runnable() {
            public void run() {
                blockingOperation();
            }
        });
    }
}

This creates a private thread that will handle the call toblockingOperation(). If a second notification arrives while the thread is still doing theblockingOperation() of the first, then the secondblockingOperation() will be queued up and executed when the first has finished.

If you have many listeners where you need to do blocking operations like this, you might consider sharing anewSingleThreadExecutor between them, for example with a static field that they all access. You might also want to have a pool of threads rather than a single thread for the blocking operations; java.util.concurrent has everything you need for that.

Finally, if you have an MBean that absolutely must not block when it sends a notification, even in the face of a badly-behaved listener that does block, then Mustang provides another new constructor for NotificationBroadcasterSupportthat allows you to specify an Executor to be used for calling listeners. Again you can use the facilities ofjava.util.concurrent to control how this works. You might want to think about the worst case: a listener that never returns. You don't want this to always cause a new thread to be created for every notification, and you don't want it to always cause a new entry to be added to a queue of tasks for every notification either. So you're going to need to throw notifications away sometimes. You can achieve this with a ThreadPoolExecutor that has a bounded queue, a bounded number of threads, and a DiscardPolicy orDiscardOldestPolicy.

With a Standard MBean, you define the management interface of the MBean using a Java interface. Getters and setters in the interface define attributes, and other methods define operations. But the only information extracted out of the interface is the names and types of the attributes and operations, and just the types of the operation parameters. Although the JMX API allows for textual descriptions to be associated with attributes, operations, and parameters, when you use a Standard MBean these descriptions have meaningless default values. Parameters also have default names like p1, p2, etc. Here's how you can use subclassing and annotations to overcome these limitations.

Suppose you have a Standard MBean that looks like this:

public interface CacheControllerMBean {
    /** Drop the n oldest entries whose size matches the given constraints. */
    public int dropOldest(int n, int minSize, int maxSize);
}

You implement the MBean in the usual way, register it in your MBean Server...

        MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
        ObjectName name = new ObjectName("com.example:type=CacheController");
        CacheController mbean = new CacheController(...);
        mbs.registerMBean(mbean, name);
   

...and then fire up jconsole to see what it looks like.

Snapshot of CacheController MBean with default metadata 

Well, this is good, the operation is there, but the information about it could be improved. The useful parameter names we gave have been replaced with p1, p2, p3, and when we let the mouse hover over the operation button, we get a tooltip with the default text "Operation exposed for management".

You wouldn't have expected the MBean Server to have been able to guess a better description than that, but you might be surprised that the parameter names that were in your originalCacheControllerMBean interface have been lost. The reason for that is that the contents of the MBeanInfo for a Standard MBean are determined using reflection, and parameter names are not available through the reflection API. (The reason for that is that parameter names are not needed when compiling or executing method calls from other classes, so they don't appear in .class files, except when they're compiled with extra debug info. We wouldn't want the behaviour to be different when compiled with debug info or not, so no parameter names for us.)

With a bit more work you can put more information into your MBeans to produce a better user interface. The technique I'm suggesting is to add annotations to your Standard MBean that will allow you to specify descriptions for operations (and attributes and MBeans) and to give parameter names in a way thatis accessible through reflection. The idea is that your MBean interface would look like this:

public interface CacheControllerMBean {
    @Description("Drop the n oldest entries whose size matches the given constraints")
    public int dropOldest(@PName("n") int n,
                          @PName("minSize") int minSize,
                          @PName("maxSize") int maxSize);
}

The @PName annotation could easily be added automatically by running some sort of script over your source files. Even the @Description could potentially be extracted automatically from doc comments.

Here's what the @PName annotation looks like:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
public @interface PName {
    String value();
}

When you're defining an annotation, you need to check out the meta-annotations in java.lang.annotation. (Meta-annotations are annotations for annotation definitions like the definition of@PName here.) They are:

Documented says whether the annotation should appear in documentation generated by the Javadoc tool or similar tools. In this case it would be redundant, giving you something like this:

dropOldest

@Description(value="Drop the n oldest entries whose size matches the given constraints")

int dropOldest(@PName(value="n")
               int n,
               @PName(value="minSize")
               int minSize,
               @PName(value="maxSize")
               int maxSize)

You probably don't want this redundant information, although it might be useful to make sure the annotations are in fact present and have the right values.

Inherited applies to annotations on classes only, so it is not relevant to any annotations for Standard MBean interfaces.

Retention specifies whether the annotation is used only by tools (like the compiler) that read source code; or by tools that read class files; or by code that uses reflection. You might want to define some annotations that work in conjunction with the apttool, in which case all three retention values could be interesting. Usually, though, you will wantRetentionPolicy.RUNTIME.

Target specifies a list of places in the Java language syntax where the annotation can be used. This one is only appropriate as an annotation on parameters, so that's what you say.

By the way, it's important that the name of the method in the@PName annotation be value and not anything else. If it were called name, say, you would have to write @PName(name="n") rather than just@PName("n").

The @Description annotation looks like this:

import static java.lang.annotation.ElementType.*;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({CONSTRUCTOR, METHOD, PARAMETER, TYPE})
public @interface Description {
    String value();
}

This should be fairly clear based on the discussion of the@PName annotation.

Now that you have these annotations, how do you arrange for them to be taken into account? The idea is to create a subclass of javax.management.StandardMBean that will add the information from the annotations to the information that is already deduced by the normal rules for Standard MBeans. TheStandardMBean class defines a number ofprotected methods that are specifically designed to be overridden for this sort of use. So you can define a class that looks like this:

public class AnnotatedStandardMBean extends StandardMBean {
    
    /** Instance where the MBean interface is implemented by another object. */
    public <T> AnnotatedStandardMBean(T impl, Class<T> mbeanInterface)
            throws NotCompliantMBeanException {
        super(impl, mbeanInterface);
    }
    
    /** Instance where the MBean interface is implemented by this object. */
    protected AnnotatedStandardMBean(Class<?> mbeanInterface)
            throws NotCompliantMBeanException {
        super(mbeanInterface);
    }

    @Override
    protected String getDescription(MBeanOperationInfo op) {
        ...
    }

    ...other overrides...
}

And the code that registers your MBean will now look like this:

        ObjectName name = new ObjectName("com.example:type=CacheController");
        CacheController cc = new CacheController(...);
        Object mbean = new AnnotatedStandardMBean(cc, CacheControllerMBean.class);
        mbs.registerMBean(mbean, name);

By the way, an advantage of this approach is that you no longer have to follow the rigid naming convention where classcom.example.CacheController has to implement interfacecom.example.CacheControllerMBean.

Let's consider the @Description annotation for operations first. The method you want to override is this one:

    protected String getDescription(MBeanOperationInfo op) {...}

You're going to want to find the method corresponding to this operation, and if it has a @Description annotation, return the value of the annotation as the description. Otherwise just return the default value ofop.getDescription().

Finding the method corresponding to anMBeanOperationInfo is not completely straightforward because the parameter types in the containedMBeanParameterInfo[] are expressed asString rather than Class. (This is because Class is not serializable.) EachString is the result of Class.getName() on the correspondingClass. You're going to need the originalClass objects so that you can call Class.getMethod to find the Methodobject and access its annotations.

Unfortunately, there is no standard method to convert back fromClass.getName() to the original Class. Class.forName comes close, but it doesn't do the right thing for primitive types like int.class (also known as Integer.TYPE). int.class.getName()returns "int", but if you give that toClass.forName it will look for a class calledint, which it is unlikely to find. So you'll need a helper method:

    static Class<?> classForName(String name, ClassLoader loader)
            throws ClassNotFoundException {
        Class<?> c = primitiveClasses.get(name);
        if (c == null)
            c = Class.forName(name, false, loader);
        return c;
    }
    
    private static final Map<String, Class<?>> primitiveClasses =
            new HashMap<String, Class<?>>();
    static {
        Class<?>[] prims = {
            byte.class, short.class, int.class, long.class,
            float.class, double.class, char.class, boolean.class,
        };
        for (Class<?> c : prims)
            primitiveClasses.put(c.getName(), c);
    }

Though it's probably not really necessary, I've got into the habit of writing Class<?> everywhere rather than just plain Class. Occasionally that avoids compiler warnings about generics.

Now that you have the classForName method, you can write the method that finds the Method object for anMBeanOperationInfo that comes from a given MBean interface:

    private static Method methodFor(Class<?> mbeanInterface,
                                    MBeanOperationInfo op) {
        final MBeanParameterInfo[] params = op.getSignature();
        final String[] paramTypes = new String[params.length];
        for (int i = 0; i < params.length; i++)
            paramTypes[i] = params[i].getType();
        
        return findMethod(mbeanInterface, op.getName(), paramTypes);
    }
    
    private static Method findMethod(Class<?> mbeanInterface, String name,
                                     String... paramTypes) {
        try {
            final ClassLoader loader = mbeanInterface.getClassLoader();
            final Class<?>[] paramClasses = new Class<?>[paramTypes.length];
            for (int i = 0; i < paramTypes.length; i++)
                paramClasses[i] = classForName(paramTypes[i], loader);
            return mbeanInterface.getMethod(name, paramClasses);
        } catch (RuntimeException e) {
            // avoid accidentally catching unexpected runtime exceptions
            throw e;
        } catch (Exception e) {
            return null;
        }
    }

That gives you enough to be able to write thegetDescription override that will return the value of the @Description annotation if there is one:

    @Override
    protected String getDescription(MBeanOperationInfo op) {
        String descr = op.getDescription();
        Method m = methodFor(getMBeanInterface(), op);
        if (m != null) {
            Description d = m.getAnnotation(Description.class);
            if (d != null)
                descr = d.value();
        }
        return descr;
    }

Getting the parameter names out of the @PNameannotation needs the same kind of code. You need to do a little more work, because there is no methodMethod.getParameterAnnotation that would allow you get the value of a particular annotation for a particular method parameter, the way you can with Method.getAnnotation. But it's easy enough to define. If you just wanted to write a method that returned the @PName annotation you would write this:

        for (Annotation a : m.getParameterAnnotations()[paramNo]) {
            if (a instanceof PName)
                return (PName) a;
        }

You can generalize to a method that works for any annotation rather than just @PName:

    static <A extends Annotation> A getParameterAnnotation(Method m,
                                                           int paramNo,
                                                           Class<A> annot) {
        for (Annotation a : m.getParameterAnnotations()[paramNo]) {
            if (annot.isInstance(a))
                return annot.cast(a);
        }
        return null;
    }

The fiddling with generics is to say that, if theannot parameter is PName.class, say, then the return type is PName. This saves the caller from having to cast. The standard methods such asMethod.getAnnotation do the same thing. Writing<A extends Annotation> means you will get a compiler error if you accidentally call the method with a class that is not an annotation.

Now you have everything you need for the override that extracts a parameter name from the @PName annotation:

    @Override
    protected String getParameterName(MBeanOperationInfo op,
                                      MBeanParameterInfo param,
                                      int paramNo) {
        String name = param.getName();
        Method m = methodFor(getMBeanInterface(), op);
        if (m != null) {
            PName pname = getParameterAnnotation(m, paramNo, PName.class);
            if (pname != null)
                name = pname.value();
        }
        return name;
    }

Using the AnnotatedStandardMBean we've just defined, the jconsole window now looks like this:

Snapshot of CacheController MBean with metadata from annotations

The tooltip contains useful text, and the parameters have real names. How gratifying!

There are still some other things you're likely to want in theAnnotatedStandardMBean class. It should be possible to add a @Description annotation to a getter and/or setter method to define the description of an attribute. You might want some support for internationalizing descriptions, for example by including a resource key in the annotation. Operations have animpact field that you might want to be able to set via annotations. All of these things can be built rather simply based on the concepts here.

Yesterday I talked about how you can use WeakReference to allow a resource to be garbage collected even if it is referenced by another object, a JMX MBean in the case in question. In fact, you can use dynamic proxies to provide a more general framework for this sort of situation.

In the specific situation I was discussing before, you have aresource such as a cache, that may be garbage collected when it is no longer referenced. And you have a controllersuch as a JMX MBean that references the resource. But you don't want this reference to stop the resource from being garbage collected.

We can generalize this as follows:

  • The resource is defined by some Java interface,Cache in our example. Yesterday Cache was a concrete class, but for the technique we're talking about here it must be an interface, typically with a concrete class likeCacheImpl that provides its implementation.
  • Some references to the resource are weak, meaning that they should not prevent the resource from being garbage collected.
  • If the resource is garbage collected, and these weak references try to use it, we want to be able to define what happens. This might just be to throw an exception, or it might be take an action such as unregistering an MBean.

Dynamic proxies are an enormously useful feature of the Java platform once you understand what they are for. Suppose you have a Java interface, like Cache. A dynamic proxy allows you to produce a Java object that implements that interface. Every time someone calls one of the interface's methods on this object, the following method from the InvocationHandler interface is called to handle it:

public Object invoke(Object proxy,
                     Method method,
                 Object[] args) throws Throwable;

The method parameter allows you to know which method of the interface was called, and the argsparameter allows you to know what arguments it was called with. (The proxy argument is usually ignored.)

Inside invoke, you can do whatever's appropriate. You can create a handler that always throws an exception, to produce a dummy implementation of an interface that checks error behaviour for example. You can create a handler that logs the method call or performs security checks before reinvoking the sameMethod on another object.

It is this second kind of possibility that interests us here. If the WeakReference is still valid, then we just want to invoke the same method on the resource. If it's not valid, then we want to invoke the user-supplied missingHandlerinstead.

Suppose the Cache interface looks like this:

public interface Cache {
    public int dropOldest(int n);
}

Suppose we create a dynamic proxy for this interface, like this:

InvocationHandler handler = new SomeInvocationHandler(...);
Cache proxy = (Cache) Proxy.newProxyInstance(<...stuff...>, handler);

Then when we later call proxy.dropOldest(5), this will result in a call to

handler.invoke(proxy, method, new Object[] {5});

where method is ajava.lang.reflect.Method representing the methodCache.dropOldest(int).

What we want handler.invoke to do is to call this same Method on the resource, assuming it still exists, and otherwise run the user-suppliedmissingHandler.

Putting this all together, we get this:

import java.lang.reflect.*;
import java.lang.ref.*;
import java.util.concurrent.Callable;

public class WeakProxy {
    public static <T> T make(T resource,
                 Class<T> interfaceClass,
                 Callable<Object> missingHandler) {
    Handler handler = new Handler<T>(resource, missingHandler);
    Object proxy = Proxy.newProxyInstance(interfaceClass.getClassLoader(),
                          new Class[] {interfaceClass},
                          handler);
    return interfaceClass.cast(proxy);
    }

    private static class Handler<T> implements InvocationHandler {
    Handler(T resource, Callable<Object> missingHandler) {
        this.resourceRef = new WeakReference<T>(resource);
        this.missingHandler = missingHandler;
    }

    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable {
        T resource = resourceRef.get();
        if (resource == null)
        return missingHandler.call();
        else
        return method.invoke(resource, args);
    }

    private final WeakReference<T> resourceRef;
    private final Callable<Object> missingHandler;
    }
}

The type parameter <T> to WeakProxy.makeallows us to specify that the resource parameter must implement the interface specified by theinterfaceClass parameter, and that the result ofWeakProxy.make also implements this interface.

In our example of a CacheControlMBean, we could use this class as follows:

...
    Cache cache = new CacheImpl();  // or whatever
    Cache weakCache =
        WeakProxy.make(cache, Cache.class, exceptionWhenMissingHandler);
    CacheControlMBean cc = new CacheControl(weakCache);
    ObjectName on = ...;
    mbeanServer.registerMBean(cc, on);
...

    private static final Callable<Object> exceptionWhenMissingHandler =
    new Callable<Object>() {
        public Object call() {
        throw new IllegalStateException("Object no longer exists");
        }
    };
...

With some more work, we could have the WeakProxyclass create a thread that calls the missingHandler as soon as the resource is garbage-collected, rather than waiting for somebody to call the proxy. The code would look something like the final version of the CacheControl MBean I posted before.

Suppose (to take my favourite example), you have some sort of cache, and you want to be able to control it via an MBean. You might have something a bit like this:

public interface CacheControlMBean {
    public int getSize();
}

public class CacheControl implements CacheControlMBean {
    public CacheControl(Cache cache) {
        this.cache = cache;
    }

    public int getSize() {
        return cache.getSize();
    }

    private final Cache cache;
}

This is a good pattern to follow, but it does have one side-effect that might be unintended. Even if theCache is no longer referenced by anything else in the application, it continues to be referenced by theCacheControlMBean as long as that is in the MBean Server. So when the Cache is no longer in use, we need to make sure that the MBean is removed.

In my opinion, the best way to achieve this is to have an explicit method that is called on the Cache when it is to be destroyed. This method frees any resources (threads, sockets, whatever) held by the Cache, and it also unregisters the CacheControlMBean.

However, it is ugly for the Cache to contain explicit logic to unregister its MBean. Much better is for it to contain logic that allows its users to register a callback that is invoked when the cache is destroyed. Then theCacheControlMBean can register such a callback that unregisters the MBean.

Here's what this might look like in the Cache:

public class Cache {
    ...
    public void addDestroyHandler(Runnable r) {
        destroyHandlers.add(r);
    }

    public void removeDestroyHandler(Runnable r) {
        destroyHandlers.remove(r);
        // Not an error if the handler wasn't there
    }

    public void destroy() {
        ...free resources...
        for (Runnable r : destroyHandlers)
            r.run();
    }

    private final List<Runnable> destroyHandlers = new ArrayList<Runnable>();
}

Now the CacheControlMBean can explicitly arrange to be unregistered when the Cache is destroyed:

// This is the basic idea, but see below for modified version
public class CacheControl implements CacheControlMBean {
    public CacheControl(Cache cache) {
        this.cache = cache;
        cache.addDestroyHandler(new Runnable() {
            public void run() {
                unregisterMe();
            }
        });
    }
    ...
}

So how does the CacheControlMBean unregister itself? The best way is to implement the MBeanRegistration interface to find out what MBean Server and ObjectName to use. As I mentioned before, you can reasonably depend on the object not being registered with more than one name.

public class CacheControl implements CacheControlMBean, MBeanRegistration {
    public CacheControl(Cache cache) {
        this.cache = cache;
    }
    ...
    public synchronized ObjectName preRegister(MBeanServer mbs, ObjectName name)
            throws InstanceAlreadyExistsException {
        if (objectName != null) {
            throw new InstanceAlreadyExistsException("Already registered as: " +
                                                     objectName);
        }
        mbeanServer = mbs;
        objectName = name;
    }

    public void postRegister(Boolean ok) {
        if (ok)
            cache.addDestroyHandler(destroyHandler);
        else
            preDeregister();
    }

    public synchronized void preDeregister() {
        cache.removeDestroyHandler(destroyHandler);
        mbeanServer = null;
        objectName = null;
    }

    public void postDeregister() {
    }

    private synchronized void unregisterMe() {
        if (objectName != null) {
            try {
                mbeanServer.unregisterMBean(objectName);
            } catch (InstanceNotFoundException e) {
                // log the exception somewhere...
            }
        }
    }

    private final Runnable destroyHandler = new Runnable() {
        public void run() {
            unregisterMe();
        }
    };
    private MBeanServer mbeanServer;
    private ObjectName objectName;
    private final Cache cache;
}
Weak MBeans

The above is a good solution if you know that theCache will be explicitly destroyed at the right time. But sometimes it is hard to determine the right time to destroy a resource like the Cache. For example, the right time to destroy the cache might be when it has no more users and there are no more ongoing operations on it. That can be quite hard to track.

An alternative is to arrange for the Cache to be destroyed when nobody references it any more. The garbage collector can tell you that. For example, the Cache class could have a finalize() method that callsdestroy().

This is usually a bad idea, because you don't know how long will elapse between the moment when the Cache is no longer referenced and the moment when its finalize() method is called. It could be hours or even days. If theCache holds resources like threads or connections, they will be wasted during those hours or days.

On the other hand, if the Cache doesn't hold any important resources, relying on garbage collection remains an option. But notice that the CacheControlMBeanreferences a Cache. As long as the MBean is registered, the Cache will never be garbage collected.

You can get around this by using a WeakReference. A WeakReference wraps a reference to another object (in our case, theCache), but if it is the only reference to the object then the object can be garbage collected anyway, and the reference becomes null. This is also true if there are severalWeakReferences and not just one.

Here's how we could rewrite CacheControl to use aWeakReference and so not prevent theCache from being garbage collected.

public class CacheControl implements CacheControlMBean, MBeanRegistration {
    public CacheControl(Cache cache) {
        this.cacheRef = new WeakReference<Cache>(cache);
    }

    public int getSize() {
        Cache c = cacheRef.get();
        if (c == null) {
            unregisterMe();
            throw new IllegalStateException("Cache no longer exists");
        }
        return c.getSize();
    }
    ...
    private final WeakReference<Cache> cacheRef;
}

If you think the MBean is the only code that is going to be interested in knowing when the Cache disappears, then you could get rid of the addDestroyHandler method from the Cache class and its finalize method. The code above makes sure that the MBean will disappear after (a) the Cache gets garbage collected and (b) you then call some method like getSize() on the MBean.

Of course both (a) and (b) represent arbitrary delays. You could get rid of the arbitrary delay before (b) by periodically checking whether the WeakReference is still valid. For example you could use a Timer to do this.

Polling with a Timer wastes time when the reference is still valid, and fails to react immediately once it no longer is. You can arrange to react punctually as soon as theCache is garbage collected by using a ReferenceQueue. We're getting into some fairly advanced Java usage here!

public class CacheControl implements CacheControlMBean, MBeanRegistration {
    public CacheControl(Cache cache) {
        this.cacheRef = new CacheReference(cache);
    }
    ...
    private class CacheReference extends WeakReference<Cache> {
    CacheReference(Cache cache) {
        super(cache, refQueue);
    }

    CacheControl getCacheControl() {
        return CacheControl.this;
    }
    }

    private static final ReferenceQueue<Cache> refQueue =
    new ReferenceQueue<Cache>();
    static {
    Runnable r = new Runnable() {
        public void run() {
        while (true) {
            CacheReference ref;
            try {
            ref = (CacheReference) (WeakReference<Cache>)
                refQueue.remove();
            } catch (InterruptedException e) {
            return;
            }
                    ref.getCacheControl().unregisterMe();
        }
        }
    };
    new Thread(r, "CacheControl cleaner").start();
    }
    ...
    private final CacheReference cacheRef;
}

This creates a thread that will wait for the reference to be cleared. (The Timer solution also created a thread, even though that was less obvious.) The same thread is shared between all CacheControl MBeans that might exist for different Caches.

It's been suggested that the JMX API could have some explicit support for "Weak MBeans" like this. I'm not sure there's enough use for them to justify including them in the API, and I'm also not sure what a general-purpose API for Weak MBeans would look like. But the above shows how you can create your own Weak MBeans if need be.

The next entry talks about a way to combineWeakReference with dynamic proxies, which you can use to solve problems of this sort, including problems that are unrelated to the JMX API.

One of the changes we made in version 1.1 of the JMX API, way back in early 2002, was to modify the serialization of certain classes. Because remote access was not part of the API at this time, this aspect had been a bit neglected in version 1.0, with the result that certain classes had underspecified or inefficient serial forms. Other implementations of the API could and did have incompatible serial forms as a result.

In version 1.1, we revisited all serializable classes and updated their serial forms where necessary. In order not to break interoperation with the previous version, we provided a system property jmx.serial.form that you could set to "1.0" to specify that you wanted to continue using the serial forms from version 1.0 of the Reference Implementation, imperfect as they were.

This worked reasonably well, but not perfectly. One problem is that a class can only have one serial form. So, even though we provided the jmx.serial.form property, you had to decide whether it was going to be 1.0 or 1.1. If you set it to 1.0, the compatibility code kicked in, but then you could not handle the new serial form. This is a fundamental constraint of the way serialization works.

However, all is not lost. Although a serializable class such asjavax.management.ObjectName can only have one serial form, you can deal with more than one version of the class in the same Virtual Machine by using class loaders. Tom Ball recently blogged about this, and I would encourage you to check out what he wrote if you are interested in this topic.

Suppose in the same application you need to interact with the old and new JMX serial forms. For example, suppose you have a management client that interacts with some servers using the JMX Remote API, which requires version 1.2 of the JMX API, and some other servers using the proprietary connectors that were typically shipped with older app servers, based on version 1.0 of the JMX API. The techniques in Tom's blog can be applied here.

Let's suppose that you're running on J2SE 5.0, which means you have JMX API 1.2 by default. Then you can connect to the servers that use the JMX Remote API without doing anything special. But for the servers that use JMX API 1.0, you'll need some magic.

Because you're running on J2SE 5.0, you'll need a class loader that does not automatically delegate to its parent class loader. As a reminder, the usual behaviour of a class loader when loading a class is to first ask the parent class loader to find it, then look for it yourself with ClassLoader.findClass. But we don't want this because the parent class loader will find the 1.2 versions of the JMX classes. This will also be true when running on J2SE 1.4 with the JMX classes in the classpath.

The technique in Tom's blog is to create a class loader that has an explicit list of class name prefixes for which we don't delegate to the parent. We could do this with a list that includes "javax.management" and maybe "com.proprietary.someappserver" or whatever. However, it's usually enough not to delegate anyclasses to the parent class loader initially, but instead try to load the class yourself, and only then delegate if you can't. That's the idea behind this variant of Tomas Hurka'sGJASTClassLoader from Tom's blog:

public class NonDelegatingClassLoader extends URLClassLoader {
    private final PermissionCollection permissions = new Permissions();
    {
        permissions.add(new AllPermission());
    }

    public NonDelegatingClassLoader(URL[] urls) {
        super(urls);
    }
    
    protected Class loadClass(String name, boolean resolve)
            throws ClassNotFoundException {
        try {
            Class c = findClass(name);
            if (resolve)
                resolveClass(c);
            return c;
        } catch (ClassNotFoundException e) {
            // Delegate to parent
            return super.loadClass(name, resolve);
        }
    }

    protected PermissionCollection getPermissions(CodeSource codesource) {
        return permissions;
    }
}

As shown in Tom's blog, this class loader is an isolated world, and the only way to execute code in that world from outside is through reflection. You could for example create a class called com.example.oldclient.Client that references the proprietary connector com.proprietary.someappserver.Connector. You package your class in /some/path/oldclient.jar and you have the proprietary stuff in /some/path/proprietary.jar, so you'll want to instantiate the NonDelegatingClassLoader something like this:

URL[] urls = {new URL("file:/some/path/oldclient.jar"),
              new URL("file:/some/path/proprietary.jar")};
ClassLoader cl = new NonDelegatingClassLoader(urls);
Class client = Class.forName("com.example.oldclient.Client", true, cl);
Method connect = client.getMethod("connect", new Class[] {String.class});
connect.invoke(null, new Object[] {"...address..."});

Or you could invoke a constructor rather than a static method, as was shown in Tom's blog.

This isn't exactly simple, but I've tested it against Sun's Java Dynamic Management Kit 4.0, which included JMX API 1.0, and I was able to connect to it from J2SE 5.0 without any jmx.serial.form tricks. It works!

The short answer is: you can, but you probably shouldn't. Here's why.

To be clear, here's the sort of thing I'm talking about:

CacheControlMBean mbean = new CacheControl(thingyCache);
mbeanServer.registerMBean(mbean, objectName1);
mbeanServer.registerMBean(mbean, objectName2);

This will work, and the same MBean will be accessible through both objectName1 and objectName2. So why shouldn't you do that?

From a high level point of view, the immediate counterquestion is, why would you want to? What does it mean for the same object to be both
domain:type=CacheControl,name=Thingies and
domain:type=CacheControl,name=Whatsits ? When you access the MBean, how is it supposed to know whether it is acting as the Thingy cache controller or the Whatsit cache controller?

Well, maybe Thingies and Whatsits are actually the same, but that's an implementation detail that you don't want to expose. Let's pretend we believe that and look at some lower-level reasons why you still don't want to use the same MBean for both of them.

MBeanRegistration

The first thing that ought to make you hesitate before registering the same MBean twice is the MBeanRegistration interface. As a reminder,MBeanRegistration looks like this:

package javax.management;
public interface MBeanRegistration {
    ObjectName preRegister(MBeanServer mbs, ObjectName name) throws Exception;
    void postRegister(Boolean done);
    void preDeregister() throws Exception;
    void postDeregister();
}

If an MBean implements this interface, then the various methods will be called over the MBean's lifecycle: the first two before and after registration, the last two before and after unregistration. This allows an MBean to know what name it has been registered under, and in what MBean Server.

But what happens if you register the same MBean with two different names? (Or in more than one MBean Server?) Then thepreRegister method will be called once for each name. The MBean had better be able to handle this.

It isn't impossible to deal with this, but it's hard. Rather than just stashing the MBeanServer andObjectName in fields, the MBean is going to need to maintain a Set<ObjectName> and maybe aSet<MBeanServer> too. The parameter to thepostRegister method indicates whether registration was successful: if it wasn't, how do we know whichObjectName to remove from the Set? ThepreDeregister and postDeregister methods don't say what ObjectName is being unregistered, so again how do we know which one to remove from theSet?

You could probably solve this problem in most cases by checking to see whether each ObjectName in the Setis still there. For postRegister(false) andpostDeregister(), you expect one of them to be missing. You'd have to be very wary of race conditions, though. And there is still no way of knowing in preDeregisterwhich ObjectName is about to be unregistered.

Notifications

If an MBean wants to emit notifications, it implements the NotificationBroadcaster interface (or better,NotificationEmitter). Then, when it emits a Notification, the notification will be sent to every listener that was added using NotificationBroadcaster.addNotificationListener. But that method doesn't have a parameter to tell you whichObjectName the client thinks it's adding its listener to. So if the same MBean is acting as the Thingy cache controller and the Whatsit cache controller, then listeners for the Thingy cache controller are presumably going to get notifications where the ObjectName returned by getSource() is the Whatsit cache controller name, and/or vice versa.

A further point is that the convenient ability to specify a reference to the MBean as the notification source and have the MBean Server replace it with the ObjectName obviously doesn't work here. Relying on that source rewriting is discouraged for other reasons, so if this were the only argument against registering the same MBean more than once it wouldn't be enough.

Inter-MXBean references

In Mustang (Java SE 6), the JMX API acquires user-defined MXBeans. (A fixed set of these already exists in java.lang.management in Tiger.) One of the convenient features of MXBeans for developers is the ability to code inter-MXBean references. An MXBean defines a getter for another MXBean type, like this:

public interface ModuleMXBean {
    public ProductMXBean getProduct();
}

The MXBean mapping converts this into an attribute calledProduct of type ObjectName. When you callgetAttribute, the ProductMXBeanreference returned by getProduct() is converted into the ObjectName under which that MXBean is registered. Obviously, for this to work that MXBean cannot be registered under more than one name. In fact MXBeans refuse to be registered under more than one name in the same MBean Server for this reason.

There are many advantages to using MXBeans, so if you are coding MBeans today you should at least be thinking about migrating them to MXBeans in the future. If you register the same MBean with more than one name then you will have problems when you migrate.

So what should you do instead?

When you are tempted to register the same MBean under two different names, most often what you really want is to have the same "resource" that has two different management views. In our example, the resource is the cache, which has one view that sees it as a cache of Thingies, and another view that sees it as a cache of Whatsits. The attributes and operations of these two views might behave the same but chances are that that is more of a coincidence than a fundamental property. As the code evolves, sooner or later you'll want some attribute to be different in each one. And as we've seen, notifications must say whether they come from one or the other. If Thingies and Whatsits are secretly the same, then when the Thingy cache overflows, the Whatsit cache will also overflow. But if I've asked to receive a notification for the former, I don't want to be told about the latter.

If you already have code that is registering the same MBean twice, you may be able to make a quick fix as follows. Instead of this...

CacheControlMBean mbean = new CacheControl(thingyCache);
mbeanServer.registerMBean(mbean, objectName1);
mbeanServer.registerMBean(mbean, objectName2);

...try this...

CacheControlMBean mbean = new CacheControl(thingyCache);
Object mbean1 = new StandardMBean(mbean, CacheControlMBean.class);
Object mbean2 = new StandardMBean(mbean, CacheControlMBean.class);
mbeanServer.registerMBean(mbean1, objectName1);
mbeanServer.registerMBean(mbean2, objectName2);

But ultimately, you'll just want to make two different MBeans that reference the same underlying resource (thingyCache here)...

CacheControlMBean thingyMBean = new CacheControl(thingyCache);
CacheControlMBean whatsitMBean = new CacheControl(thingyCache);
mbeanServer.registerMBean(thingyMBean, thingyObjectName);
mbeanServer.registerMBean(whatsitMBean, whatsitObjectName);

...which accurately reflects what you mean.

Model MBeans may also come in useful here. But that's a story for another day.

JavaOne is always a huge buzz, and this year was no exception. Of course the technical sessions are very worthwhile, so it's great news that slides and audio for all of them will be available free online. In previous years you had to pay a small subscription fee to access them.

But the biggest plus of JavaOne, like any conference, is meeting people with common interests. I brought back a densely-packed page of notes about things people told me they'd like to see in the JMX API. That should generate enough blog material to last me to autumn.

Here's a terse summary of some of the main questions and requests.

  • Metadata repository. Today you can query an MBean Server to find out the management interface of any MBean registered in it. But often you would like to find out the management interface ofall possible MBeans, even ones that aren't currently registered. That would allow you to know what classes you can give to createMBean. Some systems like CIM/WBEM already support metadata query so adding this facility to the JMX API would make it easier to implement those systems on top of it.
  • Federation and aggregation. The JMX Remote API provides a good solution for a single client connecting to a single server. But what if the client wants to manage a large number of servers? The solution proposed for JMX API 2.0 is cascading. But we probably also want to support some form ofaggregation. For example, if all of my 100 servers export an "uptime" statistic, I'd like to be able to obtain an average uptime in a single operation, rather than having to access 100 MBeans and compute the average myself.
  • Turning on and off expensive MBeans. Some MBeans may export information that is continuously sampled and that is expensive to gather. You don't necessarily want those MBeans to be running all the time. Ad hoc solutions are easy, for instance thesetThreadContentionMonitoringEnabled method in java.lang.management.ThreadMXBean. But perhaps there could be a more general convention, such as asetDetailLevel(int) method.
  • Generalized thread handling. There are a number of places where the JMX API can create threads. A connector client needs a thread to receive notifications from the server. Monitors and timers need threads to do their periodic activities. A NotificationBroadcasterSupport can dispatch a notification to listeners asynchronously rather than blocking the sender. Could we have some centralized thread handling for all of this? That might also fit better in contexts like Java EE where random thread creation is frowned on or impossible.
  • Improved notification handling. Currently the JMX Remote API has an imperfect guarantee for notification delivery: either you receive all notifications you are subscribed for, or you receive an indication that you may have lost some. (Notifications are not silently lost.) But you might already have an infrastructure like JMS or the JavaSpaces Service that guarantees reliable delivery. Could you exploit this for your notifications? Could you roll your own notification buffering?
  • Internationalizing descriptions. An MBean can have adescription for itself and for each of its contained attributes, operations, etc. How can we arrange for these descriptions to be internationalized? Ideally we'd like the client to be able to communicate its locale to the server, and the server to send back the descriptions appropriate to that locale. And we'd like some nifty way to use annotations in an MBean interface to specify things likeresource bundle names and resource keys.
  • More control over Standard MBean info. Now that a Standard MBean can have a Descriptor, it would be nice to be able to populate that Descriptor with annotations in the Standard MBean interface. Also, an MBeanParameterInfo has a name, but that name is always p1, p2, etc for a Standard MBean. It would be nice to be able to specify the name with an annotation. There are similar cases, like the impact of an operation.
  • Annotations instead of MBean interfaces. A theme I heard a lot was that people would like to be able to specify the management interface of a class by using annotations rather than having to create a separate MBean interface. For instance, you might say
    @Management
    public long getUptime() {...}
    in your class to define an Uptimeattribute.
  • PropertyEditor for attributes. I heard conflicting views on this one. One person thought it would be great if the Descriptor for an attribute could specify a PropertyEditor for it. Then a management console could allow you to view or edit that attribute in some fancy way. Another rather more utilitarian person thought it was a good thing developers couldn't do this because it meant they would do more productive things with their time instead!
  • MBean lifecycle support. The JMX API allows an MBean to know when it is created and destroyed through the MBeanRegistration interface. But often there's a distinction between creating the MBean and starting it, and between stopping it and destroying it. It could be good to have a standard interface with start() and stop() methods that MBeans could implement.
  • Transactions. It would be good if the JMX API had some form of transactions. Perhaps not the full ACID properties, and perhaps requiring MBeans to be transaction-aware if they want to export transactional semantics, but that would still be good.
  • "Weak" MBeans. An MBean frequently manages another Java object that is the "resource" to be monitored or controlled. But what if the only reference to that resource is from the MBean? Could we somehow arrange for the MBean to disappear if the resource is no longer referenced by anyone else?
  • Tomcat. So just how do you create your own MBeans in a Tomcat server, and how do you connect to them with jconsole?

Now, in fairness I should say that the snapshot of the JMX API that you can see on java.net is basically what you will get in Mustang (JDK 6) when it comes out. The stuff listed above will almost entirely be addressed in Dolphin (JDK 7) at the earliest. There are already some nifty new JMX API features in Mustang; some of the main ones were listed in a recentjava.sun.com feature.

In future entries here, I plan to talk about many of the above items in more detail, and in many cases explain how you can get some or all of the functionality using the existing API.