GroovyでEJB作成は辛い

ということで、前々回のInterface付きのEJBが単純な形でLookUpできないと前回のビジネスインターフェース無しのEJBを作る場合は@LocalBeanアノテーションをつけたほうが良いにおいて、GroovyのクラスがJavaに変換されると、インターフェースであるGroovyObjectが勝手にimplimentsされること。それを単純にEJBのセッションビーンにするとエラーになるので、避けるためには@LocalBeanアノテーションを付ける必要が有ることが判りました。

前回の記事の最後に書いたとおりに、これでGroovyでEJBが作れるのかというとそんなことはありません。
実際に付けてみると以下の様なエラーとなります。

重大: Exception while loading the app : EJB Container initialization error
javax.ejb.EJBException: Illegal non-business method access on no-interface view
     at megascus.javaee6groovy.__EJB31_Generated__MessageFacade__Intf____Bean__.$getStaticMetaClass(Unknown Source)
     at megascus.javaee6groovy.MessageFacade.<init>(MessageFacade.groovy)
     at megascus.javaee6groovy.__EJB31_Generated__MessageFacade__Intf____Bean__.<init>(Unknown Source)

EJBコンテナの初期化でエラーになっている模様。

javax.ejb.EJBException: Illegal non-business method access on no-interface view

さて、これは何なのか。再度EJBのJSR(3.4.4 Session Bean’s No-Interface View)を参照します。

前々回にてEJBを作る場合、ビジネスインターフェースを定義しなくても、勝手にインターフェースが作成されることをご紹介しました。
JSRによると勝手に以下の様な制限がかかるそうです。

Only public methods of the bean class (and any super-classes) may be invoked through the no-interface view. Attempted invocations of methods with any other access modifiers via the no-interface view reference must result in a javax.ejb.EJBException.

つまり、EJBのオブジェクトは自動的に生成されたインターフェースを介する形でアクセスしないと、publicメソッドからはアクセスが出来ないということです。*1

ごめんなさい。嘘です。@nekopさんから指摘をいただきました。
プロキシ上からはpublicメソッドしか呼べないよという事だそうです。

なぜ初期化時にエラーになるのか?

Groovyから生成されたclassファイルを逆コンパイルすることで見当がつきます。
コンパイルをすると確かに呼ばれているのですが・・・・・

@Stateless
public class GroovyClazz implements GroovyObject
{
  public GroovyClazz()
  {
    GroovyClazz this;
    MetaClass localMetaClass = $getStaticMetaClass(); 
    this.metaClass = localMetaClass;
  }

セッションビーンの中から呼ばれているだけなので特に先ほどの制限には引っかかる要件では内容に見えるので、微妙。なぜ・・・・・

回避を試みてビジネスインターフェースを作ってみる

先ほどの制限について、Session Bean’s No-Interface Viewの章にて説明されているので、もしかしてビジネスインターフェースを作成すれば回避できるのでは?と思い、作成してみました。

@Local
interface IGroovyClazz {
    void create(Message entity)
    void edit(Message entity) 
    void remove(Message entity)
    Message find(Object id) 
    List<Message> findAll() 
    List<Message> findRange(int[] range)
    int count()
}

成功したとしてもGroovyでインターフェースを作らなければいけないとか誰も嬉しくない気がしますが、しょうがない。
ただし、作ったとしても以下のとおり失敗します。

Caused by: java.lang.NullPointerException
	at org.codehaus.groovy.util.ManagedReference.<init>(ManagedReference.java:37)
	at org.codehaus.groovy.util.ManagedConcurrentMap$Entry.<init>(ManagedConcurrentMap.java:52)
	at org.codehaus.groovy.reflection.ClassInfo.<init>(ClassInfo.java:58)
	at org.codehaus.groovy.reflection.ClassInfo$LocalMap.<clinit>(ClassInfo.java:351)
	... 58 more

Groovyのライブラリの中でNullPointerExceptionが発生しています。
EJBとGroovyの相性が悪すぎるということで、断念。
一筋縄にはいかないですね。

追記 直りました。(Groovy2.1.3)

Groovy2.1.3で実行しなおしてみたところ、上のNullPointerExceptionは発生しませんでした。
良かった良かった。
なので、この記事は半分ぐらい陳腐化しました。

楽になる箇所もあるのだけど

Entityの作成とかは非常に楽になります。

@Entity
@Canonical // equals,hashCode,toStringメソッドの自動生成
@AutoExternalize // 直列化可能
@CompileStatic // 静的コンパイル
class Message {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    Long id
    String message;
}

equals,hashCode,toStringメソッドがアノテーションを付けるだけで自動生成してくれたり、getter,setterが自動生成されたり等、本当に楽です。
ちなみにGroovy2からGroovyで静的コンパイルが出来るようになりました。今回使っているのがGroovy2.0.5であったため、付けてあります。
つけるだけで速度がJavaと遜色ない程度になるらしいので、Webアプリケーションを作る限りはつけといたほうが無難でしょう。

しかし、Entityだけ作れてもEJBが上手く作れないと全部をGroovyでという形には出来ないので、全く嬉しくない。
という企画倒れなお話でした。

今回の件、@nekopさんが解説してくださっているので、そちらもご参照下さい。
「GroovyでEJB作成は辛い」の解説


    • -

どうでもいい追記

ちなみに、Groovy内部でThreadLocalを使っているようですが、WebLogicの場合、ThreadLocalを使っているとメモリリークを引き起こすという問題が存在しています。
詳しくは以下の記事をご参照下さい。

【後編】原因不明のOutOfMemoryエラー、性能劣化の問題も一挙に解消|WebLogic Channel|日本オラクル

*1:Injectionされた場合は条件を満たしています