Dependency injection using scala traits
Consider the following code example which does not depend on any kind of configuration system. We've just defined a trait, XConfiguration, which describes how we can get simple configuration data.
The idea here is to create a new trait which inheritates from XConfigurable :
One can object that it is less flexible than using for example spring and its xml configuration files, but remember that scala compiler can be easily embedded in any application, so you just have to use a scala configuration file which will get compiled at runtime... it will bring you very awesome dependencies injection features using pure scala.
Now I would like to modify XTopPlugin behavior in order to let it takes its configuration through typesafe config library, BUT without modifying anything in the code.
trait XConfigurable {
def getSetting(key:String):Option[String] = None
}
trait XPlugin extends XConfigurable {
def domain:String
}
trait XSniffPlugin extends XPlugin {
val period=getSetting("period") map {_.toLong} getOrElse 5000L
val periodInS=period/1000
}
abstract class XSSHPlugin extends XSniffPlugin {
val host = getSetting("host") getOrElse "127.0.0.1"
val username = getSetting("username") getOrElse util.Properties.userName
val password = getSetting("password")
val port = getSetting("port") map {_.toInt} getOrElse 22
def cmd:String
}
case class XTopPlugin(domain:String) extends XSSHPlugin {
val ignoreKeywords = getSetting("ignoreKeywords")
def cmd="top -c -b -d %d".format(periodInS)
override def toString =
"top each %ds through ssh to %s%s@%s".format(
period/1000, username, password map {":"+_} getOrElse "", host
)
}
The idea here is to create a new trait which inheritates from XConfigurable :
And then we'll take advantage of scala trait feature which allow us to stack a new behavior to an object at instanciation time :
object PlayWithTrait {
import com.typesafe.config.Config
import com.typesafe.config.ConfigFactory
import com.typesafe.config.ConfigException.{Missing,WrongType}
private lazy val context = ConfigFactory.parseString("""
|Defaults {
| period=5000
|}
|SSHPlugin = ${Defaults}
|SSHPlugin = {
| host:"127.0.0.1"
| port:22
|}
|TopPlugin = ${SSHPlugin}
|TopPlugin = {
| ignoreKeywords:"tty$"
|} """.stripMargin).resolve()
trait XConfigurableUsingConfig extends XConfigurable {
def config:Config
override def getSetting(key:String):Option[String] = {
try {
Some(config.getString(key))
} catch {
case x:Missing => None
case x:WrongType => None
}
}
}
...
So now our XTopPlugin instance is taking its configuration through typesafe "Config" library, using a kind of closure to give the configuration context (topConfig).
def main(args: Array[String]) {
import collection.JavaConversions._
val localCfg = Map(
"host" -> "192.168.2.1",
"username" -> "test",
"password" -> "testtest"
)
val topConfig = ConfigFactory.parseMap(localCfg).withFallback(context.getConfig("TopPlugin"))
val top = new XTopPlugin("myserver") with XConfigurableUsingConfig {def config=topConfig}
println(top)
}
One can object that it is less flexible than using for example spring and its xml configuration files, but remember that scala compiler can be easily embedded in any application, so you just have to use a scala configuration file which will get compiled at runtime... it will bring you very awesome dependencies injection features using pure scala.