初めてのJava EE 6 (JSF 2.0)

しばらく間があいてしまいました。今回はJava EE 6に含まれているWebフレームワークであるJSF2.0を簡単にさわってみたいと思います。

JSFは、前バージョンのJava EE 5からJava EEに含まれるようになりました。つまり、Java EE準拠のアプリケーションサーバであれば、ライブラリの追加なしにデフォルトで付いてきています。
これはやはり標準のメリットの1つですね。アプリケーションサーバを購入するればWebフレームワークのサポートも一応付いているわけです。
Webフレームワークのサポートを気にするところだとこれはありがたいのではないでしょうか。

しかし、JSFはイマイチ人気がありません。特に日本だとStruts1系が多く使われているような話をよく聞きます。
indeed.comのJob Trendsを見ても、ちょっとずつ増えてはいますが微妙な感じですね。
もしかすると標準技術という点と、アプリケーションサーバにバンドルされているという点で、時間をかけて広がっていくかもしれません。

そんな本格的な普及はこれからな感じのJSFですが、Java EE 6ではバージョン2.0とメジャーバージョンアップとなりました。Java EE 5ではJSF1.2でしたが、このときは果たせなかったEoDをかなり進めた感じになってます。後はAjax対応かな?目玉は。

では簡単なWebアプリを作っていきます。開発環境は前回と同じでEclipseです。

  • プロジェクトの作成で、Dynamic Web Projectを選択します。
  • Configurationで[Modify]ボタンをクリックしてProject Facetの画面を表示し、JSFとFaceletにチェックを入れます。JSF 2.0が選べないですが、1.2のままでOK。
  • [Next]ボタンを何回かクリックし、JSFの実装選択画面まで進めます。JSF Implementation Libraryを「Disable Library Configuration」にして、[Finish]ボタンをクリック。

これでJSFの準備が完了しました。Servletのみのプロジェクトと異なるのは以下の点です。

  • web.xmlJSFの設定が追加されています。例えば、サーブレット(FacesServlet)の登録など。具体的には以下の定義が追加されています。
  • faces-config.xml ・・・ JSFの設定ファイルです。プロジェクト作成時は空です。ここには主に、画面遷移情報を必要であれば*1定義します。


最初なので、簡単なフォーム入力の画面でも作ってみます。ユーザIDとパスワードを入力して、ログインボタンをクリックするログインフォームを作成してみます。

  • WebContent以下に、login.xhtmlを以下の内容で作成します。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:ui="http://java.sun.com/jsf/facelets"
	xmlns:h="http://java.sun.com/jsf/html">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Login</title>
</head>
<body>
<h:form>
	ユーザID:<h:inputText value="#{user.name}" />
	<br />
	パスワード:<h:inputSecret value="#{user.password}" />
	<br />
	<h:commandButton value="Login" action="#{loginAction.login}" />
</h:form>
</body>
</html>
  • 続いて、ログインが成功した場合の遷移先ページとしてtop.xhtmlを以下の内容で作成します。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:ui="http://java.sun.com/jsf/facelets"
	xmlns:h="http://java.sun.com/jsf/html">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Top</title>
</head>
<body>
Welcome #{user.name}!
</body>
</html>
  • さて、これで一度実行してみましょう。login.xhtmlにアクセスするとログイン画面が表示されます。適当にユーザ名とパスワードを入力し[Login]ボタンを押してみると、(まだXHTMLしか作成していないため当然ですけど)以下のエラーがでます。

HTTP Status 500 -
・・・
root cause
javax.el.PropertyNotFoundException: /login.xhtml @12,44 value="#{user.name}": Target Unreachable, identifier 'user' resolved to null

  • root cause、つまり根本原因を見ると、login.xhtmlの12行目、44列目のvalue属性のEL式、#{user.name}の'user'がnullのため解決できないというエラーと示されています。このように、エラーの箇所も教えてくれるようになってます。

