Javaを紐解くための重点キーワード

米持幸寿
日本アイ・ビー・エム
2000/10/10
2001/7/6改訂


Java Servlet(サーブレット)


サーブレットの生い立ち

 Java Servletは、Webサーバ、つまりはHTTPサーバを、Javaアプリケーション・サーバに変身させるJavaテクノロジである。そのため、Java Servletは必ずといってよいほどCGIと比較される。

 現在でも、かなりのWebプログラマーが、Perl/CGIによるアプリケーション開発を行っている。筆者の周りでも、Webアプリケーション開発のプロジェクトが始まってから十分な人数のJavaプログラマーが見つからず、「CGI なら慣れているから」という理由で、CGI開発を進めるケースがいまでも見受けられる。

 CGIは、UNIXプログラマーにとって非常に分かりやすい構造だった。CGI プログラムは、UNIXでいうところの、いわゆる「フィルタ・プログラム=パイプ・プログラム」だからである。CGIプログラムは、基本的に1つのプロセスで動作する。入力として環境変数(GET方式/PUT方式)または標準入力(PUT方式)を使い、結果として標準出力を使うという点は、UNIX プログラマーにとって非常に受け入れやすい。また、コマンドプロンプトを使って動作確認もできるので、非常に便利だ。また、開発言語が UNIXプログラマーにはなじみの深いPerl であることも理由の1つであろう。

 Java ServletとCGIの違いについて理解するために、まずはCGIについて簡単に説明しよう。図1はクライアントからのリクエストがCGIに対してのものではなかった場合のサーバ側の動作を説明している。

図1 クライアントからのリクエストがCGIに対するものでなかった場合

 次の図2が、クライアントからのリクエストがCGIに対してのものだった場合のサーバ側の動作である。

図2 クライアントからのリクエストがCGIだった場合

 しかしながらCGIには欠点があるといわれている。それは主に次のようなことである。

  1. プロセス起動のためパフォーマンスが悪い
  2. ステートレス(複数のリクエストにまたがってメモリ上でデータを維持できない)
  3. コンピュータの機種によってPerlの互換性がない

Java Servletではこれらを克服している。

Java Servletの開発手順

 Java Servletは、ユーザー・プログラムを作成するときに、HttpServletクラスを継承したクラスを作成する。ここでは、便宜上これをユーザー・サーブレットと呼ぶことにする。これは、JavaAppletと同じような思想だ。オブジェクト指向では当たり前のやり方であるが、オブジェクト指向が身に付いていない人には分かりにくいかもしれない。

 ユーザー・サーブレットは、サーバ・プロセスである「サーブレット・エンジン」に、ユーザー・クラスとしてロードされ、基本的には1つの共通インスタンスを生成する。サーブレット・エンジンはそれ自身がJavaのプログラムである。該当サーブレットに対するHTTPリクエストは、このインスタンスに対してのスレッドとしてリダイレクトされる。これによって、HTTPリクエストの処理がユーザー・プログラムによって処理され、結果がブラウザに送られる(図3)

図3 Java Servletでは、ユーザー・サーブレットがサーブレット・エンジンにクラスとしてロードされ、1つのインスタンスを生成する。クライアントからのHTTPリクエストがあると、インスタンスからスレッドが起動されて処理が行われる。処理結果はブラウザに送られる

 この方法だと、プロセスの起動や終了処理がなく、スレッドであるため高速で、使用するリソースも少ない。さらに、インスタンスは一度ロードされるとメモリ上に維持されるため、アプリケーションの状態を維持することも可能である。これによりステートフルな技術であるといわれている。さらに、Javaであることから、非常に高い移植性を発揮する。

 サーバ・プロセスであるサーブレット・エンジンがユーザー・サーブレットを呼び出すルールは、基本的に次の順序で行われる。

  1. リクエストに対するサービス・メソッドが定義されているか確認する。例えば、GETリクエストに対してはdoGetメソッド、POSTに対してはdoPostメソッド、である
  2. doXXXメソッドが1つでも定義されており、リクエストに合致するメソッドが提供されていないとき、このリクエストはそのServletによって処理できないことを意味する。例えば、doPostメソッドはあるが、doGetメソッドのないServletを作った場合、そのServletはURL指定や <A HREF=> タグによって直接参照することはできない。必ずフォームを作成して呼び出す必要がある
  3. doXXXメソッドが1つもなく、serviceメソッドがあれば、それが呼ばれる

 ユーザー・サーブレットはサーバに呼び込まれたとき、インスタンスを生成し、そのインスタンスが共通に使われることになる。共通に使われるため、スレッド間で共有できるようなものはインスタンス生成時に初期化しておきたい。これを行うためには、initメソッドを使う。これもAppletに似ているので、分かりやすいだろう。initメソッドには、ServletConfigオブジェクトが渡されることになっており、このデータを使ってサーブレットに外部データを簡単に渡すことができる。例えば、RDBにアクセスするときのJDBC-URLなどをここで渡す。ServletConfig は、サーブレットのクラスファイルと同じディレクトリにクラス名「.servlet」というファイル名でXML形式にて置く。また、終了処理も当然ながら必要だが、これはdestroyメソッドで行う。

 以上を総括すると、サーブレット・プログラムの開発(コーディング)手順は以下のようにまとめられる。

  1. HttpServletを継承したクラスを作成する
  2. 必要に応じてinit、destroyメソッドを記述する(パラメータは決まっている)
  3. 必要に応じてservice、doGet、doPostといったメソッドを記述する(パラメータや例外は決まっている)
  4. コンパイルし、サーバの定められたクラスパス上に置く

リスト1 もっとも簡単なサーブレットソース

import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;
import java.io.*;
public class EasyServlet extends

