tomcat

簡単なServlet


Webアプリケーションの作成

tomcatは C:\Users\nitta\Documents\tomcat8 (Windowsの場合) または /Users/nitta/Documents/tomcat9 (macOS の場合) にインストールされていて、 環境変数CATALINA_HOMEはこのpathを保持しているものとする。

環境変数の値を表す文法はOS(Shell)によって異なり、 Unix系 (macOS, Linux等)では ${CATALINA_HOME}, Windows では %CATALINA_HOME% である。 以下の記述では、Unix系の記法を採用して、Tomcat 8 がインストールされたパスを ${CATALINA_HOME} で表している。自分の環境に適宜読み替えて理解してほしい。

Webアプリケーションは ${CATALINA_HOME}/webapps の下に配置する。 ここでは entry というWebアプリケーションを作成する例で説明する。


webapps フォルダの中に entry フォルダを作成する

エクスプローラで、 ${CATANLINA_HOME}\webassps\entry フォルダを作成する。

注意

Tomcatは起動時の ${CATALINA_HOME}/webapps/ 以下の状態をキャッシュしている。 そのため、webappsより下のファイルを変更した場合は、 一旦Tomcatを停止して (${CATALINA_HOME}\bin\shutdown.bat をエクスプローラ上でクリック)、 その後でTomcatを起動し直す (${CATALINA_HOME}\bin\startup.bat をエクスプローラ上でクリック) 必要がある。


HTMLドキュメントを置く

[ファイル配置]
  ${CATALINA_HOME}/webapps/entry/hello.html
${CATALINA_HOME}/webapps/entry/hello.html
<html>
<head>
<title>html example</title>
</head>
<body>
<h2>Hello</h2>
This is HTML.
</body>
</html>
[ブラウザでアクセスするURL]
  http://localhost:8080/entry/hello.html




サブディレクトリを作ってみる

[ファイル配置]
  ${CATALINA_HOME}/webapps/entry/sub/sub.html
${CATALINA_HOME}/webapps/entry/sub/sub.html
<html>
<head>
<title>Sub</title>
</head>
<body>
<h2>Sub</h2>
This is Sub.
</body>
</html>
[ブラウザでアクセスするURL]
  http://localhost:8080/entry/sub/sub.html




最も簡単なサーブレット

[ファイル配置]
  ${CATALINA_HOME}/webapps/entry/WEB-INF/src/HelloServlet.java ←作成する
                                       /classes/HelloServlet.class←コンパイラで生成する

クラスファイル(拡張子が.classのファイル)は 「${CATALINA_HOME}/webapps/Webアプリケーション名/WEB-INF/classes」 フォルダに置かなくてはいけない。 ソースファイル(拡張子が.javaのファイル)はどこに置いても構わないが、 ここでは 「${CATALINA_HOME}/webapps/Webアプリケーション名/WEB-INF/src」 フォルダに置くことにする。

HelloServlet.javaで記述するのは以下の点である。

コンパイル時には、Javaの標準でないAPIを利用するので、 servlet-api.jarを環境変数CLASSPATHに含めておく必要がある。

    環境変数名: CLASSPATH
    値:         .;C:\Users\nitta\Documents\tomcat8\lib\servlet-api.jar;その他のパス
        (または .;%CATALINA_HOME%\lib\servlet-api.jar;その他のパス )  ←他の環境変数を使って記述してもよい

環境変数を設定した後で起動したプログラム(たとえばコマンドプロンプト等)では、CLASSPATH変数に値が設定されている。 CLASSPATH変数に値を設定する場合は、必ず '.' (カレント・ディレクトリ)も先頭に含めておくこと。 複数の値を設定する場合は ';' (セミコロン)でつなぐ。

${CATALINA_HOME}/webapps/entry/WEB-INF/src/HelloServlet.java
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class HelloServlet extends HttpServlet {
    public void doGet(HttpServletRequest req,HttpServletResponse res)
    throws IOException, ServletException {
	PrintWriter out = res.getWriter();
	out.println("Hello");
    }
}

コンパイルは次のように指定する。 -dオプションによって .classファイルを生成する先のフォルダを指定する(classesフォルダは前もって作っておくこと)。 また -sourcepathオプションは、コマンドラインで指定したファイル以外が必要になった ときに探すフォルダである(今回のコンパイルでは必要ないが、後で必要になる場合がある)。

