On a recent project I had written a simple scheduling service. I wanted to expose some properties and methods to JMX so I could view the configuration at runtime and so I could perform some basic operations, like starting the service.
Spring provides a convenient way of exposing your classes to JMX using annotations, eliminating a ton of boilerplate. My initial stab at decorating the service for JMX looked like:
@ManagedResource
@Component("schedulerService")
class SchedulerServiceImpl extends SchedulerService {
@Value("${scheduler.enabled:false}")
@ManagedAttribute @BeanProperty
val enabled: Boolean = false
@ManagedAttribute @BeanProperty
var started: Boolean = _
@ManagedOperation
def start(): String = {
//...
}
//...
}
@ManagedAttribute has to be put on Javabean getters and setters so I added @BeanProperty to the properties I wanted to expose. Actually I only wanted to expose the getter, but initially I wasn’t worried about that part.
I fired up jconsole and saw that the start() method was exposed but the started and enabled properties were not. The usual tool for debugging Scala-Java interop, javap, doesn’t display information on annotations, so I used jclasslib bytecode viewer. There I saw that the annotation had actually been set on the private field that Scala generated, not the Javabean setter.
After some asking around, Iulian Dragos told me about the meta-annotations in the
scala.annotation.target package. These let you target annotations at specific accessors.
This allows you to create Javabean accessor-targeted annotations like this:
@(ManagedAttribute @beanGetter) @BeanProperty val enabled: Boolean = false
That is cool but pretty verbose. Fortunately you can clean it up with a type alias:
object JmxConfig {
type ManagedGetter = ManagedAttribute @beanGetter
type ManagedSetter = ManagedAttribute @beanSetter
}
So the end result can look like:
import JmxConfig._
@ManagedResource
@Component("schedulerService")
class SchedulerServiceImpl extends SchedulerService {
@Value("${scheduler.enabled:false}")
@ManagedGetter @BeanProperty
val enabled: Boolean = false
@ManagedGetter @BeanProperty
var started: Boolean = _
//....
}
