第I章 Servlet の作成
I.1 Web サーバーサイドプログラムとはWeb サーバーサイドプログラムとは、WWW のサーバー側で実行されて動的に（つまり要求のあるたびに異なる内容の）HTML などのコンテンツを生成するプログラムのことである。このなかでポピュラーな CGI（Common GatewayInterface）は、Webサーバー上でプログラムを実行し、動的に HTML 形式などのデータなどを作成して Web ブラウザーに渡すための仕組み（プログラムとWeb サーバーの間のデータのやりとりの約束事）である。また CGI に従って実行されるプログラム自体のこともCGIと呼ばれる。CGI を記述する言語は何でも構わないが、Perl, PHP や C を使うことが比較的多いようである。
JavaScript はクライアント（Web ブラウザーが実行されているコンピューター）側でプログラムが実行されるのに対し、CGI を含むサーバーサイドプログラムはサーバー（HTML ファイルが置かれているコンピューター）側でプログラムが実行されるという違いがある。例えばアクセスカウンター・掲示板・オンラインショッピングサイトなどはクライアント上のプログラムだけでは実現できないのでサーバーサイドプログラムが必要になる。

I.2 Servletとは
本演習ではサーバーサイドプログラムの作成に Java Servlet を用いる。Servlet とは、CGI と同じように Web サーバー側でプログラムを実行するための仕組み（約束事）である。しかし CGI とはいくつかの点で区別される 。
まず、約束事は Javaのクラス・インタフェースとして提供されるので、プログラミング言語は当然 Java （または JVM ベースの言語）に限定される。
呼出しごとにいちいちプロセスを生成せず、スレッドとして実行するので効率が良い。
ある程度の期間、サーバー側で接続の情報を記憶しておくことができるなど、サーバーサイドプログラミングを支援するためのライブラリが充実している。
このため Java によるサーバーサイドのプログラミングとしては、CGI ではなくServlet を使用することが多い。例えばオンラインショッピングのための Web サ
イトなどは Servlet が得意とする分野である。
Servlet を実行するためには、Web アプリケーションサーバーが必要である。
Web アプリケーションサーバー は、コンテナーとも呼ばれ、Web ブラウザー（あるいは Apache などの Web サーバー）からの要求を受け付け、Servlet（やJSP）を起動するプログラムである。 Web アプリケーションサーバーとして有名なものに、Apache Tomcat や Jetty などがある。
なお、Apache Tomcat, Jetty や Java の開発環境（JDK, Eclipse）などのインストール方法は別ドキュメントで解説する。

オブジェクト指向言語 補足 第 1 章 p.1

有用なリンク
初めての ホームページ講座 （https://www.hajimeteno.ne.jp/） — HTMLのまとめ
Apache Tomcat （https://tomcat.apache.org/）
Jetty （https://www.eclipse.org/jetty/）

I.3 本演習の位置づけ
本演習では次のような Java のごく初歩的な知識だけを仮定する。
制御構造（if〜else文、for文、 while文）の書き方がわかること。
（C 言語の制御構文と同じ。）
継承（class 〜 extends）を利用してクラスを定義できること。
メソッドの定義の書き方がわかること。（C 言語の関数定義とほとんど同じ。）
クラス・オブジェクトの概念を理解していること。つまり、（.演算子を使って）フィールド参照・メソッド呼出しができること。
オブジェクトの生成（new）ができること。
クラスフィールド・クラスメソッドが使用できること。
import 文が書けること。（C 言語の #include に似ている。）
言い替えれば、if, else, for, while, class, extends, .（ドット）, new,import などのキーワード・演算子の使い方を理解していればよい。
あとは Java の API 仕様のドキュメント:https://docs.oracle.com/javase/jp/17/docs/api/
Java™ Platform, Standard Edition 17（Java の標準 API）
https://jakarta.ee/specifications/servlet/5.0/apidocs/
Jakarta Servlet API documentationなどで必要に応じてメソッドの使い方などを調べる必要がある。
DISCLAIMER: 本演習の主目的は、Servlet などの Web サーバーサイドプログラ
ムの作成方法を修得するというよりも、むしろ、Servlet を題材として Java のAPI の使用法に習熟することにある。
本格的な Web サーバーサイドプログラムを作成するためには、通常、Web アプリケーションフレームワーク（Java の Apache Struts や Spring Boot, Play,Wicket、Python の Django、Ruby の Ruby on Rails など）と総称されるライブラリー群を使用する。しかし、情報系の学生にとっては、生の Servlet の仕組みを知ることも意味がある。

