昔作ったScalaの言語構文とEvalの仕組みを利用した自作プラグインの作り方を紹介します。
Evalする部分については、こちらの記事を参考にさせて頂きました。
http://d.hatena.ne.jp/xuwei/20140607/1402128646
package parsers
import scala.reflect.runtime.currentMirror
import scala.tools.reflect.ToolBox
import java.io.File
import scala.io.Source
object Parser {
def eval[T](code: String): T = {
val toolbox = currentMirror.mkToolBox()
val tree = toolbox.parse(code)
toolbox.eval(tree).asInstanceOf[T]
}
def evalFile[T](file: File): T = {
eval[T](Source.fromFile(file).getLines.mkString("\n"))
}
}
次に、Pluginの基底クラスを用意します。
package plugins
trait Plugin {
def abstractMethod()
}
次は、Pluginの実装ファイル(PluginImpl.scala)を用意します。
new plugins.Plugin {
def abstractMethod() {
println("hello")
}
}
最後に、動的にファイルを実行してPluginをインスタンス化し、Plugin#abstractMethodを呼び出すメインとなるコードを実装します。
object Main {
def main(args: Array[String]) {
val pluginFile = new java.io.File("./PluginImpl.scala")
val plugin = parsers.Parser.evalFile[plugins.Plugin](pluginFile)
plugin.abstractMethod
}
}
scalaファイルはコンパイルしてjarにし、PluginImpl.scala を同じ階層に配置して実行してみます。
$ java -jar Main.jar hello
実際のシステムに実装する際には、特定のディレクトリ以下の.scalaファイルをすべて読んでインスタンス化したり、
一度読んだ後も、定期的にファイルのタイムスタンプをポーリングし、アプリケーションの再起動なしにプラグインをリロードできるようにしていました。
タイプセーフな設定ファイルなどもこの仕組みを使って実装し、システムを堅牢にするのに一役買っていました。