Teedaの入門記を書きます。 blogで書いていたけど、皆で書けるように。
Teeda入門を試すのに必要な環境作りに付いては Teeda/gettingStartedSetup を参照して下さい。 以下ではteeda-ex1プロジェクトを作成済みであるものとして説明します。
TeedaはJSF実装をベースにしたWebアプリケーションフレームワークです。
Teedaは大きく分けて2つのpartがあります。
・Teeda Core:JSF1.1の実装
・Teeda Extension:HTMLテンプレートを持ったJSF拡張フレームワーク
基本的にお奨めの使い方はTeeda Core、Teeda Extensionを共に使っていただく使い方です。
が、Teeda Coreだけでも純粋なJSF実装としてお使いいただけます。
JSFは実はServlet以外にもPortletの方にも対応することが仕様で義務付けられていますが
そちらの面もid:shinsuke_sugayaさんを中心にサポートいただいていますm(_ _)m
では本題に戻りまして。。。
Teeda ExtensionはPage駆動アーキテクチャに基づいていて、記述するのは
基本的に以下の2つだけです。
・Pageクラス(ただのPOJOクラス。クラス名をPageで終える必要がある)
・HTML(基本的にidをふるだけ。)
1HTMLファイルに対して、1Pageクラスが対応します。
この辺はサンプルを見つつ、学習するのがよいと思うので、次節以降解説します。
<?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" xml:lang="ja" lang="ja"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>Hello</title> </head> <body> Hello World!!! </body> </html>
このシンプルなHTMLをPageクラスとマッピングさせてみましょう。
HTMLの方は簡単です。
Mappingして動的に変えたい項目にidをつけたタグを振るだけです。
<?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" xml:lang="ja" lang="ja"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>Hello</title> </head> <body> Hello <span id="name">World!!!</span> </body> </html>
これをただ単にブラウザで見るとHello Worldと表示されます。
ではこれに対応するPageクラスを作成しましょう。
Pageクラスを作成するときには、EclipseプラグインDoltengを使うと便利です。
以下、Teeda/gettingStartedSetupの手順に沿ってDoltengのインストールと teeda-ex1プロジェクトの作成が終わっているものとして説明します。
teeda-ex1プロジェクトのhello.htmlのソースを開いて、HTMLソース上でCtrl+5を押してみましょう。
なにやらダイアログが表示されます。このダイアログでPageクラスを作成することができます。
DoltengではHTMLの内容を読み込んで、Pageクラスに必要そうなプロパティを
自動的に追加したPageクラスを作成してくれます。
また、QuickJUnitにおけるCtrl+9でテストクラスへの移動が出来るように
Ctrl+5でDoltengではPageクラス←→HTML間を移動できます。
(Pageがない場合にダイアログが表示されPageクラスを作成するかを聞いてきます。)
ここではダイアログで単に「Finish」ボタンを押して、Pageクラスを作成して下さい。 HelloPageクラスが作成されます。
作成されたPageクラスのプロパティnameに"Seasar2"と入力しておきましょう。
package examples.teeda.web.hello; public class HelloPage{ private String name = "Seasar2"; public String getName() { return name; } public void setName(String name) { this.name = name; } }
これで準備は整いました。
Tomcatを起動して、localhost:8080/teeda-html-example/view/hello/hello.html
というように表示してみると
Hello Seasar2
と表示されます。
このとき何が起きているのでしょうか? ひとつひとつ見ていきましょう。
まずTeedaの基本は全てidです。
idでマッピングされない項目は、動的な処理を行う集合(コンポーネント)として みなされません。
idがない場合全て単なるテキストとして処理されます。
上記の例で言えば、spanタグにnameというidをふることで
あ、このタグは動的な値を扱うのだとTeedaは解釈します。
idの振り方には規約がありますが、それはこの後一つ一つ見ていけば すぐに覚えられます。また、そのリファレンスも十二分に準備する予定です。
動的に出力する項目(outputText)はどのように実現するのでしょうか。
簡単です。
spanタグでidをふるだけ。
Hoge.html
<span id="aaa">bbb</span>
で、Hoge.htmlが読まれるとHogePageクラスのaaaプロパティ
が上記で書かれている値(bbb)の代わりに出ます。
次回は入力(inputText)を見てみましょう。
おっと。忘れちゃいけない配置についてお話しておきます。
Teedaは基本的に、Seasar2.4の配置に基づいています。
2.4でのPageクラスなどの構成は以下で書いたので割愛。
余談ですが、このようにパッケージ構成を決めることで煩雑だった
DIの設定部分を取り除いているのです。
だからS2.4は、diconをいじる必要がほとんどなく、
パッケージ構成を初期情報として与えるだけで終わりです。かんたん^^
これと同様に、HTMLの配置は以下のようにします。
ルートコンテキスト +view +[サブアプリケーション名] -各HTML
サブアプリケーションとは1ユースケースと同等です。
これを例えば、サブアプリケーションhelloの中に、HelloPage、hello.thmlとすると
ルートコンテキスト +view +hello -hello.html
となります。
Teeda(というかS2.4の規約)ではデフォルトは/view以下を
対象のHTMLとみなして処理します。
前回までにoutputTextはやりました。
今回はinputTextをやりましょう。
inputTextも非常に簡単です。
通常のinputで、typeにtext、idをarg1のように
Pageクラスのプロパティ名をつけるだけです。
add.html
<input type="text" id="arg1" title="INPUT1"/>
AddPage.java
public class AddPage { private int arg1; //getter, setterは省略
親クラスで記述しておくことも可能です。
出力、入力と来たので、あとはsubmitボタンを説明すれば
簡単なアプリを作ることが出来ます。
というわけでボタンです。
ボタンは今までと違い、ちょっとした規約があります。
といってもHTML上では相変わらずidをふるだけです。
ボタンには3種類あります。
です。
do系のボタンは、それに該当するPageクラスのメソッドが
呼ばれます。たとえばdoCalculateというidをつけておくと、
Pageクラスの同様のメソッドが呼ばれます。
add.html
<input type="submit" value="calculate" id="doCalculate"/>
AddPage.java
public String doCalculate() { result = arg1 + arg2; return null; }
ではこれでどのような画面遷移が発生するのでしょうか?
実はdoCalculateメソッドの戻り値が次の画面遷移先になっています。
そのため、doで呼ばれるメソッドはStringの戻り値を持たなければいけません。
return nullの場合、画面遷移は自画面に戻ってきます。
では、たとえばメソッドの実行後に別の画面に遷移したいときはどうすればよいのでしょうか?
簡単です。これだけです。
AddInputPage.java
public String doCalculate() { result = arg1 + arg2; return "addResult"; }
Stringの戻り値で次のPageクラス名を指定すればよいだけです。
同一サブアプリケーション内(たとえば/view/addの以下)であれば、
"addResult"のようにHTML名をかいてもらえばそれだけで遷移します。
では、別サブアプリケーションに遷移する場合は?
/view/aaa/from.htmlと/view/bbb/to.htmlとすると、
fromからtoへの遷移は、bbb_toとなります。
つまり/view以下からサブアプリケーションを含めて記述してください。
その際にアンスコでパッケージを区切るように記述します。
これはgoでもdoでもjumpでも変わりません。
非常に直感的なのがよくわかってもらえると思います。
またHTMLからも、どのメソッドが実行されるかが瞬時にわかります。
よくある設定をベースにしたフレームワークだと、画面遷移は
全て設定ファイルに記述するのが当たり前ですが(アノテーションでも同じです)、
本来見なくてもよいリソースにどこに遷移するのかを見に行かねばいけません。
Teedaではもっとも画面遷移に関係あるリソースにそのまま記述できるようにしています。
では、次はgoについて説明します。
goはdoのように特定のメソッドが呼ばれるわけではありません。
しかし、Aという画面からBという画面にロジックを通さずに
直接行きたい場合などがあると思います。そのような場合にgoを使います。
下記のaaa.htmlは、同一サブアプリケーション内のaaa2.htmlに
直接遷移します。
aaa.html
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>Page transition</title> </head> <body> <form id="aaaForm"> <input type="submit" id="goAaa2" value="go to same usecase"/> </form> </body> </html>
では、画面遷移最後のもうひとつはjumpです。
jumpはgoと同じようにロジックを通さずに画面遷移するための機能です。
では違いは何でしょうか?goとjumpには大きな違いがあります。
それは画面遷移時にValidationがあるか、ないかです。
doの場合は当然あるのですけど、goの場合も値のValidationは行います。
しかし、jumpの場合は値のValidationは行わずに画面遷移します。
たとえば下記のような足し算の計算後の画面(addResult)があったとして、
ただ単に戻るボタンを実装したいなどの場合はjumpを使うのがお勧めです。
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>Add</title> </head> <body> <form id="addResultForm"> <span id="messages"/> <span id="arg1"></span> + <span id="arg2"></span> = <span id="result"></span> <input type="submit" value="back by go" id="goAddInput"/> <input type="submit" value="back by jump" id="jumpAddInput"/> </form> </body> </html>
次はValidationについてです。
画面遷移時にValidationについての話が出てきました。
ではどのようにPageクラスのValidationをするのでしょうか。
こちらはS2JSFやS2Strutsを使ったことのある人なら馴染みの
ある方法を採用しています。
そう、定数アノテーションとTigerアノテーションです。
TeedaはJDK1.4に対応しているため、定数アノテーションでも使えます。
Tigerアノテーションでも使えますが、まずは定数アノテーションを使ってみましょう。
定数アノテーションの記法は以下のようになっています。
public static final String プロパティ名_Validator名 = "Validatorの各プロパティの設定";
例えば、下記のような足し算の入力画面のPageクラスがあるとします。
このクラスでは、引数にarg1とarg2をとり、両方とも入力必須で
桁数が3桁以上入力されなくてはいけないとします。
AddInputPage.java
public class AddInputPage { private Integer arg1; private Integer arg2; public Integer getArg1() { return arg1; } public void setArg1(Integer arg1) { this.arg1 = arg1; } public Integer getArg2() { return arg2; } public void setArg2(Integer arg2) { this.arg2 = arg2; } }
このような場合にどうすればよいのでしょうか。
上記の記述を思い出しながらやってみましょう。
まずプロパティarg1に必須項目のValidatorをしかけましょう。
どんなValidatorがあるのか今は気にしないでください。あとできちんと明記します。
public static final String プロパティ名_Validator名 = "Validatorの各プロパティの設定";
が記法なので、
public static final String arg1_TRequiredValidator = null;
というようになります。所定のValidatorに与えるプロパティがnullの場合は上記のように
してもらえればよいです。
これでarg1に必須チェックがかかります。
さて、次は入力桁が3桁以上でなければいけないというチェックですが、
標準のValidatorにLengthValidatorというValidatorがあります。
これを使いましょう。
今度はLengthValidatorのプロパティにきちんと値を渡さなくてはいけないですね。
最小の桁数が3桁なので、minimum=3というプロパティを渡します。
public static final String arg1_lengthValidator = "minimum=3";
さあ、これでValidationがかかります。
同じようにarg2でもValidationをかけることが出来ます。
先ほど示した例はこのようになります。
AddInputPage.java
public class AddInputPage { public static final String arg1_TRequiredValidator = null; public static final String arg1_lengthValidator = "minimum=3"; public static final String arg2_TRequiredValidator = null; public static final String arg2_lengthValidator = "minimum=3"; private Integer arg1; private Integer arg2; public Integer getArg1() { return arg1; } public void setArg1(Integer arg1) { this.arg1 = arg1; } public Integer getArg2() { return arg2; } public void setArg2(Integer arg2) { this.arg2 = arg2; } }
このようにValidatorをしかけていきます。
Validatorは各プロパティごとに書かれた順番どおりに呼ばれます。
上記の例だと、arg1はTRequiredが呼ばれて、その後にLengthValidatorが呼ばれます。
Tigerでも同じ仕組みで行われます。この例は各Validatorを説明した後に書きたいと思います。
次は各Validatorについて見ていきましょう。
次は各Validatorについて説明します。
ValidatorとはJSF(JavaServer Faces)で決められている画面からの入力値を
チェックする機能です。JSFとしても提供しているValidator、Teedaで提供しているValidatorが
あり、Tで始まるやつはTeedaで用意したものになっています。
数値チェック系
上記のように数値チェック系は多少かぶっているのもあります。
なぜか。それは以下のような機能を付け加えるためです。
です。
下記のように、target属性でどのボタンが押下されたときに
Validationが発生するのかを制御することができます。
public static final String number1_TRequiredValidator = "target=doCalculate";
本当は正規表現のValidatorもCommons-validatorみたいに
ひととおり準備してあげればいいんですけど、まだそこまでいってないです。
相関チェック系
相関チェック系では、主に2項目の関係でのチェックを行います。
JSF上、どいつとペアでValidationするのかはHTML上出てくる項目の
後者につけなければいけません。今のところですけど。
S2JSFのときにはGreaterEqualしかなかったので
Teedaではひととおり準備してみました。
では、今までに説明してきた機能を振り返ってみて、 簡単な足し算アプリを作成してみましょう。 仕様としては、足し算を行ってボタンを押下するとdoCalculateメソッドが 呼ばれます。未入力または桁数が3桁に満たない場合は、Validationエラーとします。
HTMLはこのようになります。 (まだ説明していない機能やディティールの部分も少し含まれますが、 本質は変わりません。後でまた説明します。)
<?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" xml:lang="ja" lang="ja"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title id="aaa">Add</title> </head> <body> <form id="addForm"> <div> <span id="messages"></span> </div> <table> <tr> <td></td><td><input type="text" id="arg1" title="INPUT1"/></td> <td><span id="arg1Message"></span></td> </tr> <tr> <td> + </td> <td><input type="text" id="arg2" title="INPUT2"/></td> <td><span id="arg2Message"></span></td> </tr> <tr> <td> = </td> <td><span id="result"></span></td> </tr> </table> <input type="submit" value="calculate" id="doCalculate"/> <input type="submit" value="back to start" id="jumpStart_index"/> </form> </body> </html>
これに対応するページクラスを作りましょう。 idに対応づく変数とアクセサはDoltengが作成してくれるので、あとは 必要なValidatorを記述するだけです。
package examples.teeda.web.add; public class AddPage { public static final String arg1_TRequiredValidator = null; public static final String arg1_lengthValidator = "minimum=3"; public static final String arg2_TRequiredValidator = null; public static final String arg2_lengthValidator = "minimum=3"; private int arg1; private int arg2; private int result; public int getArg1() { return arg1; } public void setArg1(int arg1) { this.arg1 = arg1; } public int getArg2() { return arg2; } public void setArg2(int arg2) { this.arg2 = arg2; } public int getResult() { return result; } public void setResult(int result) { this.result = result; } public String doCalculate() { result = arg1 + arg2; return null; } }
実際にTomcatを立ち上げて、確認してみましょう。 3桁入力しないとエラーでValidationが効いているはずです。
今回はエラーメッセージについてです。 Teedaでは、エラーメッセージを出すときに大きく2つの選択肢があります。
まず1の場合はどうするかというと、対象の入力項目のid+Messageと つけます。 (すでに前回の例で実は出てきていますが。)
<tr> <td></td><td><input type="text" id="arg1" title="INPUT1"/></td> <td><span id="arg1Message"></span></td> </tr>
入力項目arg1に対して、そのエラーメッセージを表示させたいときは arg1Messageとしてください。
これでバリデーションエラー時にarg1に エラーがあった場合はその内容を表示することが出来ます。
次に2の場合です。
画面全体でエラーメッセージの出す場所を統一して出す場合なども よくあると思います。
そのような場合はAllMessagesまたはMessagesを使います。
<span id="allMessages"></span> <span id="messages"></span>
AllMessagesは、Validationに引っかかった全てのエラーメッセージを 表示します。「全ての」の意味をちょっと説明する必要があります。 Teedaでは、エラーメッセージを大きく2つに分類しています。 ・idがついたコンポーネントに紐づくエラー ・idがついたコンポーネントに紐づかないエラー
前者は、arg1Messageなどで定義しているものたちだと思ってください。 後者はそれ以外で発生したエラー全てをさします。 AllMessagesはこれら全ての例外を一括で表示します。
一方Messagesは、どういったものなのでしょうか。 Messagesは上記の後者だけを表示します。 つまりidがついたコンポーネントのエラーを表示したいときは 適切な場所にmessageを埋め込まないといけません。 このあたりは画面表示上の都合で使い分けることを想定しています。
Converterは、値の変換をする機能です。
Converterは入力されてきた値をValidationする直前に適切なObjectに変換し、 描画する直前にStringに変換する機能を持っています。
Teedaでは、Validatorの同じく定数アノテーションまたはTigerのアノテーションで 指定が可能です。
下記にサンプルを示します。
public static final String now_dateTimeConverter = "pattern='yyyy/MM'";
Validatorと同様、Converterもさまざまな種類のものを用意しています。
通常、Converterに関しては、一般的なprimitiveな値のものは自動的に
Converterによる型変換が入るため、意識する必要はありません。
型変換が明示的に必要な場合は下記のConverterをアノテーションで指定してください。
デフォルトのConverter(:の後ろは登録されている名前)
拡張のConverter
(TODO Converterの指定の仕方、各Converterの説明)
SelectOneMenuとはいわゆるプルダウンの項目のことです。
SelectOneMenuでは下記のようにHTMLテンプレートで記述します。
selectタグで始まり、idにItemsをつけたList/配列のプロパティが
Pageクラスにある場合にSelectOneMenuコンポーネントで
あるとTeedaは認識します。
または、aaaItemsというList/配列のidでも認識します。
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>Selectone</title> </head> <body> <form id="form"> <select id="aaa"> <option value="1"></option> </select> <span id="aaa-out"/> <input type="submit" value="calculate" id="doAction"/> <span id="aaaItemsMessage"/> </form> </body> </html>
Page側は下記のように記述します。
aaaItemsはListまたは配列で記述します。
List/配列の中身はDtoまたはMapで記述することができます。
SelectOneMenuで選択された要素として、プロパティaaaを準備しておきます。
public class SelectOnePage { private List aaaItems; private int aaa; public String prerender() { aaaItems = new ArrayList(); AaaDto dto1 = new AaaDto(); dto1.setValue(0); dto1.setLabel("AAAA"); aaaItems.add(dto1); AaaDto dto2 = new AaaDto(); dto2.setValue(1); dto2.setLabel("BBBB"); aaaItems.add(dto2); AaaDto dto3 = new AaaDto(); dto3.setValue(2); dto3.setLabel("CCCC"); aaaItems.add(dto3); return null; } public List getAaaItems() { return aaaItems; } public void setAaaItems(List aaaItems) { this.aaaItems = aaaItems; } public int getAaa() { return aaa; } public void setAaa(int aaa) { this.aaa = aaa; } public String doAction() { return null; } }
Itemsの中身をDTOで記述する場合、Dtoはvalueとlabelという二つの要素は
最低限持たなくてはいけません。
下記のようになります。
public class AaaDto { private String label; private int value; public String getLabel() { return label; } public int getValue() { return value; } public void setLabel(String label) { this.label = label; } public void setValue(int value) { this.value = value; } }
ではMapの場合はどうなるでしょうか?
Mapの場合はもっと簡単です。
下記のように、label、valueという名前で各Mapに登録しておいてください。
ちなみに下記例では、初期表示時の選択項目を値2を持つBBBBという項目にしています。
public class SelectOne2Page { private List aaaItems; private Integer aaa = new Integer(2); public String prerender() { aaaItems = new ArrayList(); Map map1 = new HashMap(); map1.put("label", "AAAA"); map1.put("value", new Integer(1)); aaaItems.add(map1); Map map2 = new HashMap(); map2.put("label", "BBBB"); map2.put("value", new Integer(2)); aaaItems.add(map2); Map map3 = new HashMap(); map3.put("label", "CCCC"); map3.put("value", new Integer(3)); aaaItems.add(map3); return null; } public List getAaaItems() { return aaaItems; } public void setAaaItems(List aaaItems) { this.aaaItems = aaaItems; } public Integer getAaa() { return aaa; } public void setAaa(Integer aaa) { this.aaa = aaa; } public String doAction() { return null; } }
ではNullLabel(選択してください などのプルダウンに一番最初にでる文字列)を含めたい場合はどうするのでしょうか。
TeedaではPageでSelectOneMenuに必須チェックがかかっているかどうかでNullLabelが 必要かどうかを見ています。
つまり、必須あり→NullLabelあり、 必須チェックなし→NullLabelなしを基本的な挙動としています。
デフォルトのメッセージは、
org.seasar.teeda.extension.component.TSelect.NULL_LABEL = 選択してください
となっていますが、これはMessages.propertiesに定義してあるので容易にJSFの仕組みで入れ替えることが可能です。
では、挙動として全てのアプリケーションでNullLabelを出したいなどの要望もあるでしょう。
その場合はteedaCustomize.diconでカスタマイズすることが可能です。
下記のように記述します。
<component class="org.seasar.teeda.extension.util.NullLabelStrategyImpl"> <property name="forceNullLabel">@org.seasar.teeda.extension.util.NullLabelStrategy@FORCE_NULL_LABEL</property> </component>
次はRadioボタンを選択する、SelectOneRadioを使ってみましょう。
SelectOneRadioは、htmlには下記のように記述します。
spanタグで始まり、idにItemsをつけたList/配列のプロパティが
Pageクラスにある場合にSelectOneRadioコンポーネントで
あるとTeedaは認識します。
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>SelectOneRadio</title> </head> <body> <form id="selectOneRadioForm"> <span id="aaa"> <input type="radio" name="aaa" value="0" checked="checked"/>aaa <input type="radio" name="aaa" value="1"/>bbb <input type="radio" name="aaa" value="2"/>ccc </span> <span id="aaa-display"/> <input type="submit" value="calculate" id="doAction"/> <span id="aaaMessage"/> </form> </body> </html>
SelectOneMenuとほとんど変わりません。
Pageクラスは以下のようになります。
public class SelectOneRadioPage { private int aaa; private List aaaItems; public String prerender() { aaaItems = new ArrayList(); AaaDto dto1 = new AaaDto(); dto1.setValue(0); dto1.setLabel("AAAA"); aaaItems.add(dto1); AaaDto dto2 = new AaaDto(); dto2.setValue(1); dto2.setLabel("BBBB"); aaaItems.add(dto2); AaaDto dto3 = new AaaDto(); dto3.setValue(2); dto3.setLabel("CCCC"); aaaItems.add(dto3); return null; } public int getAaa() { return aaa; } public void setAaa(int aaa) { this.aaa = aaa; } public List getAaaItems() { return aaaItems; } public void setAaaItems(List aaaItems) { this.aaaItems = aaaItems; } public String doAction() { return null; } }
SelectBooleanCheckboxはYES/NO形式の簡単なチェックボックスのコンポーネントです。
(TODO つづき)
SelectManyCheckboxは複数選択可能なチェックボックスのコンポーネントです。
(TODO つづき)
Conditionは、表示/非表示をPage側の条件によって切り替えるためのコンポーネントです。
(TODO つづき)
ForEachはPage側のList/配列を繰り返すためのコンポーネントです。
(TODO つづき)
Teedaの状態維持のポリシー、それは状態をなるべくTeedaで持たないということです。
状態はHTML側に埋め込むまたは、サーバサイドのpersistence層で保管するようにしてください。
HttpSessionというライフサイクルがよくわからないものに頼らない方法を Teedaでは推奨しています。
ItemsSaveは、Base64でPage側で持つList/配列をそのまま書き出して
次画面に受け継ぐための機能です。
(TODO つづき)
Teedaのライフサイクルは主にJSFのライフサイクルのとおりになっています。
(TODO 詳細記述)
これをPOST-REDIRECT-GETのPRGパターンでまわします。 PRGパターンは下記参照。
PRGパターンとは、POST-REDIRECT-GETの組み合わせでHTTPでsubmitされたデータを処理する技法のことです。
従来のforwardベースのsubmitでは以下のような問題がありました。
このような問題への対処のための技法がPRGパターンです。
PRGでは、POSTとGETで役割を分担しています。
POSTでサーバ側のModelの状態を更新、REDIRECTでGETへ受け渡し、
GETでは画面の描画というように分かれています。
forwardベースでRELOADされたときにPOSTされてしまうのは POSTで使ったresponseをそのまま使用しているためです.
PRGではPOSTで使ったresponseはそのまま使わず、GETで常に responseを描画時に返すようになります.
このようにしておくと、RELOADボタンが押下されても、GETメソッドで
かつ空のパラメータが飛ぶため、Modelの状態を意図しない形で更新することが
なくなります.
Pageクラスにある特定のメソッドを記述しておくと、Teedaのライフサイクルの特定のタイミングで呼ぶことが出来ます.
それがinitialize、prerenderです.
initializeはPageクラスが初期化されるときに呼ばれます。 呼ばれるのは、Pageが初期化されたときの一回だけです。
一方prerenderは、画面が描画されるタイミングで毎回呼ばれます。
この2つをうまく使い分けることで、特定のタイミングで 動作するロジックを記述するタイミングを得ることができます。
TeedaはあるPageがどこの画面から来たかをわかるようにpreviousViewIdという プロパティを持っていれば、前画面名を渡すようになっています。
以下のようにPageにpreviousViewIdというプロパティを持っていると、
Teedaが自動的にインジェクトしてくれます。
package examples.teeda.web.move.aaa; public class Aaa2Page { private String previousViewId; public String prerender() { System.out.println(previousViewId); return null; } public String getPreviousViewId() { return previousViewId; } public void setPreviousViewId(String previousViewId) { this.previousViewId = previousViewId; } }
ある画面からデータがポストされてきたときに、その送り先が 同一のページであることをpostbackといいます。
TeedaではpostbackかどうかをPageクラスで認識することができます。 どのようにするかというと、postbackというbooleanのプロパティと setter/getterを準備しておくだけで、そのPageへのアクセスが初回起動時なのか postbackされてきたものかを判定することが出来ます。
下記にpostbackかどうかをprerenderの中で判定するサンプルコードを 示します。
public class HogePage { private boolean postback; public boolean isPostback() { return postback; } public void setPostback(boolean postback) { this.postback = postback; } public String prerender() { if(isPostback()) { //postback時のロジックを記述 } else { //初回起動時のロジックを記述 } } }
金額入力コンポーネント。 カンマ区切りになります。
inputTextでclass属性にT_currency
日付入力コンポーネント。 YY/MM/DD形式をある閾値を認識して、YYYY/MM/DDに。
inputTextでclass属性にT_date
HTMLに各タグごとにエラーのメッセージを出力する場合は、
input type="text" id="name" .../><span id="nameMessage"/> のようにspanタグを使って、id属性を対象となるタグのid + Messageにします。 個別のタグとは無関係なエラー、例えば、業務ロジックで発生した例外や複数の項目チェックをしてバリデーションのエラーメッセージを出力したい場合は、出力したい場所に
span id="messages"/>を記述します。そのようなメッセージは 一般的に画面の上のほうに出すことが多いようです。 画面の上に全部(個別のタグも個別のタグとは無関係なエラーも)のメッセージを出力したい場合は、 <span id="allMessages"/>をHTMLに記述します。
ある例外のときにエラーページへ遷移させる機能。
ForEach/Grid内でConverter/Validatorでエラーになったとき 自動で行数がエラーメッセージにつく。
メッセージを集約したい場合は、アノテーションで記述する必要がある。
public static final String fooItems_MESSAGE_AGGREGATION = "id=examples.teeda.web.foreach.ForeachHoge.invaid";
Page側で動的にHTMLの属性の値を変更するやり方について。
idについての細かいルールを下記に記述します。
そのため、ハイフンから後ろは無視される。
idには、Camel式での記述を推奨します。
label.propertiesから動的に値を取り込める。
lookupのロジックは、
サブアプリケーション内label.propertiesから、HTML名.idで探索
サブアプリケーション内label.propertiesから、idで探索
web直下のlabel.propertiesから。
idをふっておくと、label.propertiesから動的に変更することができる。
Teedaでは、遷移元と遷移先に同じプロパティがあれば自動的に引き継ぎます。場合によっては、引き継ぎたくないこともあるでしょう。このときに使うのがTakeOverアノテーションです。TakeOverアノテーションは、メソッドに指定します。
@TakeOver(type=TakeOverType.NEVER)の場合、次のページに何も引き継ぎません。
@TakeOver(type=TakeOverType.INCLUDE, properties="aaa, bbb")の場合、次のページにaaa, bbbプロパティのみを引き継ぎます。INCLUDEはデフォルトなので@TakeOver(properties="aaa, bbb")のように書くこともできます。
@TakeOver(type=TakeOverType.EXCLUDE, properties="aaa, bbb")の場合、次のページにaaa, bbb以外のプロパティを引き継ぎます。
input type="submit" id="doOnceHoge" value="commit"/>
のようにdoOnceではじめると、TransactionTokenが発行される。
Tokenはサーバ側で管理され、クライアント側でもDoubleSubmit防止の
JavaScriptがrenderされる。
doFinishでもTransactionTokenが発行され、DoubleSubmit防止の
JavaScriptがrenderされる。
それに加え、doFinishでは、サーバ側でviewIdごとに一定数管理している Pageの状態をすべて破棄する。
(TODO 各規約および規約のチェック順について記述する。)
TeedaAjaxについて、書いていきます。
Teeda は、サーブレット以外にもポートレットして動作することをサポートしています。 これは、JSF がポートレットをサポートしているためです(しかし、現状、ポートレットをきちんとサポートしている JSF 実装は数少ないです。Teeda はきちんとサポートする実装の一つといえます!)。 ですので、Teeda Core がポートレットをサポートしているのはもちろんですが、さらに、Teeda の拡張である Teeda Extension についてもポートレットをサポートしているので、サーブレット環境での開発と変わることがなく、ポートレットアプリケーションの開発が行えます。
まず、ポートレットについて、簡単に説明します。 Java ベースのポートレットは、JSR 168 Portlet Specification にて、ポートレット API が定義されています。 そこでは、ポートレットとは、リクエストを処理して、動的にコンテンツを生成するポートレットコンテナによって管理される、Java ベースのウェブコンポーネントで、プレゼンテーション層を情報システムに提供する交換可能なユーザーインターフェースコンポーネントとして、ポータルによって使用される(PLT.2.2)と定義されています。 また、ポータルとはウェブベースアプリケーションで、表示、シングルサインオン、異なる発信元からのコンテンツ集約を提供して、情報システムのプレゼンテーション層を提供する(PLT.2.1)と定義されています。 仕様的な定義は以上のような感じですが、簡単にまとめると、ポートレットはポータル(サーバー)上の小窓(フラグメント)で、リクエストを処理して、コンテンツをその小窓の中に表示するものです。 JSR 168 では、そのための仕様を定義しています。
ポートレットで JSF を使うメリットは何でしょうか? JSF をプレゼンテーション層のフレームワークとして利用しない場合、ポートレット API を習得する必要があります。 しかし、JSF をポートレットで利用した場合、JSF にラップされるので、ポートレット API を意識する必要はありません(PortletPreferencesなどポートレット固有の API を使う場合は別ですが)。 ですので、JSF を利用することでウェブアプリケーションを作成する感覚で、ポートレットを作成できてしまうわけです。
前置きが長くなりましたが、今まではポートレットの開発するために、学習などのコストがあったかもしれませんが、Teeda での開発を知っていれば、その知識をそのまま利用して、ポートレットの開発も行えます(ポータル固有の機能を利用する場合以外は、別途、ポートレットの開発方法を学習する必要はありません!)。Teeda で作成されたウェブアプリケーションをポートレットにするためには、以下のことを行うだけでポートレットになります。
まず、web.xml については、Teeda をサーブレットで初期化しているのであれば TeedaServlet、リスナーで初期化しているのであればTeedaConfigureListener を利用していると思います。 ポートレット環境で利用するためには、TeedaServlet であれば TeedaPortletExtendedServlet に、TeedaConfigureListener であれば TeedaPortletExtendedConfigureListener に書き換えてください。 それらに置き換えることで、S2 および Teeda でポートレットがサポートされます。 web.xml の変更はこれだけで OK です。
次に、portlet.xml ですが、これは JSR 168 で定義されているポートレット配備子です。 ポータルサーバー上に表示するポートレットの情報を記述します。 たとえば、以下のような内容をこのファイルに記述します。
<?xml version="1.0" encoding="UTF-8"?> <portlet-app version="1.0"> <portlet> <portlet-name>TeedaHtmlExample</portlet-name> <display-name>Teeda HTML Example</display-name> <description>This is an example portlet for Teeda.</description> <portlet-class>org.apache.portals.bridges.portletfilter.FilterPortlet</portlet-class> <init-param> <name>portlet-class</name> <value>org.seasar.teeda.core.portlet.FacesPortlet</value> </init-param> <init-param> <name>portlet-filters</name> <value>org.seasar.portlet.filter.S2PortletFilter,org.seasar.portlet.filter.HotdeployPortletFilter</value> </init-param> <init-param> <name>view-page</name> <value>/view/start/index.html</value> </init-param> <!-- <init-param> <name>edit-page</name> <value></value> </init-param> --> <supports> <mime-type>text/html</mime-type> <portlet-mode>VIEW</portlet-mode> <!-- <portlet-mode>EDIT</portlet-mode> --> </supports> <supported-locale>en</supported-locale> <supported-locale>ja</supported-locale> <!-- resource-bundle></resource-bundle --> <portlet-info> <title>Teeda HTML Example</title> <short-title>Example</short-title> <keywords>Teeda HTML Example</keywords> </portlet-info> </portlet> </portlet-app>
まず、portlet-name 要素の値には、ポータルにより利用される一意なポートレット名が入ります。 display-name 要素の値はポータルにより表示される(たとえば、ユーザーがポートレット一覧からポートレットを選択するときなど)ポートレットの名前で、次の description 要素の値がその説明になります。 portlet-class 要素と init-param 要素にある portlet-class および portlet-filters は変更する必要はありません。 init-param 要素の view-page の値は、表示モードでアクセスされたときにデフォルトで表示されるページのパスを記述します(編集モードで表示するページは、edit-page で指定します)。 ポートレットで表示されるポートレットには、表示モードや編集モードなどいくつかのモードがあり、たとえば、ポートレットの小窓の右上にある編集ボタンを押すと、編集モードが呼ばれます。 サポートされるポートレットモードは、supports 要素の portlet-mode 要素としてサポートするモードを指定できます。 あとは、potlet-info 要素の title、short-title、keywords 要素を変更してください。 title 要素はポートレットの小窓のバーなどに表示されるタイトルで、short-title はその短いタイトルで、ポータルがモバイル環境で表示されるときに短いタイトルが必要なときに表示されます。 keywords 要素は、そのポートレットのキーワードで , 区切りで複数指定できます。 ポータルのポートレット一覧で検索するときなどに利用されます。 必要な場所を変更して、portlet.xml として、WEB-INF の下に保存してください(他にもいろいろな要素がありますが、必要に応じて、追加してください)。
web.xml と portlet.xml を編集したら、必要な jar ファイルを追加します。 追加するものは、Apache Portals Bridges より提供されるポートレットフィルタと、Seasar Project より提供される S2Portlet です。 それぞれ以下のところから入手できます。
最後に、サーブレット環境用に作ると出力される HTML ページは、html、head、body タグなどが含まれます。 しかし、ポートレット環境においては、それらのタグはポータルが出力するため、ポートレットは、body タグより下の部分しか表示する必要はありません。 それらのタグがあったも、無視してくれるブラウザもあるかと思いますが、きちんとしたポートレットを作成したい場合には、削除してください(あとで、不要なタグを削除するフィルタを Portal Application Laboratory からリリース予定)。
以上の編集をしたら、ウェブアプリケーションを作成するのと同様に、war ファイルを作成してください。 そして、その作成された war ファイルは、ポートレットアプリケーションです。 以上で、ポートレットアプリケーションが完成しましたので、その war ファイルを各ポータルの配備手順に従って、配備してください。
Tipsについて。
JSFについて記述。 HTMLとどうやってマッピングしているかを記述。
テストの仕方について。
Teedaで薦める開発スタイル、それは段階的な開発です。
従来のJavaの開発では、まとまった機能を作りこんでからサーバを起動して確認するといった
非常にわずらわしい手段を取っていました。
一方で、LLな言語(RubyやPerl)では書いたものをその場で動かし確認するという
スタイルで開発されています。自分が書いたコードとそれがどう動くかというフィードバックのサイクルが非常に短いのです。
通常のJava開発ではこのような開発スタイルをとることはできません。
しかし、Seasar2.4のHotDeploy機能を使うと、これと同じことができるようになります。
イメージしてみましょう。
Tomcatをあげっぱなしで、HTMLを書きます。まずはMockとして簡単に動作するところまで作ってみてください。
次にDoltengでPageクラスを作ります。Ctrl+5を押すだけで後はちょっとした設定を入れるだけでPageクラスは作られます。
最初には何かエラーがでるかもしれません。これを修正して、ブラウザから再度確認します。
このちょっと修正、すぐ確認を繰り返すのです。自分の書いたコードとその結果の フィードバックループをたくさんまわしてください。
非常に効率的にさくさく作業が出来て、変に一気に作らなくちゃいけないという
プレッシャーも無いはずです。効率が良いだけじゃなく、気持ちよく開発ができるのではないでしょうか?
さくさく開発、すぐ確認。これがTeedaが進める開発スタイルです。
Seasar2.4の新機能の一つにSmartDeployというものがあります。
これによって、開発者がdiconを書いてコンポーネントを登録する必要はほとんどなくなり、
ほんとうにしなければならない開発に専念することができます。
SmartDeployは大きく3種類に分かれます。
SmartDeployのどのmodeで動いているかは、
http://localhost:8080/your_war/teedaServlet?command=list
としてもらえればわかります。
ご要望、フィードバック等は下記日記のコメントに残してもらえれば 極力取り入れます。
Seasar-userのMLでも受け付けています。
Teedaは機能が出来次第、順次リリースしていきます.
ひとまず、現在のTODOは、
Teedaは沖縄の言葉で太陽という意味です。