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のトランザクション管理をまったく使っていないので、ここからトランザクション管理をしようとすると、なにが起こるかわからない。あしからず。