1. はじめに

このドキュメントは Tomcat version 3.x の設計がどのようになってるか、 また、なぜそのように設計されたかの背景を文書化することを目的としています。 Tomcat はそれぞれの視点からコードやアイディアを提供してくれる大勢の人々に より長期にわたって改良、開発された結果、完成したものです。 Tomcat の コードの大半は昨年 2,3回は作り直されました。コードの修正にあたっては オフラインで数多くの実験やプロトタイプの開発も行われました。実際に適用 されているパターンやアイディアと実装を区別することが重要です。後に、 これらは他を変更するでしょうし、すでに多くの修正が加えられました。


2. 目標


3. デザインパターン

Chain of responsibility パターン

"一つ以上のオブジェクトに要求を処理する機会をあたえることにより、 送信するオブジェクトと受信するオブジェクトの結合を避ける"。 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 パターンの項も参照してください。

Strategy パターン

"アルゴリズムの集合を定義し、各アルゴリズムをカプセル化して、それらを交換可能にする"。

このパターンのおかげでオープンソース環境下で Tomcat が容易に進展するよう になり、開発も簡単になりました。Tomcat 3.0 のオリジナルの実装を分離し、 全般にわたる安定性を管理しながら個々のコンポーネントの再実装を行いました。 開発を始めたとき、ソースコードは何度も変更と修正が繰り返されたため非常に 読みにくくなってしまったにもかかわらず、驚くほど安定して、よくテストされて いるものでした。このパターンを適用することで、醜いアルゴリズムをモジュール化 し、さらに、よりよい実装を提供しました。

実際のアルゴリズムをコードから分離したので、問題解決の重要なポイントが 何であるかと依存関係を無くすことに注力できました。

Strategy パターンを利用すると、コードが準備された後に、各サービスの これまでのものに変わる実装を提供できるようになり(2,3 の認証モジュールと 3,4 のマッピングインターセプタが開発され、さらなる改良に入るところです。)、 安定性とパフォーマンスを重視した実装を決めることができました。

このようにして、開発するアプリケーションにもっともよく調和する コンポーネントを選べるようになりました。例えば、マッピングインターセプタ (リクエストを解析してマッピングルールを適用します。) ですが、メモリ使用 方法や実行速度を最適化したものを使えるようになりました。ISP なら、 かなりの数の Web アプリケーションを扱えるように特化したアルゴリズムを 選ぶでしょう。例えば、必要になったときのみ、そのアプリケーションのマッピング をロードするようなアルゴリズムです。(さらに頻繁にアクセスされるマッピングの キャッシュ管理なども考えられます。)

ネイティブなサーバに実装されている既存のアルゴリズムを利用する アプリケーションもあるでしょう。たいていの Web サーバには高度に調整された アルゴリズムがあります。(驚くことではありません。) そして、そのアルゴリズムは あまり多くのことをやらずして速く動くコードを手に入れたい不精なプログラマ にはすばらしいことでしょう。

このようなアルゴリズムは RequestInterceptor に実装されています。 Chain Of Responsibility パターンの項も参照してください。

Observer パターン

このパターンはコアとなるオブジェクト(Context, ContextManager)と モジュール(インターセプタ) 間の一対多の依存関係を定義しています。 コアオブジェクトが状態を変えた時に、それに依存するすべてのオブジェクトに 変化が自動的に知らされ、更新されます。

Tomcat の状態は実行時に変化します。この変化は状態を監視している モジュールに通知され、モジュールの内部データ構造を更新(できるかぎりの 最適化)します。このようにするとモジュールでツリー構造(JDK1.2 Collection API) や、これ以外の複雑な構造を使っていても、コア部分では特別なデータ構造を 使わずにシンプルなコードにしておけるメリットがあります。

また、コア部分を JDK 1.1 でも動作するように互換性を保つこともできる ようになります。同時に "最新の"機能をどれでも利用できます。 (ただし、これはアルゴリズム/データを呼出し側から分離し、Strategy パターンを 併用するとできるようになります。)

