JSF2でカスタムタグの作成

JSF2ではFaceletsに独自タグを定義するのが非常に簡単になっています。XHTMLファイルで実装する方法もあるのですが、ここではUIコンポーネントを継承した独自タグの実装と登録方法について紹介します。

まず、JSFにはタグによってUI〜という名前から始まるコンポーネントがあり、全コンポーネントの基底クラスがUIComponentです。

入力系のタグ(inputText)ではUIInputが用いられますが、簡単な例としてUIInputを継承した独自タグの作成〜登録までを行います。

いきなりタグの作成に行く前にまず、独自タグを作成するためにはencodeとdecodeについて知っておく必要があります。

encodeはHTMLを描画することを指しており、基本的にJSFのUIコンポーネントはHTMLの描画を行うために、次の3つのメソッドを順番に実行します。


encodeBegin
encodeChildren
encodeEnd

encodeBeginはその名の通り当該UIコンポーネントを描画する際に最初に呼ばれるメソッドです。次にencodeChildrenを呼び出し、最後にencodeEndメソッドが呼び出されます。それぞれのメソッドの詳しい説明は省略します。

逆にブラウザから送信されてきた情報をServer側で解析することをdecodeと言います。

独自のUIコンポーネントを実装する際はこれらのメソッド内に適切に処理を記述する必要があります。今回はタグの作り方と、登録方法の紹介がメインなので実装に関しては省略します。

では実際に独自UIコンポーネントを継承したクラスを用意します。

import javax.faces.component.FacesComponent;
import javax.faces.component.UIInput;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;

@FacesComponent("org.ui.Hoge")
public class UIHoge extends UIInput {

    public UIHoge(){
        ...
    }
    
    public void encodeBegin(FacesContext context) {
        ResponseWriter writer = context.getResponseWriter();
        ...
        String clientId = getClientId(context);
        ...
        writer.startElement(...);
        ...
    }

    public void decode(FacesContext context) {
        Map<String, String> requestMap = context.getExternalContext().getRequestParameterMap();
        ...
        String value = requestMap.get(getClientId(context));
        ...
    }

    ...
}

@FacesComponent("org.ui.Hoge")がコンポーネントを識別する名前で、必ず必要です。

次にDescriptorを作成します。このファイルはXXX.taglib.xmlという名前でなければなりません。今回は例なのでmy.taglib.xmlとしこのファイルをWEB-INF/配下に保存します。

<facelet-taglib version="2.0"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
    http://java.sun.com/xml/ns/javaee/web-facelettaglibrary_2_0.xsd">

    <namespace>http://hogeapp.ui.org/my-taglib</namespace>
    <tag>
        <tag-name>hoge</tag-name>
        <component>
            <component-type>org.ui.Hoge</component-type>
        </component>
    </tag>                        

</facelet-taglib>

component-typeが先ほど@FacesComponentで指定した名前と一致しているのが分かります。この名前の一致により、my.taglib.xmlで定義したhogeタグの実際のクラス名を識別しています。

namespaceですが、これはXHTMLで当該タグを利用する際にxmlnsで定義する名前空間です。

利用する側は次のように宣言する必要があります。

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:cc="http://hogeapp.ui.org/my-taglib">

    ...
    <h:body>
        <h:form>
            ...
            <cc:hoge ... />
            ...
        </h:form>
    </h:body>
</html>

最初にmy.taglib.xmlで定義したnamespaceを宣言する必要があります。あとは他のタグと同様に利用可能です。

最後にもうひと手間、web.xmlに次の定義を記述して完成です。

<context-param>
    <param-name>javax.faces.FACELETS_LIBRARIES</param-name>
    <param-value>/WEB-INF/my.taglib.xml</param-value>
</context-param>

今回はwarファイルにカスタムタグを配置する例を示しましたが、別のjarファイルでカスタムタグを作成し、lib配下に当該jarを配置しその中のカスタムタグを利用することもできます。

この場合、jarファイル内のMETA-INFにmy.taglib.xmlを配置します。必要に応じてfaces-config.xmlも配置可能です。もちろんカスタムタグ自体のクラスもjarファイルの中に配置する必要があります。