プログラミングメモ

ソフトウェア開発に関する技術メモ。

@javax.faces.view.ViewScoped を使うとWELDのManagedBeanでNotSerializableException

JSFで (javax.faces.beanのほうではなく)javax.faces.viewの@ViewScopedを使うと、セッション情報がシリアライズされるタイミングで以下の例外が出てしまう。

情報:   Cannot serialize session attribute com.sun.faces.application.view.activeViewContexts for session a4549e255f0feda9b2f59f24a39a
java.io.NotSerializableException: org.jboss.weld.bean.ManagedBean

環境はNetBeans 7.4 + GlassFish 4.0。 Mojarra を2.2.0 から 2.2.5 へ入れ替えても現象は変わらなかった。

セッション情報のシリアライズはたとえば以下のタイミングで発生する。

  • NetBeans上でプログラムを更新して再デプロイされるとき。
  • GlassFishのSession Managerを設定して、"memory"以外のPersistence Typeを指定しているとき。

※ STATE_SAVING_METHOD に clientを指定している場合もシリアライズが発生しそうだが、以下の説明によると、発生しないのかもしれない(未確認)。

セッション永続化によるリカバリ機能や負荷分散を使う予定がなければ、実質的に困ることはないかもしれない。とはいえ、あとからそれらの機能に頼りたい状況にもしなってしまったときに選択肢が減ってしまうし、NetBeansでの開発中にしょっちゅう例外が飛ぶのは気持ち悪い。

直接的な原因は例外メッセージのとおり、WELDのorg.jboss.weld.bean.ManagedBeanがSerializableになっていないことであるが、WELDとしては、そもそもManagedBeanにSerializableを期待するところが誤りとの見解らしい。

MojarraのJIRAには関連しそうな情報は今のところ見当たらない。MyFacesでは・・・WELDの問題としている??


回避策は見つからないので、この現象を避けたければ @javax.faces.view.ViewScoped はあきらめるしかなさそう。CDIの@SessionScopedなら問題ない。また、古い @javax.faces.bean.ViewScoped でも問題は発生しない。@javax.faces.bean.ViewScoped は @javax.faces.view.ViewScoped と同様に使えるようなので、困ることはなさそう。@javax.faces.bean.ViewScoped なBean内の変数にCDIの@Injectで別のBeanの注入するのも問題なくできた。デメリットがあるとすれば:

  • CDIのNamedやXxxScopedと旧JSFのを混在させるのがわかりにくい。
  • @javax.faces.bean.ViewScoped は deprecatedとなることが予定されている。
  • @javax.faces.bean.ViewScoped なBeanは別のBeanに@Injectで注入できない。

備考

HttpSessionにセットされているデータを見てみると、@javax.faces.view.ViewScoped を使ったときには、

  • 名前 com.sun.faces.application.view.activeViewContexts に ConcurrentHashMap がセットされており、
  • その ConcurrentHashMap の先で ConcurrentHashMap がネストして、
  • そのキーで ManagedBean が使われていた。

古いほう(@javax.faces.bean.ViewScoped)を使ったときは、com.sun.faces.application.view.activeViewContexts はHttpSessionにはセットされていなかった。