HttpServlet {
 public void init( ServletConfig config ) throws  ServletException {
  super.init( config ); // ここで、必要に応じて初期化処理
 }

public void destroy() {
  super.destroy(); // ここで、必要に応じて終了処理
 }
 

public void service( HttpServletRequest req,  HttpServletResponse resp )
 throws ServletException, IOException {
  PrintWriter out = resp.getWriter();
  out.println( "<HTML>" );
  out.println( " <BODY>" );
  out.println( " <H1>カンタンなサーブレット!</H1>" );
  out.println( " </BODY>" );
  out.println( "</HTML>" );
 }
}

 旧来のJavaアプリケーション・サーバでは、サーブレットのクラスファイルはアプリケーション・サーバの特定のクラスパスに、JSP、HTML、JPG、GIFのようなコンテンツ・データはHTTPサーバのドキュメント・ルートに置かれるようにデザインされていた。

 しかし、J2EE対応サーバでは、これらのファイルはすべて*.WAR(Web Application Archive)といわれるJARファイルに入れて配布、配置が行われることになった。今後、サーブレットなどを開発するときには、クラスファイルと同じディレクトリでファイルの書き込みを行うような行為は避けるべきである。


セッション管理

 Java Servlet にはセッション管理機能が備えられている。本当の意味で、サーブレットのステートフルを支えているのは、このセッション管理機能である。Java Servlet の仕様は、Sun Microsystems社のJavaWebServer(JWS)で提供され、その後分離した仕様として公開された後、J2EEに吸収された。セッション管理はJWS 1.1 alpha時代から追加された考え方であり、システム(サーブレット・エンジン)がこの管理を行う。基本的な考え方は以下のようなものである。

 HTTPリクエストは、基本的にセッションレスである。1回のリクエスト/レスポンスでセッションが終了し、それ以降は関係が維持されない。そのため、複数の画面で構成されるようなアプリケーションを開発するとき、それぞれの画面での持続性を維持する機能をHTTPリクエストより上層で提供する必要がある(図4)

図4 HTTPリクエストは、基本的にはセッションレスである。HTTPには2つのリクエストが同じクライアントかどうかを確認する情報はない

 これを実現しているのがセッション管理である。セッション管理には、セッションIDといわれる自動生成されるストリングとセッション・オブジェクトといわれるクラス・オブジェクトが関連している。セッションIDによってWebブラウザとセッション・オブジェクトをひも付けし、セッション・オブジェクトに自由にデータを貼り付けることによってWebブラウザとユーザー・データを管理しようというものである。

 セッションIDは基本的にWebブラウザのクッキーに保存される。あるブラウザが初めてアプリケーション・サーバにアクセスしてきたとき、セッション管理機構がセッションIDを生成し、クッキーに保存する。次回以降のアクセスでは、このクッキーを読み出すことにより、そのWebブラウザが前にアクセスにきたことのあるものであることと、どのセッション・オブジェクトを利用しているかを認識する(図5)

図5 セッション管理の方法。基本的にセッションIDはWebブラウザのクッキーに保存される

JSP、Servletを使ったセッション管理

 Webブラウザがクッキーをオフにしていると、セッション管理がうまくいかない。セッション管理機構がセッションIDをWebブラウザ上に保存できないからである。携帯電話などのブラウザ機能でも最新のものを除いてクッキーが使えない。

 このとき、セッション管理を行う方法として「URL再書き込み」という方法がある。これは、JSPやServletのAPIを使って、リンクのURLやFORMのactionに指定されるURLにセッションIDを埋め込む技術である。通常「MyServlet」というServletが呼び出される場合、URLは、

http://hostname/servlet/MyServlet

のようになるが、URL再書き込みでは、

http://hostname/servlet/MyServlet$sessionid=xxxxxxxxx

のようになる。これはURLなので、HTTPリクエストのURL部分に一緒に書き込まれるため、これによってもセッション管理がうまくいく。

 では、なぜこの方法が標準ではないのだろうか。それにはきちんとした理由がある。

 HTTPプロトコルを、インターネット上で電子商取引に使うとき、暗号化が必要である。これには多くの場合SSLという暗号化技術が使われる。SSLはHTTPプロトコルの本文部分を暗号化する。HTTPリクエストの本文には、POST方式で作られたパラメータやクッキー情報が入る。GET方式でパラメータ渡しをする場合や、URL再書き込みによるセッションIDなどは、この暗号化に含まれない。このため、ネットワーク・トレースなどによるのぞき見行為に対して、URL再書き込みによるセッションIDは無防備であるといえる。SSLを使ったサイトを構築するためには、GET方式やURL再書き込みは向いていないということである。

 1回1回のHTTPリクエストは、スレッド処理としてServletに送られる。基本的には、同じインスタンスで、複数のWebブラウザからのHTTPリクエストが、スレッドとなって同時に処理される可能性がある。これらがどうやって別々のセッションデータを管理するのだろうか。

 サーブレットの処理ルーチンとなるサービス・メソッド(service、doGet、doPostなど)には、パラメータとしてHttpServletRequest と HttpServletResponseオブジェクトが渡る。そのリクエストを送ってきたWebブラウザとひも付けされたセッション・オブジェクトは、HttpRequest.getSession()によってリクエスト・オブジェクトのインスタンスから取得することができる。セッション・オブジェクトには、名前を付けたオブジェクトを自由に貼り付けたり、取り出したりできるので、これによってWebブラウザごとのセッションにまたがったデータをメモリ上で簡単に管理することができる。これには、putValue、getValue メソッドを使う(図6)

図6 putValue、GetValueメソッドを使ってWebブラウザごとのセッションにまたがったデータをメモリ上で管理することができる


Javaを紐解くための重点キーワード





Java Agile フォーラム 新着記事
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Java Agile 記事ランキング

本日 月間