HelloServlet.javaのコンパイル
C:\Users\nitta> echo %CLASSPATH%      ← CLASSPATH環境変数を表示する
.;C:\Users\nitta\Documents\java\lib\mariadb-java-client-2.7.0.jar;C:\Users\nitta\Documents\tomcat8\lib\servlet-api.jar
C:\Users\nitta> cd Documents/tomcat8/webapps/entry      ← entryに移動する
C:\Users\nitta\Documents\tomcat8\webapps\entry> cd WEB-INF      ← WEB-INFに移動する
C:\Users\nitta\Documents\tomcat8\webapps\entry\WEB-INF> javac -encoding utf8 src/HelloServlet.java -d classes -sourcepath src      ← コンパイルする




web.xmlの記述

[ファイル配置]
  ${CATALINA_HOME}/webapps/entry/WEB-INF/classes/HelloServlet.class
                                       /src/HelloServlet.java
                                       /web.xml ←このファイルを作成する

web.xmlは、Webアプリケーションの動作を記述するためのファイルで、 Deployment Descriptor (配備記述子)と呼ばれる。 このファイルはXML形式で表現する。

web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                      http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
  version="3.1"
  metadata-complete="false">
  <servlet>
    <servlet-name>HelloServlet</servlet-name>
    <servlet-class>HelloServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>HelloServlet</servlet-name>
    <url-pattern>/HelloServlet</url-pattern>
  </servlet-mapping>
</web-app>
url-patternの記述呼び出すときのURLの指定
/aaahttp://マシン名:ポート番号/entry/aaa
/aaa/bbbhttp://マシン名:ポート番号/entry/aaa/bbb
/aaa/bbb.ccchttp://マシン名:ポート番号/entry/aaa/bbb.ccc

url-patternの記述には先頭に'/'が必要なことに注意すること。

${CATALINA_HOME}/webapps/examples/WEB-INF/web.xmlを参考に記述すること。 web.xmlの最初のweb-appタグに関する記述はタイプミスをしやすいので、 エディタでコピーしてしまう方がよい。 コピーしたあと metadata-complete要素の値を "true"から"false"へ変更しておくこと。

metadata-complete要素を"false"にしておくと、 Servlet3.0テクノロジーで導入された web fragmentやAnnotationの機能を使うことができる。 (これらの機能については次週以降に述べる。)

[ブラウザでアクセスするURL]
  http://localhost:8080/entry/HelloServlet




tomcatのオートリロードの設定

ここで HelloServlet.javaを書き換えてみる。

HelloServlet.javaの書き換え
*** ../webapps/entry/WEB-INF/src/HelloServlet.java	Thu Nov 24 12:08:44 2016
--- ../webapps/entry/WEB-INF/src/HelloServlet2.java	Wed Jul 27 17:40:14 2011
***************
*** 5,10 ****
      public void doGet(HttpServletRequest req,HttpServletResponse res)
      throws IOException, ServletException {
  	PrintWriter out = res.getWriter();
! 	out.println("Hello");
      }
  }
--- 5,10 ----
      public void doGet(HttpServletRequest req,HttpServletResponse res)
      throws IOException, ServletException {
  	PrintWriter out = res.getWriter();
! 	out.println("Hello2");
      }
  }

