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 = _ //.... }