JVMにはjavaagentという特別な仕組みがあるのを知ってますか?
自分が雑に思いついた具体例を書いておくと、例えばdatadogのtrace用のSDKがjavaagentで実装されてますね。 使用例は色々あるかもしれませんが、javaagentの例や、それの仕組みを詳細に解説するのが今回の目的ではないので、そこの詳細は各自ググってください。
すごく大雑把にいうと、特定の規約に従った普通とは違ったclassを定義して、そのpathを -javaagent:
という引数とともに渡すと、普通のmainより先に特別に呼ばれる、という感じだと思います。
(正確には、単に先に呼ばれるより面白いことが出来るはずだが、今回はそこは使わないので説明割愛)
また、
こういったsbt pluginもありますが
「今回はこれの話ではありません!」
もちろん、このplugin自体は場合によって有用ですが、微妙に違う観点の話をするからです。
sbt-javaagent pluginは、既存のjavaagentを利用する際のpluginだと思われますが、今回のテーマは
「1つのsbt projectでjavaagentの定義と利用」
です。
「そもそも、そんなことしたい場面ある?」
というツッコミはありえます。その場面の話は後でするとして、ひとまずその方法を雑に説明するというか、実際のコードを書いたので、それを貼り付けます。
https://github.com/xuwei-k/javaagent-sbt-example/commit/84bd5c3644892d855af9f061d03ef6d781a3c4c3
ざっくり解説をすると
- javaagentは依存ライブラリ使って頑張ることも不可能ではないらしいが、今回は面倒なのでpure javaでライブラリなしで書いた
- するとScalaの依存も必要ないのでautoScalaLibraryやcrossPathsの設定をしてある
- javaagent作るのに絶対必要なものとしてsbtで
packageOptions += Package.ManifestAttributes("Premain-Class" -> "ここに名前")
を追加 - 利用する側は
(agentのプロジェクト / Compile / packageBin).value.getCanonicalPath
で得たpathをs"-javaagent:${agentJar}=${arg}"
のようにjavaOptions
に渡す - javaagent利用する場面は、testやrunなどいくつかあり得るが、いずれにせよforkしないと意味ないので、今回はtestなので
Test / fork
をtrue
に設定 - こうやって1つのsbt projectに収めてしまえば、特に明示的にjavaagentのjarをpublishLocalなどしなくても、これで自動で使えて便利
という感じです。
そもそもそんなことしたい場面ある?
の件ですが
今回はtest
と書きましたが、案外テストで使ったら便利な場面あるのでは?という、雑な思いつきです。
こちら公式ドキュメントからの引用ですが
https://www.scala-sbt.org/1.x/docs/Testing.html
When forking, the ClassLoader containing the test classes cannot be provided because it is in another JVM. Only use the () => Unit variants in this case.
とあります。よく考えれば当たり前ですね。
ではsbtやテストライブラリ側で、その他に「汎用的で便利な必ず実行前に同じJVM上でフック出来る仕組み」が存在するのか?というと、自分は知りません。 むしろ実は存在するなら教えてください。
そこで今回のように雑にjavaagent使ってみる例なわけですが
- 必ず同じJVMで起動される
- 必ず全てのテストの実行開始より前に処理を挟み込める
というのは便利だと思いませんか?具体的に何をするか?というと、それはもちろん例えばDatabaseの初期化処理といった Tests.Setup
と同じような用途なわけですが、
頑張れば他のJVMからの処理で不可能ではない場合があるとしても、同じJVMで実行した方が都合がいい場合も多々ありますよね?
また、(本格的にやるなら既存の仕組みを使えばいいが)、簡易的にJVM自体のthreadやmemoryや、その他(同じJVM内部でないと取得が面倒な)色々な状態を継続的に取りたい、といった場合も、それ用の定期的に起動するcron的な処理をjavaagent側で挟んでしまうと便利かもしれません。
sample作ってみた程度で、これ以上の何かがあるわけではないので、これでおしまいです。
単に Tests.Setup
の代替以上に、工夫次第では面白い便利なことが出来ると思うので、何か思いついたら作ってみてください。