|
|
||
Eamonn McManus's Blog
«Going beyond JDK 5.0's out-of-the-box management |
Main
| Using annotation processors to save method parameter names »
Adding Descriptors to MBeans in TigerPosted by emcmanus on June 08, 2006 at 09:34 AM | Comments (0)Mustang (Java SE 6) includes the ability to give additional information about MBeans to management clients via Descriptors, as I described previously. But what if you are not yet able to migrate to the Mustang platform? As I hinted in that previous entry, all is not lost. You can still use Descriptors, though it's more work. We often hear from developers that they'd dearly love to move to the latest and greatest Java SE platform, but they can't. Their app server isn't yet supported on that platform; or their giant mission-critical financial application would require an enormous test campaign on the new platform, that the QA department doesn't have the resources to do; or their pointy-haired boss fears the unknown or doesn't see what the fuss is all about. So if there's some way to make the shiny new features available on older platforms, these developers are all for it! DescriptorsDescriptors allow you to give additional information about MBeans to management clients. For example, a Descriptor on an MBean attribute might say what units it is measured in, or what its minimum and maximum possible values are. As of Mustang, Descriptors are a basic part of the JMX API and are available in all types of MBeans. In particular, the Mustang version of the jconsole tool will display an MBean's descriptors, as the following screenshot illustrates:
The descriptor here includes some standard
descriptor fields, namely You can perfectly well use Mustang jconsole to connect to a JMX agent running on Tiger (J2SE 5.0). (You can even connect to an earlier J2SE version such as 1.4, though in that case you will need to have the JMX and JMX Remote API classes in the classpath of your application.) So even if you can't migrate your application to Mustang, it's perfectly feasible to install a second JDK, the Mustang one, and just use its jconsole to connect to your application running on Tiger. Likewise you could write your own management client running on Mustang, even though your application is still running on Tiger. So if you could somehow arrange for your MBean to contain descriptors in Tiger, then you could show them with your Mustang jconsole or access them in your Mustang management client. And this is in fact possible. Descriptors on TigerThe trick is that descriptors have always existed in the JMX
API, but only in conjunction with Model MBeans. Model MBeans
are an advanced (not to say obscure) part of the API, so you
probably haven't paid much attention to them. But the key point
here is that the
Model MBean package defines a set of subclasses of the
standard metadata classes such as Suppose we want to create a Standard MBean, but with descriptor fields as in the snapshot above. Here's how we might go about it. First, we create a subclass of MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); ObjectName name = new ObjectName("com.example.MyApp:type=Test"); Map<String, Object> descriptorMap = new HashMap<String, Object>(); descriptorMap.put("author", "Éamonn McManus"); descriptorMap.put("immutableInfo", "true"); descriptorMap.put("interfaceClassName", TestMBean.class.getName()); descriptorMap.put("mxbean", "false"); Test test = new Test(); Object mbean = new DescriptorStandardMBean(test, TestMBean.class, descriptorMap); mbs.registerMBean(mbean, name); Here's what the
public class DescriptorStandardMBean extends StandardMBean {
private final Map<String, ?> mbeanDescriptorMap;
public <T> DescriptorStandardMBean(
T impl, Class<T> intf,
Map<String, ?> mbeanDescriptorMap)
throws NotCompliantMBeanException {
super(impl, intf);
this.mbeanDescriptorMap = mbeanDescriptorMap;
}
public DescriptorStandardMBean(
Class<?> intf,
Map<String, ?> mbeanDescriptorMap)
throws NotCompliantMBeanException {
super(intf);
this.mbeanDescriptorMap = mbeanDescriptorMap;
}
The The Now let's look at how we attach our descriptor to the MBean's
@Override
public MBeanInfo getMBeanInfo() {
MBeanInfo mbi = super.getMBeanInfo();
if (mbi instanceof ModelMBeanInfoSupport) // we already rewrote it
return mbi;
Descriptor d = new DescriptorSupport();
for (Map.Entry<String, ?> entry : mbeanDescriptorMap.entrySet())
d.setField(entry.getKey(), entry.getValue());
d.setField("descriptorType", "mbean");
if (d.getFieldValue("name") == null)
d.setField("name", mbi.getClassName());
mbi = new ModelMBeanInfoSupport(
mbi.getClassName(),
mbi.getDescription(),
makeModelMBeanAttributeInfos(mbi.getAttributes()),
makeModelMBeanConstructorInfos(mbi.getConstructors()),
makeModelMBeanOperationInfos(mbi.getOperations()),
null, // no ModelMBeanNotificationInfo[]
d);
cacheMBeanInfo(mbi);
return mbi;
}
We override To make the descriptor, we use the Model MBeans impose a gratuitously annoying rule, which is that
every descriptor must have a Next we make the Finally, we call Now let's look at that tedious code I mentioned:
private static ModelMBeanAttributeInfo[]
makeModelMBeanAttributeInfos(MBeanAttributeInfo[] mbais) {
ModelMBeanAttributeInfo[] mmbais = new ModelMBeanAttributeInfo[mbais.length];
for (int i = 0; i < mbais.length; i++) {
MBeanAttributeInfo mbai = mbais[i];
ModelMBeanAttributeInfo mmbai = new ModelMBeanAttributeInfo(
mbai.getName(),
mbai.getType(),
mbai.getDescription(),
mbai.isReadable(),
mbai.isWritable(),
mbai.isIs());
mmbais[i] = mmbai;
}
return mmbais;
}
private static ModelMBeanOperationInfo[]
makeModelMBeanOperationInfos(MBeanOperationInfo[] mbois) {
ModelMBeanOperationInfo[] mmbois = new ModelMBeanOperationInfo[mbois.length];
for (int i = 0; i < mbois.length; i++) {
MBeanOperationInfo mboi = mbois[i];
ModelMBeanOperationInfo mmboi = new ModelMBeanOperationInfo(
mboi.getName(),
mboi.getDescription(),
mboi.getSignature(),
mboi.getReturnType(),
mboi.getImpact());
mmbois[i] = mmboi;
}
return mmbois;
}
private static ModelMBeanConstructorInfo[]
makeModelMBeanConstructorInfos(MBeanConstructorInfo[] mbcis) {
ModelMBeanConstructorInfo[] mmbcis = new ModelMBeanConstructorInfo[mbcis.length];
for (int i = 0; i < mmbcis.length; i++) {
MBeanConstructorInfo mbci = mbcis[i];
ModelMBeanConstructorInfo mmbci = new ModelMBeanConstructorInfo(
mbci.getName(),
mbci.getDescription(),
mbci.getSignature());
mmbcis[i] = mmbci;
}
return mmbcis;
}
Each of these methods has the same outline. We start with the
original If you're like me, you'll be very annoyed at the fact that
these three methods look exactly the same (four if you add
private static <I extends MBeanFeatureInfo, M extends I> M[]
makeModelMBeanFeatureInfos(I[] mbfis, Class<M> modelClass) {
M[] mmbfis = newArray(modelClass, mbfis.length);
Converter<M> converter = getConverter(modelClass);
for (int i = 0; i < mbfis.length; i++) {
I mbfi = mbfis[i];
M mmbfi = modelClass.cast(converter.convert(mbfi));
mmbfis[i] = mmbfi;
}
return mmbfis;
}
A static initializer then initializes a
So, OK, now we've defined Wait a minute! We're missing something important: } Right. So now let's see what jconsole looks like connecting to our test program on Tiger:
Success! Well, mostly. You can see that the descriptor fields
we defined are indeed present: You can use similar tricks to add descriptors to individual
attributes. In the
Finally, note that there is no
Everything here will work on J2SE 1.4 as well, except you'll have to add the JMX and JMX Remote APIs to the classpath of your application, and create your own MBean Server and RMI Connector instead of the ones built-in to Tiger. Bookmark blog post: CommentsComments are listed in date ascending order (oldest first) | Post Comment | ||
|
|