[[Teeda]] Teedaの入門記を書きます。 blogで書いていたけど、皆で書けるように。 #contents * 環境作り [#ec8afccf] Teeda入門を試すのに必要な環境作りに付いては [[Teeda/gettingStartedSetup]] を参照して下さい。 * Teedaとは [#e88a6d02] 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クラスが対応します。 この辺はサンプルを見つつ、学習するのがよいと思うので、次節以降解説します。 * 最初の一歩 [#o65972e7] +まずはHello Worldで基本的な動きをつかんでみましょう。 <?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を使うと便利です。 (Doltengは下記サイトから取得できます。) http://eclipse.seasar.org/updates/3.1beta/ Doltengをインストールして、Ctrl+5を押してみましょう。 なにやらダイアログが表示されます。このダイアログでPageクラスを作成することができます。 DoltengではHTMLの内容を読み込んで、Pageクラスに必要そうなプロパティを 自動的に追加したPageクラスを作成してくれます。 また、QuickJUnitにおけるCtrl+9でテストクラスへの移動が出来るように Ctrl+5でDoltengではPageクラス←→HTML間を移動できます。 (Pageがない場合にダイアログが表示されPageクラスを作成するかを聞いてきます。) では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 [#pb211f76] 動的に出力する項目(outputText)はどのように実現するのでしょうか。 簡単です。 spanタグでidをふるだけ。 Hoge.html <span id="aaa">bbb</span> で、Hoge.htmlが読まれるとHogePageクラスのaaaプロパティ が上記で書かれている値(bbb)の代わりに出ます。 次回は入力(inputText)を見てみましょう。 *配置について [#d37b6e2a] おっと。忘れちゃいけない配置についてお話しておきます。 Teedaは基本的に、Seasar2.4の配置に基づいています。 2.4でのPageクラスなどの構成は以下で書いたので割愛。 余談ですが、このようにパッケージ構成を決めることで煩雑だった DIの設定部分を取り除いているのです。 だからS2.4は、diconをいじる必要がほとんどなく、 パッケージ構成を初期情報として与えるだけで終わりです。かんたん^^ - http://d.hatena.ne.jp/shot6/20060818#1155832996 これと同様に、HTMLの配置は以下のようにします。 ルートコンテキスト +view +[サブアプリケーション名] -各HTML サブアプリケーションとは1ユースケースと同等です。 これを例えば、サブアプリケーションhelloの中に、HelloPage、hello.thmlとすると ルートコンテキスト +view +hello -hello.html となります。 Teeda(というかS2.4の規約)ではデフォルトは/view以下を 対象のHTMLとみなして処理します。 *inputText [#yfdb00e1] 前回までに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と画面遷移(do) [#b30b3fe2] 出力、入力と来たので、あとはsubmitボタンを説明すれば 簡単なアプリを作ることが出来ます。 というわけでボタンです。 ボタンは今までと違い、ちょっとした規約があります。 といってもHTML上では相変わらずidをふるだけです。 ボタンには3種類あります。 -do -go -jump です。 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ではもっとも画面遷移に関係あるリソースにそのまま記述できるようにしています。 *submitと画面遷移(go) [#m5e82a6f] では、次は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> *submitと画面遷移(jump) [#s9fba02b] では、画面遷移最後のもうひとつは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 [#o30f5eb5] 次は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の種類について [#s1d0874f] 次は各Validatorについて説明します。 ValidatorとはJSF(JavaServer Faces)で決められている画面からの入力値を チェックする機能です。JSFとしても提供しているValidator、Teedaで提供しているValidatorが あり、Tで始まるやつはTeedaで用意したものになっています。 数値チェック系 -TRequiredValidator 必須チェック -LengthValidator 長さチェック -LongRangeValidator longでのレンジチェック -DoubleRangeValidator doubleでのレンジチェック -TByteLengthValidator byteでのレンジチェック -TDoubleRangeValidator 拡張したdoubleでのレンジチェック -TLongRangeValidator 拡張したlongでのレンジチェック -TNumberLengthValidator 整数部・小数部での桁数チェック -TRegularExpressionValidator 正規表現でのチェック 上記のように数値チェック系は多少かぶっているのもあります。 なぜか。それは以下のような機能を付け加えるためです。 -どのボタンを押したときにValidationをするかを指定できるようにするため -メッセージを出力するためのidを自由に変更できるようにするため です。 下記のように、target属性でどのボタンが押下されたときに Validationが発生するのかを制御することができます。 public static final String number1_TRequiredValidator = "target=doCalculate"; 本当は正規表現のValidatorもCommons-validatorみたいに ひととおり準備してあげればいいんですけど、まだそこまでいってないです。 相関チェック系 -TGreaterEqualValidator -TGreaterThanConstantValidator -TGreaterValidator -TLessEqualValidator -TLessValidator 相関チェック系では、主に2項目の関係でのチェックを行います。 JSF上、どいつとペアでValidationするのかはHTML上出てくる項目の 後者につけなければいけません。今のところですけど。 S2JSFのときにはGreaterEqualしかなかったので Teedaではひととおり準備してみました。 *簡単なアプリの作成 [#ge733f40] では、今までに説明してきた機能を振り返ってみて、 簡単な足し算アプリを作成してみましょう。 仕様としては、足し算を行ってボタンを押下すると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が効いているはずです。 * Message/AllMessages/Messages [#w5bf6dd4] 今回はエラーメッセージについてです。 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を使います。 AllMessagesは、Validationに引っかかった全てのエラーメッセージを 表示します。「全ての」の意味をちょっと説明する必要があります。 Teedaでは、エラーメッセージを大きく2つに分類しています。 ・idがついたコンポーネントに紐づくエラー ・idがついたコンポーネントに紐づかないエラー 前者は、arg1Messageなどで定義しているものたちだと思ってください。 後者はそれ以外で発生したエラー全てをさします。 AllMessagesはこれら全ての例外を一括で表示します。 一方Messagesは、どういったものなのでしょうか。 Messagesは上記の後者だけを表示します。 つまりidがついたコンポーネントのエラーを表示したいときは 適切な場所にmessageを埋め込まないといけません。 このあたりは画面表示上の都合で使い分けることを想定しています。 *Converter [#u6b92f5b] Converterは、値の変換をする機能です。 Converterは入力されてきた値をValidationする直前に適切なObjectに変換し、 描画する直前にStringに変換する機能を持っています。 Teedaでは、Validatorの同じく定数アノテーションまたはTigerのアノテーションで 指定が可能です。 下記にサンプルを示します。 public static final String now_dateTimeConverter = "pattern='yyyy/MM'"; (TODO)Converterの種類を書くこと。 *SelectOneMenu [#q14b61ec] *SelectOneRadio [#b0268a05] *SelectBooleanCheckbox [#qd10c7a7] *SelectManyCheckbox [#o5f0afd3] *Foreach [#v7828d50] *Grid [#hd3340c1] *Teedaの状態維持のポリシー [#i602f222] *ItemsSaveと状態維持 [#dec73883] *Teedaのライフサイクル [#a1af32d2] *PRGパターンについて [#z70755d2] PRGパターンとは、POST-REDIRECT-GETの組み合わせでHTTPでsubmitされたデータを処理する技法のことです。 従来のforwardベースのsubmitでは以下のような問題がありました。 -URLの表示がずれる -POSTした後に画面がRELOADされたときに再度POSTされてしまう -戻るボタン -RELOADしたときにブラウザの警告メッセージが出る このような問題への対処のための技法がPRGパターンです。 PRGでは、POSTとGETで役割を分担しています。 POSTでサーバ側のModelの状態を更新、REDIRECTでGETへ受け渡し、 GETでは画面の描画というように分かれています。 forwardベースでRELOADされたときにPOSTされてしまうのは POSTで使ったresponseをそのまま使用しているためです. PRGではPOSTで使ったresponseはそのまま使わず、GETで常に responseを描画時に返すようになります. このようにしておくと、RELOADボタンが押下されても、GETメソッドで かつ空のパラメータが飛ぶため、Modelの状態を意図しない形で更新することが なくなります. *initializeメソッド、prerenderメソッド [#h556283b] Pageクラスにある特定のメソッドを記述しておくと、Teedaのライフサイクルの特定のタイミングで呼ぶことが出来ます. それがinitialize、prerenderです. initializeはPageクラスが初期化されるときに呼ばれます。 呼ばれるのは、Pageが初期化されたときの一回だけです。 一方prerenderは、画面が描画されるタイミングで毎回呼ばれます。 この2つをうまく使い分けることで、特定のタイミングで 動作するロジックを記述するタイミングを得ることができます。 *previousViewId [#j2577e41] 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 [#r3c3a395] ある画面からデータがポストされてきたときに、その送り先が 同一のページであることをpostbackといいます。 *拡張input(金額入力) [#b6ea6230] *拡張input(日付入力) [#d3971f96] *エラーページ [#sae48f38] *エラーメッセージの表示と集約 [#ca5aa65f] *Tigerでのアノテーション [#c7f208f4] *DynamicProperty [#ef8ffbdf] *idの細かいルール [#x0af521d] *label [#ka556fd3] *title [#s271ec23] *TakeOverアノテーション [#dd31c1ab] *double submit防止(doOnce) [#j9af26e1] *doFinish [#p1b893e1] *Teeda Extensionの拡張ポイント [#i0adf303] *各規約詳細 [#v97223c4] *Ajaxについて [#u9a21853] TeedaAjaxについて、書いていきます。 *Portletについて [#b7f9246c] *Tips [#o9a6ffde] *内部構造 [#l7f64a3d] *テストについて [#mc8f08ad] *Teedaで薦める開発スタイル [#q34a7de7] Teedaで薦める開発スタイル、それは段階的な開発です。 従来のJavaの開発では、まとまった機能を作りこんでからサーバを起動して確認するといった 非常にわずらわしい手段を取っていました。 一方で、LLな言語(RubyやPerl)では書いたものをその場で動かし確認するという スタイルで開発されています。自分が書いたコードとそれがどう動くかというフィードバックのサイクルが非常に短いのです。 通常のJava開発ではこのような開発スタイルをとることはできません。 しかし、Seasar2.4のHotDeploy機能を使うと、これと同じことができるようになります。 イメージしてみましょう。 Tomcatをあげっぱなしで、HTMLを書きます。まずはMockとして簡単に動作するところまで作ってみてください。 次にDoltengでPageクラスを作ります。Ctrl+5を押すだけで後はちょっとした設定を入れるだけでPageクラスは作られます。 最初には何かエラーがでるかもしれません。これを修正して、ブラウザから再度確認します。 このちょっと修正、すぐ確認を繰り返すのです。自分の書いたコードとその結果の フィードバックループをたくさんまわしてください。 非常に効率的にさくさく作業が出来て、変に一気に作らなくちゃいけないという プレッシャーも無いはずです。効率が良いだけじゃなく、気持ちよく開発ができるのではないでしょうか? さくさく開発、すぐ確認。これがTeedaが進める開発スタイルです。 *SmartDeployの切り替え方 [#o600ef42] Seasar2.4の新機能の一つにSmartDeployというものがあります。 これによって、開発者がdiconを書いてコンポーネントを登録する必要はほとんどなくなり、 ほんとうにしなければならない開発に専念することができます。 SmartDeployは大きく3種類に分かれます。 +HotDeploy +CoolDeploy +WarmDeploy *TeedaでのDoltengの使い方 [#ndd9ceab] *ご要望 [#qcdeec77] ご要望、フィードバック等は下記日記のコメントに残してもらえれば 極力取り入れます。 http://d.hatena.ne.jp/shot6/ *その他 [#d024a5d9] *TeedaのUPDATEについて [#r2d7c3d2] Teedaは機能が出来次第、順次リリースしていきます.