昔作った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ファイルをすべて読んでインスタンス化したり、
一度読んだ後も、定期的にファイルのタイムスタンプをポーリングし、アプリケーションの再起動なしにプラグインをリロードできるようにしていました。
タイプセーフな設定ファイルなどもこの仕組みを使って実装し、システムを堅牢にするのに一役買っていました。