[特別企画]
JavaServer Facesを理解する
(前編)
JSFの構造を探る

五十嵐由
2003/7/26

INDEX
JavaServer Facesがもたらすメリット
JSFを構成するコンポーネントとJSFアプリケーション
JSFページのライフサイクル
JWSDPのインストール
サンプルアプリケーションに見るJSF

 サービスをいち早く提供したい、開発費用を少しでも軽減したいという企業、組織の要求が高まる中、それにこたえるべく開発効率の向上を目的とした数多くのJ2EEアプリケーションフレームワークが提供されています。ひとえにJ2EEアプリケーションフレームワークといっても、ビジネスロジックを構築するためのもの、Webアプリケーションの画面遷移を定義するもの、ユーザーインターフェイスを構築するものなど、数多くの種類が存在します。よって、開発者は構築するアプリケーションに適したフレームワークを選択する必要があると同時に、フレームワークの特性を十分に理解したうえで利用する必要があります。

 さて、今回紹介する「JavaServer Faces」(以下JSF)は、今後J2EEの世界では標準になるであろうWebアプリケーションのインターフェイスを構築するためのフレームワークです。JSFは、現在Java Community Process (JSR-127)で仕様策定が進められています。JSFは現在EA4が提供されており、サン・マイクロシステムズが提供している「Java Web Services Developer Pack 1.2」(以下JWSDP)に含まれています。今回はこのJWSDPを使用して、JSFの仕組みと使用方法について解説します。なお、JWSDPには、JSF以外にもJAXB、XWS-Security、SAAJなどの最新のWeb Services、 XML関連技術が含まれています。また、アプリケーションサーバとしてTomcat 5も同梱されています。

 また、JSFは今年(2003年)開催された「JavaOne 2003」でサン・マイクロシステムズが発表した「Project RAVE」の中核となる技術です。Project Raveは、GUIでWebアプリケーションを開発できるツールで、マイクロソフトのVisual Studio .NETと同様にフォームにWebアプリケーションを構成する部品をドラッグ&ドロップで配置し、部品毎にイベントハンドラを記述するといった開発スタイルを実現します。JSFは、IBM、ボーランド、オラクルも自社のツールに採用することを表明しており、JavaによるWebアプリケーションの開発が飛躍的に容易になる可能性を秘めています。

 本稿では2回の連載でJSFを解説していきます。前編では、JSFのアーキテクチャの解説に加え、実際のコードを見ながらJSFを理解します。後編では、JSFアプリケーションの構築手順について詳しく解説する予定です。

  JavaServer Facesがもたらすメリット

 前述しましたが、JSFはWebアプリケーションのインターフェイスを構築するためのフレームワークです。JSFを使用することにより、Webアプリケーションのビジネスロジックとプレゼンテーションデザインを完全に分離することができます。これによりビジネスロジックを構成する開発者とプレゼンテーションデザイナーの役割分担を明確にすることができ、Webアプリケーションの開発効率を上げることができます。しかし、この点だけを比較したのでは、JSPを使用する場合と大差がないといえるでしょう。JSFを使用する最大の特徴は、プレゼンテーションコンポーネント(テキストフィールドやボタン、セレクトボックスなど)をJSPカスタムタグライブラリを使用して表現することができ、さらにそれらのコンポーネントに対して、ステート情報の保持や入力値のチェック(バリデータ)、型の変換(コンバータ)、イベントの制御(イベントハンドラ)、データモデルへのマッピング、といった機能を与えられることです。

 例えば、「ステート情報の保持」機能は、各コンポーネントに保存されたアトリビュートを透過的に保持します。実例を挙げると、あるアプリケーションでユーザー登録フォームに入力した後、登録ボタンをクリックした際にエラーが発生し、フォームを再読み込みする必要があった場合でも、同一セッションであればフォームがクリアされることがないのです。

 そのほかの機能については、この後の「サンプルアプリケーションに見るJSF」で解説しますのでここでは省略しますが、インターフェイスを構築するうえで必要な機能をより簡単に実現できることがJSFを利用するメリットなのです。また、コンポーネントを独自に作成するためのフレームワークも用意されており、自由にUIコンポーネントを作成することも可能です。

 ところで、JSFの記述形式は特別なスクリプトやマークアップ言語を用いるわけではありません。前述したようにJSPカスタムタグライブラリが用意されているため、いままでJSPを記述したことのあるデザイナーであれば容易にページを作成することができるでしょう。

 もう1つの特徴として、JSF APIはServlet API上に構築されており、多様なクライアントデバイスに対して出力するために、そのほかのプレゼンテーションテクノロジを併用することも可能です。

  JSFを構成するコンポーネントと
  JSFアプリケーション

 JSFは、次の機能を実現するためのAPI群およびRI(参照実装)から構成されます。

  • UIコンポーネント
  • ステート管理
  • イベントの管理
  • バリデーション
  • データコンバージョン
  • ページナビゲーション定義
  • 国際化

 また、参照実装にはJSPから簡単にこれらの機能を利用するためのJSPカスタムタグライブラリ(コンポーネントタグライブラリ、コアタグライブラリ)が用意されています。

 JSFアプリケーションは、通常のWebアプリケーションと同様にWebコンテナで動作します。そして、次のコンポーネントから構成されます。

  • コンポーネントタグライブラリ(UIコンポーネントを表現するためのJSPカスタムタグライブラリ)
  • コアタグライブラリ(イベントハンドラおよびバリデータ、コンバータなどを表現するためのJSPカスタムタグライブラリ)
  • サーバサイドでステートフルオブジェクトとして表現されるUIコンポーネント
  • バリデータ、コンバータ、イベントハンドラ、ナビゲーションハンドラ
  • JavaBean(データモデル:JSFではモデルオブジェクトと呼ばれる)
  • サーバサイドヘルパークラス(データベースアクセスビーンなど)
  • JSFぺージ

 JSFアプリケーションは、コンポーネントタグライブラリとコアタグライブラリを用いUIコンポーネントを表現します。そのためUIコンポーネントを表現するために、HTMLなどのマークアップランゲージを使用してハードコードする必要はありません。またコアタグライブラリを使用することで前述したJSFの機能を容易に使用することができます。

  JSFページのライフサイクル

 JSFページのライフサイクルはJSPページのそれに似ています。クライアントはHTTPリクエストを作成し、サーバはHTMLに変換されたレスポンスを返します。ただし、JSFには前述したようなさまざまな機能があるため、JSPのライフサイクルとは異なったものになっています。

