[update: fixed Ruby script by using ManagementFactory::newPlatformMXBeanProxy instead of MBeanServerInvocationHandler::newProxyInstance based on Daniel comment]
I needed to write a script to automate the management of a Java application using JMX which I could put in a crontab.
I found some explanation on how to script JMX in jconsole with BeanShell or with Groovy. It’s a good approach but:
- I want the scripts to be executable in a cron (and not from jconsole GUI)
- I’d prefer to write them in Ruby
It turns out that it is straightforward to write such scripts using JRuby and Java 5 or later. To keep things simple, let say I want to write a script which will be called periodically to run the garbage collector on a long-running Java application.
First let use jconsole as our long-running application. We will start jconsole with all the System properties required to manage its JVM through JMX/RMI connector:
jconsole -J-Dcom.sun.management.jmxremote \
-J-Dcom.sun.management.jmxremote.port=3000 \
-J-Dcom.sun.management.jmxremote.ssl=false \
-J-Dcom.sun.management.jmxremote.authenticate=falsegc.rb
The script to run the garbage collector is simple:
module JMX require 'java' include_class 'java.util.HashMap' include_package 'java.lang.management' include_package 'javax.management' include_package 'javax.management.remote' url = JMXServiceURL.new "service:jmx:rmi:///jndi/rmi://localhost:3000/jmxrmi" connector = JMXConnectorFactory::connect url, HashMap.new mbsc = connector.mbean_server_connection memory_mbean = ManagementFactory::newPlatformMXBeanProxy mbsc, "java.lang:type=Memory", MemoryMXBean::java_class memory_mbean.verbose = !memory_mbean.verbose puts "verbose = #{memory_mbean.verbose}" memory_mbean.gc end
This gc.rb script is doing several things:
- it
require 'java'so our scripts will run only on JRuby. - it includes all the required classes and packages .
- it connects to jconsole’s MBeanServer using the “standard” JMX URL
service:jmx:rmi:///jndi/rmi://localhost:3000/jmxrmi - it creates a proxy of the
MemoryMXBeanbased on the MBeanServerConnectionmbscand the ObjectNamejava.lang:type=Memory - for “fun”, it toggles the
verboseboolean of thememory_mbean - and finally, it runs the garbage collector by calling
memory_mbean.gc
When we run the script:
$ jruby gc.rb
verbose = truewe see that a gc has been performed by jconsole’s JVM:
[Full GC 3754K->3172K(6076K), 0.0895140 secs] [GC 3680K->3189K(6076K), 0.0008180 secs] [GC 3701K->3281K(6076K), 0.0016410 secs] [GC 3793K->3320K(6076K), 0.0014080 secs] ...
I just need to put the call to this script in my crontab and I’m done.
MBeans in JRuby classpath
Writing such scripts works fine for most cases but there is a caveat: it requires to have the MBean Java class in JRuby classpath to be able to create a proxy using either MBeanServerInvocationHandler::newProxyInstance or ManagementFactory::newPlatformMXBeanProxy (if the MBean is a MXBean as it is the case in this script).
If you have access to the jar containing the MBeans, you just need to put it $JRUBY_HOME/lib/ directory to be able to access it from Ruby scripts.
Still it should possible to map JMX API with Ruby metamodel to get rid of this requirement and develop “JMX-generic” scripts.
Next Step: Generic JMX Scripts
The next step would be to create Ruby objects representing JMX MBeans where their attributes & operations will be added dynamically to
the corresponding Ruby instances by examining the MBeanInfo.
The previous example would look like:
memory_mbean = MBeanObject.new mbsc, "java.lang:type=Memory" memory_mbean.verbose = !memory_mbean.verbose memory_mbean.gc
The big change will be that we will no longer require to have the MBeans in JRuby classpath making it simpler to manage many Java applications (e.g. Tomcat, JBoss, etc.)
To be continued…

March 22nd, 2007 at 20:26
Hi Jeff,
Nice blog entry, but I’d like to bring something to your attention:
The MBeans used in the JVM Management & Monitoring API are not regular MBeans, they’re MXBeans. Although generic support for custom MXBeans was only added in JDK 6, JDK 5 had a built-in support for M&M MXBeans.
In the general case, using MBeanServerInvocationHandler.newProxyInstance does not work for MXBean interfaces. Instead you should be using ManagementFactory.newPlatformMXBeanProxy as I have explained in one of my posts: http://blogs.sun.com/jmxetc/entry/how_to_retrieve_remote_jvm#comment-1174563727000
MBeanServerInvocationHandler.newProxyInstance works only for regular MBeans.
In other words:
So you should correct your code and use ManagementFactory.newPlatformMXBeanProxy instead of MBeanServerInvocationHandler.newProxyInstance.
Hope this helps,
– daniel
May 31st, 2007 at 10:57
[...] e_method instead of eval based on a poignant explanation of eval-less metaprogramming. In Part I, I created a JRuby script to manage a Java application using JMX. In this e [...]
July 16th, 2007 at 10:45
[...] :newPlatformMXBeanProxy instead of MBeanServerInvocationHandler::newProxyInstance based on Daniel comment] I needed to write a script to automate the managemen [...]
September 5th, 2007 at 17:02
[...] of JMX on JRuby came up recently and I decided to play around. I found a great starter on Jeff Mesnil’s blog, but I decided I hated the syntax. Ruby has spoiled me. Ac [...]