im dealing with a codebase that has builds MBeans (for export to jmx).
the original code simply builds an MBeanInfo instance:
@Override
public MBeanInfo getMBeanInfo() {
MBeanAttributeInfo[] attrs = //SLOW TO BUILD
return new MBeanInfo(...attrs...);
}
since the mbean attributes are expensive to build, and this method get called rather frequently (even with no jmx clients attached), i've tried creating a subclass of MBeanInto that lazily calculates those attributes:
public class LazyMBeanInfo extends MBeanInfo implements Externalizable {
private transient AttributeCallback callback = null;
private volatile MBeanAttributeInfo[] lazyAttrs = null;
public LazyMBeanInfo(...AttributeCallback callback...) throws IllegalArgumentException {
super(className, description, null, constructors, operations, notifications);
this.callback = callback;
}
@Override
public MBeanAttributeInfo[] getAttributes() {
MBeanAttributeInfo[] val = lazyAttrs;
if (val != null) {
return val.clone(); //match upstream behaviour
}
if (callback == null) {
throw new IllegalStateException("BUG");
}
val = callback.buildAttributes();
if (val == null) {
val = new MBeanAttributeInfo[0];
}
lazyAttrs = val;
return val.clone();
}
public interface AttributeCallback {
MBeanAttributeInfo[] buildAttributes();
}
}
the problem is that JMX (over RMI) serializes the MBeanInfo object, and then in jconsole (or jvisualVM) i get an error:
so - can i somehow implement Externalizable and serialize myself as an instance of the parent class? ideally i'd like this to work:
public class LazyMBeanInfo extends MBeanInfo implements Externalizable {
//same as before, plus:
@Override
public void writeExternal(ObjectOutput out) throws IOException {
MBeanInfo vanilla = new MBeanInfo(...);
out.writeObject(vanilla);
}
}
but it doesnt.
is this possible somehow ?
Unless you are using [highly dynamic] DynamicMBeans, I don't see why the MBeanInfo needs to be rebuilt for every call to getMBeanInfo()
, but ....
Your LazyMBeanInfo can be made to work (though I have not tested this specific case). MBeanInfo already implements Serializable, so what you want is for the serialization process to write out an MBeanInfo, not a LazyMBeanInfo, since the client probably doesn't have that class in its classpath. However, LazyMBeanInfo can implement this method:
Object writeReplace() throws ObjectStreamException;
at which point you write out the underlying MBeanInfo. See the Serializable JavaDoc, specifically:
Serializable classes that need to designate an alternative object to be used when writing an object to the stream should implement this special method with the exact signature:
ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;
In that way, the actual object can be an instance of LazyMBeanInfo, but what you write out can be an actual MBeanInfo, built from your cached lazyAttrs.
Having said that, rather than implementing a build-on-first-call approach, I would implement a build-before-first-use by simply building the full MBeanInfo when the MBean is first created, or when the MBean is registered. Then just return the pre-built MBeanInfo on each getMBeanInfo()
call.
To do this at MBean registration time, implement the MBeanRegistration interface, and build the cached MBeanInfo in the postRegister method.