では、EL式が解決できるように修正していきます。

  • エラーが出ないように対応するクラスを作成します。初めにUserクラスです。単なるJavaBeanで、アノテーションが2個付いただけ。意味は後で説明します。
package example;

import java.io.Serializable;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;

@ManagedBean
@SessionScoped
public class User implements Serializable{

	String name;
	String password;
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
}
  • 次に、h:commandButtonのaction属性で指定した#{loginAction.login}に対応する、LoginActionクラスです。
package example;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;

@ManagedBean
public class LoginAction {

	@ManagedProperty("#{user}")
	User user;
	
	public void setUser(User user) {
		this.user = user;
	}
	
	public String login() {
		//ここでログイン処理があると想定
		System.out.println(user.getName());
		System.out.println(user.getPassword());
		
		//ログインOKと想定
		return "/top";
	}
}

以上で、もう1回[login]ボタンを押してみます。今度は無事に、top.xhtmlに遷移することができます。

これは、JSF 1.xを知っている人は驚きなのではないかな?ここまでXMLは一切書いていませんね。JSF 2.0でようやく定義レスで開発できるようになりました。

では、飛ばしたところの説明です。JavaクラスではいくつかJSFアノテーションが付いていました。

  • Userクラス
    • @ManagedBean ・・・ JSFのManagedBeanとして管理するために付けるアノテーションです。JSF 1.xだとfaces-config.xmlXMLでクラスを設定していましたが、JSF 2.0ではこのアノテーションを付けるだけです。
    • @SessionScoped ・・・ これも上記と同じで、faces-config.xmlで設定していた物。このBeanのスコープを定義します。ここでは、Userクラスはログインユーザ情報を持つ物なのでセッションにしています。
  • LoginActionクラス
    • @ManagedBean ・・・ Userクラスと同様。
    • @ManagedProperty ・・・ これも同じで、faces-config.xmlで設定していた物。元々JSF 1.xにも簡易的なDIの機能があって、XMLで設定していました。JSF 2.0ではアノテーションでできるようになってます。パラメータにEL式を渡せば、対応するオブジェクトがインジェクトされます。ここでは、#{user}と指定しているのでUserクラスのインスタンスが入るわけです。また、このアクション実行と同時に、入力フォームがポストされていますので、その内容が反映された状態となっています。
    • login()の戻り値 ・・・ "/top"という文字列を返却しています。JSF 2.0からは、ページ名(拡張子はあり/なしどちらでもOK)を返すとそのページをRenderしてくれるようになっています。つまり、faces-config.xmlに画面遷移定義の記述が必須ではなくなっています*2


最後にXHTMLです。素のServlet/JSPだけのWebアプリと異なるのは、JSFではHTMLの各タグに対応したカスタムタグが予め用意されているのでそれを使います。(ここでは紹介しませんが、通常のHTMLタグを書きつつもJSFのカスタムタグとして認識させる方法もあります)

後は、EL式でJavaのクラスとバインディング(結びつけ)を行うところがポイントですね。
これで対応するManagedBeanに画面からの値が自動的に設定されたり、任意のメソッドを呼ぶことができます。login.xhtmlだと以下の関係になっています。

  • ---> Userクラスのnameに設定される
  • ---> Userクラスのpasswordに設定される
  • ---> ボタンのクリックでLoginAction#login()を実行する。

まぁ、EL式を見るだけでなんとなく想像つきますよね。

なお、JSF 1.xを知っている方は、JSPではなくてXHTMLとなっている部分で戸惑うかもしれません。JSPと変わる点として、スクリプトレットが書けないのが一番大きなところかもしれませんね。でもJSF 2.0でJSPが使えなくなっている訳ではないのでご安心を。ただし、XHTMLだとTilesのようなテンプレートが出来たりとか高機能な部分もあるので、スクリプトレットにこだわりがなければXHTMLの方がいいのでは、と思います。

*1:JSF2.0からは、アクションの戻り値で直接画面遷移可能になっているので必須ではない。

*2:最近のWebフレームワークってみんなこんな感じですよねぇ。