
| Tapestryとは | ||||||||||||||||||||||||||||||
|
一言で言えば、Tapestryは、Webデザイナとプログラマの作業を完全に分離し、両者が平行して作業を進めることを可能にするプレゼンテーション用のフレームワークです。 JavaでWebアプリケーションを開発する上で、プレゼンテーション(デザイン)とロジック(プログラムコード)の分離は大きな課題の一つです。JSPやタグライブラリなど、この課題に取り組んだものが既にいくつかあります。 しかしその多くが、Webデザイナかプログラマのどちらかに、大きな負担を強いるものでした。 Tapestryは、両者がお互いの持分をまもりつつ、協力して作業することを可能にします。もっと具体的に言うと、Webデザイナが作業の対象とするファイルと、プログラマが作業の対象とするファイルは異なります。そして、Webデザイナが作業対象とするのは、ほぼ完全な(X)HTMLファイルです。タグライブラリのような見慣れないタグや、JSPのスクリプトのような難解な文字列は存在しません。JSPやタグライブラリと異なり、実際にアプリケーションサーバやサーブレットエンジンに配備しなくても、そのHTMLファイルをブラウザで開くだけで、デザインを確認することができます(動的に生成される部分も「それなりに」表示されるので、かなりの精度で確認できます)。 プログラマの作業も大したことはありません。通常のJavaのソースと、簡単なXMLの設定ファイルを書くだけです。 Apple社のWebObjectsという製品をご存知の方は、この後を読み進むにつれ、TapestryとWebObjectsが同じアイデアに基いていると感じられるでしょう。私もそう思います。 | ||||||||||||||||||||||||||||||
| Tapestryを使うメリット | ||||||||||||||||||||||||||||||
Tapestryを使う主なメリットは、Webデザイナとプログラマの作業を分離できるという特徴から派生します。
| ||||||||||||||||||||||||||||||
| Tapestryを使ったアプリケーションの構成 | ||||||||||||||||||||||||||||||
|
Tapestryで言うところのアプリケーション(以降、Webアプリケーションと区別するときはTapestryアプリケーションと呼ぶ)は、Webアプリケーションの一部として動作します。そして、一つのWebアプリケーションは、複数のTapestryアプリケーションを含むことができます。 Tapestryアプリケーションで最も中心的な役割を果たすのは、Tapestryアプリケーション全体をコントロールしているApplicationServlet(Tapestryアプリケーション開発者は、これを継承したものを利用する)です。このApplicationServletに、XMLで書かれたTapestryアプリケーションの仕様を表す設定ファイルのパスを渡せば、設定ファイルに従ってTapestryがアプリケーションを制御してくれます。 Tapestryアプリケーションの残りの部分は、ページやページ内の動的コンテンツ部分を表すコンポーネントなどです。なお、Tapestryアプリケーションは、必ずHomeと名づけられたページを持っていなければなりません。このページは、Tapestryアプリケーションに最初に訪れたときに表示されるページです。
| ||||||||||||||||||||||||||||||
| Tapestryを使う準備 | ||||||||||||||||||||||||||||||
それでは実際にTapestryを使ってみましょう。ここでは、Webコンテナ(Servletエンジン or アプリケーションサーバ)としてTomcat4.0.3を利用した例を示します。Tapestryは、Servletをベースとしていますので、これ以外のWebコンテナでも、簡単にセットアップできるでしょう。J2SEのバージョンは、1.4以上を利用します。
まず次のものをダウンロードします。
以降、Tomcatがインストールされているディレクトリを$TOMCAT、Tapestryがインストールされているディレクトリを$TAPESTRYと表します(例: $TOMCAT/bin/startup.sh, $TAPESTRY/lib/ext)。 | ||||||||||||||||||||||||||||||
| 最初のTapestryアプリケーション(demo1) | ||||||||||||||||||||||||||||||
では、簡単なTapestryアプリケーションを作って動かしてみましょう。
| ||||||||||||||||||||||||||||||
| アクションと変化(demo2) | ||||||||||||||||||||||||||||||
|
最初のTapestryアプリケーションは、今日の日付を表示するだけの非常に初歩的な動的ページでした。次は、ユーザのアクションに従ってページの内容を変える、もう少し本格的な動的ページを作ってみましょう。 今回のTapestryアプリケーションでは、ユーザがリンクをクリックする度に、お勧めサイト集の中からサイトを一つ選びそのサイトへのリンクを表示するページを作ってみます。画面イメージは次のようになります。
「リンクの名前」の部分はサイトの名称に置き換わり、そのサイトへのリンクになります。「次のリンク」をクリックすると、「リンクの名前」の部分が次のサイトの名称に変わり、リンクも変わります。 今回は、まずHTMLテンプレートとページ仕様から作ってみましょう。次のようになります。siteNameというJWCIDが付いているところは、前回の日付を挿入する仕組みと同じですね。Insertというコンポーネントを使っています。新しく覚えなければならないのは、JWCIDがurlとrefreshになっている部分です。
まずJWCIDがurlの方から見ていきます。ページ仕様を見ると、このコンポーネントはAnyというコンポーネントであることが分かります。このAnyというコンポーネントは任意のタグをHTMLテンプレートに挿入するためのものです。 Anyコンポーネントはelementという名のパラメータを一つ持っています。elementパラメータには、どのタグを挿入するかを指定します。<img>タグを挿入したければimgを、<pre>タグを挿入したければpreを値として指定します。ここではアンカータグを挿入したいのでaを指定しています。値の設定にbindingではなく、static-bindingタグを使用しています。bindingタグはプロパティの値を取得して動的に値を割り当てるのに対し、static-bindingタグは、仕様ファイルに書かれた値を静的に割り当てます。 もう一つhrefという名のパラメータに値をバインドしています。実はコンポーネントのパラメータには2種類あって、コンポーネントで明確にパラメータ名とその意味および必須か省略可能かが決められているもの(Anyコンポーネントのelementパラメータに相当)をフォーマルパラメータ、コンポーネントで特に決まった名前を決めていないもの(Anyコンポーネントのhrefパラメータに相当)をインフォーマルパラメータと呼びます。Anyコンポーネントは、elementというフォーマルパラメータの値をタグの名前として扱い、インフォーマルパラメータのパラメータ名をタグの属性名に、インフォーマルパラメータの値を属性値として挿入する仕様になっています。インフォーマルパラメータのおかげで、Anyコンポーネントは様々なHTMLのタグの数多くの属性をうまく扱えるようになっているのです。 インフォーマルパラメータhrefにバインドする値には、プロパティパスでurlを指定してます。実際はdemo2.Home.getUrl()メソッドが返す値が使用されます。 HTMLテンプレートのJWCIDがurlの部分をよく見ると、aタグで書かれています。このタグはAnyのelementパラメータで指定したタグに置き換えられるので、spanでも何でもいいのですがHTMLテンプレートをブラウザで表示させたときと、実際に動作させたときの見た目を一致させるためにaタグを使用しています。同様にhref="dummy"という記述も、Anyコンポーネントのhrefインフォーマルパラメータで置き換わるので、HTMLテンプレートの見た目の正確さを向上させる以上の意味はありません。ただし、style="..."はAnyコンポーネントで特に指定していないので、Anyコンポーネントは何も変更せずそのまま、style属性を出力します。うまくできているでしょ。 次にJWCIDがrefreshの方を見てみましょう。ページ仕様を見ると、このコンポーネントはDirectになっています。Directコンポーネントは、HTML上はリンクを挿入するという動作をします。しかし、もっと便利な機能も持っています。挿入されたリンクをクリックすると、特定のメソッドを呼び出してくれるのです。どのメソッドが呼び出されるかは、Directコンポーネントのlistenerパラメータで指定します。property-path属性でlisteners.nextSiteが指定されています。通常はプロパティパスの起点のクラス(ここではdemo2.Home)のインスタンスに対して、getListeners().getNextSite()というメソッドが呼び出されて、取得されたリスナのメソッドが呼び出されるところですが、プログラマは、このような複雑な動作を気にしないで済むようになっています。「listeners.メソッド名」とプロパティパスに書くと、起点のクラス(ここではdemo2.Home)の「メソッド名」という名のメソッドを呼び出してくれます。実際は、(間接の)親クラスのcom.primix.tapestry.AbstractComponent等で、Javaのリフレクション機能などを使って動的にリスナを作成し、結果として単に起点のクラスのメソッドが呼び出されるようになっているのです。 難しい話しは置いといて、「listeners.メソッド名」とプロパティパスに指定すればよいと覚えておきましょう。ここの記述では、demo2.HomeクラスのnextSiteメソッドを呼び出すような設定になっています。 HTMLテンプレートでは、JWCIDがurlの時と同様、refreshでもaタグやhref="dummy"と記述していますが、こう書いてある理由もurlの時と同様、HTMLテンプレートをブラウザで見た時の表示イメージの正確さを向上させているだけです。 次にロジックの実装を見てみましょう。ページ仕様で、プロパティパスurlと、siteNameを指定しました。また、Directのlistenerパラメータで、プロパティパスlisteners.nextSiteを指定しましたので、getUrl(), getSiteName(), nextSite()メソッドを実装しなければなりません。nextSiteはvoid型で、String[]型と、IRequestCycleの引数をとります。引数の詳細については次の例題で説明します。ソースができたらコンパイルしておきましょう。
最後に、アプリケーション仕様の作成とweb.xmlの変更を行って、仕上げましょう。
以下に、ファイルの一覧を示します。強調してあるファイルは、追加/変更したファイルです。確認が済んだら、Tomcatをリスタートして http://localhost:8080/demostry/demo2/ にアクセスしてみてください。意図どおりの動作をするでしょうか?
では、ブラウザを2つ開いて、交互にブラウザを使い分けながら、出来上がったアプリケーション(http://localhost:8080/demostry/demo2)の「次のリンク」をクリックしてみてください。一つのブラウザだけ使った場合と、お勧めサイトの出現順が異なると思います。このdemo2の例では、アプリケーションの状態(次のどのお勧めサイトを表示するか)がセッションごとに独立に管理されておらず、ユーザ間で共有されていることを示しています。Tapestryは、明示的に指定しない限り、このようにセッションごとに情報を持ちません。少ないリソースで効率よくサーバが動作できるように、こういう仕様になっています。 もちろん、ショッピングサイトなどでは、ユーザごとに別々に情報を管理しなければなりません。しかし、検索サイトなど、サーバ側でユーザごとに情報を持たなくても提供できるサービスも多いのです。アクセスの多いサイトほど、Cookieやパラメータを使って、できるだけ(サーバ側ではなく)クライアント(ブラウザ)側に情報をもたせるように工夫されています。 | ||||||||||||||||||||||||||||||
| 複数のページと、データの共有(demo3)【執筆途中】 | ||||||||||||||||||||||||||||||
|
ここまで、すべてHomeというただ一つのページからなるアプリケーションばかり作ってきました。そろそろ複数のページからなるアプリケーションを作ってみましょう。また、セッションごとに個別にデータを持つ方法も紹介します。テーマとしては、先程ちょっと触れた検索サイトを、あえてサーバ側でクライアントごとの情報を持つ方式で作ってみましょう。具体的には、サーバ側でセッションのデータとして検索条件や検索結果、何件目を表示しているかっといった情報を持つようにします。 |