Facelets入門(第二回)Faceletsの使い方 (つづき) by Jacob Hookom 17 Aug 2005 01:00 EDT |
本稿は、Faceletsに関する連載の第二回です。Faceletsは、JSFでアプリケーションを作るための新しい技術です。第一回はFaceletsの入門編でしたが、本稿ではJacobが、Faceletsを使って実際にJSFのアプリケーションを作るやり方の第一歩を説明します。
この連載の初回で述べたように、Faceletsは正しいXMLをコンパイルします。わずか数行のファイルでも、とにかくそれは正しいXML文書でなければなりません。
FaceletsはSAXを使ってドキュメントをコンパイルし、TagHandler
オブジェクトのステートのないツリー(ステートレスなツリー)作ってFacelets
オブジェクトの中に収めます。Faceletsはスレッドセーフですから、複数のリクエストから同時にアクセスされるような大規模な展開でも安心して使えます。Faceletsはコンパイル時に、警告メッセージや、ライブラリに関する情報、JSF内部のビュー処理に関する情報などをプログラマに提供します。
Faceletsのコンパイルの実行は、次のように簡単です:
// FaceletFactoryを取得してFaceletを作る FaceletFactory factory = FaceletFactory.getInstance(); Facelet f = factory.getFacelet(viewToRender.getViewId()); // UIViewRootの中身を作る f.apply(context, viewToRender);
Faceletsのアーキテクチャに関する詳しい説明は、今後の記事でいたします。
Faceletsはエラーを綿密に扱います。たとえば属性の値を指定するELの式で、タイプミスをしたとしましょう。するとFaceletsは、コンパイル時に次のようなエラーメッセージを出力します:
greeting.xhtml @18,97 <input action="#{success[}"> Error Parsing: #{success[}
ご覧のようにFaceletsは、エラーが起きたファイル、行、そしてカラムまで表示します。それにもちろん、エラーを起こした成分と属性も表示します。
Faceletsはまた、デバッグメッセージの処理と出力のためにjava.util.loggingパッケージを使います。詳しくは、このパッケージのAPIドキュメンテーションを読んでください。
Faceletsでは、XMLファイルやJavaのコードを使ってコンポーネントライブラリを定義できます。ただしここでは、今後よく使われると思われるXML方式について説明します。それは、次のような形になります:
<facelet-taglib> <namespace>http://www.mycompany.com/jsf</namespace> <tag> <tag-name>bar</tag-name> <component> <component-type>javax.faces.Data</component-type> <renderer-type>com.mycompany.Bar</renderer-type> </component> </tag> </facelet-taglib>
たったこれだけで、あなたのコンポーネントをFaceletsの一員にすることができました。FaceletsはこのようなXMLファイルを、次の二つのうちどちらかの方法で参照します:
web.xml
の中で、"facelets.LIBRARIES
"というinit-paramの値として、";"で区切って羅列しておく。これらのファイルは、(web.xml
に書くのだから)Strutsのコンフィギュレーションファイルなどと同じく、アプリケーション固有のリソースです。META-INF
フォルダの中に拡張子が".taglib.xml
"のファイルを収めます。Faceletsはこれらのファイルを、ちょうどJSPのtldファイルの場合のように、コンパイル時に拾い上げます。 facelet-taglib
ファイルの中で、コンバータやヴァリデータ、それにカスタムのTagHandler
も指定できますから、ドキュメントの処理をとても細かく制御できます。
上と同じfacelet-taglib
ファイルによって、Faceletsの中で使うヴァリデータやコンバータを表現できます。たとえば:
<facelet-taglib> <namespace>http://www.jsfcentral.com/public</namespace> <tag> <tag-name>validateRegExp</tag-name> <validator> <validator-id>foo.bar.RegExp</validator-id> </validator> </tag> <tag> <tag-name>convertUtilDate</tag-name> <converter> <converter-id>foo.bar.UtilDate</converter-id> </converter> </tag> </facelet-taglib>
この場合もやはりFaceletsは、JavaServer Facesの既存のAPIと密接に連動しますから、定義はご覧のように簡単です。Faceletsはあなたが定義したConverter
やValidator
に、必要な属性を自動的に結び付けます。
気の早いあなたは、Faceletsに自作のカスタムタグを加えたい、と思っていますね。それはたとえば、UIComponent
やValidator
, Converter
などに独自のプロパティや属性を加えて独自の動作をさせたい場合です。
Faceletsには、あなたのその仕事を楽にするための基盤的なクラスがたくさんあります。ひとつの例として、<c:if>
のソースを見てみましょう:
public final class IfHandler extends TagHandler { protected final TagAttribute test; protected final TagAttribute var; public IfHandler(TagConfig config) { super(config); this.test = this.getRequiredAttribute("test"); this.var = this.getAttribute("var"); } public void apply(FaceletContext ctx, UIComponent parent) throws IOException, FacesException, ELException { boolean b = this.test.getBoolean(ctx); if (this.var != null) { ctx.setAttribute(var.getValue(ctx), new Boolean(b)); } if (b) { this.nextHandler.apply(ctx, parent); } } }
たったのこれだけです!。ご覧のようにFaceletsは“善良な市民”のように自律的に振る舞い、コンストラクタ注入(*)を利用して依存性を受け取り、ドキュメントをコンパイルします(コンストラクタ引数でタグの属性値を取得する)。Faceletsとそのタグはすべてこのようにステートレス(ステートがない)ですから、タグハンドラオブジェクトをプールするJSPとは違います。技術的な詳しいお話は今後の記事で述べますから、ここでは上のコードを簡単に説明しましょう。
〔*:constructor injection, 依存性注入の3つの形式のうちのひとつ。依存性をコンストラクタ引数で受け取る。参考:Inversion of Control Containers and the Dependency Injection pattern(Martin Fowler)。コンストラクタ注入は、コンストラクト直後のオブジェクトが、それだけでそのまま完全に使える状態なので、別名、“善良な市民”デザインパターンとも呼ばれる。参考:A Brief Introduction to IoC(Sam Newman)。”他人--たとえばセッターメソッドなど--をあてにしない”という意味かなあ。〕
<c:if>
がextendsするTagHandler
は、カスタムタグを作るための基盤的クラスの好例です。このTagHandler
は、JSPのTagSupport
やBodyTagSupport
に相当します。
タグの構築時に、そのタグのTag
オブジェクトを表すTagConfig
が渡されます。TagConfig
と、それから得られるTag
オブジェクトは、 Faceletsドキュメントの中のタグの位置、子のタグ、属性など、ドキュメントの構造を伝えます。
TagAttributess
はXMLドキュメントの中で指定されている属性の表現です。このクラスにはELの式を処理し、必要なタイプ変換をするためのメソッドがたくさんあります。これらもやはりステートレスですから、引数として現在のステートを表すFaceletContext
を渡す必要があります。
もうひとつ注目していただきたいのは、子のタグをメンバ変数nextHandler
で表現していることです。子は必要なだけ何度でも適用できますし、またこの<c:if>
の場合(test==falseの場合)のようにまったく適用されないこともあります。
カスタムタグを書いたら、それを次のようにしてfacelet-taglib
ファイルに書き加えます:
<facelet-taglib> <namespace>http://www.jsfcentral.com/public</namespace> <tag> <tag-name>if</tag-name> <handler-class>com.jsfcentral.IfHandler</handler-class> </tag> </facelet-taglib>
以上は、Faceletsフレームワークの使い方に関する簡単な入門記事です。今後の記事では、テンプレートの作成などFaceletsの重要な機能と、Faceletsのカスタマイズについて、説明します。