Search |
||
Cleaning up an MBean when its resource diesPosted by emcmanus on July 20, 2005 at 1:47 PM PDT
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 the
In my opinion, the best way to achieve this is to have an
explicit method that is called on the However, it is ugly for the Here's what this might look like in the
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
// 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
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 MBeansThe above is a good solution if you know that the
An alternative is to arrange for the This is usually a bad idea, because you don't know how long
will elapse between the moment when the On the other hand, if the You can get around this by using a
Here's how we could rewrite
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 Of course both (a) and (b) represent arbitrary delays. You
could get rid of the arbitrary delay before (b) by periodically
checking whether the Polling with a
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 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 combine »
Related Topics >>
Open JDK Comments
Comments are listed in date ascending order (oldest first)
|
||
|