しばしば、あるデータが複数のページから必要になるという状況があるでしょう。 それは、複数ページのウィザード画面かもしれませんし、ログイン中のユーザを追跡するためのオブジェクトかもしれません。
単なるページデータの永続化では不十分です。永続化フィールドは特定のページに適用され、ページ間では共有されないからです。
そのかわり、Application State Object (ASO) を使うことができます。
ASOの値はページ外へ自動的に格納されます; デフォルトの格納方式ではセッションに格納されます。
メモ: ASOにおけるTapestryの命名規約はServlet APIの命名規約に従っていません。しかし、慌てないでください: ASOは(デフォルトでは)セッションに格納されます。Servletコンテキストではありません。ASOはユーザー毎に一意であり、ユーザー間で共有されることはありません。
ASOを保持するフィールドには ApplicationState でアノテーションします。
例:
public class MyPage { @ApplicationState private MyState myState; . . . }
他のコンポーネントやページで、同じ型のフィールドを宣言しApplicationStateアノテーションを与えると(フィールド名が異なっていても)、同じ値を共有します。とても簡単です。
Tapestry 4 ユーザーへ: ASOを使うための設定を行う必要も論理名を指定する必要もなくなりました。 Tapestry 5 ではASOの識別にクラス名を使うため、論理名は不要になりました。
最初のASOへのアクセスでASOは自動的に生成されます。ASOにはパブリックで引数無しのコンストラクタがあるのが典型的でしょう ... しかし、Tapestry IOC サービスの実装クラスと同じように、ASOもコンストラクタ経由で依存性の注入を行うことができます。
ASOフィールドに値を代入するとその値が格納されます。 nullを代入するとASOは削除されます(その後のフィールドの読み出しで、新しいインスタンスが生成されます)。
スケーラブルなWebアプリケーションでは、不要なサーバ側セッションを生成しません。 特に、Webアプリケーションの最初のアクセスでセッションが生成されるのを避ける事ができれば、 より多くのユーザーからの要求を処理する事ができるようになります。 そのため、ASOの生成を避ける事ができるのであればそうすべきです。
しかし、それはどうやって回避する事ができるのだろう? "myState != null" とただチェックするだけでASOは生成されてしまい、それがセッションに格納されてしまいます。
そうするかわりに、もうひとつのフィールドを用意します:
private boolean myStateExists;
このフィールドはASOの伴侶となり、ASOがすでに存在しているかどうか確認するのに用いることができます。 このフィールド用のアノテーションはありません; フィールドの名前によって示されます(ASOを格納するフィールドの名前に "Exists" を加えた名前です)。 また、boolean型でプライベートなインスタンス変数でなければなりません。
一方、ASOをnullのままにしておくこともできます:
@ApplicationState(create=false) private MyState myState;
この場合、MyState型のASOが存在しなければ myState フィールドはnullのままです。 ASOがすでに生成されている場合(そのフィールドに値を代入するか、create=true となっているMyState型のASOフィールドが他にある場合)は、非nullとなっているでしょう。
各ASOはある永続化方式によって管理されます。デフォルトの永続化方式は "session" で、ASOはセッション内に格納されます。 セッションは必要に応じて生成されます。
設計上、Application State Object は可変オブジェクトです。これが意味することは、ASOはセッションから読み出された後に更新されるかもしれないということです。 これは、クラスタリング を行う場合に問題となります。更新されたASOはクラスタ内の他のサーバに伝播されなければならないからです。
Tapestryはこれを自動的に行います; セッションから読み出されたASOは、リクエストが終了するときにセッションに書き戻されます。 これは、アプリケーションサーバがクラスタに対してASOの新しい状態を同期させるトリガとなります。
これを最適化することができます: OptimizedApplicationStateObject インターフェースをASOで実装することができます。このインターフェースはASOが "dirty" で書き戻す必要があるかどうかを制御する機能を提供します。 これにより、大半のリクエストでASOの内部状態が変化しないという場合にはセッションに書き戻さずに済みます。
この最適化を行う最も簡単な方法は、BaseOptimizedApplicationStateObject を拡張し、setterメソッドで markDirty() を実行することです。
通常、ASOの設定は永続化方式をデフォルト以外のものに変更する必要がある場合に行います。 今のところ、組み込みの永続化方式はただひとつだけですが、将来的には複数の方式が組み込まれるでしょう。
また、ある特定のASOがどのようにインスタンス化されるのかを制御するための設定を行うことができます。 ASOが生成される際に何らかの値をインジェクトする必要があったり、初期化する必要があるかもしれません。 このような場合、ApplicationStateCreator オブジェクトを用意し、ASOの生成が必要なときにこのオブジェクトを呼び出すことができます。 このテクニックはまた、クラス ではなく インターフェース によってASOの型を表したい場合に使用できます: そのインターフェースを実装するクラスについて知っているApplicationStateCreatorを設定する必要があります。
ApplicationStateManagerへのContributionによってASOの設定を行います。 あなたのアプリケーションモジュールで次のようにします:
public void contributeApplicationStateManager(MappedConfiguration<Class, ApplicationStateContribution> configuration) { ApplicationStateCreator<MyState> creator = new ApplicationStateCreator<MyState>() { public MyState create() { return new MyState(new Date()); } }; configuration.add(MyState.class, new ApplicationStateContribution("session", creator)); }
MyStateがASOの型で、その型に対するApplicationStateCreatorを設定しています。 いくつかの総称型を修飾していますが、それは必須ではありません。
このApplicationStateCreatorは、デフォルトコンストラクタの代わりに現在の日時を受け取るコンストラクタを使用して新たなMyStateインスタンスを生成しています。 これは単なる一例にすぎません。
最後に、永続化方式名とApplicationStateCreatorを指定して ApplicationStateContribution を生成し、設定しています。