Monday, April 13, 2009

Exporting POJO via JMX and Groovy

In several previous blog postings, I have described how to expose Java classes via Java Management Extensions (JMX). In particular I have described exposing non-JMX-aware classes via JMX using direct ModelMBeans, via the Spring Framework, via Apache Commons Modeler, and via EasyMBean. Another approach for easily exposing a non-JMX class as a JMX MBean is to let Groovy do the work.

Groovy JMX Builder is now included in the core Groovy distribution and so no longer requires a separate download. I am using Groovy 1.6.1 for the examples in this posting. The GroovyDoc-based documentation for the groovy.jmx.builder package is included with the Groovy 1.6.1 API documentation.

To demonstrate use of Groovy and Groovy JMX Builder to expose a POJO as a JMX MBean, I will first start with a POJO. This class is shown next (I've left out comments for brevity):

HelloJmx.java


package dustin.examples;

public class HelloJmx
{
private String salutation = "Hello";

public HelloJmx() {}

public String hello(final String addressee)
{
return this.salutation + ", " + addressee;
}

public String getSalutation()
{
return this.salutation;
}

public void setSalutation(final String newSalutation)
{
this.salutation = newSalutation;
}
}


This simple Java POJO can be exposed easily as a JMX MBean via Groovy as shown in this Groovy code sample.

exportHelloWorldViaBasicGroovyJmx.groovy


import dustin.examples.HelloJmx
import groovy.jmx.builder.JmxBuilder

JmxBuilder jmxBuilder = new JmxBuilder()
def hello = new HelloJmx()
jmxBuilder.export { bean(hello) }

println "Use JConsole or VisualVM to see exposed Calculator"
println "Press ENTER to exit."
Console console = System.console();
console.readLine()


With Groovy installed on my machine, running this script is easy as shown in the next screen snapshot:



As the screen snapshot indicates, running the Groovy script is very similar to running the Java launcher to run a Java application.

I can run a general JMX client such as JConsole or VisualVM against this Groovy-enabled JMX-exposed application. The next screen snapshot shows how to connect to the Groovy enabled JMX-exposed application on the local machine (it lacks an obvious label, but it is the process that is not JEdit and not JConsole itself).


Once I connect to this Groovy/JMX-enabled application, I can view its MBeans as shown in the next screen snapshot.



Although the HelloJmx class provided a setSalutation method, the Salutation attribute is read-only as shown in the above screen snapshot. This is the default behavior of Groovy's JMX Builder. In addition, the ObjectName of the bean is the default-based jmx.builder.ExportedObject:type=dustin.examples.HelloJmx@11845181.

It would be nice to customize the JMX support a little more. This is easily done with Groovy JMX Builder as I will demonstrate in the remainder of this blog post.

To expose the "setter" for the Salutation attribute in the HelloJmx example, I need to make it "writable." Editing the Groovy script to explicitly state descriptive information about how the Salutation attribute should be exposed makes this possible and is demonstrated in the next code listing.

exportHelloWorldViaAdvanceGroovyJmx.groovy


import dustin.examples.HelloJmx
import groovy.jmx.builder.JmxBuilder
JmxBuilder jmxBuilder = new JmxBuilder()
def hello = new HelloJmx()
jmxBuilder.export
{
bean(target:hello,
name:"dustin.example.hello:type=GroovyJmx",
attributes: [
"Salutation":[desc: "Salutation to use with name.",
readable: true,
writable: true,
defaultValue:"Hello"] ],
operations: "*")
}
println "Use JConsole or VisualVM to see exposed Hello World"
println "Press ENTER to exit."
Console console = System.console();
console.readLine()


In the above Groovy script, I overrode the default ObjectName with the new name of "dustin.example.hello:type=GroovyJmx" ("type" gets special treatment in JConsole). I also expressed more details about the Salutation attribute, including specifying that it is writable. Two screen snapshots showing the results of the attribute metadata description and the customized object name are shown next:






Conclusion

Groovy provides another handy approach for exposing Java objects via JMX for management and monitoring.

3 comments:

David Chang said...

very nice post, but I have problem to run it with the following command, can you tell me what did I do wrong please ?

Thanks,
David

[david@david-fedora jmx]$ groovy -classpath /usr/share/groovy/lib/groovy-1.6.1.jar:/usr/java/jdk1.6.0_13/jre/lib/rt.jar:. exportHelloWorldViaBasicGroovyJmx.groovy
java.lang.SecurityException: Prohibited package name: java.lang
at java.lang.ClassLoader.preDefineClass(ClassLoader.java:480)
at java.lang.ClassLoader.defineClass(ClassLoader.java:615)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:124)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:260)
at java.net.URLClassLoader.access$000(URLClassLoader.java:56)
at java.net.URLClassLoader$1.run(URLClassLoader.java:195)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at org.codehaus.groovy.tools.RootLoader.oldFindClass(RootLoader.java:152)
at org.codehaus.groovy.tools.RootLoader.loadClass(RootLoader.java:124)
at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:621)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:124)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:260)
at java.net.URLClassLoader.access$000(URLClassLoader.java:56)
at java.net.URLClassLoader$1.run(URLClassLoader.java:195)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at org.codehaus.groovy.tools.RootLoader.oldFindClass(RootLoader.java:152)
at org.codehaus.groovy.tools.RootLoader.loadClass(RootLoader.java:124)
at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
at org.codehaus.groovy.tools.GroovyStarter.rootLoader(GroovyStarter.java:98)
at org.codehaus.groovy.tools.GroovyStarter.main(GroovyStarter.java:130)

Dustin said...

David,

I think the explicit inclusion of the runtime JAR (rt.jar) on the classpath is what is causing that SecurityException. This JAR should be available implicitly/automatically anyway. I suggest trying to remove the explicit rt.jar from the classpath. You may also want to make sure that you have JAVA_HOME set.

David Chang said...

Thanks Dustin, your suggestion works :) I didn't set JAVA_HOME due to the current JDK was set by the command 'alternatives' so I don't have a fixed path to JDK.