Fujitsu The Possibilities are Infinite

 

Java技術によるWebアプリケーション開発入門
4章:プログラムの配備と設定情報


Servletでは業務を遂行するリソース(プログラム群)を所定の場所に所定の構造で格納します。 ここではその構造を示します。 合わせてServletアプリに係わる様々な情報を格納する配備記述子とそこに設定する情報についても説明します。

ドキュメントルート

Servletのリソースはアプリケーション単位で格納されます。 ここでアプリケーションとは一まとまりの業務の単位のことです。 これについては前章の(注7)をご覧ください。 各アプリケーションのリソースを格納するディレクトリの先頭をドキュメントルートと言います。

ドキュメントルートの下にはhtmlやイメージファイルなど外部に公開するリソースとともにWEB-INFという名のディレクトリを配備することになっています。 WEB-INF配下のファイルは外部のクライアントからは見ることができません。 Servletアプリなど外部に公開しない情報はここに格納します。

この様子をTomcat(注8)の例で示します。 Tomcatではwebappsというディレクトリの下に各アプリケーションのドキュメントルートを配置します。 以下の例ではApplとWebShopの2つのアプリケーションが配備されています。 Applの内容は示されていませんが、WebShopと同じようなディレクトリ構成になっています。


図解: アプリの配置

ここでドキュメントルート直下にWEB-INFディレクトリは必須ですが、それ以外のディレクトリの名前や配置方法は必須ではありません(htmlやimageディレクトリについてはServletの規約書には it is recommended, but not required. と書かれています)。

