ELとCDI管理Beanがうまく結びつかないとき
Struts1.xユーザがJSF2.0に移行すると、javax.el.PropertyNotFoundExceptionが500エラーの原因として表示されたり、@ModelでアノテートしたCDI管理Beanのパラメータが#{bean.property}でうまく出力されないことがあると思います。
この原因は主に2つあります。
1. CDI管理Beanにgetter/setterメソッドを忘れている
Strutsのように操作(Action)と状態(ActionForm)が分かれたプログラミングモデルに慣れていると、どうしても見落としがちなのがアクセッサです。例えば、以下のような簡単なBeanを想定します。
CDI管理Bean(StrutsのAction相当)のソース
@Model public class BookController { /* 画面から入力したデータをインジェクション */ @Inject private Book book; /* ビジネスロジックはEJBで */ @EJB public BookManager bookManager; /* アクションメソッド */ public String entryBook() { bookManager.createBook(book); return "nextpage.xhtml"; } /* 忘れがちなgetterとsetter */ public void getBook { return book;} public Book setBook(Book book) { this.book = book; } }
この例ではgetBookとsetBookを忘れた状態で、JSFページから#{book.author}のようにEL式でアクセスすると、PropertyNotFoundExceptionが発生してブラウザに500エラーが表示されます。通常のCDI管理Beanは、JSF管理Beanと異なり、getter/setterがなくてもインジェクションできるのですが、インジェクションしたオブジェクトをEL式から参照させる場合は必須になります。
参考 : JavaEE6 Tutorial Adding / Setter and Getter Methods
http://docs.oracle.com/javaee/6/tutorial/doc/gjbbp.html
2. 名前が重複している
私自身はまったのは、CDI管理Beanのプロパティ名と、JSFページのデータテーブルが持つのvar属性の名前が重複してうまく動かなかったことです。前述したサンプルソースに加えて、図書のリストをCDl管理Beanに持たせたとします。
@Model public class BookController { @Inject private Book book; /* 図書リストを追加 */ private List<Book> bookList = new ArrayList<Book>(); @EJB public BookManager bookManager; public String entryBook() { // createBook()はListを返してくれる bookList = bookManager.createBook(book); return "nextpage.xhtml"; } /* 忘れがちなgetterとsetter */ // 長くなるので省略。bookList分のアクセッサも忘れずに。 }
この状態で、JSFページに以下のようにdataTableを定義すると、BookControllerクラスが持つbookと、dataTableが持つbookで名前が重複してしまいます。
<h:dataTable value="#{bookController.bookList}" var="book">
手元のJBossAS7.1では、重複したときの動作はCDI管理Beanクラスの名前が優先されました。これは実装により異なるかもしれません。名前重複の場合は例外がthrowされないので注意が必要です。
今回は『はまりどころ』を書きましたが、JSF2.0自体はStrutsと比べて複雑なXMLの書き方を覚えなくていいので、便利な代物です。
これからJavaWebアプリケーションプログラミングを覚える方にはおすすめです。