このドキュメントは Tomcat version 3.x の設計がどのようになってるか、 また、なぜそのように設計されたかの背景を文書化することを目的としています。 Tomcat はそれぞれの視点からコードやアイディアを提供してくれる大勢の人々に より長期にわたって改良、開発された結果、完成したものです。 Tomcat の コードの大半は昨年 2,3回は作り直されました。コードの修正にあたっては オフラインで数多くの実験やプロトタイプの開発も行われました。実際に適用 されているパターンやアイディアと実装を区別することが重要です。後に、 これらは他を変更するでしょうし、すでに多くの修正が加えられました。
正確さ。 Servlet と JSP の仕様を出来るだけ正確に 実装すること。
コードの品質 と明解さが Apache の標準に達して いなければいけません。もっとたくさんのコメントや文書の提供を 求めています。:-)
セキュリティ。 ユーザの Web アプリケーションは Applet のようにサンドボックスの中で動作することになり、 また、 サンドボックスの外にアクセスできてはいけないものです。 Servlet API は(私が知る限り)このような機能を提供できる唯一の API です。 つまり、ISP が各ユーザの WAR ファイルを展開する 場合でも、システム セキュリティやユーザ間で発生する妨害の可能性を心配する必要があり ません。また、企業が JSP や Web アプリケーションを 使う場合も、 セキュリティ上の欠陥を検査する必要がありません。(驚くほど難しく 高価で複雑な Web サーバにおいてもです。)
組み込みの容易さ。 Servlet コンテナは様々な アプリ ケーションと連携して動作できるようにコンテナ自身のコードは組み込み 易くなっていなければなりません。Tomcat は(Apache のような) Web サーバがServletを利用できるようにするサービスと、スタンドアローン アプリケーションとして HTTP と Servlet 双方を利用できるようにする サービスの2つのタイプを提供しています。Tomcat はコンポーネント として利用し、アプリケーションが(スタンドアローンサーバあるいは Web サーバと連携する形態で) Web サービスを提供できるようになっている べきだと考えています。このようにしてアプリケーションが Servlet インターフェースを提供できるようにします。例えば、IDE 用途を想定した Web ベースの設定ですが、Tomcat を動かし、 Servlet によりアプリ ケーション内部の API をアクセスするだけで、そのようなものを作れる ようになります。さらに、既存の Web サーバの 中に Tomcat を組み込む ことも考えています。このようにして Apache などの Web サーバ向けに Servlet と JSP による一般的な Web プログラミングを提供したいと 考えています。
パフォーマンス。 Apache がそうであるように、 パフォー マンスはコードの品質ほど重要ではありません。パフォーマンスは正しい アルゴリズムとデータ構造によってもたらされるものであり、低レベルの ハッキングによって向上するものではありません。ただし、重要なパスには 注目し、可能な限りオーバヘッドを最小限にとどめるように気を付ける 方針をとっています。ここでは、特別な機能を利用するアプリケーション だけが犠牲を払うだけですむことにも気を付けます。
既存の Web インフラストラクチャへの統合。 Web アプリ ケーションは Web サーバ上で動作するので、既存のコードとともに動作 できること、また、既存のコードからの円滑な 切替え方法を提供できる ようにする方針です。既存の最適化や Apache のような成熟した Web サーバをできるだけ利用し、各モジュールや拡張が Tomcat とともにうまく 動作するようにします。
モジュールの構成。 コアとなる部分は出来るだけ小さく するべきです(実際にはそれほど小さくありませんが :-))。また、個々の 機能は通常モジュールで提供されるべきです。目的にあったモジュール を選ぶようにすれば、トースターのような小さなところでも巨大なサーバの 中でも使えるようになります。同一モジュールであっても複数のバージョン を用意することで 様々な VM や特殊な環境向けの最適化を行います。
再利用可能なコンポーネント がどこにでもあること。 一般的であると認められる機能は Tomcat コアへの依存を最小限にする べきです。この方針は Tomcat 3.3 の開発が始められるとより明確になる でしょう。その時には "モジュール" とはなんであるかをはっきりさせ、 Tomcat とは無関係な他のプロジェクト でも使えるような一般用途で、 しかも各々が独立したコンポーネントのコレクションを作る予定です。 複数のプロジェクトで繰り返し検討、テスト、機能強化が行われた コンポーネントがあればより柔軟なインテグレーションに貢献するでしょう。 この場合、Facade パターンを 使い、それぞれのインターフェースを変更 せずに個々のコンポーネントを統合します。(Tomcat ではこの方法で コンポーネントを連携させています)
"一つ以上のオブジェクトに要求を処理する機会をあたえることにより、 送信するオブジェクトと受信するオブジェクトの結合を避ける"。 Tomcat では Servlet API の実装と要求の処理(マッピング、認証など)がインターセプタにより 処理されています。オペレーションは互いにつながっていて、あるインターセプタが それを処理するまで、それに沿って転送されます。
インターセプタは、Tomcat の実装と拡張に使われたメカニズムです。インター セプタにより要求の送信オブジェクト(Tomcat コア、Servlet) と実際の受信 オブジェクト(要求されたオペレーションを実行するモジュールのひとつ)の結合を 避けられます。実際に実装を提供するモジュールを知る必要はありません。
このパターンを Tomcat に採用した理由は主に 2つあります。
まったく同一のパターンが IIS のフィルタフォームの部分で使われています。 これは NES の SAF に相当するもので Apache でいえばモジュールとフックにあたり ます。この方法はこれらのサーバを拡張する主要なツールとなっています。また、 Tomcat と Web サーバの統合でも同じ方法を使っています。 (Tomcat でもこの パターンを使ったので、既存のコードの再利用やサーバ拡張ができました。)この パターンがもたらす、ハイパフォーマンスと拡張性がうまく功を奏する事実が あったことが採用の決め手になりました。
Tomcat はアプリケーションや様々なサービスを提供する外部パッケージと統合 することも目的としています。これは展開を決定することにもなりますが、同時に コアや基本モジュールがどれか特別なハンドラの実装に依存してはならないことを 意味します。その一例として認証をとりあげてみましょう。Tomcat がアプリ ケーションに組み込まれたとすると認証サービスは Tomcat のデフォルトインター セプタが提供しますが、その実装は静的な XML ファイル、Java ではないネイティブ な Web サーバ、JAAS(Java Authorization and Authentication Service)、 プライベート API など、どれでもかまいません。
実装はインターセプタとイベントで構成されます。(おそらく 3.3 か 3.4 で このような実装になるでしょう。) Strategy パターンの項も参照してください。
"アルゴリズムの集合を定義し、各アルゴリズムをカプセル化して、それらを交換可能にする"。
このパターンのおかげでオープンソース環境下で Tomcat が容易に進展するよう になり、開発も簡単になりました。Tomcat 3.0 のオリジナルの実装を分離し、 全般にわたる安定性を管理しながら個々のコンポーネントの再実装を行いました。 開発を始めたとき、ソースコードは何度も変更と修正が繰り返されたため非常に 読みにくくなってしまったにもかかわらず、驚くほど安定して、よくテストされて いるものでした。このパターンを適用することで、醜いアルゴリズムをモジュール化 し、さらに、よりよい実装を提供しました。
実際のアルゴリズムをコードから分離したので、問題解決の重要なポイントが 何であるかと依存関係を無くすことに注力できました。
Strategy パターンを利用すると、コードが準備された後に、各サービスの これまでのものに変わる実装を提供できるようになり(2,3 の認証モジュールと 3,4 のマッピングインターセプタが開発され、さらなる改良に入るところです。)、 安定性とパフォーマンスを重視した実装を決めることができました。
このようにして、開発するアプリケーションにもっともよく調和する コンポーネントを選べるようになりました。例えば、マッピングインターセプタ (リクエストを解析してマッピングルールを適用します。) ですが、メモリ使用 方法や実行速度を最適化したものを使えるようになりました。ISP なら、 かなりの数の Web アプリケーションを扱えるように特化したアルゴリズムを 選ぶでしょう。例えば、必要になったときのみ、そのアプリケーションのマッピング をロードするようなアルゴリズムです。(さらに頻繁にアクセスされるマッピングの キャッシュ管理なども考えられます。)
ネイティブなサーバに実装されている既存のアルゴリズムを利用する アプリケーションもあるでしょう。たいていの Web サーバには高度に調整された アルゴリズムがあります。(驚くことではありません。) そして、そのアルゴリズムは あまり多くのことをやらずして速く動くコードを手に入れたい不精なプログラマ にはすばらしいことでしょう。
このようなアルゴリズムは RequestInterceptor に実装されています。 Chain Of Responsibility パターンの項も参照してください。
このパターンはコアとなるオブジェクト(Context, ContextManager)と モジュール(インターセプタ) 間の一対多の依存関係を定義しています。 コアオブジェクトが状態を変えた時に、それに依存するすべてのオブジェクトに 変化が自動的に知らされ、更新されます。
Tomcat の状態は実行時に変化します。この変化は状態を監視している モジュールに通知され、モジュールの内部データ構造を更新(できるかぎりの 最適化)します。このようにするとモジュールでツリー構造(JDK1.2 Collection API) や、これ以外の複雑な構造を使っていても、コア部分では特別なデータ構造を 使わずにシンプルなコードにしておけるメリットがあります。
また、コア部分を JDK 1.1 でも動作するように互換性を保つこともできる ようになります。同時に "最新の"機能をどれでも利用できます。 (ただし、これはアルゴリズム/データを呼出し側から分離し、Strategy パターンを 併用するとできるようになります。)
そして、コア部分はコードの可読性を損なうような最適化をしなくてもより シンプルに作ることができます。複雑な構造はモジュール、つまり Observer の中にのみ存在することになり、同期的に情報を維持します。
このような実装は ContextInterceptor に適用しました。(おそらくこの 実装は Tomcat 3.3 か 3.4 で標準的な Event/Listener に置き換えられる でしょう。) ContextManager, Context, Container は内部状態が変化する たびに通知すると、インターセプタが自身の内部状態を更新するようになって います。
あるクラスのインターフェースを tomcat.core が求める他のインター フェースへ変換します。これが "他のアプリケーションへの統合" という 要求を可能にした立役者です。
Adapter は Tomcat が外部の API やモジュールを再利用するにあたり、 それらを変更したり、 Tomcat 自身に(特定の API やアプリケーションに依存する ような)変更を加えずにすむという点でとても重要なパターンです。また、 Adapter パターンを使うと、複数の API を使ってさまざまな処理を実現できます。 たとえば認証を行う場合に JAAS を使ったり、J2EE API 群を使う、あるいは Web サーバに組み込まれている専用の API を使うなどです。 Realm インターフェースを ハードコーディングしたり、何かの API にすべてを合わせるようなことをする 代わりに、統合したいすべてのAPIに対してアダプタを作るだけです。(JAAS は このような "汎用的な認証 API " としては有力な候補ですが、 シンプルな認証を扱うには大きすぎます。)
Adapter パターンは Web サーバと統合するところでも使われています。 Request, Response, Container がアダプターとなり、これらが等価的な オブジェクトとして Web サーバ内で動作します。
Adapter は "Chain of responsibility" パターン、 "Strategy" パターンと共に使われ、"遅延プログラミング" (あるいは既存 API の再利用も :-)) も可能になります。
ほとんどのインターセプタが Adapter パターンを使って実装されています。 J2EEInterceptor はアダプタのとても良い例です。J2EEInterceptor の一方では Tomcat のインターフェース(インターセプタ)を持ち、もう一方では J2EE インターフェースを持っています。
" あるオブジェクトへのアクセスを制御するために、そのオブジェクトの "代理" または入れ物を提供します。" これが Tomcat の本体と Servlet、ユーザアプリケーションを分離するメインツールとなっています。 クラスローダに手を加えるような方法が拡張のために使われることもありますが、 このパターンは均整のとれたメカニズムであり、Facade パターンと組み合わせて 多くの利点を引き出せます。
これは、必要になるまで重い処理(String の生成など)を遅らせたり、コア部分で より効果的なインターフェースを使うことができる(Facade パターンを参照して ください)ので、パフォーマンスにおいても重要な役割を果たします。
tomcat.core の制約や要求と違っていても、Servlet API はアプリケーション 開発者が簡単に使えるようにまた、安全であるように設計されました。
Proxy パターンは JDK 1.3 においてタイプセーフなインターフェースの実装にも 使われています。(ダウンキャストは許されません。)
その理由は?もともとセキュリティを考慮したものですが、この パターンは多くのパフォーマンス最適化の中心的な存在であり、多くの機能を 提供します。
あるクラスのインターフェースを別のインターフェースに変換します。特に、 アプリケーションを(正しい) Servlet API で提供するために使われます。このとき コア部分の処理により効果の期待できるインターフェースを使います。
"Facade" という名前はセキュリティ関係のディスカッションや 多くのインターフェースにおいて、しばしば(誤って)使われてきました。本来は Facade はパフォーマンスのためや内部API群の上にServlet APIを実装するために 重要なのに対して、"Proxy" がセキュリティに関して責任を負うべき です。例えば、HttpServletRequestは、多くのコアインターフェースへのアクセスを 組み合わせています。
その理由は? Servlet API には Servlet 開発者から見た目的と要求があります。 Web サーバと tomcat.core には 違うことが要求されますが、正しい API を使えば、いくつもの最適化を提供する ことも可能です。例えば、特別なコンポーネントを使ったり、(既存の)Web サーバ 用のアダプタを使えば String の使い過ぎを避けられます。
Facade パターンがもたらすもう一つの重要な恩恵は (必ず変わってしまう) 現在の Servlet API から Tomcat の内部構造を切り離しているところにあります。 さらに、ひとつの Tomcat インスタンスが Servlet API の複数の "プロファイル" をサポートできるようにもしてくれます。例えば、 Servlet API の 2.2 から 2.3 への移行は、2.2 の Web アプリケーションが 同一のコンテナで動作できるので、とてもスムーズに行うことができます。 つまり、ユーザは仕様のバージョンが変わるたびに、すべてのアプリケーションを 書き直す(あるいは再コンパイルする)必要はありません。このように、Tomcat 3.3 は Servlet API 2.2, 2.3 双方のサポートが可能になっています。
このコンポーネントは、Tomcat におけるリクエストの表現です。これは、 基本的なアプリケーションの表現のための、"アダプタ" として動作します。 例えば、 Web サーバの場合は、このコンポーネントは (request_rec *) 型の Java 言語による表現であるといえます。また、 Tomcat が Web サーバではない アプリケーションに組み込まれているなら、このコンポーネントはそのアプリ ケーション的にみたリクエストの概念を表現します。
このコンポーネントは受動的なオブジェクトで、すべてのオペレーションを モジュールに委譲します。出来るだけ "遅延" 評価を行うことが重要で、 こうすることによってコードを簡単にしておくことができます。
これらのオブジェクトは HttpServletRequest や HttpServletResponse の 複製ではありません。javax パッケージのインターフェースは Web アプリケーション 開発者のために設計されたものですが、 Tomcat の core には違うことが 要求されます。 HttpServletRequest を request_rec のアダプタとして実装するのは 難しく、(セキュリティなどの理由からアプリケーションに要求される) String ベースの表現に拘束されるために、パフォーマンスに多大な影響を与えたでしょう。
例えば、Tomcat 3.2 では内部バッファを外にだして利用することにしました。 Stream/Writer インターフェースを使う代わりに、コアコンポーネントが バッファリングや char/byte 変換を直接扱えるようにしたものです。
XXX Web サーバと共通する概念、つまり、リクエストだけ、をコアに持ち込んだ のはとてもいいアイディアかもしれません。そして、HTTP コネクションデータを 表現する Connection コンポーネントを追加しています。 Request/Response コンポーネントを用意した唯一の理由は Servlet モデルに合わせるためですが、コ アの役割は Web サーバが使うためのモデルに合わせることです。この構想は すばらしいでしょう。
Tomcat のバッファリングや char/byte 変換を完全に制御したかったというのが このコンポーネントを作った理由です。というのも、(実験によると) ここが もっとも重い処理のうちの一つだからです。このコンポーネントを用意したので、 外部の byte/char コンバータをプラグインすることも可能になりました。 ご存知のように、バッファからバッファへの多重コピーはほとんどのシステムで 重要なファクタであることが証明されています。このことは Tomcat 3.2 では 他にもっと大きな要因があったため明らかになりませんでしたが、限界に 近づくにつれ、最も重要な問題の一つであることが判明しました。
jasper と他のテンプレートシステムの統合部分にその機能の重要さが あらわれています。ここでは "static" なテキストが中間バッファを 使わずにダイレクトにサーバに運ばれてきて、バイナリ表現(エンコーディング)が プリコンパイルされることになります。
このコンポーネントは Tomcat 3 の中ではたいへん特別で重要な コンポーネントです。最初に解決しなければならない問題は byte から char への変換でした。charset は後になってわかります (Content-Type ヘッダが解析されるか、Servlet API 2.3 でユーザが設定するようになるかです)。 加えて、受け取った byte 列から String への変換はエンコーディングが決まるまで、 (正しく)実行されません。
MessageBytes コンポーネントはこれ以外にもガーベッジコレクションにかかる コストを低減する重要なメカニズムになるという利点もあります。ユーザが すべてのヘッダにアクセスするような時間はほとんどありませんし、 (URL コンポーネントなどの)リクエストの情報にアクセスする時間もほとんど ないでしょう。これは "HelloWorld"e; のような Servlet (あるいは単になにかの情報を表示するようなServlet)はこれまで 30 - 40 の String が割り当て(さらに CG)られていたものが、 MessageBytes コンポーネントを使えば、String の割り当てを必要としなくなったことが 理由です。
さらに(はっきりとわかるものではありませんが) リクエスト解析でも、この コンポーネントの利点があります。 String を使う解析は非効率的なコードに なりがちであることはよく知られていることです。Java には StringTokenizer クラスや substring()/indexOf() メソッドのような一般的な用途の ユーティリティがありますが、これらはあくまで "一般用途" 向けに設計されていて、Tomcat の用途にはそぐわないものです。 Tomcat 3.0 はコード解析に Java の一般的なユーティリティメカニズムを 使った結果、一つのリクエストに 100以上の Stringの割り当てが必要でした。
Java にとって String は "不変"であるという特殊な特徴を 持つために、とても重要なクラスだということに注意してください。これは セキュリティという観点からすると大きな意味を持っていますが、Servlet API の ほとんどのメソッドは返り値やパラメータに String を使っています。一方、 String は再利用出来ないので、サーバ側の操作では、非常に高く付きます。 ガーベジコレクションとメモリに関する解説も読んでください。
Context コンポーネントは Web アプリケーションを表現するものです。 このコンポーネントには web.xml と server.xml で定義可能なすべての プロパティがあります。
現時点の実装は複雑なのでいろいろと手を加えて変更しなくてはなりません。
Context コンポーネントは contextMap() メソッドのコールバックが完了すると Request コンポーネントに関連付けられます。デフォルトではマッピングインター セプタ (デフォルトのインターセプタは SimpleMapper) がこの部分を実装して いますが、他のインターセプタは (最初のリクエストがあったときに、動的に コンテクストに追加して ~user の URI で各ユーザのコンテンツ提供をサポート するなどの)外部機能を提供します。
このコンポーネントはあるプロパティの一般的なセットを共有する URL グループを表現しています。これは Apache のディレクトリ単位の設定に 似ています。 Servlet API の仕様では "標準"プロパティは(Servlet の マッピングで指定される)ハンドラとセキュリティ関連のプロパティです。
Container コンポーネントは requestMap() メソッドの処理が完了すると Request コンポーネントに関連付けられます。SimpleMapper はこのような処理の "コア" 実装で、プレフィックスや正確でしかも拡張性のあるマッピングの サポート機能を提供します。他のインターセプタは(JspInterceptor のような)ある サブセットの最適化されたマッピングやスキーマのカスタムマッピングの実装を 提供します。
ここはかなり重要なオペレーションです。(おそらくもっとも高価な 処理にちがいないのですが、残念ながらこれが原因となるパフォーマンス低 下より以前に他のいくつかのホットスポットをまだ取り除かなくてはいけません。) より洗練されたデータ構造や(ツリーなどの)アルゴリズムを使ったこれまでとは違った 実装が期待されるところです。 "進化"についての解説を参照してください。
Tomcat 3.3 ではマッピングの後でコンテキストやコンテナ毎にインターセプタを 指定出来るようになります。 Tomcat はコンテナがどれで、リクエストがどの コンテナに属しているかを知っていて、ある特別な URL に対してのみ定義されている 特別なインターセプタを起動できます。これは一般的な Web サーバが提供する機能に 似ています。(処理の順序は NES(Netscape Enterprise Server) に似ています。)
Tomcat が実行を開始するメインのエントリポイントです。このコンポーネントは モジュールのアクティビティを順序良く整理します。
Tomcat が組み込まれているアプリケーション(Web サーバ、一般的な アプリケーション、Tomcat スタンドアローンサーバ)は実行を制御します。 組み込まれている Tomcat は Request/Response アダプタを生成し、リクエストを 処理する ContextManager を使うようなアダプタを要求します。
XXX おそらく、Server か TomcatContainer という名前の方がいいのでしょう。
Interceptor コンポーネントは Tomcat の細分化された各機能を組み立て、 拡張するためのメカニズムを表現しています。Tomcat は機能のほとんどが モジュールで実装されています。モジュールは Tomcat のコアオブジェクトに 働きかけ Tomcat に処理を施し、機能を拡張します。これらは "Chain of Resposibility" パターン、"Strategy" パターンを 採用した設計になっていますが、Apache 流のやり方の影響を受けたもので、 ISAPI(フィルタ)や NSAPI(saf)も参考にしました。ご存知のように(コアを含む) Apache の大部分はシンプルなフックのメカニズムで実装されています。
ある興味深いサブプロジェクトでは、インターセプタが主な Web サーバ メカニズムと相互に実行可能であり、mod_perl と同じように使えることを 実現しようとしています。例えば、Java 言語を使って簡単にサーバを拡張できる ようにすることです。(言い換えると、既存のサーバモジュールの機能をすべて 発揮できるようなアクセス方法を提供することです。)
リクエストの処理において認証の解析、承認、セッション、(ヘッダが 送られるまえの)レスポンスの確定、(例えば HTTP/1.1 をサポートするのに 使えますが、バッファが送られる前の)バッファの確定、本文の前後など、 様々な局面から制御出来るようになるでしょう。
インターセプタは Web サーバとの統合機能を提供します。 (バージョン3.2 以前のTomcat では特殊な "Connector" インターフェースが使われて いましたが、Connector は実際にはインターセプタのサブセットでしたし、Web サーバとの統合は単にコンテナの起動/停止を制御する以上のものが要求されて いました。このため、インターセプタのメカニズムを導入するほうがいいと 判断しました。)
現状で Tomcat はいくつものフックを定義しています。他に必要に応じて 追加されるのは:
contextMap
requestMap
XXX その他
コア部分を出来るだけシンプルに、また可読性を保つために、いくつかの 機能が(インターセプタを使わずに)コアの中で実装されています。これらは 特殊なヘルパーとして実装されています。例えば、フォームデータの読み込みや エンコーディングなどです。ヘルパーを機能別にグループ分け、簡単に改造でき ることを指標にしました。 Tomcat の他の部分に与える影響を最小に保ちながら 書き換えや置き換えが可能です。これはチームでの開発作業や新たに加わる コントリビュータがコアを壊すのではないかという危惧を抱かずに Tomcat の 改良を進められる一つの方法です。
XXX サーバの初期化と設定コンテキストの add/init/shutdown/remove など。
Web サーバへの各リクエストは Web アダプタが 受け取ります。このアダプタは Request/Response アダプタが提供することに なります。次に、ContextManager.service() メソッドの実行が開始されると、 Tomcat のコアはリクエストの処理を始めます。(Tomcat が Web サーバではない アプリケーションに組み込まれたときには)HTTPトランザクション以外のたとえば、 サブリクエストや Web ベースではないリクエストでも同様のパスを実行します。
リクエスト解析。Context Manager が ("Chain of responsibility パターン" により) 解析結果をいくつもの インターセプタに 委譲することで、各インターセプタはリクエストを 処理する機会が 与えられます。Tomcat には仕様書で定義され、設定 (server.xml) に静的に記述されるマッピングに基づくデフォルトの実装が あります。また、自動的にすべての webapp ディレクトリに付加される インターセプタもあります。これ以外のインターセプタは "~user" 表記によるコンテンツ提供機能を自動的に追加するなどさまざまな機能を それぞれに遂行しています。パフォーマンスに関する 解説も読んでください。 ("Strategy パターン" により)別のアルゴリズムを割り当てる ようなマッピングも可能です。このマッピングは Tomcat が既存の Web サーバに統合される場合はその Web サーバのマッパを使うことも可能 です。
マッピングした結果、数多くのパターンやルールをリクエストに割り 当て ることができるようになり、リクエストが属する "コンテナ" を決定することもできるようになります。コンテナはいくつかのプロパティが 組になっていますが、リクエストハンドラやセキュリティ制約、その他の 特別な属性も含まれています。
ContextManager は承認をインターセプタ群に委譲します。もしも、 ある役割が要求されていたら、Tomcat は一連の認証処理を開始しますが、 そこでは認証 API を使用するアダプタが ("Strategy" により) 実作業をこなしています。
ハンドラが割り当てられます。高価な、あるいは複雑な操作は モジュールが 扱います(例えば isUserInRole() メソッドが一連の 承認処理の中で実行され るようにです)。この過程は(Jigsaw や JWS(Java Web Server) の Servlet チェイニングのように)いくつでも フィルタをかけられるようになっています。 ただ、今までのところでは 必要性を感じていません。
リクエストを処理する過程で Tomcat は通知をその処理段階を 監視しているモジュール("Observer")に送ります。例えば、 モジュールがトランザクションコンテキスト(J2EE インターセプタ)をセット あるいは装備できる機会を与えるために通知を送ります。(つまり、個々の 操作がきめ細かなチューニングを行うための間をとるわけです。)
このモデルは Apache や IIS, NES で使われているのと同じものです。 (すべての通知について交わりではなく結びとなるように表現しようと努力しました。) なぜか?というとパフォーマンスを 上げることがもともとの理由です。また、このモデルは有名でありよく練られている という事実があり、他のモデルよりも信頼できるのも理由になっています。 "Strategy" パターンを採用するとコード体系が (Tomcat 3.0 に 比べると)大幅に改善され、いろいろな最適化を施せるようになることが わかりました。
その他の理由には Tomcat に Web サーバとして動作できる機能を追加したアイディアに関連するものがあります。 これは Web サーバを Java のクリーンな API を使ってモジュール(フィルタ、 SAF、フック)として開発する余地を与えることです。Web サーバの実装方法は いくつもありますが、この設計は Apache, IIS, NES をモデルにし、双方向で コードを再利用できるようにしました。
SimpleMapper は contextMap や requestMap へと振り分けるもので Servlet API で 定義されている標準のマッピングルールを実装しています。SimpleMapper は 決して動作が速いとは言えませんが、何度もテストされ安定した モジュールです。 現バージョンのコードの大部分は Tomcat 3.0 から 持ち込まれ、原型を残しています。SimpleMapper に代わる他の解が 現われて、より発展する ことを願っています。
LoaderInterceptor will set the context ClassLoader. There are 2 modules - one that provide a custom class loader to be used with JDK1.1 and one that plugs JDK1.2 URLClassLoader.
LoaderInterceptor はコンテキストのクラスローダを セットします。LoaderInerceptor には 2 つのモジュールがありますが、 一つは JDK 1.1 で使えるようになったカスタムクラスローダを提供する モジュールで、もう一つは JDK 1.2 の URLClassLoader をプラグインする モジュールです。
"Strategy" パターンを利用すると別のアルゴリズム提供が容易に なり、コアモジュールに影響を与えずに実装することもできるようになります。 いずれ、新しい "ストラテジー" がテストされることになるでしょうし (最初は実験的ですが、次の段階で製品として)、ゆくゆくは "コア" モジュールに置き換わることもあるでしょう。
Tomcat のコアはリクエストを処理する最低限の役割だけを果たすように 設計されています。ユースケースや様々なモジュールの要求に基づいてコアを "進化させながら" いくつもあるメソッドや構造体も出来るだけ 小さくなるように努力しました。バージョンが 3.3 か 3.4 になるころには 安定したコアになっていると期待しています。
これらは度重なる機能追加やバグ修正のためにとても複雑で醜い(!) コードだったTomcat 3.0 からスムーズな進化を遂げたメソッドです。 リファクタリングがいかに素晴らしいかを証明するよい例です。
Tomcat の開発においてはコードが出来るだけスタンドアローンのコンポーネント となるように、また、Tomcat のコアに依存しないようにしました。例えば、 クラスローダやセッションマネージャ、TCPサーバ、認証ですが、これらは別の アーキテクチャにも容易にとりいれられるようになっています。これらはコアの インターフェースへのリファレンスを持っていません(持つべきではありません)。
このような設計にした理由はいくつもありますが、まず、Tomcat は Servlet API を実装することを第一の目的とするべきであり、動作の問題点を追求するべきでは ないと考えます。また、Tomcat を構成するコンポーネントは他のサーバで も十分使えるように一般化されているべきとも考えています。
たとえば、セッションマネージャですが、これは大抵の Servlet コンテナに プラグインで追加できるようになっています。セッションマネージャはある オブジェクトデータベースの形になっているなど、コンテナの内部構造について 詳細な知識までもを持つ(持つべきではない)と考えます。特定のコンテナに 依存しないように、また、アダプタを使って別のコンテナにもフックをかけられる ようにコードを書くことにかなりの時間を費しました。
XXX は別のドキュメントに移動しました!
プロクシとファサードマネージャはセキュリティを保証するうえで、 たいへん重要な役割を担っています。これらは Web アプリケーションが Tomcat 自身との通信を制御する方法を提供することでセキュリティを 保証しています。このような方法をとると特殊なクラスローダのメカニズム (と神経質な管理者には思える)を問題視されても、できるだけ多くの情報を 提供すれば、プロクシはとても効果のあるデザインパターンであると 提言できます。
tomcat.core はある (Java の)特権下で動作しますので、コードが 安全であるように記述されていること、ユーザが任意のメソッドを 実行できないようになっていることに注意を払わねばなりません。このため、 どのメソッドを public にするか、また、外部から内部のオブジェクト インスタンスにどのようにアクセスするかについては厳しい検討が要求されます。 FacadeManagerはエントリへの唯一の接点であるべきです。
アプリケーションがパフォーマンスを拡張するのに必要な内部 API の 提供もまた要求されます。例えば、Jasper が内部バッファを利用できるように するなどの拡張です。
ContextManager を生成し、スタート、ストップさせるには Tomcat API を 使ってください。コンフィギュレーション API を統合するのはとても簡単です。 正しいオブジェクトが生成され、setterメソッドが実行される範囲においては Tomcat はどのようにセットアップされたかについては考慮しません。
統合においては "Adapter" パターンが使われています。 インターセプタは Tomcat 内部へのフックを提供して、リクエスト処理の すべての局面で制御することができます。
商用の Web サイトの大多数が 4つの代表的な Web サーバのどれかで 動作しています。Tomcat はそれらの Web サーバと連携してうまく動作 することを確認しようとしています。結局、Servletは Web アプリケーションの 道具として使われているわけです。:-)
現在のようなアーキテクチャにしましたが、それでもなお、数えきれない ほどの問題があります。例えば、Servlet API 2.2 で定義されたマッピングは 既存の Web サーバのマッピングとは違います。フォームベースのログインを 統合するのはとても難しいことです。(同じ認証方法を使って Java ではない リソースまでも保護するためにはネイティブコードがかなり必要になります。)
問題点のほとんどのものはいまだにかなりの労力が必要な状態です。しかし、 現在のアーキテクチャは問題を扱えるようになっているという希望をもって います。もっと大きな目標はインターセプタを既存の Web サーバの拡張へと 変貌させることです。そうなれば フック/モジュール/SAF/フィルタは C (や mod_perl) に代わって Java で開発されるようになるでしょう。
Tomcat のデザインは Apache のモジュール/フックや ISAPI, NSAPI の機能をベースにしています。すべてに対して何が一般的であるかを 考える際には既成概念に捕らわれないように注意しました。そう遠くないうちに、 Tomcat はより多くのフックを持てるようになるでしょうし、他の Web サーバと同レベルの拡張性を提供できるようになるでしょう。 "必要になったときに" のルールに従い、フックを追加する ようにしてきました。これまでは数多くのコールバックがまだ追加されて いるところです。
Apache や IIS, NES のドキュメントと、そしてコードも同じように 読んでください。違う名前を使うことと Java スタイルのインターフェースで あることを除けば、モデルはまったく同じです。
マッピングは以下のようになっています。
|
Tomcat |
Apache |
ISAPI |
NSAPI |
Other |
|---|---|---|---|---|
|
Request / Response |
request_rec |
|
|
|
|
ServletWrapper |
Handler |
|
|
|
|
Context |
server_rec / |
|
|
|
|
Container |
per_dir_config |
|
|
|
|
Interceptor |
Module / hooks |
|
|
|
|
ContextManager |
|
|
|
|
このドキュメントに欠けているもの:
図/UML ダイヤグラム
ポリシーによるセキュリティ - 標準的ですが、実装においては さらなる情報を追加する必要があります。
JDK1.2 / JDK1.1 との関係 - tomcat-dev メーリングリストの スレッドに基づくもの。
現在の RequestMapper のアーキテクチャと将来像 - carlos@aper.net のメールに基づくもの。
再ロード/クラスローダ - 実装を明解にする作業を行っているので、 これが終ったらドキュメントを追加する予定です。
要求メモ - 当面、実際のデザインと実装を明解にする仕事を 優先しなくてはいけないので、スタンドアローンのユーティリティに してください。
パフォーマンス - Tomcat 3.3 を実装するにつれ、遅くなる でしょうし、 変化していくでしょう。10月(Apache カンファレンス)前に 完全なものを 作る予定です。Tomcat の主な修正がパフォーマンスに どれほど影響を与えたか、またオーバヘッドについて常に注意を 払わねばなりません。
スタンドアローンコンポーネントすべての詳細なドキュメント。 あまり変わりそうにない、加えて、Tomcat に依存しないこことを説明 する必要のあるコンポーネントについてです。
特に、XMLMapper とその設定、JNDIの設定 に付いての説明
モジュールのライフサイクルの説明と図
認証モジュールの説明
Integration with apache - config generation,etc - unfortunately we don't know yet very well how to do it.
Apache との統合 - 設定の自動生成など - 残念ながら どのようにすればいいかについてまだ不明点があります。