サーブレット昔話

この記事は Java EE Advent Calendar 2017 の 21 日目です。

今日はまたお話回です。

今回はサーブレットの黎明期について取り上げたいと思います。サーブレット登場当時、私は大学生でその存在を知っていましたが、同時にあまり興味がなかったこともあって (ちょうど専攻分野の建築環境工学・都市環境工学が面白くなってプログラミングから遠ざかっていた時期と重なります)、リアルタイムではそれほど多くの情報を得ていなかったことをはじめにお断りしておきます。

サーブレット前史

World Wide Web (WWW) が CERN の Tim Berners-Lee によって開発され、初めてインターネットに公開されたのは 1991 年のことになります。最初に開発された Web サーバーである CERN httpd は静的コンテンツしか扱えませんでしたが、1993 年に登場した NCSA httpd にはコンテンツを動的に生成するいくつかの仕組みが備わっていました。そのうちの 1 つ、Common Gateway Interface (CGI) は、Web サーバーから外部のプロセスを呼び出し、そのプロセスの標準出力をコンテンツとして返すことができる機能でした。CGI の登場により Web アプリケーションというものが発生し、インターネットの商用開放によって爆発的に普及することになります。NCSA httpd は CGI の普及とともに大きくシェアを伸ばしますが、メンテナンスの停滞から派生版の Apache HTTP Server (1995 年) に取って代わられてゆきます。その後に登場した Web サーバーの多くも CGI をサポートし (実は Apache Tomcat にも CGI が備わっています)、NCSA httpd の独自仕様だったものがデファクトスタンダードとなりました。

CGI は画期的な仕組みでしたが、いくつかの欠点も抱えていました。

  • CGI はリクエストのたびに外部プロセスが呼び出されるため、1990 年代のコンピュータの性能ではプロセス起動のオーバーヘッドが無視できませんでした。特に当時は Unix のシェルスクリプトや Perl スクリプトといったインタプリタが多用されていたため、その傾向が顕著でした (パフォーマンス改善のため C などのコンパイラ言語で実装し直すケースも少なくありませんでした)。これを改善するために、起動したプロセスを使いまわす FastCGI や、スクリプトのインタプリタを Web サーバーと直結させる mod_perl 等が開発されます。
  • CGI から呼び出されるプロセスは最終的な出力をすべてレンダリングしなければならないことが挙げられます。特に動的な HTML コンテンツを生成する場合、タグの出力処理は相応の負担になりました (CGI で Perl が好まれて使用された背景には、文字列操作が容易な Perl で HTML 出力にかかる手間を軽減させる意図がありました)。なお、私は当時 Sun OS 4 上の C コンパイラで CGI のプログラムを作成していましたが、これはパフォーマンスを考慮したものではなく、単純に当時の私が Perl を知らなかったからです。これに対する抜本的な対策として考案されたのがいわゆるテンプレートエンジンであり、代表的なものが PHP (1995 年) です。
  • CGI の処理は 1 回の呼び出しで完結しなければならず、複数回の呼び出しをまたいで状態を保持することができません。これは CGI の欠点というよりは HTTP そのものが持つ制約でもありますが (状態を持たない故に HTTP は簡潔な仕組みになったとも言えます)、Web アプリケーションとしては状態を持てないことが不利に働くこともあります。例えば、ネットショッピングのショッピングカートのような仕組みは、複数回かつ複数プログラムにわたる呼び出しで状態を保持できないと成り立ちません。この問題を解決するために導入されたのが Cookie という仕組みですが、当時はすべての Web ブラウザが Cookie に対応していたわけではなく、また Cookie に対応した Web ブラウザであってもユーザー設定により Cookie を無効化されることもあり、確実な方法とは言えませんでした。

サーブレットの登場は、はっきりとは覚えていませんが、1998 年頃には最初のバージョンが登場していたと記憶しています。CGI が登場してから約 5 年後のことになります。

サーブレットと CGI

サーブレットが採用し、そして Struts の流行によりサーバーサイド Java で一般的となったリクエスト・レスポンス・モデルは、原理的には CGI とほぼ同じものです。言い換えると、サーブレットと CGI は類似技術ということになります。

サーブレットでは CGI の低レベル API 故の複雑さを、Java 技術とオブジェクト指向プログラミングで改善したものです。

サーブレットにはリクエストとレスポンスをハンドリングするのに最低限必要な機能セットを定義した Servlet インタフェース、Servlet インタフェースの汎用的な実装である GenericServlet クラス、そして GenericServlet を HTTP に特化させた HttpServlet が標準で用意されていました (後の JavaServer Faces では HttpServlet を拡張した FacesServlet がコア部分となっています)。サーブレットの設計では典型的な継承中心のモデリングがなされていたことが伺えます。

私のような CGI からサーブレットに移行した者にとって、サーブレットでは CGI のようなヘッダー文字列の出力処理を記述しなくてよいことが大きな驚きでした。CGI プログラミングではこのヘッダー出力がかなりの負荷になっており (Perl が CGI の標準的言語として使われるようになった背景には Perl であればヘッダー文字列の編集が比較的容易で、さらには CGI.pm というユーティリティが用意されていたことが挙げられます)、それがなくなるだけでもプログラミングはだいぶ楽になりました。

