Targeting annotations to Javabean accessors in Scala

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.

Annotation set on "started" field



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 = _

  //....
}

Speak Your Mind

*