Composing Traits and Declarative Validation

Scala’s traits are a nice fit for JSR-303 validation. Here’s an example. Suppose we have a web service interface that has methods like:

@WebService
trait Notification {
  def deleteTopic(apiKey: String, topicId: Long)
  def getSubscriber(apiKey: String, userId: String): Subscriber
  def unsubscribe(apiKey: String, userId: String, topicId: Long, context: SubscriptionContext)
  //...
}

Notice that the methods share many but not all of the same parameters. For example, all of them take apiKey. Our validation code should be factored to handle common parameters independently.  JSR-303 supports this very well. However, we also want to return validation errors to the caller with names that correspond directly to the web service method parameters. For example, if deleteTopic() is called with an invalid topicId, we want to respond with a fault like:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
   <soap:Body>
      <soap:Fault>
         <faultcode>soap:Client</faultcode>
         <faultstring>Errors in request</faultstring>
         <errors>
           <error>
             <inputElement>topicId</inputElement>
             <errorMessage>99 is invalid</errorMessage>
          </error>
        </errors>
      </soap:Fault>
   </soap:Body>
</soap:Envelope>

The parameter name/validation error requirement would be a pain to handle in Java because the validation object would need to be composed as a hierarchy of individual objects. The hierarchy would be reflected in the validation errors and would have to be manually flattened before returning the errors to the caller.

In our web service implementation we will validate the parameters by creating a flat object containing the method parameters as properties.  Then we will call throwFaultIfInvalid() which will validate the parameters and throw a Fault exception if there are errors. For example:

  def unsubscribe(apiKeyIn: String, userIdIn: String, topicIdIn: Long, context: SubscriptionContext) {
    val cmd = new UnsubscribeCmd {
      apiKey = apiKeyIn
      userId = userIdIn
      topicId = topicIdIn
      subscriptionContext = context
    }
    throwFaultIfInvalid(cmd)

    // Logic to do unsubscribe...
  }

Above, cmd is a stack of five validation traits on top of a base Object.

@UnsubscribeCmdValid
trait UnsubscribeCmd extends UserIdCmd with TopicIdCmd with SubscriptionContextCmd {
  var subscription: Subscription = _
}

@UserIdValid
trait UserIdCmd extends ApiKeyCmd {
  @NotEmpty var userId: String = _
  lazy val subscriber: Option[Subscriber] = // findSubscriber(userId)
}

@TopicIdValid
trait TopicIdCmd extends ApiKeyCmd {
  @NotZero var topicId: Long = _
  lazy val topic: Option[Topic] = // findTopic(topicId)
}

@SubscriptionContextValid
trait SubscriptionContextCmd extends ApiKeyCmd {
  @NullInvalid var subscriptionContext: SubscriptionContext = _
}

@ApiKeyValid
trait ApiKeyCmd {
  @NotEmpty var apiKey: String = _
  lazy val application: Option[Application] = // findApplication(apiKey)
}

The validator for UnsubscribeCmd looks like:

class UnsubscribeCmdValidator extends Validator[UnsubscribeCmdValid, UnsubscribeCmd] {
  @Autowired var subscriptionDao: SubscriptionDao = _

  def isValid(obj: UnsubscribeCmd, context: ConstraintValidatorContext): Boolean = {

    ifSome(for (subscriber  obj.subscription = subscription
    }
  }

Nice! Traits allow us to compose the validators together in a way that results in very clean validation objects and validation code.

Side note

Sadly, as of Scala 2.9 you can’t create custom JSR-303 annotations (like @UnsubscribeCmdValid above) which require RetentionPolicy.RUNTIME in Scala. Go, right now, and vote for SI-32 so we don’t have to keep writing these in Java.   Thank you for your support.

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

  //....
}

Hello, world!

Enough futzing with themes, let’s get to coding!

  object HelloWorld {
    def main(args: Array[String]) {
      println("Hello, world!")
    }
  }