そして、コア部分はコードの可読性を損なうような最適化をしなくてもより シンプルに作ることができます。複雑な構造はモジュール、つまり Observer の中にのみ存在することになり、同期的に情報を維持します。

このような実装は ContextInterceptor に適用しました。(おそらくこの 実装は Tomcat 3.3 か 3.4 で標準的な Event/Listener に置き換えられる でしょう。) ContextManager, Context, Container は内部状態が変化する たびに通知すると、インターセプタが自身の内部状態を更新するようになって います。

Adapter パターン

あるクラスのインターフェースを 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 インターフェースを持っています。

Proxy パターン

" あるオブジェクトへのアクセスを制御するために、そのオブジェクトの "代理" または入れ物を提供します。" これが Tomcat の本体と Servlet、ユーザアプリケーションを分離するメインツールとなっています。 クラスローダに手を加えるような方法が拡張のために使われることもありますが、 このパターンは均整のとれたメカニズムであり、Facade パターンと組み合わせて 多くの利点を引き出せます。

これは、必要になるまで重い処理(String の生成など)を遅らせたり、コア部分で より効果的なインターフェースを使うことができる(Facade パターンを参照して ください)ので、パフォーマンスにおいても重要な役割を果たします。

tomcat.core の制約や要求と違っていても、Servlet API はアプリケーション 開発者が簡単に使えるようにまた、安全であるように設計されました。

Proxy パターンは JDK 1.3 においてタイプセーフなインターフェースの実装にも 使われています。(ダウンキャストは許されません。)

その理由は?もともとセキュリティを考慮したものですが、この パターンは多くのパフォーマンス最適化の中心的な存在であり、多くの機能を 提供します。

Facade パターン

あるクラスのインターフェースを別のインターフェースに変換します。特に、 アプリケーションを(正しい) 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 双方のサポートが可能になっています。


4. 主なコンポーネント ( tomcat.core )

Request/Response コンポーネント

このコンポーネントは、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 サーバが使うためのモデルに合わせることです。この構想は すばらしいでしょう。

OutputBuffer コンポーネント

Tomcat のバッファリングや char/byte 変換を完全に制御したかったというのが このコンポーネントを作った理由です。というのも、(実験によると) ここが もっとも重い処理のうちの一つだからです。このコンポーネントを用意したので、 外部の byte/char コンバータをプラグインすることも可能になりました。 ご存知のように、バッファからバッファへの多重コピーはほとんどのシステムで 重要なファクタであることが証明されています。このことは Tomcat 3.2 では 他にもっと大きな要因があったため明らかになりませんでしたが、限界に 近づくにつれ、最も重要な問題の一つであることが判明しました。

jasper と他のテンプレートシステムの統合部分にその機能の重要さが あらわれています。ここでは "static" なテキストが中間バッファを 使わずにダイレクトにサーバに運ばれてきて、バイナリ表現(エンコーディング)が プリコンパイルされることになります。

MessageBytes コンポーネント

このコンポーネントは 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 コンポーネント

Context コンポーネントは Web アプリケーションを表現するものです。 このコンポーネントには web.xml と server.xml で定義可能なすべての プロパティがあります。

現時点の実装は複雑なのでいろいろと手を加えて変更しなくてはなりません。

Context コンポーネントは contextMap() メソッドのコールバックが完了すると Request コンポーネントに関連付けられます。デフォルトではマッピングインター セプタ (デフォルトのインターセプタは SimpleMapper) がこの部分を実装して いますが、他のインターセプタは (最初のリクエストがあったときに、動的に コンテクストに追加して ~user の URI で各ユーザのコンテンツ提供をサポート するなどの)外部機能を提供します。

Container コンポーネント

このコンポーネントはあるプロパティの一般的なセットを共有する URL グループを表現しています。これは Apache のディレクトリ単位の設定に 似ています。 Servlet API の仕様では "標準"プロパティは(Servlet の マッピングで指定される)ハンドラとセキュリティ関連のプロパティです。

