※本記事は「サーブレットとは?その役割やHelloWorldサンプルコードのご紹介」の続編です。
(0)目次&概説
(2) サーブレットコンテナの基本
(2-1) サーブレットコンテナとは?
(2-2) サーブレットの構造と必須設定
(2-2-1) コンテナから呼び出される為の設定
(2-2-2) サーブレットに必要なパッケージ
(2-2-3) サーブレットのURL指定
(2-3) サーブレットのライフサイクル
(2-3-1) サーブレットのライフサイクルの概要
(2-3-2) サーブレットのライフサイクルのサンプルコード
(2-4) サーブレットのログ出力
(2) サーブレットコンテナの基本
(2-1) サーブレットコンテナとは?
Javaサーブレットを動かす時に必要なソフトで、サーブレット処理の中心的な役割を果たします。コンテナはクライアントからのリクエストを受け取ると、サーブレットアプリケーションを起動します。
(2-2) サーブレットの構造と必須設定
(2-2-1) コンテナから呼び出される為の設定
アプリがコンテナから呼ばれるためには、HttpServletクラスを継承してdoGetメソッドをオーバーライドする必要があります。HttpServlet クラスはサーブレットとクライアントが通信を行うための最低限の処理が定義されています。具体的なプログラムは以下の通りです。
①HttpServlet クラスの継承
//「(2-3-2) サーブレットのライフサイクルのサンプルコード」で言う13行目 public class HelloLifeCycle extends HttpServlet {
②doGet メソッドのオーバーライド
//「(2-3-2) サーブレットのライフサイクルのサンプルコード」で言う25行目 protected void doGet(HttpServletRequest request, HttpServletResponse response)
サーブレットには上記のような数種類のdoXXXXメソッドが用意されており(doGet, doPostなど)、サーブレットがどのHTTPメソッドによって呼び出されたかで、doXXXXのメソッドを使い分けます。例えば、コンテナがクライアントからGETのリクエストを受け取ったら、doGetメソッドを呼びます。
(2-2-2) サーブレットに必要なパッケージ
サーブレットで必須のパッケージは以下で、明示的にインポートする必要があります。
import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;
(2-2-3) サーブレットのURL指定
2つ方法があり、①はサーブレットのプログラム内でアノテーションを記述するします(Tomcat7.0から利用可能)。②はweb.xmlと呼ばれる定義ファイルを用意して設定する方法です。
①「@WebServletアノテーション」を使う方法
//記載例 //HelloServletで言う15行目 @WebServlet("/HelloWorldServlet")
②「web.xml」にサーブレット登録する方法
「web.xml」と呼ばれるファイルを「WEB-INF」直下に配置します。web.xmlはデプロイメントディスクリプタとも呼ばれており、JSPやサーブレットにおけるWebアプリの配備(デプロイ)の情報を記述したxml形式の設定ファイルです。コンテナにより異なりますがTomcatの場合はweb.xmlというファイル名です。以下にweb.xmlに登録するいくつかの主要情報の記載方法(タグ)を紹介します。
<web-app> <servlet> <!--Servletの内部エイリアス名≒論理名--> <servlet-name>HelloServlet</servlet-name> <!--Servletの完全修飾名(正式名称)--> <servlet-class>studyc.HelloWorldServlet</servlet-class> </servlet> <!--Servletの外部エイリアス名≒呼び出す時のURLパターン(「/」の場合はすべて)--> <servlet-mapping> <servlet-name>HelloServlet</servlet-name> <url-pattern>/HelloWorldServlet</url-pattern> </servlet-mapping> </web-app>
上記のweb.xmlファイルはWEB-INFのディレクトリに配備します。筆者の環境の場合は下記の通りです。
/[アプリケーションROOTディレクトリ]/WEB-INF/ /home/admin/eclipse-workspace/TestProject/WebContent/WEB-INF/
(2-3) サーブレットのライフサイクル
(2-3-1) サーブレットのライフサイクルの概要
サーブレットは一度インスタンス化されると、コンテナにより破棄されるまでメモリに残り続けるため、2回目以降の処理が速くなります。右記のようなライフサイクルで処理が発生しますが、その中でも「1」は初回起動時、「5」は更新やアンロード時のみ実行されます(メモリから削除される直前に一回だけ実行される)。
①サーブレットのインスタンス化
②initメソッドの実行(初期化)
アプリケーションが呼び出された時に一度だけinitメソッドをコールします。ここには例えば変数の初期化やDBのマウント・オープンなど初期化で必要な処理があれば記述します。これによりアプリケーションが「ロード状態」となります。上記で「アプリケーションが呼び出された時」は具体例で言うと、例えばブラウザからサーブレットのページを最初に要求した時などが挙げられます。
③serviceメソッドの実行(処理振り分け)
クライアントからのリクエストがある都度、コンテナはserviceメソッドを呼出して、リクエストの種類に応じてdoXXXXメソッドを実行します。上記で「クライアントからのリクエスト」は具体例で言うと、例えばブラウザから2回目以降にページが要求された時などが挙げられます。
④destroyメソッドの実行(終了処理)
アプリが破棄される時にdestroyメソッドが実行されてメモリ上から削除されます。具体的なタイミングの例として、プログラムに変更が加わって再度コンパイルするタイミング等が挙げられます。
(2-3-2) サーブレットのライフサイクルのサンプルコード
以下にサーブレットライフサイクルのHelloWorldのプログラムをご紹介します。
package studyc; import java.io.IOException; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet("/HelloLifeCycle") public class HelloLifeCycle extends HttpServlet { private static final long serialVersionUID = 1L; @Override public void init(ServletConfig config) throws ServletException { super.init(config); ServletContext application = this.getServletContext(); System.out.println("*** Init method is executed"); application.log("*** Init method is executed"); } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter().append("Served at: ").append(request.getContextPath()); ServletContext application = this.getServletContext(); System.out.println("*** doGet method is executed"); application.log("*** doGet method is executed"); } @Override public void destroy() { ServletContext application = this.getServletContext(); System.out.println("*** Destroy method is executed"); application.log("*** Destroy method is executed"); } }
上記の動作には事前のログ設定が必要です。以降に記載する手順は正規のものではなく、色々調べながら筆者の環境で行った手順を備忘のために記録している事をご承知おきください(もし指摘などあれば是非教えて欲しいです)。
(2-4) サーブレットのログ出力
HelloLifeCycle.javaのサンプルプログラムでは「localhost.yyyy-mm-dd.log」というログファイルを出力しようとしています。ログファイルの出力先については、サーブレット・コンテナによって実装が異なりますがTomcatの場合は、サーブレットの動作を定義する「server.xml」や「logging.properties」にて設定を行います。 「localhost.yyyy-mm-dd.log」についてはファイルがうまく出力されない場合は下記の対応を試してみました。
(2-4-1) 対処①:VM起動オプションの設定
また、それでもログが出力されない場合はVMの起動オプションの指定を行いました。
①Eclipseメニューの[Run]→[Run Configurations]→[Apache Tomcat]→[Tomcat V8.5 Server at Localhost]と選択します。
②[Run Configurations]の右側の「Arguments」タブにて「VM arguments」にて以下の文言を記載して、起動オプションを指定します。
-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.util.logging.config.file="%CATALINA_BASE%\conf\logging.properties"
(2-4-2) 対処②:logging.propertiesの準備
logging.propertiesのコピーを以下のパスに格納しました。logging.propertiesやserver.xmlやweb.xml等の設定ファイルは大きく2種類、「①アプリ全体用」と「②アプリ個別用」がありますが、今回は「①アプリ全体用」のものを「②アプリ個別用」にコピーしました。
//***①コピー元:アプリ全体用の格納先 /usr/local/src/apache-tomcat-8.5.42/conf/ //***②コピー先:アプリ個別用の格納先 /home/admin/eclipse-workspace/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/conf/
上記の設定を行うと下記のパスにログファイルが生成されました!
/home/admin/eclipse-workspace/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/logs/localhost.yyyy-mm-dd.log
↓コンソールの標準出力はこんな感じ。
↓ログファイルの中身