図1 JSFのライフサイクル

・Reconstitute Request Tree
JSFページが呼び出されたとき、まず初めにコンポーネントツリーが作成されます。そのページにあるコンポーネントは、図2のようにツリー構造で表されJSFのコンテキスト(FacesContext)に登録されます。

図2 1つのフォームにテキストフィールドとサブミットボタンが存在するツリー

・Apply Request Values
コンポーネントツリーが作成された後、各コンポーネントはリクエストパラメータから値を取得し、その値を保持します。再読み込みリクエストがあった場合は、「Render Response」に渡ります。もし値のコンバート(定義された型への変換)が失敗したら、コンポーネントにひも付いたエラーメッセージが生成されFacesContextにキューされます。このメッセージは「Process Validator」フェイズを介し「Render Response」フェイズに渡ります。

・Process Validations
このフェイズではツリーに登録されているコンポーネントごとにアトリビュートの値を検証します。無効であればFacesContextにエラーメッセージを加え、「Render Response」フェイズに渡ります。「Apply Request Values」フェイズでコンバージョンエラーがあった場合も、「Render Response」フェイズに渡ります。

・Update Model Values
「Process Validations」フェイズでアトリビュートが有効であると判断された場合、その値を該当するモデルオブジェクト値としてセットします。モデルオブジェクトプロパティによって指定されている型にコンバートできない場合は、「Render Response」フェイズに渡りエラーメッセージを表示します。

・Invoke Application
このフェイズではフォームのサブミットや別のページへのリンクなどアプリケーションレベルのイベントをハンドルします。デフォルトのActionListnerはコンポーネントのアクションをNavigationHandlerによって、アプリケーションのコンフィグレーションファイルで定義したナビゲーションルールに従いページを表示します。

・Render Response
このフェイズでは、FacesContextに保持しているコンポーネントを表示します。「Apply Request Values」「Process Validations」フェイズでエラーが発生した場合は、エラーメッセージを表示します。

 それではさっそくJSFアプリケーションを動かしてみましょう。冒頭で述べたとおり、現在JSF(EA4)はJWSDPに収録されています。よって、まずJWSDPをインストールします。

  JWSDPのインストール

 本稿ではWindows 2000上でのインストール手順について解説します。Windows用のバイナリ(約46Mbytes)を以下のURLからダウンロードします。また、JWSDP 1.2では、JDK 1.4.1以降のJDKが必要となりますので、これを事前にインストールしてください。

「Java Technology & Web Services Downloads」
http://java.sun.com/webservices/downloads/webservicespack.html

 ダウンロードしたファイル「jwsdp-1_2-windows-i586.exe」をダブルクリックし、インストーラを起動してインストールを行います。設定項目は、JDKのインストールディレクトリ、JWSDPのインストールディレクトリ、Web Proxyサーバ、Tomcatの管理者ID、Passwordのみですので非常に簡単にインストールできます。インストールの手順、インストール後のパスの設定方法など詳細は上記URLを参照してください。

