In Part I, I created a JRuby script to manage a Java application using JMX.

In this entry, I'll explain how to remove the dependency on the MBean proxies by taking advantage of Ruby to dynamically add the MBean attributes to a Ruby object representing the MBean.

MBean proxies

Using MBean proxies (using either ManagementFactory.newPlatformMXBeanProxy() or MBeanServerInvocationHandler.newProxyInstance()) is always the simplest way to interact with MBeans. However it also means that the MBean interfaces must be available to the JMX client. This can be sometimes problematic. For example, to use a proxy on your MBean (as opposed to the JVM MBeans that I'm using in my examples for convenience), you must add the jar containing your MBeans in JRuby classpath. If it is not a problem, good news, go for it. However if it makes the setup too complex, let's try to break this dependency.

MBean attributes

To make it simple, I'll focus on the MBean attributes. We will create a Ruby object which makes it simple and elegant to read the value of a MBean attribute without creating a proxy of the MBean (by simple & elegant, I mean something else than calling directly MBeanServerConnection.getAttribute(ObjectName, String)).

We will again use jconsole as our managed java application (see Part I to start jconsole with all System properties required to manage it remotely).

Going directly to the conclusion, the code to display a list of all loggers defined by java.util.logging is

JRuby's MBean class

MBean is a Ruby class which creates objects with specific methods to access all the attributes associated to the ObjectName passed in parameter using the MBeanServerConnection to connect to a remote MBeanServer.

This class only defines a constructor initialize which accepts a MBeanServerConnection and a String representing an ObjectName which is used to create a @object_name field.

It retrieves the MBeanInfo associated to the @object_name thanks to @mbsc. It then iterates on the MBeanAttributeInfo using info.attributes.

That's in this iterator that the magic happens:

self.class.instance_eval do
define_method mbean_attr.name do
@mbsc.getAttribute @object_name, "#{mbean_attr.name}"
end
end

It calls instance_eval to add a method to the instance class. This method is created using define_method with mbean_attr.name as the method name. It returns the value of the mbean attribute by calling @mbsc.getAttribute for the given mbean_attr.name of the @object_name of the MBean.

What does this mean? Executing logging = MBean.new mbsc, "java.util.logging:type=Logging" will create the logging object and add a method to this object to access the LoggerNames attribute: