Grails JMX Configuration Using Annotations

Post by Matt Nohr

I have a grails application that uses the JMX plugin to expose MBeans. The good part is that it automatically exposes some built in MBeans like hibernate and log4j. The bad part is that it exposes too much information for my services and hiding that information can be cumbersome. I wanted to get around this by using the JMX plugin for most of my application, but allow Spring’s JMX annotation support for more fine-grained control where needed.

Problem With Just the Plugin

Using the JMX plugin should work in most cases. However, the dynamic power of it can also cause complexity. For example, here is a simple service that uses the JMX plugin:

class MyExposedService {
    def myOtherService
    def exposedAttribute
    def hiddenAttribute

    static expose = ['jmx']

    //I want this exposed (but not setter)
    def getExposedAttribute() {
        exposedAttribute
    }

    //should not be exposed
    def doNotExposeMe(){
        //...
    }
}

Here is what you get in VisualVM (or jconsole):

Visual VM with JMX Plugin

You can see all of the dynamically injected grails magic is exposed. If you were to look at the MBean attributes you would also see things like MetaClass.

You could mark some methods as private or use the “excludeMethods” mechanism from the plugin, but it leads to a fairly messy (in my opinion) implementation, especially in non-trivial cases. I wanted to avoid this as well because any new methods that were added would automatically be exposed if I forgot to add them to the list of excluded methods. If interested, this is what you would have to add to exclude method for this test:

static jmxexpose = [
	'excludeMethods':
	'doNotExposeMe,'+
	'getExpose,setExpose,'+
	'getJmxexpose,setJmxexpose,'+
	'getMetaClass,setMetaClass,'+
	'getMyOtherService,setMyOtherService,'+
	'getHiddenAttribute,setHiddenAttribute']

Solution

I wanted to use Spring’s annotation support in addition to the JMX plugin. My final class should look like this:

import org.springframework.jmx.export.annotation.ManagedAttribute
import org.springframework.jmx.export.annotation.ManagedResource

@ManagedResource
class MyExposedService {
    def myOtherService
    def exposedAttribute
    def hiddenAttribute

    static expose = ['jmx']

    @ManagedAttribute(description="Some exposed attribute")
    def getExposedAttribute() {
        exposedAttribute
    }

    //should not be exposed
    def doNotExposeMe(){
        //...
    }
}

There is one annotation at the class level, and one for each attribute that should be exposed. You could also use @ManagedOperation where appropriate.

To use annotations I first created a new MBean exporter in grails-app/conf/spring/resources.groovy:

import org.springframework.jmx.export.MBeanExporter
import org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource
import org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler
import org.springframework.jmx.support.MBeanServerFactoryBean

beans = {
    //create/find the mbean server
    mbeanServer(MBeanServerFactoryBean) { 
        locateExistingServerIfPossible=true
    }

    //use annotations for attributes/operations
    jmxAttributeSource(AnnotationJmxAttributeSource)
    assembler(MetadataMBeanInfoAssembler) {
        attributeSource=jmxAttributeSource
    }

    //create an exporter that uses annotations
    annotationExporter(MBeanExporter){
        server=mbeanServer
        assembler=assembler
        beans=[:]
    }
}

The trick here is to not use “exporter” as the name of MBeanExporter. This is because that is what the plugin uses and if you change the behavior of that MBeanExporter you will get errors with the hibernate and datasource MBeans the plugin automatically configures because they do not use annotations. Any other name should work.

You could also use the “name” attribute on the @ManagedResource annotation in the class to dynamically name the MBeans using MetadataNamingStrategy, but I choose this approach to match the naming with what is in the JMX plugin.

The specific beans are set up in Bootstrap.groovy like this:

import org.codehaus.groovy.grails.commons.ApplicationAttributes;
import org.springframework.jmx.export.MBeanExporter

class BootStrap {
    def grailsApplication
    def init = { servletContext ->
        bootstrapAnnotationJmx(servletContext.getAttribute(ApplicationAttributes.APPLICATION_CONTEXT))
    }
    def destroy = {
    }

    private def bootstrapAnnotationJmx(ctx){
        //pull out the app name to use as mbean domain
        def appName = grailsApplication.metadata['app.name']

        //create the service(s) to expose
        def exposed = ctx.getBean('myExposedService')

        //add service(s) to the annotation exporter
        MBeanExporter annotationExporter = ctx.getBean("annotationExporter")
        annotationExporter.beans."${appName}:name=MyExposedService,type=services" = exposed
        annotationExporter.unregisterBeans()
        annotationExporter.registerBeans()
    }
}

This could not be done in resources.groovy because the DI work is not done when resources.groovy is loaded, so items like the reference to MyOtherService would get set to null.

And the result matches what I wanted:

VisualVM Jmx Using Annotations

Once this is set up you can use Spring’s configuration to configure notifications or other advanced JMX features not supported by the plugin.

Resources
JMX Plugin: http://grails.org/plugin/jmx
Spring JMX reference: http://static.springsource.org/spring/docs/2.0.x/reference/jmx.html
MBean Export the Groovy Way: http://grails.org/MBean+export+the+Groovy+way

This entry was posted in Agile Processes and tagged , , , , . Bookmark the permalink.

Related Posts:

2 Responses to Grails JMX Configuration Using Annotations

  1. Pingback: Questa settimana in Grails (2012-21) - luca-canducci.com - Il blog di Luca Canducci: notizie, tips e nuove tecnologie dal mondo dell’IT.

  2. Pingback: An Army of Solipsists » Blog Archive » This Week in Grails (2012-21)

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>