オブジェクト指向言語 補足 第 1 章 p.2

I.4 Servlet の作成
CGI も Servlet も、単純に言ってしまえば、HTML のデータ ⦅JPEG や PNG など
HTML 以外のデータを出力する CGI や Servlet も考えられるが、はじめは簡単のために HTML を生成するプログラムのみ扱う。⦆を生成するプログラムである。
次に示すのは現在の時刻を表示する Servlet である。
ファイル MyDate.java
1 import java.io.IOException;
2 import java.io.PrintWriter;
3 import java.util.Calendar;
4
5 import jakarta.servlet.ServletException;
6 import jakarta.servlet.annotation.WebServlet;
7 import jakarta.servlet.http.HttpServlet;
8 import jakarta.servlet.http.HttpServletRequest;
9 import jakarta.servlet.http.HttpServletResponse;10
11 @WebServlet("/MyDate")
12 public class MyDate extends HttpServlet {13String[] youbi = {"日", "月", "火", "水", "木", "金",1415@Override16
protected void doGet(HttpServletRequest request,
17
HttpServletResponse response)18
throws ServletException, IOExce
19
response.setContentType("text/html; charset=UTF20PrintWriter out = response.getWriter();
21out.println("<html><head></head><body>");22
23
Calendar cal = Calendar.getInstance();24
out.printf("%d年%d月%d日%s曜日%d時%d分%d秒%n",25cal.get(Calendar.YEAR),
26
cal.get(Calendar.MONTH) + 1,
27
cal.get(Calendar.DAY_OF_MONTH),
28
youbi[cal.get(Calendar.DAY_OF_WEEK) - 1],29
cal.get(Calendar.HOUR_OF_DAY),30
cal.get(Calendar.MINUTE),
31
cal.get(Calendar.SECOND));
32
33
out.println("</body></html>");34
out.close();
35}
36 }

Servlet は HttpServlet というクラス を継承して作成する。このクラスにServlet として必要なほとんどの機能が実装されているので、必要なところのみ書き換えれば Servlet が実行できるようになっている。
import jakarta.servlet.http.〜という形の 6つの import 文は大抵の Servlet で必要で、jakarta.servlet および jakarta.servlet.http というパッケージに属するクラスを利用するたオブジェクト指向言語 補足 第 1 章 p.3

めにある。
@WebServlet("/MyDate") は Servlet の URI のパスを指定するためのアノテーションである。クラスファイルに/MyDate というパスの情報が書き込まれ、コンテナーがその情報を読み出して、そのパスに Servlet を配備する。
Servlet の処理は基本的に doGet（または後述の doPost）というメソッドの中に記述する。上のメソッド定義の最初の部分:
public void doGet(HttpServletRequest request,HttpServletResponse response)throws ServletException, IOExceptionからわかるように、doGet/doPost は HttpServletRequest クラス 、HttpServletResponseクラス の 2 つの引数を取る。それぞれ要求 (request)と応答 (response) を表すオブジェクトである。
最後の throws ServletException, IOException の部分は、この doGetというメソッドが、ServletException や IOException という例外（後述）を発生するかも知れないということを宣言する Java の構文である。
このメソッドの最初の文のresponse.setContentType("text/html; charset=UTF-8");は、以下に続くデータが HTML のデータで文字コードが UTF-8 であるということをブラウザーに伝える役割を持つ。また、PrintWriter out = response.getWriter();
は、ブラウザーにデータを送るための出力ストリームを取得する。これ以降
out オブジェクトの printf（あるいは、 println, print）メソッドを呼び出すことにより、データを出力することができる。
PrintWriter クラスの printf は書式制御の機能つきの出力メソッドである。 C 言語の printf 関数に相当する。 %d や %s などの書式制御は C 言語のprintf のときとほぼ同じ意味である。一方、%n はプラットフォーム固有の改行コード（つまり、Unix では \n、 Windows では \r\n）を挿入する Java のprintf 特有の書き方である。
println あるいは print はやはり出力のためのメソッドだが、 %d や %s のような書式制御の機能はない。println は最後に改行を出力し、一方 print は改行しない。
なお出力の最後に out.close() を呼び出してストリームを閉じておく。
問 I.4.1 上の Servlet プログラムを改造して、現在の秒によって、ブラウザーに表示されるときの文字の色が変わるようなサーブレット ColoredDate.javaを作成せよ。例えば、0 〜 19 秒が黒、20 〜 39 秒が青、40 〜 59 秒が赤などである。
ヒント:

オブジェクト指向言語 補足 第 1 章 p.4

Calendar クラスの使い方については
https://docs.oracle.com/javase/jp/17/docs/api/java.base/java/util/Calendar.html
を参照すること。
色を変えるには HTMLのタグ <font color='red'>…</font>などを用いる。
（HTML の規格では、上の red の周りの引用符は上のように一重引用符「'」でも二重引用符「"」でも良い。Java（C でも同じ）のプログラム
で、二重引用符「"」自体を出力したいときは、
out.println("<font color=\"red\">") のように、「"」の前にバックスラッシュ「\」をつける 必要がある。
問 I.4.2 アプリケーションルートに、画像ファイル（例えばimages/foo.png）を用意しておいて、サーブレットで <img
src='images/foo.png' /> と出力すると画像を表示できる。
現在の秒によって、ブラウザーに表示されるページの背景画像が変わるようなサーブレット ChangeBackground.java を作成せよ。
ヒント:
背景画像を変えるには HTMLの body タグで <bodybackground='images/foo.png'>…</body> のように指定する。
素材: http://www.3776m.com/sozai/（素材の館）

I.5 （参考）Servletの設置
以下では、Eclipse 等を使用しないで手作業でコンパイルし・Web アプリケーションサーバーに設置する方法を、概略だけ述べる。
Servlet を実行するには、まずコンパイルが必要である。次のコマンドで.class ファイルを作成する。
javac -classpath servlet-api.jar MyDate.java“servlet-api.jar”の部分は、実際には ServletAPI が含まれている JAR ファイル（Java のライブラリーファイル）へのパスに置き換える。このパスは使用する Web アプリケーションサーバーにより異なる。
Servlet を実際に設置するには、生成されたクラスファイル（ソースファイルの名前が MyDate.java の場合、MyDate.class）を、Servlet の仕様で定められたディレクトリー構成:-（Web アプリケーションルート）- WEB-INF （ディレクトリー）- web.xml （設定ファイル）- classes （ディレクトリー）- （class ファイル）- lib （ディレクトリー）- （JAR ファイル）

オブジェクト指向言語 補足 第 1 章 p.5

の classes というディレクトリーの下に置く。
Web アプリケーションルートは任意の場所に置くことができるが、Web アプリ
ケーションサーバーのデフォルトのディレクトリーがある。このデフォルトディレクトリーの子として例えば OOPL というディレクトリーを作成し、このディレクトリーを Web アプリケーションルートとする（つまり、OOPL の子としてWEB-INF ディレクトリー を作成する）と、
http://hostname:8080/OOPL/MyDateという URL でMyDate サーブレットの実行結果を見ることができる。
hostname の部分は Web アプリケーションサーバーを実行しているホストの名前または IP アドレスである。
Servlet の開発中はサーブレットと WWW ブラウザーは同一のコンピューターで実行 していることが多いので、その場合は hostname はlocalhost（あるいは 127.0.0.1）となる。

I.6 ファイル・ディレクトリー操作
Servlet のようなサーバーサイドプログラムは、アクセスカウンターにせよ、掲示板にせよファイルやデータベースにアクセスする必要がある場合が多い。
（でなければ、クライアントサイドのプログラムで実現できることがことが多い。）
以下では Java のファイルやディレクトリー操作の API を使用し、サーバーサイドでファイルアクセスを行なう Servlet を作成する。

I.7 アクセスカウンター
アクセスカウンターはもっとも代表的なサーバーサイドプログラムで、Web ページに対するアクセスの回数を記録し、表示するものである。以下に紹介する
簡易版アクセスカウンター Servlet では、アクセスの回数はサーバー上のファイルに記録しておく。
ファイル Counter.java
1 import java.io.BufferedReader;
2 import java.io.File;
3 import java.io.FileNotFoundException;
4 import java.io.FileReader;
5 import java.io.FileWriter;
6 import java.io.IOException;
7 import java.io.PrintWriter;
8
9 import jakarta.servlet.ServletException;
10 import jakarta.servlet.annotation.WebServlet;
11 import jakarta.servlet.http.HttpServlet;
12 import jakarta.servlet.http.HttpServletRequest;
13 import jakarta.servlet.http.HttpServletResponse;
14
15 @WebServlet("/Counter")
16 public class Counter extends HttpServlet {17@Override
18
protected void doGet(HttpServletRequest request,
19
HttpServletResponse response)20
throws ServletException, IOExce21response.setContentType("text/html; charset=UTF-

オブジェクト指向言語 補足 第 1 章 p.6

222324
25
26
27
28
2930
3132
33
3435
36
37
38
39
40
41
42
43
4445
46
47
48
4950 }

PrintWriter out = response.getWriter();out.println("<html><head></head><body>");int i;
File f = new File(getServletContext().getRealPath("/WEB-INF/counter.txt"
BufferedReader fin = null;try {fin = new BufferedReader(new FileReader(f));
i = Integer.parseInt(fin.readLine());} catch (FileNotFoundException// ファイルがな| NullPointerException// ファイルが空| NumberFormatException e) { // 数でないなら
i = 0; // 0 に} finally {if (fin != null) {fin.close(); // closeを忘れない}}
PrintWriter fout = new PrintWriter(new FileWrite
fout.println(++i);
fout.close(); // closeを忘れない}

out.printf("あなたは %d番目の来訪者です。%n", i);out.println("</body></html>");out.close();// closeを忘れない

この例では counter.txt というファイルにアクセス回数を記録している。このファイルは、Web アプリケーションルートフォルダの下の WEB-INF というフォルダに置かれている。counter.txt の中身は 1 行のみの数字だけのファイルである。
注: counter.txt のような Web アプリケーションのデータのファイルをWeb アプリケーションルートフォルダに置くのは、テストのときは手軽だが、アプリケーションのバージョンアップがやりにくくなる可能性がある。実運用するときは、他のアプリケーションに専用の場所に置くことを検討する必要がある。
プログラム中のgetServletContext().getRealPath(…)という式は、WEB アプリケーションルートからのパスを受け取り、ファイルシステム中の絶対パスを返す。getServletContext は HttpServlet クラスのメソッドであり、getRealPath は SevletContext クラス（正確にはインタフェース）のメソッドである。これらのメソッドの詳細は Java の API 仕様のドキュメント（例えば HttpServlet クラスの場合は、オブジェクト指向言語 補足 第 1 章 p.7

https://jakarta.ee/specifications/servlet/5.0/apidocs/jakarta/servlet/http/httpservlet）
を参照すること。
（参考） 次のような簡単なサーブレットを実行してみると、getRealPath メソッドの結果を確認することができる。デバッグのときにこのようなやり方が有効である。
ファイル GetRealPathExample.java
1 import java.io.IOException;
2 import java.io.PrintWriter;
3
4 import jakarta.servlet.ServletException;
5 import jakarta.servlet.annotation.WebServlet;
6 import jakarta.servlet.http.HttpServlet;
7 import jakarta.servlet.http.HttpServletRequest;
8 import jakarta.servlet.http.HttpServletResponse;
9
10
11 @WebServlet("/GetRealPathExample")
12 public class GetRealPathExample extends HttpServlet
13@Override
14
protected void doGet(HttpServletRequest request,
15HttpServletResponse response16
throws ServletException, IOE
17response.setContentType("text/plain; charset=UT
18
PrintWriter out = response.getWriter();
19
out.println(getServletContext().getRealPath("/W20
out.close();
21}
22 }

また、
File f = new File(path);
BufferedReader fin = new BufferedReader(new FileReader(f));…
fin.close();は、ファイルから入力するときの常套句である。FileReader クラス はファイルから文字ストリームを読み込むためのクラス、BufferedReader クラス は文字をバッファリングすることによって、文字ストリームから文字を効率良く読み込むためのクラスである。FileReader クラス、BufferedReader クラスの一般的な使用法は API 仕様で確認することができる。上記の … の部分では、上で用意された fin というオブジェクトに対して、標準入力（System.in）からの入力と同じようにファイルからの入力が可能になる。最後の close() を忘れるとファイルの内容が消えてしまったりするので、注意が必要である。
また、Integer.parseInt は Java で文字列（String）を整数（int）に変換するためのクラスメソッドである。（C 言語の atoi 関数に相当する。）
同様に、オブジェクト指向言語 補足 第 1 章 p.8

PrintWriter fout = new PrintWriter(new FileWriter(f));…
fout.close();は、ファイルへ出力するときの常套句である。
FileWriter クラス はファイルに文字ストリームを書き込むためのクラス、PrintWriter クラス は文字ストリームのフォーマットされた出力のためのクラスである。FileWriter クラス、 PrintWriter クラスの一般的な使用法は
API 仕様で確認することができる。
ここで用意された fout というオブジェクトに対して、標準出力（System.out）に対するのと同じメソッドである print や println が使用できる。
ここまでの部分で、ファイルから数字を読み込み、一つ増やした数字をファイルに書き込んでいる。 p>

I.8 Java の例外処理
Counter.java では、ファイルからの入出力処理の周りを try 〜 catch 〜という形で囲っている。これは Java の例外処理の構文である。
try 〜 catch文のもっとも基本的な使い方は次のような形である。
try {文の並び 0} catch (例外型 1,1 | … | 例外型 1,k1 変数 1) {}}文の並び 1…
catch (例外型 n,1 | … | 例外型 n,kn 変数 n) {文の並び n文の並び0で例外が起こらなかった場合は、そのまま次へ行く。
文の並び0の中で、例外が起こったときには 文の並び0の間の残りの文は無視され、例外が例外型i,1〜例外型i,ki （ただし i = 1 ⋯ n）にマッチするならば、文の並びiが実行される。マッチする例外がなかった場合には、文の並びi（i = 1⋯n）は実行されない。

この場合、現在実行しているメソッドを呼び出した式を囲んでいる try 〜catch 文を探す。それもなければ、さらにメソッド呼出しの履歴をさかのぼって、メソッド呼出しを囲んでいる try 〜 catch 文を探す。それでもなければプログラムを終了する。
また、try 〜 catch 文の最後に “finally { 文の並び }”という形（finally節）がつく場合もある。その場合、finally 節の文の並びは例外が起こったか否かにかかわらず、必ず最後に実行される。
先ほどのプログラム例では、counter.txt というファイルが見つからなかった
場合、FileNotFoundException という例外が起こり、これに対応する

オブジェクト指向言語 補足 第 1 章 p.9

catch 節の中で、カウンターの値を 0 に設定している。
問 I.8.1 Counter.java をカウンターの値が特別な値（例えば 10 の倍数など）になったときは、メッセージを変えたり、色を変えたりするようなサーブレット
KiribanCounter.java に改造せよ。（整数の割算の剰余を求める演算子は、 C 言語と同様 % である。例えば x を 10 で割ったあまりは x % 10 と書く。）
問 I.8.2 アプリケーションルートの images ディレクトリーに、 1.png, 2.pngなどの名前で数字画像ファイルを用意しておいて、このアクセスカウンターや時刻表示 CGI で 1, 2, と表示する代わりに <img src='images/1.png'>,<img src='images/2.png'> などにしておくと、数字を画像で表示するアクセスカウンターができる。
数字を画像として表示するアクセスカウンター ImageCounter.java を作成せよ。
問 I.8.3 数字を画像で表示する時刻表示サーブレット ImageDate.java を作成せよ。
（参考）ファイルを利用しない簡易アクセスカウンター
Servlet のインスタンスは、ページのアクセス毎に生成されるのではなく、いったん生成されると、Web アプリケーションサーバーの中で保持され 2 回目以降のアクセスでは、以前に生成された Servlet のインスタンスが再利用される。このため、フィールドにデーターを保持しておけば、ファイルを使用しなくても次のようなプログラムで簡易アクセスカウンターを実現できる。
ファイル Counter0.java
1 import java.io.IOException;
2 import java.io.PrintWriter;
3
4 import jakarta.servlet.ServletException;
5 import jakarta.servlet.annotation.WebServlet;
6 import jakarta.servlet.http.HttpServlet;
7 import jakarta.servlet.http.HttpServletRequest;
8 import jakarta.servlet.http.HttpServletResponse;9
10 @WebServlet("/Counter0")
11 public class Counter0 extends HttpServlet {12int i = 0;// フィールドとして宣言する
13
14@Override15
protected void doGet(HttpServletRequest request,
16
HttpServletResponse response)17
throws ServletException, IOExce
18
response.setContentType("text/html; charset=UTF19
PrintWriter out = response.getWriter();
20
out.println("<html><head></head><body>");21
out.printf("あなたは %d番目の来訪者です。", i++);22
out.println("</body></html>");23
out.close();// closeを忘れない
24}
25 }

オブジェクト指向言語 補足 第 1 章 p.10

ただし、ファイルに書き込まないので、Web アプリケーションサーバーを再起
動すると、カウンターが 0 に戻ってしまう。完全なアクセスカウンターにするためには、Web アプリケーションサーバーの終了時にカウンターの値をファイルに保存し、起動時にファイルからカウンターの値を読み込むように改造する必要がある。
問 I.8.4 HttpServlet クラスのメソッドを調べて、Web アプリケーションサーバー起動・終了時の保存・読み込み操作を追加し、Counter0.java をより完全なアクセスカウンター Counter1.java に改良せよ。

I.9 ディレクトリー操作
つぎのサーブレットはあるディレクトリーのインデックス（ファイルの一覧）を生成する。
ファイル DirIndex.java
1 import java.io.File;
2 import java.io.IOException;
3 import java.io.PrintWriter;
4
5 import jakarta.servlet.ServletException;
6 import jakarta.servlet.annotation.WebServlet;
7 import jakarta.servlet.http.HttpServlet;
8 import jakarta.servlet.http.HttpServletRequest;
9 import jakarta.servlet.http.HttpServletResponse;10
11 @WebServlet("/DirIndex")
12 public class DirIndex extends HttpServlet {13
protected void doGet(HttpServletRequest request,
14
HttpServletResponse response)15
throws ServletException, IOExce
16
response.setContentType("text/html; charset=UTF17
PrintWriter out = response.getWriter();18
19// ↓適切なパスに変更する
20
String path = getServletContext().getRealPath("/21File dir = new File(path);
22
String[] files = dir.list(); // dir にあるファイル
23
24
out.println("<html><head></head><body>");25
out.println("<pre>");26
27
int i;
28
out.printf("%sのファイル一覧%n%n", path);29
for (i = 0; i < files.length; i++) {30
out.println(files[i]);// files の各要素を順
31}
32
33
out.println("</pre>");34
out.println("</body></html>");35
out.close();
36}
37 }

オブジェクト指向言語 補足 第 1 章 p.11

ディレクトリーの中のファイル名の一覧は、File クラスの list メソッドでString の配列として得ることができる。また、配列の要素の数は length というフィールドを調べることによってわかる。
問 I.9.1 DirIndex.java で、3 日前より変更された日付が新しいファイルには“NEW!” というマークをつけるサーブレット NewDirIndex.java に改造せよ。
例えば、ディレクトリーに old.txt という 4 日前に変更されたファイルとnew.txt という 1 日前に変更されたファイルがあるときは DirIndex は次のような HTML を出力する。
<html><head><title>ディレクトリー</title></head><body><ul><li>new.txt NEW!</li><li>old.txt</li></ul></body></html>
ヒント: java.io.File クラスの lastModified メソッドとjava.util.Calendar クラスの getTimeInMillis メソッドを用いる。

キーワード:
HttpServlet クラス, doGet メソッド, throws, getServletContext メソッド, getRealPath メソッド, File クラス, FileReader クラス,
BufferedReader クラス, FileWriter クラス, PrinterWriter クラス, 例外処理, try 〜 catch 文 , length（配列）オブジェクト指向言語 補足 第 1 章 p.12