EJBの最悪さを知らない人々は幸福である。幸福な人々には大変恐縮だが、暗澹たる事実のひとつをお知らせしよう。
EJBコンテナ内では、普通の方法でスレッドを作ってはいけない。
今度はEJB 3.0だそうだが、そもそも最初から作り直せと言いたい。MSの.NETがよくできている(後発だから当然だが)のを見るにつけ、「貧乏人はJavaをやれ」と言われている気分だ。
普通の方法でスレッドを作ってはいけないが、普通でない方法ならある。JCA 1.5で導入された javax.resource.spi.work.WorkManager
を使う方法だ。具体的にどんなコードになるのか、ちょっと調べれば出てくるだろう――しかしこの期待はあっさりと裏切られた。
この記事が典型例だ。WorkManagerインスタンスをJNDIで取ってくる、と書いてあるだけで、具体的にどこでどうやってインスタンスを生成し、JNDIにバインドするのかは書いていない。この問題について書いた記事は、どこをどう探しても、出てこない。そして、WorkManagerについて書いてある記事は、ひとつの例外もなく、どこか知らないところで生成・バインドされたインスタンスを取ってきているのだ。これらの記事を書いた連中は、実際に動くコードを自分の手元に持っていたのだろうか。どれかがオリジナルで、ほかの記事はオリジナルをコピペしたのではないだろうか。
私が書くのは記事ではなくソフトウェアなので、実際に動くコードが必要だ。JNDIで取ってくるのは正しい方法なのかもしれないが、私は違う方法をとった。
以下に実際のコードを示す。まず、Runnable相当のクラスから。
import javax.resource.spi.work.Work;
public class MyWork implements Work {
public void release() {
System.out.println("MyWork#release");
}
public void run() {
System.out.println("MyWork#run");
}
}
そしてEJB。
import java.util.Set;
import javax.ejb.EJBException;
import javax.ejb.SessionBean;
import javax.management.Exception;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.resource.spi.work.WorkManager;
/**
* @ejb.bean
* name="My"
* type="Stateless"
* view-type="remote"
* transaction-type="Bean"
* @ejb.transaction type="Required"
*/
public abstract class MyBean implements SessionBean {
/**
*
* @ejb.interface-method view-type="remote"
* @throws EJBException
*/
public void calltest() throws EJBException {
try {
ObjectName name = new ObjectName("jboss.jca:service=WorkManager");
ObjectInstance wmo = mbserver.getObjectInstance(name);
WorkManager wm = (WorkManager) mbserver.invoke(wmo.getObjectName(), "getInstance",
new Object[] {}, new String[] {});
wm.scheduleWork(new MyWork());
System.out.println("MyWork scheduled");
} catch (Exception e) {
e.printStackTrace();
}
}
}
これが私の結論だ。
ちなみに私はEJBのトランザクション管理をまったく使っていないので、ここからトランザクション管理をしようとすると、なにが起こるかわからない。あしからず。