インストーラ起動後の画面

 インストールに成功すると、Windowsのプログラムメニューに、「Java(TM) Web Service Developer Pack1.2」が追加されます。このメニューから[Start Tomcat]を選択しTomcatを起動します。

  サンプルアプリケーションに見るJSF

 Tomcatの起動後、http://localhost:8080/にアクセスしてみましょう。スクリーンショットのような画面が表示されます。このページは、JWSDPのトップページで、サンプルアプリケーションやドキュメントのリンクが含まれており、JSFのサンプルアプリケーションへのリンクもあります。

WSDPのトップページ

 JSFのサンプルアプリケーションは4種類提供されています。今回は、「Guess Number Sample」というシンプルなアプリケーションを例に、JSFの基本について説明することにします。

JWSDPに付属するサンプルアプリケーション。今回はこの中から「Guess Number Sample」を例にJSFを説明する

 このアプリケーションは、ユーザーに1〜10までの番号を入力させ、サーバ側でランダムに生成した値と一致するかを判定するというものです。「Guess Number Sample」をクリックした後、次のページで「Click here to gess Duke`s number」をクリックします。

 アプリケーションが起動したら、画面に適当な数値を入力し、「Submit」をクリックしてください。入力した文字がサーバ側で計算した数値と一致するかの結果が返ってきます。

「Guess Number Sample」の画面。入力した数値とコンピュータが発生させた数字を比較し結果を表示する

 それでは、このアプリケーションのソースコードを見てみます。まず、数値を入力するページから見てみましょう。

リスト1 greeting.jsp
1:<HTML>
2:    <HEAD> <title>Hello</title> </HEAD>
3:    <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
4:    <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
5:    <body bgcolor="white">
6:    <h2>Hi. My name is Duke.  I'm thinking of a number from 0 to 10.
7:    Can you guess it?</h2>
8:    <jsp:useBean id="UserNumberBean"
 class="guessNumber.UserNumberBean" scope="session" />
9:    <f:use_faces>
10:    <h:form id="helloForm" formName="helloForm" >
11:        <h:graphic_image id="wave_img" url="/wave.med.gif" />
12:        <h:input_number id="userNo" numberStyle="NUMBER"
13:                         valueRef="UserNumberBean.userNumber">
14:            <f:validate_longrange minimum="0" maximum="10" />
15:        </h:input_number> 
16:     <h:command_button id="submit" 
action="success" label="Submit" commandName="submit" />

17:         <p>
18:       <h:output_errors id="errors1" for="userNo"/>
19:    </h:form>
20:    </f:use_faces>
21:</HTML>

 JSFには、各UIコンポーネント(UIForm、UIInputなど)をHTMLにレンダリングするためのカスタムタグライブラリが提供されており、このタグライブラリを使用してJSPを開発します。リストの3、4行目ではJSFのタグライブラリをインポートしています。これは、JSFを利用するJSPでは決まった書式になります。8行目では、JSPのjsp:useBeanアクションを使用し、guessNumber.UserNumberBeanクラスをUserNumberBean IDでSessionスコープにインスタンス化しています。

 9行目に、初めてJSFのタグが登場します。use_facesタグは、ほかのJSFタグのコンテナタグで、すべてのJSFコア、コンポーネントタグは、<use_faces>で始まり、</use_faces>で終わるタグ内に記述する必要があります。

 10行目のformタグは、HTMLのformに対応するタグで、HTMLと同様このformタグ内にテキストボックス、サブミットボタンなどに対応したタグを定義します。12〜15行目のinput_numberがHTMLのテキストボックスに対応しますが、ここではさまざまなJSFの機能を使用しています。

 まず、numberStyle="NUMBER"要素についてですが、ここではJSFのコンバータの機能を使用しています。コンバータは、入力値をJavaの対応する型に変換するための機能で、JSFには標準でさまざまなコンバータが提供されています。また、開発者自身がコンバータを開発し、機能を追加することも可能です。ここでは、JSFが標準で提供するコンバータを利用しており、numberStyleにNumberが指定されているように、入力文字を数値型に変換します。

 次に、validate_longrangeタグです。こちらも先ほどのコンバータと同様にJSFが標準で提供するバリデータの機能を使用しています。バリデータは、入力された値が、アプリケーション側で期待する規則に従っているかを検証(必須フィールドのチェック、文字入力パターンのチェックなど)する機能で、ここでは、minimum="0"、maximum="10"と指定することで、値が0〜10の範囲にあるかを検証しています。試しに範囲外の値を入力してみてください。エラーメッセージが表示されるかと思います。また、13行目のvalueRef="UserNumberBean.userNumber"については、次のJavaBeansの説明で触れます。16行目のcommand_buttonは、HTMLのsubmitに対応するタグになりますが、このタグ内で使用されているactionについては後ろで説明します。18行目のタグは、input formにアルファベットなどを入れた場合にエラーメッセージを表示します。

 次に、リスト1のgreeting.jsp内でインスタンス化していたJavaBeansのコードを見てみます(リスト2)。

リスト2 JavaBeansのコード(UserNumberBean.java)
1: import java.util.Random;
2: public class UserNumberBean {
3: 
4:    Integer userNumber = null;
5:    Integer randomInt = null;
6:    String response = null;
7:    
8:    public UserNumberBean () {
9:    Random randomGR = new Random();
10:    randomInt = new Integer(randomGR.nextInt(10));
11:    }
12:    public void setUserNumber(Integer user_number) {
13:        userNumber = user_number;
14:    }
15:    public Integer getUserNumber() {
16:        return userNumber;
17:    }
18:    public String getResponse() {
19:        if(userNumber.compareTo(randomInt) == 0)
20:        return "Yay! You got it!"; 
21:    else 
22:        return "Sorry, "+userNumber+" is incorrect.";
23:    }
24:}

 このクラスは特にJSFの機能は使用しておらず、通常のJavaBeansとなっています。このクラスでは、ユーザーが入力した値を保持し、ランダムに生成した値と比較し一致していれば「Yay! You got it!」というメッセージを、値が異なっていれば、「 Sorry, "+userNumber+" is incorrect」を返します。なお、文字列の表示はresponse.jspで行っています。

 さて、ここまで見てきてお分かりだと思いますが、このJSFサンプルアプリケーションでは、通常のWebアプリケーションにあるような、ServletRequestのgetParameterで入力値を受け取り、値をチェックし、ビジネスロジックが終わったら、RequestDispacherを使用してほかのJSPにforwardするというコードが必要ありません。

 すなわち、入力値の受け取りからビジネスロジックの終了まではinput_numberタグ内のvalueRef="UserNumberBean.userNumber"ですでに完了しています。この指定により、ユーザーがデータをサブミットした際に、UserNumberBeanのuserNumberフィールドに値が設定されていたというわけです。また、結果を表示するためのページの指定は、コード中に直接記述しません。faces-config.xmlというJSFに固有の設定ファイルに定義します(リスト3)。

リスト3 faces-config.xml
1: <?xml version="1.0"?>
2: 
3: <!DOCTYPE faces-config PUBLIC
4:  "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.0//EN"
5:  "http://java.sun.com/dtd/web-facesconfig_1_0.dtd">
6:
7: <faces-config>
8:
9:  <navigation-rule>
10:    <from-tree-id>/greeting.jsp</from-tree-id>
11:    <navigation-case>
12:      <from-outcome>success</from-outcome>
13:      <to-tree-id>/response.jsp</to-tree-id>
14:    </navigation-case>
15:  </navigation-rule>
16:
17:  <navigation-rule>
18:    <from-tree-id>/response.jsp</from-tree-id>
19:    <navigation-case>
20:      <from-outcome>success</from-outcome>
21:      <to-tree-id>/greeting.jsp</to-tree-id>
22:    </navigation-case>
23:  </navigation-rule>
24:
25: </faces-config>

 9〜15行目の<navigation-rule>定義がページの指定です。12行目<from-outcome>の値「success」が、greeting.jspのcommand_buttonタグのactionの値(success)に対応しており、入力値の変換、値の妥当性の検証に成功したら、<to-tree-id>で指定されたページ(response.jsp)に進むというルールになります。

リスト4 respnse.jps
1:<HTML>
2:    <HEAD> <title>Guess The Number</title> </HEAD>
3:    <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
4:    <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
5:    <body bgcolor="white">
6:    <f:use_faces>
7:    <h:form id="responseForm" formName="responseForm" >
8:        <h:graphic_image id="wave_img" url="/wave.med.gif" />
9:    <h2><h:output_text id="result" 
10:                 valueRef="UserNumberBean.response"/></h2>
11:    <h:command_button id="back" label="Back" action="success"
12:        commandName="back" /><p>
13:
14:    </h:form>
15:    </f:use_faces>
16:</HTML>

 さて、ここまでで前編は終了です。JSFを使うことでいかにWebアプリケーションの記述が簡単になるかがご理解いただけたと思います。

 次回の後編では、JSFの機能をふんだんに取り入れたアプリケーションの開発手順について詳細に解説します。

 

INDEX
特別企画:JavaServer Facesを理解する
前編 JSFの構造を理解する
  後編 JSFによるWebアプリケーション開発



連載記事一覧






Java Agile フォーラム 新着記事
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Java Agile 記事ランキング

本日 月間