Container コンポーネントは requestMap() メソッドの処理が完了すると Request コンポーネントに関連付けられます。SimpleMapper はこのような処理の "コア" 実装で、プレフィックスや正確でしかも拡張性のあるマッピングの サポート機能を提供します。他のインターセプタは(JspInterceptor のような)ある サブセットの最適化されたマッピングやスキーマのカスタムマッピングの実装を 提供します。

ここはかなり重要なオペレーションです。(おそらくもっとも高価な 処理にちがいないのですが、残念ながらこれが原因となるパフォーマンス低 下より以前に他のいくつかのホットスポットをまだ取り除かなくてはいけません。) より洗練されたデータ構造や(ツリーなどの)アルゴリズムを使ったこれまでとは違った 実装が期待されるところです。 "進化"についての解説を参照してください。

Tomcat 3.3 ではマッピングの後でコンテキストやコンテナ毎にインターセプタを 指定出来るようになります。 Tomcat はコンテナがどれで、リクエストがどの コンテナに属しているかを知っていて、ある特別な URL に対してのみ定義されている 特別なインターセプタを起動できます。これは一般的な Web サーバが提供する機能に 似ています。(処理の順序は NES(Netscape Enterprise Server) に似ています。)

ContextManager コンポーネント

Tomcat が実行を開始するメインのエントリポイントです。このコンポーネントは モジュールのアクティビティを順序良く整理します。

Tomcat が組み込まれているアプリケーション(Web サーバ、一般的な アプリケーション、Tomcat スタンドアローンサーバ)は実行を制御します。 組み込まれている Tomcat は Request/Response アダプタを生成し、リクエストを 処理する ContextManager を使うようなアダプタを要求します。



XXX おそらく、Server か TomcatContainer という名前の方がいいのでしょう。

Interceptor コンポーネント

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 はいくつものフックを定義しています。他に必要に応じて 追加されるのは:

です。

Helpers ( tomcat.helpers ) コンポーネント

コア部分を出来るだけシンプルに、また可読性を保つために、いくつかの 機能が(インターセプタを使わずに)コアの中で実装されています。これらは 特殊なヘルパーとして実装されています。例えば、フォームデータの読み込みや エンコーディングなどです。ヘルパーを機能別にグループ分け、簡単に改造でき ることを指標にしました。 Tomcat の他の部分に与える影響を最小に保ちながら 書き換えや置き換えが可能です。これはチームでの開発作業や新たに加わる コントリビュータがコアを壊すのではないかという危惧を抱かずに Tomcat の 改良を進められる一つの方法です。


5. 実行パス ( ContextManager and core )

初期化

XXX サーバの初期化と設定コンテキストの add/init/shutdown/remove など。

Request の処理

Web サーバへの各リクエストは Web アダプタが 受け取ります。このアダプタは Request/Response アダプタが提供することに なります。次に、ContextManager.service() メソッドの実行が開始されると、 Tomcat のコアはリクエストの処理を始めます。(Tomcat が Web サーバではない アプリケーションに組み込まれたときには)HTTPトランザクション以外のたとえば、 サブリクエストや Web ベースではないリクエストでも同様のパスを実行します。


リクエストを処理する過程で Tomcat は通知をその処理段階を 監視しているモジュール("Observer")に送ります。例えば、 モジュールがトランザクションコンテキスト(J2EE インターセプタ)をセット あるいは装備できる機会を与えるために通知を送ります。(つまり、個々の 操作がきめ細かなチューニングを行うための間をとるわけです。)

このモデルは Apache や IIS, NES で使われているのと同じものです。 (すべての通知について交わりではなく結びとなるように表現しようと努力しました。) なぜか?というとパフォーマンスを 上げることがもともとの理由です。また、このモデルは有名でありよく練られている という事実があり、他のモデルよりも信頼できるのも理由になっています。 "Strategy" パターンを採用するとコード体系が (Tomcat 3.0 に 比べると)大幅に改善され、いろいろな最適化を施せるようになることが わかりました。