サーブレットでは、ランタイム環境であるサーブレット・コンテナがプロセスとして動作し、各サーブレットはその中のスレッドとして動作するよう設計されていました。スレッド生成のオーバーヘッドはプロセスのそれに比べるとはるかに小さいため、パフォーマンス向上に大きく貢献しました。マルチスレッド環境では通常、スレッド間の協調動作が課題となってきますが、サーブレットはリクエストごとに生成され独立したスレッドで動作するため、そうした問題とも無縁でした。Java EE サーバーがリクエストを受け取ってそれをサーブレットに渡すまでの過程は、現在では複雑になっていますが (「C10K 問題」で検索してみてください)、サーブレットから見た限りでは特に変化はありません。

サーブレットが CGI よりも優れている点として、HttpServlet に限定されますが、状態を保持する仕組み (セッション・オブジェクト) が備わっていることが挙げられます。CGI では専ら Cookie に頼っていましたが、サーブレットではサーブレット・コンテナで状態を保持できるようになっています。状態を保持してもそれを Web ブラウザと共有できなければ意味がないのですが、それについてはセッションの ID (JSESSIONID) を Cookie で保持するか、Cookie が使用できない場合にはクエリ文字列に付加することで、それを可能としています。

Java Server Pages の導入

サーブレットは CGI と比較すると Web アプリケーションを容易に構築することが可能となりましたが、出力の HTML をすべて動的に生成する手間についてはそれほど大きくは変わりませんでした。サーブレットが登場した時点で、PHP や Active Server Pages (ASP) といった、HTML のひな型にデータを流し込んで出力 HTML を生成するテンプレート・エンジンが既に登場していました。これらに対抗して開発されたものが Java Server Pages (JSP) です。

JSP は、厳密に言うとテンプレート・エンジンではありません。JSP は HTML のひな型に Java のコードを含める、ASP に酷似した形式のファイルで画面を記述しますが、実行時には同じ動きをするサーブレットに変換して、それを実行する方式を採っていました。JSP からサーブレットへの変換には、Apache Tomcat の Jasper をはじめとする JSP エンジンが用いられ、通常 JDK (JRE ではなく) を必要としました。JSP は実行時に実質的なコンパイルが行われるため、純粋なテンプレート・エンジンと比較して実行速度において不利でした。それでもサーブレットよりも容易に画面を実装できる手段であり、Java EE 5 までは代替技術がなかったこともあり広く用いられました。Java EE 6 になって Facelets が JSP 代替技術として採用され、Java EE 7 の構想初期段階では JSP を Java EE から削除することも検討されましたが、結局は現状維持とされ、Java EE 8 に至るまでその地位を保ち続けています。

Model 1 と Model 2、そして Struts

JSP は HTML のひな型に Java のコードを含めることが可能です。これをスクリプトレットといいますが、スクリプトレットを多用すると画面とロジックが密接に結びついてしまい、時としてメンテナンスを困難にさせてしまいます (これは ASP や PHP にも共通して言えることです)。そこで当時の Sun Microsystems では、Smalltalk の Model-View-Controller (MVC) アーキテクチャを参考に、JSP からロジックを分離して View に見立て、Controller としてサーブレットを置き、両者を JavaBeans の Model で連携するアーキテクチャを提案します。ロジックの動作によって得られた結果は、Controller であるサーブレットに伝わり、そこから Model である JavaBeans によって View の JSP へと伝えられます。ただし、オリジナルの MVC アーキテクチャでは Model の変更が View に対して通知されるのに対して、Sun の提案では View である JSP が Model である JavaBeans の状態を確認する (つまり Model-View 間のデータの流れが逆になる) ため、厳密には MVC とは言えないところがあります。そのため、この Sun の提案は Model 2 アーキテクチャと名づけられました。一方でスクリプトレット主体の方法を Model 1 アーキテクチャと呼んで区別しました。実際のところ Model 2 アーキテクチャという呼び名はそれほど普及せず、多くの開発者は MVC と呼んでいます。

この Model 2 アーキテクチャに準拠したアプリケーション開発フレームワークとして一世を風靡したのが、現在ではレガシー Java システムの代名詞ともなった Apache Struts (Struts 0.x~1.x) です。サーブレットと JSP で Model 2 アーキテクチャ準拠のアプリケーションを開発しようとすると、幾分ぎこちない上に相応量の定型処理が発生していました。Struts はそれに対して明確な答えを持っており、オープンソースでもあったことから拡張も比較的容易でした。多くの開発者が Struts を受け入れ、各々のやり方で Struts をカスタマイズしながら普及させてゆきました。Struts 自身は 2007 年頃から更新されておらず、その数年後に正式な開発終了が宣言されましたが、現在に至るまで使用され続けています。

Struts 2 は Apache Foundation に寄贈された WebWorks という Web アプリケーション・フレームワークを Struts 風のネーミングにリファクタリングしてリリースしたもので、ここで取り上げた Struts とは本質的に異なります。

Struts は多くのライブラリを発明しました。Apache Commons のいくつかは Struts 発祥のライブラリで (Log4J も Struts 標準ロガーとして名を挙げたコンポーネントです)、後に JSTL として採用される JSP のタグライブラリも Struts のタグライブラリをベースに開発されたものです。タグライブラリは Model 2 アーキテクチャが目指した画面とロジックの分離をさらに強固なものし、後の JavaServer Faces (JSF) にまで影響を与えています。


明日は @suke_masa さんです。