I have a class with a method that takes a function as parameter. This function is the strategy that determines how a file is split into chunks. This is the signature of the method.
def split(r: RemoteFileInfo, append: Boolean, workDir: File, strategy: (Long) => Int = defaultStrategy): LinkedHashSet[Chunk] = { ... }
There is a companion object that provides a number of predefined strategies. I wanted to make the strategy configurable by adding it to the Akka application.conf file.
This is a snippet from the application.conf.
akka { ... } downloader { ... dependencies { splitter = "com.nidkil.downloader.splitter.DefaultSplitter" splitterStrategy = "ratioStrategy" merger = "com.nidkil.downloader.merger.DefaultMerger" validator { checksum = "com.nidkil.downloader.validator.ChecksumValidator" size = "com.nidkil.downloader.validator.FileSizeValidator" } cleaner = "com.nidkil.downloader.cleaner.DefaultCleaner" } }
I used the following code to load the strategy at runtime.
def strategy = { import scala.reflect.runtime.{ universe => ru } val strategyName = config.getString("downloader.dependencies.splitterStrategy") val instanceMirror = ru.runtimeMirror(getClass.getClassLoader).reflect(DefaultSplitter) val strategyMethod = ru.typeOf[DefaultSplitter.type].declaration(ru.newTermName(strategyName)).asMethod instanceMirror.reflectMethod(strategyMethod) }
For further details on how the custom settings are load using an Akka extension check the following post.
Now I can load the method using the following code.
val settings = Settings(system) val strategy = settings.strategy println(strategy(1024 * 1024 * 1))
To pass it as an argument to a method an extract trick is required: explicit conversion to the function signature. Recall the function signature is Long => Int
. The function signature of MethodMirror is Any => Any
. When passing the function as is an error is thrown ‘type mismatch; found : reflect.runtime.universe.MethodMirror required: Long => Int’. So we add an explicit conversion.
val strategyFunc: Long => Int = strategy(_).asInstanceOf[Int] split(remoteFileInfo, true, workDir, strategyFunc)
Pretty neat trick, right? Happy coding 🙂