その他の理由には Tomcat に Web サーバとして動作できる機能を追加したアイディアに関連するものがあります。 これは Web サーバを Java のクリーンな API を使ってモジュール(フィルタ、 SAF、フック)として開発する余地を与えることです。Web サーバの実装方法は いくつもありますが、この設計は Apache, IIS, NES をモデルにし、双方向で コードを再利用できるようにしました。


6. インターセプタ ( tomcat.context, tomcat.request)

標準モジュール

カスタムモジュールの追加

進化による革命

"Strategy" パターンを利用すると別のアルゴリズム提供が容易に なり、コアモジュールに影響を与えずに実装することもできるようになります。 いずれ、新しい "ストラテジー" がテストされることになるでしょうし (最初は実験的ですが、次の段階で製品として)、ゆくゆくは "コア" モジュールに置き換わることもあるでしょう。

Tomcat のコアはリクエストを処理する最低限の役割だけを果たすように 設計されています。ユースケースや様々なモジュールの要求に基づいてコアを "進化させながら" いくつもあるメソッドや構造体も出来るだけ 小さくなるように努力しました。バージョンが 3.3 か 3.4 になるころには 安定したコアになっていると期待しています。

これらは度重なる機能追加やバグ修正のためにとても複雑で醜い(!) コードだったTomcat 3.0 からスムーズな進化を遂げたメソッドです。 リファクタリングがいかに素晴らしいかを証明するよい例です。




7. 再利用可能なコンポーネント ( tomcat.util )

Tomcat の開発においてはコードが出来るだけスタンドアローンのコンポーネント となるように、また、Tomcat のコアに依存しないようにしました。例えば、 クラスローダやセッションマネージャ、TCPサーバ、認証ですが、これらは別の アーキテクチャにも容易にとりいれられるようになっています。これらはコアの インターフェースへのリファレンスを持っていません(持つべきではありません)。

このような設計にした理由はいくつもありますが、まず、Tomcat は Servlet API を実装することを第一の目的とするべきであり、動作の問題点を追求するべきでは ないと考えます。また、Tomcat を構成するコンポーネントは他のサーバで も十分使えるように一般化されているべきとも考えています。

たとえば、セッションマネージャですが、これは大抵の Servlet コンテナに プラグインで追加できるようになっています。セッションマネージャはある オブジェクトデータベースの形になっているなど、コンテナの内部構造について 詳細な知識までもを持つ(持つべきではない)と考えます。特定のコンテナに 依存しないように、また、アダプタを使って別のコンテナにもフックをかけられる ようにコードを書くことにかなりの時間を費しました。

スレッドプール




8. セキュリティ

XXX は別のドキュメントに移動しました!

プロクシとファサードマネージャはセキュリティを保証するうえで、 たいへん重要な役割を担っています。これらは Web アプリケーションが Tomcat 自身との通信を制御する方法を提供することでセキュリティを 保証しています。このような方法をとると特殊なクラスローダのメカニズム (と神経質な管理者には思える)を問題視されても、できるだけ多くの情報を 提供すれば、プロクシはとても効果のあるデザインパターンであると 提言できます。

tomcat.core はある (Java の)特権下で動作しますので、コードが 安全であるように記述されていること、ユーザが任意のメソッドを 実行できないようになっていることに注意を払わねばなりません。このため、 どのメソッドを public にするか、また、外部から内部のオブジェクト インスタンスにどのようにアクセスするかについては厳しい検討が要求されます。 FacadeManagerはエントリへの唯一の接点であるべきです。

アプリケーションがパフォーマンスを拡張するのに必要な内部 API の 提供もまた要求されます。例えば、Jasper が内部バッファを利用できるように するなどの拡張です。

内在するリスクと問題

