nekop's blog

OpenShift / JBoss / WildFly / Infinispanの中の人 http://twitter.com/nekop

JBoss ASでbsh(BeanShell)デプロイヤを利用する

JBoss ASにはbsh(BeanShell)というスクリプト言語で記述されたコードをデプロイするbshデプロイヤサービスが標準で用意されている。BeanShellの構文はJavaと一緒なので習得のコストはゼロ。使うには.bshという拡張子のファイルを.warや.earなどの通常のアプリケーションと同じようにデプロイするだけ。

http://www.beanshell.org/

まずは一番簡単なサンプル。

print("hello world");

型指定が必要ないので、コンテナ内でEJB, JMS, JDBCなんかテストするのにすごく便利。catch構文なども型不要でcatch (ex) { }でおっけー。例えばEchoというパラメータを単にリターンするだけのEJB3を書いて、その呼び出しを行いたい場合はこんな感じ。echo()メソッドの呼び出しにキャストは必要ない。

print(new javax.naming.InitialContext("Echo/local").echo("hello world"));

ついでにJMSでQueueに一つメッセージを送信する例。

context = new javax.naming.InitialContext();
cf = context.lookup("ConnectionFactory");
destination = context.lookup("queue/TestQueue");
conn = cf.createQueueConnection();
session =  conn.createQueueSession(false, javax.jms.Session.AUTO_ACKNOWLEDGE);
sender = session.createSender(destination);
sender.send(session.createTextMessage("hello world"));
sender.close();
session.close();
conn.close();

bshスクリプト自身はRunnableインタフェースを実装していることになっているので、別スレッドの処理を実装するのにも重宝する。また、start()/stop()メソッドがあるとJBoss ASのServiceMBeanとして認識されるので、この2つを組み合わせてメモリ量を定期的にダンプするようなサービスなら一瞬で書ける。

finished = false;
interval = 10000;

run() {
    while (!finished) {
        r = Runtime.getRuntime();
        System.out.println("total: " + r.totalMemory() + ", free: " + r.freeMemory() + ", used:" + (r.totalMemory() - r.freeMemory()));
        Thread.sleep(interval);
    }
}
objectName() {
    return "bsh:service=PrintMemory";
}
start() {
    finished = false;
    new Thread(this).start();
}
stop() {
    finished = true;
}

BeanShellでJBoss ASのServiceMBeanを記述する方法なんかはWikiにも載っている。

http://community.jboss.org/wiki/BSHDeployer

BeanShellの良いところはJava 1.4までの構文をそのまま解釈できるところ。型指定があってもなくてもどちらでも動作する。なので、ちょっとしたコード片の動作を確認したいときはそのまま.bshにコピペしてしまえば良い。

良くないところは細かい点でいくつかある。まず、今はもうメンテされていないということ。上にわざわざJava 1.4と書いたので、もう気づいている人もいるかもしれないが、Java 5で拡張されたGenericsや拡張for文などの構文は解釈できないし、恐らく今後も期待できない。あと、.bshファイル内では新しいクラスやインタフェースは定義できない。同じようにbshでServiceMBeanを記述したときにfoo()などの独自メソッドを用意しても、MBeanインタフェースに定義されたオペレーションとは認識されないのでJMX経由ではその定義したfoo()は呼び出せない。このような場合にはいつも通りJavaで記述してクラスファイルを作成する必要がある。これらの点についてはそこまでするのなら普通にJavaで書いたらいいじゃん、という程度のものなので、あまり気にはならないのだけど。

そうそう、構文エラーのあるBeanSellをデプロイしたときに、たまに何もエラー報告されずに静かに終了するパターンがあるので気をつけましょう。print文とかはさめば大体すぐわかるよ!