GlassFish 4.0 の CDI に潜む罠

この記事はGlassFish Advent Calendar 2013の20日目として新たに書き下ろしたものです。昨日は菊田 @kikutaro_ さんの「Embedded GlassFishで困った時にやってたこと」でした。


今回はGlassFish 4.0のCDIに関する事柄、特にハマりどころについてお話しします。
Java EE 7のリファレンス実装であるGlassFish 4.0は、CDI 1.1のリファレンス実装であるWeld 2.0を含んでいます。CDI 1.1はbeans.xmlが省略可能になったほか、いくつかの機能と落とし穴が追加されました。まず、CDI 1.1の概要と、落とし穴の原理原則については、上妻 @n_agetsu さんの発表スライドとブログ記事をご覧ください。

ブログ記事はこちらです:

Java EE環境におけるCDIのデフォルト化
http://n-agetsuma.hatenablog.com/entry/2013/08/18/130358

原理原則はとても大切です。これらをよく踏まえた上で、以下をお読みください。

Java EE 7では、JAX-RS 2.0の新機能により、JAX-RSのリソースクラス(@Pathがついたクラス)をEJBとしなくてもCDIによるInjection(平たく言うと@Injectの使用)が可能になりました。上妻さんのスライドにあるように、beans.xml省略時にスコープ指定を忘れたり、あるいはbeans.xmlの設定が適切でなかったりと言った理由でInjectionが行われないことは多々あります。ところが、それらの設定がすべて仕様通り問題なくても、「ある条件」が追加されると、以下の例外がスローされInjectionが失敗することがあるのです。

org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at Injectee ...

この問題は筆者がGlassFish 4.0のリリース直前に GLASSFISH-20597GLASSFISH-20255と重複)として報告したもので、「ある条件」とはこの場合Bean Validationの併用でした。JAX-RS 2.0の目玉機能がCDIおよびBean Validationへの対応だったはずなのに、両者を同時に利用すると例外が、しかもHK2(OSGiラッパー・ライブラリ)というGlassFishの基盤に近い部分からスローされるのです。

これはJAX-RS 2.0のリファレンス実装であるJersey 2.0のCDI連携部分にバグがあり、Bean Validationが使用されているとInjectionが上手く行われないというものでした。直後に修正パッチが提供され、またリソースクラスをEJB(セッションBean)にすることでも回避は可能でした。ただ、この問題はまだ氷山の一角に過ぎず、関連する問題ではJAXB実装のMOXyでトラブったり、場合によってはEJB(セッションBean)にしてもInjectionにしくじったケースもあるようです。

結論から言ってしまうと、GlassFish 4.0のJAX-RSとCDIの連携はまともに機能しません。しかし、この不具合は現在開発中のGlassFish 4.0.1での解消を目指しており、この記事の執筆段階では多数のバグフィックスを含むJersey 2.4.1(現時点における最新版)のマージ作業が始まっています。JerseyはGlassFish 4のコンポーネントの中で最も頻繁にアップデートが行われていて、Jersey 2.0リリース以降に修正されたバグの数だけを見ても、Jersey 2.1で14、Jersey 2.2で17(累積31)、Jersey 2.3で30(累積61)、Jersey 2.3.1で2(累積63)、Jersey 2.4で27(累積90)、Jersey 2.4.1で15(累積105)となっています。なお、前述の問題はJersey 2.1から2.2にかけて修正されているようで、GlassFish 4.0.1 Promoted Build 3(Jersey 2.2がバンドルされています)ではとりあえず問題なく使用することができます。


そして明日は、件の上妻 @n_agetsu さんの投稿予定です。すごい偶然ですね。この記事の原稿自体は早期に上がっていて(Advent Calendar開始直後に書きためておいたストックの1本)、構成時には全く意識していなかったのですが、結果的に上妻さんの前に上妻さんの過去記事を参照したこの記事が来ることになってしまいました。