分離。ひとつのアプリケーションが HttpServletRequest への リファレンスを Hashtable のなかで維持できます。もしもサーバが 再帰的に使われると すると "できの悪い" アプリケーションが すべてのアクティブなリクエストへの参照を亡きものにするかもし れません。あるいは、他の Web アプリケーションのパラメータや 情報にアクセスできるようにしてしまうかもしれません。
Tomcat 内部では HttpServletRequest はプロクシであり、実際の リクエスト に対してのファサード(窓口)です。Tomcat は Request xオブジェクトを再利用していて、また、(設定のオプションにもより ますが) 新たに、HttpServletRequestFacades を各リクエストに対して 生成します。
コンストラクタへのアクセス。アプリケーションが許可されて いないアクセスを見つけ出すために既存の内部 API を利用し、また 実行できます。
内部 API にアクセスする唯一の方法は context/request の属性を 使うことです。ただし、"信頼できる" アプリケーションだけが メソッドを実行できるようになっていて、信頼できることを意味する フラグが セットされていない場合は null を返すようにしています。 他の方法で 内部 API にアクセスできてしまう方法がないことを確実に するために、また、public なコンストラクタやメソッドを出来るだけ 減らすために、さらなる検討が要求されています。加えてオブジェクトが 生成される 際には(JDK1.1 互換の)セキュリティチェックのメカニズムを 追加する必要もあります。(例えば、JDK1.1 では中身のないメソッドで TomcatSecurity オブジェクトを追加し、JDK1.2 には 1.2 の セキュリティシステムで TomcatPermission を使うようにします。)
DoS - 巨大なデータが POST される問題
Tomcat は POST でリクエストされると HTTP のボディ部分を すべて読みこもうとします。上限を設けるか、少なくとも悪用に対して 警告を出す必要が あります。
DoS - 一般
DoS 攻撃の方法はいくらでも考えられます。攻撃であるかどうかを 確認し、その影響を低減するメカニズムを提供する必要があります。

9. 組み込みの Tomcat

ContextManager を生成し、スタート、ストップさせるには Tomcat API を 使ってください。コンフィギュレーション API を統合するのはとても簡単です。 正しいオブジェクトが生成され、setterメソッドが実行される範囲においては Tomcat はどのようにセットアップされたかについては考慮しません。

統合においては "Adapter" パターンが使われています。 インターセプタは Tomcat 内部へのフックを提供して、リクエスト処理の すべての局面で制御することができます。


10. Web サーバへの統合

商用の Web サイトの大多数が 4つの代表的な Web サーバのどれかで 動作しています。Tomcat はそれらの Web サーバと連携してうまく動作 することを確認しようとしています。結局、Servletは Web アプリケーションの 道具として使われているわけです。:-)

現在のようなアーキテクチャにしましたが、それでもなお、数えきれない ほどの問題があります。例えば、Servlet API 2.2 で定義されたマッピングは 既存の Web サーバのマッピングとは違います。フォームベースのログインを 統合するのはとても難しいことです。(同じ認証方法を使って Java ではない リソースまでも保護するためにはネイティブコードがかなり必要になります。)

問題点のほとんどのものはいまだにかなりの労力が必要な状態です。しかし、 現在のアーキテクチャは問題を扱えるようになっているという希望をもって います。もっと大きな目標はインターセプタを既存の Web サーバの拡張へと 変貌させることです。そうなれば フック/モジュール/SAF/フィルタは C (や mod_perl) に代わって Java で開発されるようになるでしょう。

Tomcat とその他の Web サーバ

Tomcat のデザインは Apache のモジュール/フックや ISAPI, NSAPI の機能をベースにしています。すべてに対して何が一般的であるかを 考える際には既成概念に捕らわれないように注意しました。そう遠くないうちに、 Tomcat はより多くのフックを持てるようになるでしょうし、他の Web サーバと同レベルの拡張性を提供できるようになるでしょう。 "必要になったときに" のルールに従い、フックを追加する ようにしてきました。これまでは数多くのコールバックがまだ追加されて いるところです。

Apache や IIS, NES のドキュメントと、そしてコードも同じように 読んでください。違う名前を使うことと Java スタイルのインターフェースで あることを除けば、モデルはまったく同じです。

マッピングは以下のようになっています。

Tomcat

Apache

ISAPI

NSAPI

Other

Request / Response

request_rec
conn_rec




ServletWrapper

Handler




Context

server_rec /
per_dir_config




Container

per_dir_config




Interceptor

Module / hooks




ContextManager








このドキュメントに欠けているもの: