JMX Managed Bean Configuration in Grails

I wrote this up at least 16 months ago and never posted it. I was working in Grails 2.x.x at the time, had moved to some other tech for a while and now find myself doing some Grails 3.0.x projects. I am still pretty captivated by the framework although it’s been much more heavily “springified” now. I’ll make some posts on these later experiences soon.

In the mean time…

There are cases that arise in any application where it might be useful to enable JMX to set or query certain properties “live” through the JMX protocol. A common case is the log level of an application. For Java applications, a JMX managed bean can be used with a boolean property to set the log level. We can do this with a Grails application as well.

There is a JMX plugin available in the open source community which automatically configures and exposes various objects as managed beans within the application. This plugin is located here. This plugin works great and is very useful, but by default exposes things including configuration information that you may not wish to expose.

As an alternative, it’s not too terribly hard to configure JMX managed beans as needed without using the plugin, rather by leveraging some mechanisms built into the underlying Spring container. The basic approach to this consists of the following tasks.

  • Create the class you want to expose via JMX and annotate the class with the appropriate Spring JMX support annotations.
  • Perform spring bean registration of the beans you want to expose via JMX.
  • Configure Spring Framework JMX support beans to enable JMX within the application.
  • Register the Spring beans as JMX managed beans within the bootstrap configuration.
  • At this point you will have a JMX managed bean that is accessible via a JMX client.

Below is a sample of a simple class, annotated with the appropriate Spring JMX support annotations.

Code blocks are all #groovylang. This has been tested to work with Grails version 2.2.4.

JmxApplicationProperty.groovy

package net.lharbour.sample

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

/*
 * This class contains the resource you will manage via a JMX console.
 */
@ManagedResource
class JmxApplicationProperty {

    private boolean logLevel = false

    @ManagedOperation
    void setLogLevel(boolean logLevel) {
       this.logLevel = logLevel
    }

    @ManagedAttribute
    boolean getLogLevel() {
       logLevel
    }
}

Below is a sample of the code you will need to insert for the configuration and initialization of JMX support and your JMX managed bean(s).

conf/spring/resources.groovy

import net.lharbour.sample.JmxApplicationProperty
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

// Place your Spring DSL code here
beans = {
   jmxApplicationProperty(JmxApplicationProperty) {} //this is an example of a basic spring bean with a single property that will be exposed via JMX
   //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=[:]
   }
}

Next you will need to set up the Spring AnnotationExporter. This will parse and configure the beans within the spring container that have the Spring JMX annotations.

conf/BootStrap.groovy

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']

     //find the beans to expose, currently just a single. There
     //may be a way to do this other than by name and possibly as an array.

     def exposed = ctx.getBean('jmxApplicationProperty')

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

Leave a Reply