Scala Quick Start – Making Dropwizard configuration more Scala friendly

Travel very far with Dropwizard and Scala and you’ll eventually encounter a few obstacles. Here’s a solution to one of them, generating Scala collections when processing the configuration file

In Scala Quick Start – Developing a microservice with Dropwizard we created a simple-as-possible password validation microservice using Dropwizard and just the Scala language. Tavelling down that short, short road we didn’t encounter any issues specific to using Scala with this Java-focussed framework. Travel much further down that road though and you will eventually encounter a bump or two.

As mentioned in the previous post there are several ways to ease those bumps. We could avoid them by sticking with Java for the Dropwizard service wrapper and delegate to our nice, clean Scala code for the business logic. We could alternatively take advantage of the work done by those who’ve been down this road before and start instead with the dropwizard-scala package.

Or we can ride these bumps ourselves and perhaps learn a lesson or two along the way.

The code for this updated example is available on GitHub.

A less micro microservice

The first version of our passwordValidator service supported a single password policy with some very simple YAML configuration:-

rule: ^(?=.*[a-z])(?=.*[A-Z])(?!.*\\s).{6,}$
help: Password must be at least six characters with at least one
          upper-case and one lower-case character

In this post we’re going to expand the service a little to support multiple, named password policies and our YAML file will therefore contain a policy map instead:-

policies:
  simple:
    rule: ^(?=.*[a-z])(?=.*[A-Z])(?!.*\\s).{6,}$
    help: Password must be at least six characters with at least one
              upper-case and one lower-case character
  strong:
    rule: ^(?=.*\\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\\s).{8,}$
    help: Password must be at least eight characters with at least one
              upper-case character, one lower-case character and one digit

We now have two policies, simple and strong, the latter requiring an extra couple of characters and at least one number. Since our configuration will now have multiple policy instances, a logical refactoring of our Configuration sub-class would be to move the policy details out into a separate, re-usable class as follows:-

class PasswordPolicy {
  @JsonProperty var rule : String = _
  @JsonProperty var help : String = _
}

class PasswordValidatorConfiguration extends Configuration {
  @JsonProperty var policies : Map[String, PasswordPolicy] = _   
}

The Jersey resource that provides our microservice endpoint needs some attention at this point:-

@Path("/validatePassword")
class PasswordValidatorResource(rule: String, help: String) {
  @GET def validate(@QueryParam("password") password: String) : String = {
    if(password.matches(rule)) 
      "OK" 
    else 
      help
  }
}

For a start we need to replace the rule and help constructor parameters with our policy map and change the implementation to look up the policy details from the map. This also raises the question of how it knows which policy to apply. We’ll add an extra query parameter to take care of that too:-

@Path("/validatePassword")
class PasswordValidatorResource(policies : Map[String,PasswordPolicy]) {
  @GET def validate(@QueryParam("policy") policy: String,
                    @QueryParam("password") password: String) : String = {
    if(password.matches(policies(policy).rule))
      "OK"
    else
      policies(policy).help
  }
}

So far, so simple. The last tweak we need to make is to pass in the policy map when we construct our Jersey resource during service initialisation-

class PasswordValidatorApplication extends
        Application[PasswordValidatorConfiguration] {
  def run(configuration: PasswordValidatorConfiguration,
        environment: Environment) : Unit = {
    environment.jersey().register(new PasswordValidatorResource(
        configuration.policies))
  }
}

And we’re done.

Sadly though, it doesn’t work.

$ sbt "run server etc/passwordValidator.yaml"
[info]Running PasswordValidatorApplication server etc/passwordValidator.yaml
etc/passwordValidator.yaml has an error:
  * Failed to parse configuration at: policies; Can not construct instance
    of scala.collection.immutable.Map, problem: abstract types either 
    need to be mapped to concrete types, have custom deserializer, 
    or be instantiated with additional type information
 at [Source: N/A; line: -1, column: -1] (through reference chain: 
     PasswordValidatorConfiguration["policies"])

