一角獣は夜に啼く

ただの日記です。

思ってることとか考えたこととか適当に書きます。 主にソフトウェア開発の話題を扱う 「ひだまりソケットは壊れない」 というブログもやってます。

Groovy で IRC ボットを書いてみて得られた知見たち

IRC ボット i401 に触発されてしばらく前に yaya って名前の IRC ボットを Java で書いたりしてたのだけれど、IRC のイベントに対して反応する処理をプラグイン的に書きたい場合に Java だと微妙にやりにくいかなー、と思ったりして Groovy で書きなおした。

初めてそれなりに Groovy を書いてみていろいろ学びもあったので、得られた知見を書き残しておくことにする。

Groovy in Action

Groovy in Action

  • 作者: Dierk Konig,Guillaume Laforge,Paul King,Cédric Champeau,Hamlet D'arcy
  • 出版社/メーカー: Manning Pubns Co
  • 発売日: 2014/05
  • メディア: ペーパーバック
  • この商品を含むブログを見る

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 には正規表現リテラルがある。

"文字列" =~ /正規表現/ という感じで、=~ 演算子を使えば Matcher が返される。

==~ という正規表現もあって、こっちはマッチするかどうかを真偽値で返す。

感想

Groovy 便利!