Tapestry
Tapestryとは
Tapestryとは,Apache Jakarta Project (http://jakarta.apache.org/tapestry/) で開発されている Webアプリケーションのプレゼンテーション層のフレームワークです. プレゼンテーション層のフレームワ−クにはStrutsやJSFなどの有名 なフレームワークが数多く存在します,Tapestryはその中でもWebア プリケーション開発におけるデザイナとプログラマの作業をシーム レスに結合することを目標に置いたフレームワークです.
Webアプリケーション開発の問題点
Webアプリケーションの開発現場には,多くの場合2種類の開発者が 存在します.1つはWebページの画面デザインを担当するデザイナと, Webアプリケーションの(プレゼンテーション/アプリケーション)ロ ジックを実装するプログラマです.Webアプリケーションの開発にお ける問題点の一つにデザイナとプログラマの分業の難しさがあげら れます.Webアプリケーションの画面は一般的にHTMLで記述されるが 動的に決まる画面にはデザイナが理解できないカスタムタグやスク リプットが多く含まれてしまいます.画面をWYSIWYGで開発できるツー ルも登場してきたが,デザイナにとって扱いやすいものではなく, 現状で受け入れられているとは言いがたいです.このような問題の ために現在のWebアプリケーションの画面デザインをデザイナだけで 完結することができず,デザイナが作ったモックアップをプログラ マが変更しなくてはなりません.
Tapestryの特徴
Tapestryは上記の問題点を解決するためのプレゼンテーション層の フレームワークで「デザイナ」と「プログラマ」の分業を可能にし ます.Tapestryの最大の特徴はデザイナが作成した画面をほとんど そのままプログラマが利用できる点です.またスクリプトレットや カスタムタグが混入するJSPやASPとは対照的に,Tapestry用の画面は トラディショナルなHTML作成ツールで開発でき,プログラマが変更 した後のページでもデザイナの編集を可能にします.

Tapestryアプリケーションの構造
Tapestryアプリケーションは以下の要素で構成されます.
- アプリケーション仕様
- ページ仕様
- ページテンプレート
- ページコンポーネント
- Java Web Component
アプリケーションを構成するページの記述やアプリケーション全 体のコンフィグレーションは「アプリケーション仕様」に記述さ れます.一方で,各ページの情報はページ仕様,ページテンプ レート,ページコンポーネントに記述されます.ページ固有のデー タや処理を「ページコンポーネント」に記述しページのデザイン を「ページテンプレート」で作成します.ページコンポーネント とページテンプレートの各要素を「ページ仕様」でまとめあげます.
Java Web Component(JWC)はTapestryで利用するプレゼンテーショ ンのコンポーネントです.JWCをページテンプレートに配置するこ とで動的なHTMLのレンダリングやイベントハンドリングなどを実 現します.JWCはユーザが簡単に作成することもできます.
以下は現在時刻を表示するアプリケーションのアプリケーション 仕様,ページ仕様,ページテンプレ−ト,ページコンポーネント です.この中でWebアプリケーションのページデザインに関するコー ドがページテンプレートです.特殊な属性「jwcid」を除くと HTMLと同じになります.現在時刻は動的に決定される内容なので ページ仕様でjwcidとJWC,ページコンポーネントをバインドしま す.このようにページテンプレートをできるだけHTMLの形に残し おくことでデザイナが作業をしやすくしています.

Tapestryサンプルアプリケーション
以下にTapestryを使ったサンプルアプリケーションについて説明し ます.サンプルアプリケーションは「ブラックジャックゲーム」です. アプリケーションの大まかな流れを以下に示します.
- ユーザ名と所持金を指定して入場する
- 賭け金を指定してゲームを開始する
- トランプを引いていき数字が21を超えない範囲でディーラより 合計が大きかったら勝ち
- 勝った場合は賭け金の2倍のお金がもらえる
- ゲームは連続して行える
- ゲームを終了したら現在の所持金を表示する

ページテンプレートの作成
今回のアプリケーションには,「Home」「Entry」「Blackjack」 「Result」のページがあります.はじめにWebアプリケーションのペー ジをデザインしてページテンプレートを作成します.ページテンプ レートはHTMLを作成する方法と同様にして作成できます.今回のア プリケーションの中心となる「Blackjack」ページを以下に示します.

動的に変化する部分がすべて表示されているのでレイアウトが少々崩れますが ページテンプレートがブラウザで表示できる程にHTMLに近いことがわかります.
ページ定義の作成
「Blackjack」ページには,
- ユーザ名の表示(テキスト)
- 所持金の表示(テキスト,イメージ)
- トランプの表示(イメージとテキストの混合)
- トランプの数字の合計の表示(テキスト)
- 賭け金の入力(テキストボックス)
- ゲームの開始(ボタン)
- カードの配り直し(ボタン)
- カードの追加(ボタン)
- 勝負する(ボタン)
- ゲームを終了する(リンク)
- エントリページに戻る(リンク)
という,動的に決まる要素やコントロールが存在します.これらの 要素とページコンポーネント,JWCをバインドするためにページ定義 を記述します.「Blackjack」ページのページ定義の一部を以下に示 します.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE page-specification PUBLIC
"-//Apache Software Foundation//Tapestry Specification 3.0//EN"
"http://jakarta.apache.org/tapestry/dtd/Tapestry_3_0.dtd">
<page-specification class="org.sapid.grape.sample.tapestry.blackjack.page.Blackjack">
<description>Tapestry Blackjack Application Main Page</description>
<property-specification name="yourCard" type="org.sapid.grape.sample.tapestry.blackjack.session.Card"/>
<property-specification name="myCard" type="org.sapid.grape.sample.tapestry.blackjack.session.Card"/>
<component id="You" type="Insert">
<binding name="value" expression="visit.name"/>
</component>
<component id="YourPoint" type="Insert">
<binding name="value" expression="visit.yourPoint"/>
</component>
<component id="YourCards" type="Foreach">
<binding name="source" expression="visit.yourCards"/>
<binding name="value" expression="yourCard"/>
<static-binding name="element">td</static-binding>
</component>
<component id="YourCard" type="Card">
<binding name="mark" expression="yourCard.mark"/>
<binding name="number" expression="yourCard.number"/>
</component>
<component id="MoneyCoins" type="Coins">
<binding name="money" expression="visit.money"/>
</component>
<component id="AddYourCard" type="Submit">
<binding name="listener" expression="listeners.addYourCard"/>
</component>
<component id="Duel" type="Submit">
<binding name="listener" expression="listeners.duel"/>
</component>
<component id="BetField" type="ValidField">
<binding name="value" expression="visit.bet"/>
<binding name="validator" expression="beans.betValidator"/>
<static-binding name="displayName">賭け金入力ボックス</static-binding>
</component>
<component id="Start" type="Form">
<binding name="listener" expression="listeners.start"/>
<binding name="delegate" expression="beans.delegate"/>
</component>
<bean name="delegate" class="org.apache.tapestry.valid.ValidationDelegate"/>
<bean name="betValidator" class="org.apache.tapestry.valid.NumberValidator" lifecycle="request">
<set-property name="required" expression="true"/>
<set-property name="clientScriptingEnabled" expression="true"/>
<set-property name="minimum" expression="0"/>
<set-property name="maximum" expression="visit.money"/>
<set-property name="requiredMessage" expression=""{0}へ賭け金を入力してください""/>
<set-property name="invalidNumericFormatMessage" expression=""{0}へは数字を入力してください""/>
<set-property name="invalidIntegerFormatMessage" expression=""{0}へは整数値を入力してください""/>
<set-property name="numberRangeMessage" expression=""{0}へは0から所持金までの数字を入力してください""/>
<set-property name="numberTooSmallMessage" expression=""{0}へは0以上の数字を入力してください""/>
<set-property name="numberTooLargeMessage" expression=""{0}へは所持金以下の数字を入力してください""/>
</bean>
</page-specification>
ページ仕様の「component」でページテンプレートに出現するjwcid に対応するコンポーネントを記述します.例えばBlackjackページテ ンプレートにあるトランプの左側にはユーザの名前するために 「jwcid="You"」と記述されたspanタグがあります.最初 のcomponentは,その部分をJWCの「Insert」コンポーネントを用い てセッションのname属性の内容で置き換える記述です(visitはセッ ションの内容を保持するオブジェクトです).ほかのコンポーネント としては繰り返しを表現する「Foreach」やボタンアクションでサー バサイドに処理をとばす「Submit」,入力値を検証する 「ValidField」などが使われています.
ページコンポーネントの作成
ページに固有のロジック(入出力処理やイベント処理)をページコン ポーネントに記述します.Blackjackページではボタンによるカード の追加や勝負の実施などのイベント処理があり,それらを Blackjackページコンポーネントに記述します.
package org.sapid.grape.sample.tapestry.blackjack.page;
import org.apache.tapestry.IRequestCycle;
import org.apache.tapestry.html.BasePage;
import org.sapid.grape.sample.tapestry.blackjack.session.Visit;
public class Blackjack extends BasePage{
public void addYourCard(IRequestCycle cycle){
Visit visit = (Visit)getVisit();
visit.addYourCard();
}
public void duel(IRequestCycle cycle){
Visit visit = (Visit)getVisit();
int yourPoint = visit.getYourPoint();
while(yourPoint <= 21 && yourPoint > visit.getMyPoint()){
visit.addMyCard();
}
int myPoint = visit.getMyPoint();
if(yourPoint >= 22 && myPoint >= 22){
visit.setFlag(Visit.DRAW);
visit.setMoney(visit.getMoney()+visit.getBet());
}else if(yourPoint >= 22 && myPoint <= 21){
visit.setFlag(Visit.LOSE);
}else if(yourPoint <= 21 && myPoint >= 22){
visit.setFlag(Visit.WIN);
visit.setMoney(visit.getMoney()+visit.getBet()*2);
}else{
if(yourPoint > myPoint){
visit.setFlag(Visit.WIN);
visit.setMoney(visit.getMoney()+visit.getBet()*2);
}else if(myPoint > yourPoint){
visit.setFlag(Visit.LOSE);
}else{
visit.setFlag(Visit.DRAW);
visit.setMoney(visit.getMoney()+visit.getBet());
}
}
}
}
上記はページコンポーネントの一部抜粋でカードの追加ボタンと勝 負ボタンのイベント処理が記述されています.TapestryのSubmitに 関するコーディングはページ定義でバインドされたメソッドを記述 するのみでStrutsに比べてシンプルに記述できます.
JWCコンポーネントの自作
Blackjackページに出現するトランプは表示される絵柄が動的に変化 します.そこで今回は数字とマークを指定するとそのトランプを出 力するJWC「Card」を自作します.Cardはページ定義で「YourCard」 というjwcidと対応しています.JWCを自作する場合に必要となるファ イルは以下の3つになります.
- コンポーネント定義
- HTMLテンプレート : コンポーネントがHTMLや既存のJWCを出力する場合に利用できる
- コンポーネントクラス : コンポーネントにロジックが含まれる場合に作成する
CardはBlackjackページテンプレートの以下の部分に対応するJWCです.
<div style="width:40px;height:60px;background-image:url(../images/paper.gif);text-align:center;"> <span style="position:relative;top:12px;"><img src="../images/clover.gif"/><br/><font color="black">1</font></span> </div>
はじめにCardコンポーネントのインタフェースをコンポーネント定義 に記述します.以下のコンポーネント定義ではCardはmark属性と number属性を持ち,レンダリング処理に画像ファイルを用いることを 定義しています.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE component-specification PUBLIC
"-//Apache Software Foundation//Tapestry Specification 3.0//EN"
"http://jakarta.apache.org/tapestry/dtd/Tapestry_3_0.dtd">
<component-specification class="org.sapid.grape.sample.tapestry.blackjack.component.Card"
allow-body="no" allow-informal-parameters="yes">
<description>トランプのカードを表示する</description>
<parameter name="mark" type="java.lang.String" direction="in" required="yes" />
<parameter name="number" type="int" direction="in" required="yes" />
<context-asset name="clover" path="/images/clover.gif"/>
<context-asset name="diamond" path="/images/diamond.gif"/>
<context-asset name="spade" path="/images/spade.gif"/>
<context-asset name="heart" path="/images/heart.gif"/>
<context-asset name="paper" path="/images/paper.gif"/>
</component-specification>
次にCardのレンダリングをコンポーネントクラスに実装します.レン ダリング内容はページテンプレートを元にロジックを追加していきます.
package org.sapid.grape.sample.tapestry.blackjack.component;
import org.apache.tapestry.AbstractComponent;
import org.apache.tapestry.IMarkupWriter;
import org.apache.tapestry.IRequestCycle;
public abstract class Card extends AbstractComponent {
public abstract String getMark();
public abstract void setMark(String mark);
public abstract int getNumber();
public abstract void setNumber(int number);
protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle) {
writer.begin("div");
writer.attribute("style",
"width:40px;height:60px;background-image:url("
+ cycle.getEngine().getContextPath()
+ getAsset("paper").getResourceLocation().getPath()
+ ");text-align:center;");
writer.begin("span");
writer.attribute("style","position:relative;top:10px;");
writer.beginEmpty("img");
writer.attribute("src", cycle.getEngine().getContextPath()
+ getAsset(getMark()).getResourceLocation().getPath());
writer.beginEmpty("br");
writer.begin("span");
if (getMark().equals("diamond") || getMark().equals("heart")) {
writer.attribute("style", "color:red;");
} else {
writer.attribute("style", "color:black");
}
writer.print(getNumber());
writer.end();
writer.end();
writer.end();
}
}
renderComponentメソッドにレンダリング内容を実装しています. mark属性とnumber属性によって出力する数字や数字の色,絵柄を変更 しています.
おわりに
Tapestryはデザイナとプログラマの分業を目標としたフレームワー クで,Strutsに比べてデザインとロジックを分離しやすくなります. Tapestryは開発が非常に活発で現在はversion4のβ晩がリリースさ れています.またeclipseでのTapestry開発環境としてspindleプラ グインが提供されています.興味があれば皆さんも勉強してみては いかがでしょうか?
by Yuuki MIZUNO