このファイルをコンパイルし直した後で、ブラウザで先ほどのURL (http://マシン名:ポート番号/entry/HelloServlet) にアクセスし直しても(or reloadしても)表示は変わらないはずである。 Tomcatはサーブレットのクラスファイルを読み込んだ後は キャッシュしてしまうので、クラスファイルを変更しても その変更は即座には動作に反映されない。 (Tomcatを再起動すれば反映される。)

クラスファイルを変更する度にTomcatを再起動するのは煩わしいので、 以下のようにオートリロードの設定をしておくとよい。 META-INF の下に context.xmlというファイルを作成する。

[ファイル配置]
  ${CATALINA_HOME}/webapps/entry/META-INF/context.xml
(注意) META-INF/の下であることに注意。WEB-INF/の下ではない。
${CATALINA_HOME}/webapps/entry/META-INF/context.xml
<Context reloadable="true" />


「オートリロード」機能が有効になったことを確認しておくこと。



パッケージ付きのサーブレット

[ファイル配置]
  ${CATALINA_HOME}/webapps/entry/WEB-INF/src/foo/BarServlet.java
${CATALINA_HOME}/webapps/entry/WEB-INF/src/foo/BarServlet.java
package foo;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class BarServlet extends HttpServlet {
    public void doGet(HttpServletRequest req,HttpServletResponse res)
    throws IOException, ServletException {
	PrintWriter out = res.getWriter();
	out.println("bar");
    }
}


web.xmlへの記述の追加
*** ../webapps/entry/WEB-INF/web.xml.01	Fri Nov 18 19:12:38 2016
--- ../webapps/entry/WEB-INF/web.xml.02	Fri Nov 18 19:12:55 2016
***************
*** 9,16 ****
--- 9,24 ----
      <servlet-name>HelloServlet</servlet-name>
      <servlet-class>HelloServlet</servlet-class>
    </servlet>
+   <servlet>
+     <servlet-name>BarServlet</servlet-name>
+     <servlet-class>foo.BarServlet</servlet-class>
+   </servlet>
    <servlet-mapping>
      <servlet-name>HelloServlet</servlet-name>
      <url-pattern>/HelloServlet</url-pattern>
    </servlet-mapping>
+   <servlet-mapping>
+     <servlet-name>BarServlet</servlet-name>
+     <url-pattern>/BarServlet</url-pattern>
+   </servlet-mapping>
  </web-app>


BarServlet.javaのコンパイル
> cd %CATALINA_HOME%\webapps\entry\WEB-INF 
> javac -encoding utf8 src\foo\BarServlet.java -d classes -sourdepath src  ← fooパッケージに属するソースをコンパイルする
> dir classes\foo 
...
2020/10/30 16:56         621 BarServlet.class   ← classes/foo/の下にclassファイルが生成されている
...



[ブラウザでアクセスするURL]
  http://localhost:8080/entry/BarServlet




他のクラスを使うサーブレット

[ファイル配置]
  ${CATALINA_HOME}/webapps/entry/WEB-INF/src/Oracle.java
                                             OracleServlet.java

運勢を占うサーブレットを作る。

乱数を発生させて、その結果に応じて異なる文字列を返す getResult()メソッドを持つ Oracleクラスを作成する。

${CATALINA_HOME}/webapps/entry/WEB-INF/src/Oracle.java
import java.util.*;
public class Oracle {
    private static Random random = new Random();
    public String getResult() {
	int n = random.nextInt(10);
	if (n < 2 ) return "very good";
	else if (n < 5) return "good";
	else if (n < 7) return "bad";
	else return "very bad";
    }
}

Oracleクラスを呼び出すサーブレットとしてOracleServletクラスを作成します。

${CATALINA_HOME}/webapps/entry/WEB-INF/src/OracleServlet.java
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class OracleServlet extends HttpServlet {
    public void doGet(HttpServletRequest req,HttpServletResponse res)
    throws IOException, ServletException {
	PrintWriter out = res.getWriter();
	Oracle ora = new Oracle();
	String result = ora.getResult();
	out.println("Your fortune is " + result);
    }
}


web.xmlへの記述の追加
*** ../webapps/entry/WEB-INF/web.xml.02	Fri Nov 18 19:12:55 2016
--- ../webapps/entry/WEB-INF/web.xml.03	Fri Nov 18 19:13:08 2016
***************
*** 13,18 ****
--- 13,22 ----
      <servlet-name>BarServlet</servlet-name>
      <servlet-class>foo.BarServlet</servlet-class>
    </servlet>
+   <servlet>
+     <servlet-name>OracleServlet</servlet-name>
+     <servlet-class>OracleServlet</servlet-class>
+   </servlet>
    <servlet-mapping>
      <servlet-name>HelloServlet</servlet-name>
      <url-pattern>/HelloServlet</url-pattern>
***************
*** 21,24 ****
--- 25,32 ----
      <servlet-name>BarServlet</servlet-name>
      <url-pattern>/BarServlet</url-pattern>
    </servlet-mapping>
+   <servlet-mapping>
+     <servlet-name>OracleServlet</servlet-name>
+     <url-pattern>/OracleServlet</url-pattern>
+   </servlet-mapping>
  </web-app>

コンパイルするときはOracleクラスのソースの置き場所を示すために 必ず -sourcepathオプションを指定する必要がある。

OracleServlet.javaのコンパイル
> cd %CATALINA_HOME%  
> cd webapps/entry/WEB-INF  
> javac -encoding utf8 src\OracleServlet.java -sourcepath src -d classes   
> dir classes\Oracle*class  
...
classes/Oracle.class
classes/OracleServlet.class
...



[ブラウザでアクセスするURL]
  http://localhost:8080/entry/OracleServlet ←reloadする度に表示が変わる。