Groovy で IRC ボットを書いてみて得られた知見たち
IRC ボット i401 に触発されてしばらく前に yaya って名前の IRC ボットを Java で書いたりしてたのだけれど、IRC のイベントに対して反応する処理をプラグイン的に書きたい場合に Java だと微妙にやりにくいかなー、と思ったりして Groovy で書きなおした。
初めてそれなりに Groovy を書いてみていろいろ学びもあったので、得られた知見を書き残しておくことにする。
- 作者: Dierk Konig,Guillaume Laforge,Paul King,Cédric Champeau,Hamlet D'arcy
- 出版社/メーカー: Manning Pubns Co
- 発売日: 2014/05
- メディア: ペーパーバック
- この商品を含むブログを見る
Groovy に関して得られた知見
CliBuilder によるコマンドライン引数の解析
- CliBuilder (groovy 2.2.1 API)
- Groovyでコマンドライン引数を解析するにはCliBuilderというものを使うらしい - (U^ω^)わんわんお! - @linerlockの日記
- Groovy CliBuilder 使い方
CliBuilder
クラスというものがあって、コマンドライン引数の解析を簡単にできる。 Groovy はわりとスクリプトを書くのに使われることが多いと思うので、こういうのが手軽に使えるのは便利。
クラスの動的ロード
Groovy には GroovyClassLoader
というクラスローダがある。 この parseClass
メソッド を使えば、指定のディレクトリ中のすべての groovy ファイルを読み、それぞれのクラスで定義されているクラスを読み込むということができる。 ファイル名とクラス名を同じにしないといけないとか、パッケージに従ってディレクトリを切らないといけないとかそういうことがないので、プラグイン的に使用するクラス群を読み込むときに便利。
clearCache
メソッド があるので、クラスの再読み込みも簡単にできる。
セキュリティマネージャ
ユーザーが入力した Groovy スクリプトを実行して結果を返したい、というようなときに、ファイルシステムにアクセスできないようにしたりとかする必要がある。
Groovy は Java のセキュリティモデルに統合されているので、Java と同様に SecurityManager
を使用できる。
Java なら次のような感じで -D
オプションで java.security.manager
を指定することでセキュリティマネージャを有効化できる。
java -Djava.security.manager Xxx
が、Groovy でセキュリティマネージャを有効にするには、
groovy -Djava.security.manager xxx.groovy
みたいな感じはだめっぽい。 JAVA_OPTS
環境変数で指定するようにするか、プログラム中で記述するかのどちらかが良さそう。 セキュリティポリシーが書かれたファイルの指定も含めてプログラム中で記述する場合は次のような感じ。
System.setProperty("java.security.policy", "java.policy") System.setSecurityManager(new SecurityManager())
セキュリティマネージャについては次のページが参考になる。
Grape を使って読み込まれるライブラリはホームディレクトリの .goovy/grapes ディレクトリ下に置かれるので、Grape を使って読み込まれるライブラリに対するセキュリティポリシーの記述は次のような感じになる。
grant codeBase "file:${user.home}/.groovy/grapes/-" { // ... };
AST 書き換えで interrupt 可能にする
ユーザーが入力した Groovy スクリプトを実行する場合、無限ループなどが書かれていたときのことを考えてタイムアウトできるようにしたい。 中断可能なようにちゃんと書かれた処理ならば、Thread
に対して interrupt
メソッドを呼び出せば良いのだけれど、ユーザーが入力した処理が interrupt
されたときにちゃんと止まるとは限らない。
while (true);
とか。
Groovy では、コンパイラの設定により、AST (抽象構文木) 書き換えによって interrupt をハンドリングできるようにプログラムを変更できる。
import org.codehaus.groovy.control.CompilerConfiguration import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer def config = new CompilerConfiguration() config.addCompilationCustomizers(new ASTTransformationCustomizer(ThreadInterrupt)) def shell = new GroovyShell(config)
すごい!
コマンド実行
Groovy の String
クラスには String#execute
メソッドがあって、簡単に外部コマンドを実行できる。
def proc = "git pull".execute() proc.waitFor() // 実行完了を待つ // ステータスを取得したり標準出力やエラー出力を見る println "return code: ${ proc.exitValue()}" println "stderr: ${proc.err.text}" println "stdout: ${proc.in.text}" // *out* from the external program is *in* for groovy
感想
Groovy 便利!