(注8) Tomcat(トムキャット)はServletコンテナを実装したアプリケーションサーバソフトです。 Jakarta(ジャカルタ)プロジェクト(http://jakarta.apache.org/)からオープンソースとして公開されています。 TomcatはSunからJava Servlet技術の互換性を確認するための公式リファレンス(標準)として認定されています。

WEB-INFディレクトリ

前述のようにドキュメントルートの直下にはWEB-INFディレクトリが必要です。 WEB-INFには次のようにclassesディレクトリ、libディレクトリ、およびweb.xmlファイルを配置します。


図解: WEB-INFの中
  • classesディレクトリ…プログラマーが作ったServletアプリをコンパイルした結果のクラスファイル(xxx.class形式のファイル)を配置します。
  • libディレクトリ…Javaのjar(注9)ファイルを配置します。 Servletアプリは直接classesディレクトリに格納せず、jarファイルとしてパッケージ化してこのディレクトリに格納することもできます。
  • web.xmlファイル…Servletアプリにかかわる様々な情報が設定されたファイルです。 このファイルは配備記述子(Deployment Descripter)と呼ばれます。

1つのServletアプリには多くのファイルが含まれます。 例えば上の例で示したWebShopアプリにはhtmlやイメージファイル、およびWEB-INF配下のクラスファイル群やweb.xmlファイルなどが含まれます。 war(Web ARchive)ファイルはこれら一式をまとめてzip形式で圧縮したものです。 このファイルを、アプリケーションを配備するフォルダ(Tomcatならwebapps)直下に置いておくと、コンテナの起動時に自動的に展開されます。

(注9) jar(ジャーと読む)はJava ARchiverの略で、複数のファイルをzip形式で圧縮したものです。 jarはJavaで標準的に使用されている圧縮ファイル形式で、Javaのライブラリなどがこの形式で提供されます。 この形式で格納しておけばJavaが自動的に展開して実行してくれます。

配備記述子(web.xmlファイル)

配備記述子はXML(Extensible Markup Language)で記述されており、次のようにXML宣言、DTD宣言、本体の部分からなっています。 DTD(Document Type Definition:文書型定義)とは記述の順序や項目名を定めた定義書です。


図解: 配備記述子

配備記述子の本体は<web-app>で囲まれています。 この<web-app>をルート要素と言います。 本体の記述要素はDTDで定めた通りの名称と順序でなければなりません。 本体に設定する主な情報は次の通りです。

  • Contextパラメーター......アプリケーション全体に共通の初期値を設定する
  • リスナー......イベント発生時に通知されるクラスの名前を設定する
  • Servlet定義......Servletの登録名、クラス名、初期値などを設定する
  • Servletマッピング......このServletにアクセスできるURLを規定する
  • エラーページ......エラー発生時に自動的に表示するページを指定する
  • セキュリティ......セキュリティ制約や認証方式を指定する

以下では配備記述子本体に設定する主な情報について説明します。

Servlet定義

Servlet定義ではServletの正式名と、そのServletが呼ばれたときに実行するServletアプリのクラス名を指定します。 クラス名にはWEB-INF/classesからの相対パス名が含まれているので、コンテナは実行すべきアプリを特定できます。 また必要ならそのServlet固有の初期値も設定できます。 以下にServlet定義の例を示します。 <servlet>要素はルート要素<web-app>の直下に記述します。


図解: Servlet定義

<servlet-name>要素にはServletの正式名称、<servlet-class>には完全なクラス名を定義します。 上の例ではcatalogというServletが指定されたら、そのプログラムはcom.abc.Catalogにあることを示しています。 <init-param>要素については別項で説明します。

次項で説明するServletマッピングではServletの正式名称とURLとの対応を定めているので、コンテナはServlet定義とServletマッピングの正式名称を突き合わせてURLからクラス名を割り出すことができます。 <servlet>要素は複数個記述することができるので、あるURLが来たらAクラス、別のURLが来たらBクラスのように、URLによって処理するクラスを指定できます。

Servletマッピング

HTTPでhtmlファイルをアクセスするときは、URLでは対象ファイルが格納されているディレクトリをそのまま指定します。 例えば次のURLではxyz会社のサーバのabcディレクトリ配下のdefディレクトリ配下のindex.htmlファイルをアクセスしています。


図解: URL

しかし、これでは外部の悪意ある人にサーバ側の構造が全部わかってしまうので、Servletではアプリが配備される場所とは無関係にURLを指定できるようにしました。 この対応を示すのがServletマッピングです。 Servletマッピングを使うと、サイトでServletを使っていることを隠すことができます。 また任意のURLを持つ既存のページをそのままServletで置き換えることもできます。 <servlet-mapping>要素はルート要素<web-app>の直下に記述します。


図解: Servletマッピング

Servletマッピングでは * を使って複数のURLを受け入れることもできます。 例えば<url-pattern>に /catalog/* と記述すると /catalog/abc や /catalog/cde/fg といったURLも受け入れることができます。 また *.xyz と記述すると拡張子がxyzのURLをすべて受け入れることができます。

Servletごとの初期値の設定と取得

DBと接続するにはログインIDやパスワードなどが必要ですが、これをプログラムにハードコーディングしてしまうと後の変更が大変です。 そこでこのような値は配備記述子に設定しておき、プログラムが実行時にそれを読み込むようにしておくことで、プログラムと設定値の分離ができるようになっています。

設定の例を示します。 <servlet>要素の下位の<init-param>要素の中に<param-name>と<param-value>のペアで初期値を記述します。 以下の例では"login"と言う名前で"torus"と言う値が、"password"と言う名前で"pass-me"と言う値が設定されています。


図解: 初期値の設定

これらを取得するにはgetInitParameterメソッドを使用します。 このメソッドはServletConfigインターフェースで定義されていますが、3章の冒頭で述べたようにこのインターフェースはServletアプリが継承しているHttpServletクラスに実装されているので、そのまま(頭に xxx. を付けずに)呼び出すことができます。 以下のようにすると変数logには上で設定したtorusがセットされます。


図解: 初期値の取得

<param-name>の方を取得するにはgetInitParameterNamesメソッドを使います。


図解: <param-name>の取得

アプリケーションに共通の初期値の設定と取得

アプリケーション全体で共通の値を設定することもできます。 それはに配備記述子の<context-param>要素の中に<param-name>と<param-value>のペアで記述します。 <context-param>要素はルート要素<web-app>の直下に記述します。


図解: アプリケーション共通の初期値の設定

ここに記述された情報を取得するにはServletContextインターフェースのgetInitParameterメソッドを使用します。 メソッド名は同じですが、前項で説明したServletConfigインターフェースのgetInitParameterメソッドとは別物なので注意してください。

このメソッドを使うには、次のようにまずServletContextのオブジェクトを取得して、それをもとにgetInitParameterを呼び出します。


図解: アプリケーション共通の初期値の取得

こちらにも<param-name>を取得するgetInitParameterNamesメソッドがあります。

getParameterとgetInitParameter

getInitParameter系と似た構造のメソッド群に、2章で説明したgetParameter系(画面の入力値取込)とgetHeader系(HTTPのヘッダー情報取込)があります。 これらには以下のように3種類のメソッドがありましたが、ここで説明したgetInitParameter系は2種類しかありません。 すなわち、複数の値を取り込むメソッドが無いのです。 これは配備記述子がxmlで記述されており1つの名前に複数の値を定義することはありえないからです。

画面入力値取込 ヘッダー情報取込 配備記述子設定値取込 内容の説明
getParameter getHeader getInitParameter 値を1つ取り込む
getParameterValues getHeaders 無し 複数の値を取り込む
getParameterNames getHeaderNames getInitParameterNames 名前を取り込む

配備記述子に設定する情報のまとめ

先に説明したように配備記述子の各要素の順序はDTDで厳密に決められています。 以下に次章で説明する要素も含め本稿に出てくる要素をまとめて示します。


図解: 配備記述子の要素

配備記述子には以上の他にも多数の要素が定義されています。 詳細は(注3)でご紹介したServlet仕様書を参照してください。

確認問題

(1)

WEB-INFディレクトリはどこに配置されますか。

(2)

WEB-INFと同レベルのディレクトリにはどんなファイルが格納されますか。

(3)

WEB-INF配下に配置されるディレクトリやファイルには何がありますか。

(4)

プログラマーが作ったServletアプリをコンパイルした結果を格納するディレクトリの名称は何ですか。

(5)

jarファイルを格納するディレクトリの名称は何ですか。

(6)

ある業務を行うアプリケーションを構成するファイルを全部集めて圧縮したファイルを何と言いますか。

(7)

配備記述子はどこに配置されますか。 またそのファイル名は何ですか。

(8)

配備記述子のServlet定義でServletの正式名称を記述する要素名は何ですか。 また完全なクラス名を記述する要素は何ですか。

(9)

配備記述子のServletマッピングで受け入れるべきURLを指定する要素名は何ですか。

(10)

配備記述子に初期値を設定する際にペアで使用する要素名は何と何ですか。


解答

(1) ドキュメントルート直下
(2) 外部に公開するhtmlファイルやイメージファイル
(3) classesディレクトリ、libディレクトリ、web.xmlファイル
(4) classesディレクトリ
(5) libディレクトリ
(6) warファイル
(7) WEB-INFディレクトリ直下、web.xml
(8) <servlet-name>、<servlet-class>
(9) <url-pattern>
(10) <param-name>と<param-value>

コラム(ServletConfigとServletContextの位置づけ)

本章では名前のよく似たServletConfigとServletContextの2つのインターフェースが出てきました。 これらを整理しておきましょう。

繰り返しになりますが、一まとまりの業務を遂行するためのプログラム群をアプリケーションと言い、1つのアプリケーションには複数のServletプログラムを含ませることができたのでした(前章(注7)参照)。 ここで、ServletContextはアプリケーションに1つだけ存在し、ServletCongigは各Servletプログラムに1つだけ存在します。

contextは文脈、背景と言う意味ですからServletContextはServletが動作する背景、すなわちアプリケーションのことを指すと言って良いでしょう。 一方configはconfiguraionの略で、ここでは設定と言う意味で使われています。 したがってServletConfigはServletごとの設定に係わるもの言うことになります。

ところで当初はServletContextはサーバ全体の情報を取得する手段でした。 つまりサーバ全体で1つだけ存在し、サーバ全体の状態を調べるときに使うオブジェクトでした。 ところがAPIの2.2版で仕様が変更になりServletContextはアプリケーションごとに存在し、各アプリケーションの管理もするようになったのです。

したがって、ServletContextには、すでに述べたgetAttribute系やgetInitParameter系のように各アプリケーションごとの情報を扱うメソッド以外に、以下のようなサーバ全体や他のアプリに関わるメソッドが混在しています。

getContext(URL) 他のアプリのServletContextオブジェクトを返す
getResource(path) 指定されたパスにマップされたリソースへのURLを返す
getServerInfo() Servletコンテナの名前とバージョンを返す
getVersion系 このコンテナがサポートするServletAPIの版数を返す