というわけで、NetBeans6.1の上でJSF1.2のコンポーネントを作ってみようと思う。
まずは、コンポーネントのプロジェクトを入れるためのフォルダを適当に作る。
とりあえず、安直に「JSFComponent」にしてみました。
そのフォルダにMyStaticText-Runtimeという名前で、Java Class Libraryのプロジェクトを作る。
ライブラリフォルダは、ひとつ上のフォルダのlibを指定する。
下の画像はプロジェクト作成後のプロパティから見れるライブラリフォルダの設定です。
さて、色々作る前に、今回作るものの全体像を少し説明します。
作るものは、MyStaticTextという名前のコンポーネント。
id value styleくらいを指定できる非常にシンプルなものになります。
使い方のイメージとしてはこんな感じ。
<kzk:MyStaticText value="Hello JSF Component" />
これで、画面にHello JSF Componentが表示されたらOK!
コンポーネントに必要なもの
ということで、簡単にJSF1.2のコンポーネントを作るために必要なものを列挙します。
- コンポーネントクラス(この例ではMyStaticTextクラスになります)
- タグハンドラ(この例ではMyStaticTextTagクラスになります)
- レンダラ(この例ではMyStaticTextRendererになります)
- TLDファイル(タグにどんなプロパティがあるか、とかを定義します。)
- faces-config.xmlファイル(コンポーネントやレンダラを登録します。)
作ってみよう
というわけでさくっとtldを作ります。
名前はMyStaticText.tldです。新規作成からタグライブラリ記述子を選んで下記のような感じに入力して完了をクリックする。
次にタグハンドラを作成します。
タグハンドラも新規作成から作ることが出来ます。これも下記のように入力して次へ。
次の画面では、タグがもつ属性の指定を行ったりtldの選択を行います。
tldはさっき作ったやつを選択します。
属性はid, value, style, bindingの4属性でOKです。
ここらへんで色々コンパイルエラーが出ると思います。気持ち悪いので出ないようにします。
とりあえず、ライブラリの追加からJSF1.2を追加。
いったんライブラリをインポートしてからじゃないと追加できないと思います。
そして、glassfish-v2のlibの下にあるjavaee.jarもライブラリに追加します。
追加のときに、ライブラリフォルダにコピーする方法にすることを忘れないように。これで、コンパイルエラーが無事なくなりました。次へ行きます。
次は、コンポーネントにあたるクラスを作ります。
クラス名はMyStaticTextにします。コンポーネントのファミリやレンダラーのタイプを指定しないといけないのと、javax.faces.UIOutputから継承するように作ります。
UIOutputがidプロパティとvalueプロパティあたりは持っててくれるので、styleプロパティだけを実装するかたちになります。
package com.wankuma.kazuki.statictext;
import javax.faces.component.UIOutput;
import javax.faces.context.FacesContext;
public class MyStaticText extends UIOutput {
public static final String MY_STATIC_TEXT_RENDERER_TYPE = "com.wankuma.kazuki.statictext";
public static final String MY_STATIC_TEXT_COMPONENT_TYPE = "com.wankuma.kazuki.statictext";
public static final String MY_STATIC_TEXT_FAMILY = "javax.faces.Output";
private String style;
@Override
public String getFamily() {
return MY_STATIC_TEXT_FAMILY;
}
@Override
public String getRendererType() {
return MY_STATIC_TEXT_RENDERER_TYPE;
}
public String getStyle() {
return style;
}
public void setStyle(String style) {
this.style = style;
}
}
1つ使ってないMY_STATIC_TEXT_COMPONENT_TYPEというのがありますが、これは後で使うので宣言だけしときます。
ここまできたら、styleプロパティの値を保持しておくためのコードを書きます。
リクエストを跨いでも値失わないぜ的な処理のはず…
saveStateとrestoreStateの2メソッドを実装する。
@Override
public Object saveState(FacesContext context) {
Object[] values = new Object[2];
values[0] = super.saveState(context);
values[1] = style;
return values;
}
@Override
public void restoreState(FacesContext context, Object state) {
Object[] values = (Object[]) state;
super.restoreState(context, values[0]);
style = (String) values[1];
}
そしたら、これにあわせてTagハンドラも修正していく。
まずは、親クラス!!親クラスは、SimpleTagSupportではなくてUIComponentELTagというクラスを使うことになってるらしい。
そしてdoTagメソッドではなくて、setPropertiesメソッドをオーバーライドしてタグの値をコンポーネントに書き戻すみたいだ。後は、このタグで使うコンポーネントとレンダラの識別子を返すメソッドも実装する。
さくっと実装する。因みに、UIComponentELTagクラスがidプロパティを既に持ってるので、残りの部分を実装している。
package com.wankuma.kazuki.statictext;
import javax.faces.component.UIComponent;
import javax.faces.webapp.UIComponentELTag;
public class MyStaticTextTag extends UIComponentELTag {
private String value;
private String style;
private String binding;
@Override
public String getComponentType() {
return MyStaticText.MY_STATIC_TEXT_COMPONENT_TYPE;
}
@Override
public String getRendererType() {
return MyStaticText.MY_STATIC_TEXT_RENDERER_TYPE;
}
@Override
protected void setProperties(UIComponent component) {
super.setProperties(component);
MyStaticText mst = (MyStaticText) component;
if (value != null) {
mst.setValue(value);
}
if (style != null) {
mst.setStyle(style);
}
}
public void setValue(String value) {
this.value = value;
}
public void setStyle(String style) {
this.style = style;
}
public void setBinding(String binding) {
this.binding = binding;
}
}
次にレンダラを作成する。
クラス名は、MyStaticTextRendererにする。レンダラの親クラスはjavax.faces.render.Rendererを指定する。
今回は、超単純なコンポーネントなので、encodeBeginでhtmlを出力するだけです。
子要素を持ってたりするコンポーネントだと、encodeEnd?とかを使うことになるみたいだ。
まぁspanで囲まれたテキストを出力するように作ってみた。
package com.wankuma.kazuki.statictext;
import java.io.IOException;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.render.Renderer;
public class MyStaticTextRenderer extends Renderer {
@Override
public void encodeBegin(FacesContext context, UIComponent component) throws IOException {
ResponseWriter writer = context.getResponseWriter();
MyStaticText mst = (MyStaticText) component;
writer.startElement("span", mst);
writer.writeAttribute("id", mst.getId(), null);
writer.writeAttribute("style", mst.getStyle(), null);
writer.writeText(mst.getValue(), null);
writer.endElement("span");
}
}
そしたら、最後に!!faces-config.xmlを作成します。場所はMETA-INFです。
ここには、基本的にコード内で指定していたコンポーネントタイプとレンダラタイプが、実際にどのクラスに結びついているのかというのを定義するみたいだ。
<?xml version="1.0" encoding="UTF-8"?>
<faces-config version="1.2"
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-facesconfig_1_2.xsd">
<component>
<component-type>com.wankuma.kazuki.statictext</component-type>
<component-class>com.wankuma.kazuki.statictext.MyStaticText</component-class>
</component>
<render-kit>
<renderer>
<description>MyStaticText Component</description>
<component-family>javax.faces.Output</component-family>
<renderer-type>com.wankuma.kazuki.statictext</renderer-type>
<renderer-class>com.wankuma.kazuki.statictext.MyStaticTextRenderer</renderer-class>
</renderer>
</render-kit>
</faces-config>
ここまでの作業を終えると、大体下のような感じのプロジェクトになってる。
多分これでコンポーネント完成~。ということで実験してみる。
実験用のWebアプリケーションを新規作成します。
JSFComponentフォルダ(MyStaticText-Runtimeプロジェクトと同じフォルダ)にMyStaticTextTestという名前でWebアプリケーションを新規作成する。
ここでは、ライブラリフォルダをMyStaticText-Runtimeと同じ場所に指定している。
アプリケーションサーバにはGlassFish v2を選択して次へ
フレームワークは、当然JSFを選択する(Visualじゃないほうね)
そして、JSF1.2のライブラリも追加しておく。
出来上がったプロジェクトのライブラリにMyStaticText-Runtimeプロジェクトを追加する。
追加したら、welcomeJSF.jspにtaglibディレクティブを追加する。
<%@ taglib prefix="kzk" uri="http://blog.wankuma.com/kazuki/components" %>
これで、さっき作ったタグが使えるようになる!!
ということで早速書いてみた。h1タグでJavaServer Facesを表示してる次の行に下のように入力する。
<kzk:MyStaticText id="myStaticText1" value="Hello JSF Component" style="background:red;" />
そしたら、おもむろに実行!!
お~っちゃんと表示された~。
感想:作るものが多くて地味に大変。凝ったものになってくると考えただけで…!?