Our service won’t start because out-of-the-box Dropwizard only knows how to marshal the configuration data into Java classes, whereas our Scala configuration classes want to see an instance of scala.collection.immutable.Map.

Running home to Java

We could run home to Java at this point and change our Configuration class to mint a Java Map instead:-

class PasswordValidatorConfiguration extends Configuration {
  @JsonProperty var policies : java.util.Map[String, PasswordPolicy] = _   
}

This indeed works and initially we might not be too fazed by letting some Java classes through into our marshalled configuration. We might be less chipper though when we realise the price we have to pay in terms of losing some of Scala’s syntactic sauce:-

@Path("/validatePassword")
class PasswordValidatorResource(
      policies : java.util.Map[String,PasswordPolicy]) {
  @GET def validate(@QueryParam("policy") policy: String, 
                    @QueryParam("password") password: String) : String = {
    if(password.matches(policies.get(policy).rule)) 
      "OK" 
    else 
      policies.get(policy).help
  }
}

We could get around that by converting the Java map to a Scala map when we instantiate our Jersey resource. This is actually very easy to do if we leverage the JavaConverters decorators and accept a mutable Map, rather than an immutable one, in our resource class:-

import scala.collection.JavaConverters._

…

class PasswordValidatorApplication extends
        Application[PasswordValidatorConfiguration] {
  def run(configuration: PasswordValidatorConfiguration,
        environment: Environment) : Unit = {
    environment.jersey().register(new PasswordValidatorResource(
          configuration.policies.asScala))
  }
}

Enabling Dropwizard to mint Scala collections

It would be much nicer though if we could make Dropwizard create Scala collections when it marshals the configuration. Pleasingly this also turns out to be the easiest solution to implement.

At its heart, Dropwizard uses Jackson to marshal its configuration into objects and Jackson has a useful little Scala language package which gives us the necessary deserializer classes. To plug these in, first of all we need to add the right build for our Scala version to build.sbt:-

name := "passwordValidator"
version := "1.0.0"
scalaVersion := "2.11.8"

libraryDependencies += "io.dropwizard" % "dropwizard-core" % "1.0.5"
libraryDependencies += "com.fasterxml.jackson.module" % 
    "jackson-module-scala_2.11" % "2.8.4"

Then we need to register its DefaultScalaModule with the Jackson ObjectMapper that Dropwizard uses. We can get at this through the io.dropwizard.setup.Bootstrap class:-

public class Bootstrap<T extends Configuration> {
    …
    public ObjectMapper getObjectMapper() {
        return objectMapper;
    }
}

An instance of which is passed to the Application’s initialize method, which we can override in order to tweak the ObjectMapper:-

import com.fasterxml.jackson.module.scala.DefaultScalaModule
import io.dropwizard.setup.Bootstrap

…

class PasswordValidatorApplication extends
        Application[PasswordValidatorConfiguration] {
  def run(configuration: PasswordValidatorConfiguration,
        environment: Environment) : Unit = {
    environment.jersey().register(new PasswordValidatorResource(
        configuration.policies))
  }

  override def initialize(bootstrap:
        Bootstrap[PasswordValidatorConfiguration]) : Unit = {
    bootstrap.getObjectMapper().registerModule(new DefaultScalaModule)
  }  
}

With the Scala deserialisers installed our configuration now gets loaded up with Scala Map instances and our service starts correctly:-

$ curl http://localhost:8080/validatePassword?policy=simple&password=IAmValid
OK

$ curl http://localhost:8080/validatePassword?policy=strong&password=IAmValid
Password must be at least eight characters with at least one upper-case 
character, one lower-case character and one digit

And as an added bonus, our more Scala friendly Jackson ObjectMapper alleviates the need for us to annotate our configuration properties with @JsonProperty. We can clean these two classes up a little as follows:-

class PasswordPolicy {
  var rule : String = _
  var help : String = _
}

class PasswordValidatorConfiguration extends Configuration {
  var policies : Map[String, PasswordPolicy] = _   
}

Leave a Reply

Your email address will not be published. Required fields are marked *