Servletの作成 †
概要 †
Hello World! - 詳細 †
毎度おなじみ(?)の「Hello World!」をサーブレットで表示することにより、サーブレットの基礎を理解します。
doGet()とdoPost() - 詳細 †
doGet()メソッドとdoPost()メソッドの役割について理解します。
リクエストパラメータの取得 - 詳細 †
リクエストパラメータの取得方法について理解します。
全角文字を含むパラメータの取得 - 詳細 †
全角文字を含むパラメータを、文字化けしないように取得する方法について理解します。
特殊文字のエスケープ - 詳細 †
特殊文字をエスケープする必要性と、その方法を理解します。
他の画面の呼び出し - 詳細 †
他の画面を呼び出す3つの方法と、それぞれの用途について理解します。
画面間の情報の共有 - 詳細 †
画面間で情報を共有する3つの方法と、それぞれの用途について理解します。
サーブレットの初期化・終了処理 - 詳細 †
Webアプリケーションの定義ファイルの書き方と、サーブレットの初期化・終了処理について理解します。
Cookieの読み書き - 詳細 †
サーブレットでCookieを扱う方法を理解します。
バイナリデータの出力 - 詳細 †
サーブレットを使ってバイナリデータを出力する方法を理解します。
総合課題 - 詳細 †
今までに出てきた知識を活用して、簡単なアプリケーションを作成します。
詳細 †
Hello World! †
以下に、「Hello World!」と表示するだけのサーブレット「HelloWorldServlet?」のソースを示します。
サンプルソース:HelloWorldServlet?.java
package sample.servlet;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class HelloWorldServlet extends HttpServlet
{
@param @param @exception @exception
@Override
protected void service(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException
{
res.setContentType("text/html; charset=Windows-31J");
PrintWriter out = res.getWriter();
out.println("<html>");
out.println("<head>");
out.println("<title>Hello World!</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>Hello World!</h1>");
out.println("</body>");
out.println("</html>");
}
}
ポイントを整理すると、次のようになります。
- 「javax.servlet.http.HttpServlet?」クラスを継承する。
- 「java.io」, 「javax.servlet」, 「javax.servlet.http」パッケージは頻繁に使用するので、importしておいた方が良いでしょう。
- service()メソッドをオーバーライドする。
- 場合によっては、service()メソッドでなく、doGet()メソッドやdoPost()メソッドをオーバーライドすることもあります。
詳細については、doGet()とdoPost()を参照して下さい。
- レスポンスにContentType?にタイプと文字コードを設定する。
- この設定を行わないと、全角文字を出力すると文字化けします。
- この設定を変更することで、HTMLだけでなく画像データをバイナリで出力したりすることもできます。
- レスポンスからPrintWriter?を取得する。
- PrintWriter?を使ってHTMLを出力する。
- リクエストクラスとレスポンスクラス
サーブレットでのブラウザとのやり取りは、リクエストクラス(javax.servlet.http.HttpServletRequest?)とレスポンスクラス(javax.servlet.http.HttpServletResponse?)を使って行います。
リクエストクラスの役割は、ブラウザからの入力情報の受け取りです。
フォームの入力パラメーター, クッキー情報, ヘッダ情報などを取得することができます。
一方、レスポンスクラスの役割は、ブラウザへの結果の出力です。
この2つのクラスは頻繁に使用することになりますので、よく覚えておいて下さい。
- 重要
サーブレットでは、絶対にフィールドは使わないようにしましょう。
理由は、サーブレットはマルチスレッドで実行されるからなのですが、完全に理解するのは難しいので詳しくは説明しません。
とにかく、「サーブレットではフィールドは使えない」と覚えておいて下さい。
(ただし、定数は使用しても構いません。)
- setContentType?()に「シフトJIS」を文字コードとして指定したい場合は、「Shift_JIS」は使用せずに「Windows-31J」や「SJIS」を使用して下さい。
これは、「Shift_JIS」のエイリアスがJDKのバージョンによって異なるからです。
なお、「Windows-31J」や「SJIS」はよく似ていますが別の文字集合で、次のような違いがあります。(「Windows-31J」と「MS932」は同じです。)
- 収録されている文字が異なります。たとえば、「Windows-31J」はNEC特殊文字,IBM特殊文字を含んでいますが、「SJIS」は含んでいません。
- 「Windows-31J」は,MS-DOSにおけるNECやIBMの拡張した文字群を収録していますが、一部の重複している文字は、Unicodeの同じコードポイントに割り当てられています。このために、一度読み込んでUnicodeに変換してしまうと、元のファイルに戻すことができないことがあります。
- 一部の文字に対して、Unicodeに変換する際のコードポイントが違います(~, ∥, -, ¢, £, ¬など)。
(この問題についての詳細は、http://www.ingrid.org/java/i18n/encoding/shift_jis.htmlを参照して下さい。)
- サーブレットでは、HTMLのMETA要素で文字コードの指定を行うとかえって文字化けの原因となりますので、使わないようにしましょう。
(この問題についての詳細は、http://www.ingrid.org/java/jserv/i18n/corruptedchar.htmlを参照して下さい。)
- 課題
上の例では、「Hello World!」と固定で出力するだけなので面白みがありませんし、それだけの機能を実現するならHTMLで十分です。
そこで、プログラムらしくこのような九九の表を作成するサーブレット「KukuServlet?」を作成して下さい。
doGet()とdoPost() †
- GETメソッドとPOSTメソッド
- サーバーがクライアント(ブラウザ)からパラメータを受け取る方法として、GETメソッドとPOSTメソッドがあります。
(他にもありますが、この二つを理解できれば十分です。)
- POSTメソッド
<form>タグで「method="post"」と指定した場合に使用される送信方法です。
<input>タグのnameにパラメータ名、valueにパラメータの初期値を設定します。
これはテキストボックスやテキストエリアなどのユーザの入力値をサーバへ渡す場合によく使用されます。
(例)
<form action="/xxx/サーブレット名" method="post">
<input type="text" name="param1" value="textの初期値"> --- テキストボックス
<textarea name="param2">textareaの初期値</textarea> --- テキストエリア
<input type="hidden" name="param3" value="hiddenの値"> --- ブラウザからは見えない
</form>
- doGet()とdoPost()
GETメソッドでサーブレットが呼ばれた場合、doGet()というメソッドが呼び出されます。
一方、POSTメソッドでサーブレットが呼ばれた場合、doPost()というメソッドが呼び出されます。
この仕組みを使えば、同じサーブレットでもGETメソッドで呼び出した場合とPOSTメソッドで呼び出した場合で異なる処理を行わせることができます。
サンプルソース:GetPostServlet?.java
package sample.servlet;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class GetPostServlet extends HttpServlet {
@param @param @exception @exception
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
res.setContentType("text/html; charset=Windows-31J");
PrintWriter out = res.getWriter();
out.println("<html>");
out.println("<head>");
out.println("<title>GetPostServlet</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>GETメソッドでサーブレットが呼び出されました</h1>");
out.println("</body>");
out.println("</html>");
}
@param @param @exception @exception
protected void doPost(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
res.setContentType("text/html; charset=Windows-31J");
PrintWriter out = res.getWriter();
out.println("<html>");
out.println("<head>");
out.println("<title>GetPostServlet</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>POSTメソッドでサーブレットが呼び出されました</h1>");
out.println("</body>");
out.println("</html>");
}
}
このサーブレットは、GETメソッドで呼び出された場合は「GETメソッドでサーブレットが呼び出されました」、
POSTメソッドで呼び出された場合は「POSTメソッドでサーブレットが呼び出されました」と表示します。
ちなみに、最初に説明したservice()メソッドは、GETメソッドの場合もPOSTメソッドの場合も両方呼び出されます。
そこで、GETメソッドとPOSTメソッドで処理を分ける必要がない場合は、service()メソッドをオーバーライドすればどちらにも対応できます。
(実際は、service()メソッドのデフォルトの動作でリクエストのヘッダ情報に基づいてdoGet()やdoPost()を呼び分けているだけなので、この処理自体を上書きしてしまうというわけです。)
- 課題
上のサーブレットを、GETメソッドとPOSTメソッドの両方で呼び出すHTMLを作成し、実際に呼び出してみて下さい。
リクエストパラメータの取得 †
- getParameter()
リクエストパラメータは、リクエストクラス(javax.servlet.http.HttpServletRequest?)の getParameter(String name) メソッドで取得することができます。
このとき、nameにはパラメータ名を指定します。
対応するパラメータが存在しない場合はnullが返されます。
サンプルソース:GetParameterServlet?.java
package sample.servlet;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class GetParameterServlet extends HttpServlet {
@param @param @exception @exception
@Override
protected void service(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
res.setContentType("text/html; charset=Windows-31J");
PrintWriter out = res.getWriter();
out.println("<html>");
out.println("<head>");
out.println("<title>リクエストパラメータの取得</title>");
out.println("</head>");
out.println("<body>");
out.println("<center><h1>リクエストパラメータの取得</h1></center>");
out.println("<hr>");
String param = req.getParameter("param");
out.println("param=" + param);
out.println("</body>");
out.println("</html>");
}
}
上記で作成したサーブレットを呼び出すためのHTMLコード
callGetParameterServlet.html
- getParameterValues?()
チェックボックスなどで同じ名前のパラメータが複数送られた場合、getParameter() メソッドでは最初の値しか取得できないので問題が生じます。
そういう場合は、getParameterValues?(String name) メソッドを使用すれば、全ての値を配列で取得することができます。
パラメータに値が1つしかない場合は、配列の大きさは1です。
getParameter() メソッドと同じく、nameにはパラメータ名を指定し、対応するパラメータが存在しない場合はnullが返されます。
サンプルソース:GetParameterValuesServlet?.java
package sample.servlet;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class GetParameterValuesServlet extends HttpServlet {
@param @param @exception @exception
@Override
protected void service(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
res.setContentType("text/html; charset=Windows-31J");
PrintWriter out = res.getWriter();
out.println("<html>");
out.println("<head>");
out.println("<title>リクエストパラメータを配列で取得</title>");
out.println("</head>");
out.println("<body>");
out.println("<center><h1>リクエストパラメータを配列で取得</h1></center>");
out.println("<hr>");
String[] param = req.getParameterValues("param");
out.print("param=");
if (param == null) {
out.println("null");
}
else {
for (int i = 0; i < param.length; i++) {
if (i != 0) {
out.print(", ");
}
out.print(param[i]);
}
out.println();
}
out.println("</body>");
out.println("</html>");
}
}
上記で作成したサーブレットを呼び出すためのHTMLコード
callGetParameterValuesServlet.html
- getParameterNames?()
パラメータ名がわからない場合は、getParameterNames?() メソッドで全てのパラメータ名を取得することができます。
ただし、この戻り値は文字列の配列ではなく java.util.Enumeration なので注意が必要です。
サンプルソース:GetParameterNamesServlet?.java
package sample.servlet;
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class GetParameterNamesServlet extends HttpServlet {
@param @param @exception @exception
@Override
protected void service(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
res.setContentType("text/html; charset=Windows-31J");
PrintWriter out = res.getWriter();
out.println("<html>");
out.println("<head>");
out.println("<title>全てのリクエストパラメータを取得</title>");
out.println("</head>");
out.println("<body>");
out.println("<center><h1>全てのリクエストパラメータを取得</h1></center>");
out.println("<hr>");
for (Enumeration<String> e = req.getParameterNames(); e
.hasMoreElements();) {
String paramName = e.nextElement();
String paramValue = req.getParameter(paramName);
out.println(paramName + "=" + paramValue + "<br>");
}
out.println("</body>");
out.println("</html>");
}
}
上記で作成したサーブレットを呼び出すためのHTMLコード
callGetParameterNamesServlet.html
- 重要
- 上のサンプルプログラムでは、パラメータの値に全角文字が含まれていると"?"に文字化けしてしまいます。
この問題の解決方法は、次の全角文字を含むパラメータの取得で解説します。
- 上のサンプルプロプラムのように取得したパラメータの値をそのまま出力するのは、実は重大なセキュリティホールにつながるので危険です。
詳しくは、特殊文字のエスケープで解説します。
- 課題
2つの整数をリクエストパラメータとして受け取り、足し算を行うサーブレット「CalcServlet?」を作成して下さい~。
(整数以外が入力された場合はエラーが発生しても構いませんが、余力がある場合は「整数を入力して下さい」というメッセージが表示されるようにしてみて下さい。)
callCalcServlet.html
全角文字を含むパラメータの取得 †
Tomcatで全角文字を含むパラメータを、getParameter()メソッドで普通に取得すると、"?"に文字化けしてしまいます。
そこで、以下のような特殊な変換処理を行う必要があります。
サンプルソース:GetJapaneseParameterServlet?.java
package sample.servlet;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class GetJapaneseParameterServlet extends HttpServlet {
@param @param @exception @exception
@Override
protected void service(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
res.setContentType("text/html; charset=Windows-31J");
PrintWriter out = res.getWriter();
out.println("<html>");
out.println("<head>");
out.println("<title>全角文字を含むパラメータの取得</title>");
out.println("</head>");
out.println("<body>");
out.println("<center><h1>全角文字を含むパラメータの取得</h1></center>");
out.println("<hr>");
String param = req.getParameter("param");
byte[] bytes = param.getBytes("8859_1");
String japaneseParam = new String(bytes, "Windows-31J");
out.println("param=" + japaneseParam);
out.println("</body>");
out.println("</html>");
}
}
上記で作成したサーブレットを呼び出すためのHTMLコード
callGetJapaneseParameterServlet.html
このソースで行っている処理を完全に理解する必要はありません。
今は「お約束」として覚えておくだけで結構です。
なお、new String(bytes, "Windows-31J")の部分は、 java.io.UnsupportedEncodingException? という例外を投げるので注意が必要です。
また、getParameter() でパラメータが存在せずにnullが返された場合、変換処理を行う際に java.lang.NullPointerException? が発生します。
- 重要
- 全角文字を含むパラメーターが文字化けするかどうかは、アプリケーションサーバによって異なります。
アプリケーションサーバの中には、全角文字を含むパラメータに対応する処理を自動的に行ってくれるものもあります。
そのようなアプリケーションサーバでこの変換処理を行うと、二重に処理が行われてしまい、かえって文字化けしてしまうので注意が必要です。
また、パラメータの取得の度にこの変換処理を行っていたら、クライアントの文字コードが変わったときの修正が大変です。
そこで、通常はサーブレットで getParameter()を使用する度に変換処理を直接実装するのではなく、パラメータの変換処理を専門に行うクラスを作成し、その中で必要に応じて変換処理を行います。
そうすると、環境依存の少ない、汎用性の高いプログラムを作成することができます。
- 課題
上の説明にある、全角文字を含むパラメータの変換処理を専門に行うクラス「ParameterConverter?」を作成してみましょう。
このクラスは、コンストラクタでリクエストクラスのインスタンスを受け取り、getParameter(String name) メソッドで全角パラメータを変換してから返します。
変換を行うかどうかのフラグと、クライアントの文字コードは定数にしておくとよいでしょう。
(余力があれば、getParameterValues?(String name) メソッドも作成してみましょう。)

特殊文字のエスケープ †
- 特殊文字をエスケープする必要性
パラメータの値をそのまま表示するような処理を行っている場合、タグなどの特殊文字が入力された場合に問題が生じます。
例えば、パラメータの値が「<input>」という文字列の場合、そのまま表示するとinputタグの入力フィールドが表示されてしまいます。
さらには、パラメータにJavaScript?などが渡された場合、訪問者のブラウザから不正にCookie情報を取得したり、架空のフォームを表示させて訪問者から個人情報等を収集される可能性もあります。
この問題は「クロスサイトスクリプティング問題」と呼ばれ、重大なセキュリティホールとなります。
この問題を回避するためには、次のように特殊文字を「&xxx;」という形式の代替文字にエスケープする必要があります。(最後の「;」は忘れやすいので注意)
| 特殊文字 | 代替文字 |
| & | &; |
| < | <; |
| > | >; |
| ” | "; |
| (半角スペース) |  ; |
- (補足)
- 半角スペースは変換しなくてもセキュリティ上の問題はありません。
しかし、変換しないとHTMLの仕様上、複数のスペースが連続していても1つにまとめられてしまうので、入力データと出力結果が違って見えてしまいます。
- 課題
上記の特殊文字をエスケープする専用のメソッドを作成し、パラメータを出力する場合は必ずそのメソッドを通すようにします。
さらに、そのメソッドを特殊文字のエスケープを行う専用のクラスに定義しておくと、どのサーブレットやJSPからも使用することができるので、より汎用性の高いプログラムを作成することができます。
解答サンプル:
EscapeUtil.java
他の画面の呼び出し †
- リダイレクト
レスポンスクラス(javax.servlet.http.HttpServletResponse?)の sendRedirect(String location) メソッドで、指定したURLにリダイレクトすることができます。
この方法では外部のサイトのURLも指定することができます。
しかし、この方法では遷移先のページにパラメータを引き渡すことはできないので、同じWebアプリケーションの別のURLに遷移させたい場合は、通常は下のフォワードを使用します。
サンプルソース:SendRedirectServlet?.java
package sample.servlet;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class SendRedirectServlet extends HttpServlet {
@param @param @exception @exception
@Override
protected void service(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
res.sendRedirect("http://www.google.com");
}
}
- フォワード
RequestDispatcher?(javax.servlet.RequestDispatcher?)の forward(ServletRequest? request, ServletResponse? response) メソッドで、サーバ上のリソース(サーブレット, JSPファイル, HTMLファイル)にリクエストをフォワードすることができます。
このRequestDispatcher?というクラスのインスタンスは、リクエストクラス (javax.servlet.http.HttpServletResponse?)またはサーブレットコンテキストクラス (javax.servlet.ServletContext?)の getRequestDispatcher?(String path) メソッドで取得することができます。
この方法では遷移先のページにパラメータを引き渡すことができます。
path に指定するURLには、Webアプリケーション名は含めないので注意して下さい。
また、自分自身のWebアプリケーション内のリソースにしかアクセスできないので、外部のサイトのURLや、同じサーバの別のWebアプリケーションのURLを指定することはできません。
サンプルソース:ForwardServlet?.java
package sample.servlet;
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ForwardServlet extends HttpServlet {
@param @param @exception @exception
@Override
protected void service(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
RequestDispatcher rd = req
.getRequestDispatcher("/servlet/GetParameterServlet");
rd.forward(req, res);
}
}
- インクルード
RequestDispatcher?(javax.servlet.RequestDispatcher?)の include(ServletRequest? request, ServletResponse? response) メソッドで、サーバ上のリソース(サーブレット, JSPファイル, HTMLファイル)の内容をレスポンスにインクルードすることができます。
インクルードというのは、簡単に言えば部品として取り込むということです。
このRequestDispatcher?というクラスのインスタンスは、フォワードの場合と同じく、リクエストクラス (javax.servlet.http.HttpServletResponse?)またはサーブレットコンテキストクラス (javax.servlet.ServletContext?)の getRequestDispatcher?(String path) メソッドで取得することができます。
path に指定するURLには、Webアプリケーション名は含めないので注意して下さい。
また、自分自身のWebアプリケーション内のリソースにしかアクセスできないので、外部のサイトのURLや、同じサーバの別のWebアプリケーションのURLを指定することはできません。
サンプルソース:IncludeServlet?.java
package sample.servlet;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class IncludeServlet extends HttpServlet {
@param @param @exception @exception
@Override
protected void service(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
res.setContentType("text/html; charset=Windows-31J");
PrintWriter out = res.getWriter();
out.println("<html>");
out.println("<head>");
out.println("<title>インクルード</title>");
out.println("</head>");
out.println("<body>");
out.println("<center><h1>インクルード</h1></center>");
out.println("<hr>");
out.println("インクルード開始>>><br>");
RequestDispatcher rd = req.getRequestDispatcher("/html/hogehoge.html");
rd.include(req, res);
out.println("<<<インクルード終了<br>");
out.println("</body>");
out.println("</html>");
}
}
この結果として、このような画面が生成されます。
includeResult.html
画面間の情報の共有 †
- リクエスト・セッション・アプリケーション属性
サーブレットでは、画面間で情報を共有するために、リクエスト・セッション・アプリケーション属性という3つの方法が用意されています。
(これらは、変数を入れておく箱のようなものだと思って下さい。)
| 種類 | 管理しているクラス | 管理しているクラスの取得方法 | 管理しているクラスの取得方法 | 対象 | 主な用途 |
| リクエスト | javax.servlet.http.HttpServletRequest?(リクエストクラス) | service(), doGet(), doPost()メソッドの引数として与えられる | 現在のリクエストが終了するまで | リクエストごと | JSPへの情報の引渡し |
| セッション | javax.servlet.http.HttpSession?(セッションクラス) | リクエストクラスの getSession() メソッド | 最初にセッションオブジェクトが作成されてから消去されるまで(タイムアウト有り) | ユーザごと | 画面間の情報の引渡し |
| アプリケーション | javax.servlet.ServletContext?(サーブレットコンテキストクラス) | サーブレットの getServletContext?() メソッド | アプリケーションの開始から終了までずっと | アプリケーション全体 | アプリケーション全体で共有する情報(あまり使用されない) |
<注>
アプリケーションサーバを再起動すると、基本的に全ての属性が削除されます。(ときどき削除されないこともあります)
属性を登録・取得・削除するには、管理しているクラスの以下のメソッドを使用します。
イメージとしては、リクエストパラメータと同じように、名前とオブジェクトを関連付けて登録しておいて、後で名前をキーにしてオブジェクトを取り出すといった感じです。
なお、属性の値には int などの基本データ型は使用することはできません。
| メソッド | 機能 |
| void setAttribute(String name, Object object) | 属性名にオブジェクトを結びつけて登録します。指定した名前がすでに使用されている場合は、上書きされます。 |
| Object getAttribute(String name) | 指定した名前に対応する属性を返します。指定された名前に一致する属性が無い場合は nullを返します。 |
| java.util.Enumeration getAttributeNames?() | 全ての属性名を返します。 |
| void removeAttribute(String name) | 指定した名前に対応する属性を削除します。 |
サンプルソース:UseAttributeServlet?.java
package sample.servlet;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class UseAttributeServlet extends HttpServlet {
@param @param @exception @exception
@Override
protected void service(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
HttpSession session = req.getSession();
ServletContext sc = getServletContext();
req.setAttribute("RequestAttribute", new Integer(1));
session.setAttribute("SessionAttribute", new Integer(2));
sc.setAttribute("ApplicationAttribute", new Integer(3));
res.setContentType("text/html; charset=Windows-31J");
PrintWriter out = res.getWriter();
out.println("<html>");
out.println("<head>");
out.println("<title>リクエスト・セッション・アプリケーション属性の使用</title>");
out.println("</head>");
out.println("<body>");
out.println("<center><h1>リクエスト・セッション・アプリケーション属性の使用</h1></center>");
out.println("<hr>");
Integer requestAttribute = (Integer) req
.getAttribute("RequestAttribute");
Integer sessionAttribute = (Integer) session
.getAttribute("SessionAttribute");
Integer applicationAttribute = (Integer) sc
.getAttribute("ApplicationAttribute");
out.println("リクエスト属性=" + requestAttribute);
out.println("セッション属性=" + sessionAttribute);
out.println("アプリケーション属性=" + applicationAttribute);
out.println("</body>");
out.println("</html>");
}
}
- セッション属性を使用する際の注意
- セッションをむやみに使いすぎない
セッションはユーザごとに作成されるので、あまり大きなオブジェクトをセッション属性に登録するのは危険です。
たとえ1ユーザあたりのメモリ使用量は大したことはなくても、10,000人~100,000人規模のユーザが使用した場合には大量のメモリを使用することになり、メモリエラーを引き起こす可能性があります。
- 不要になった属性は直ちに削除する
セッションに登録されているオブジェクトは、ガベージコレクションの対象になりません。
そのため、不要になった属性をいつまでも削除せずに放置しておくと、メモリが足りなくなる恐れがあります。
- セッションがタイムアウトになった場合を想定したプログラムを組む
セッションがタイムアウトになると、getAttribute() メソッドで属性を取得しようとしたときにnullが返されます。
そのため、これを考慮したプログラムを組んでおかないと、取得した値を使用しようとしたときに java.lang.NullPointerException? が発生してしまいます。
なお、セッションのタイムアウト時間は設定ファイルで変更できます。(Tomcatの場合、デフォルトでは30分)
- 課題
セッション属性を使用して、呼び出される度にカウントを増やしていき、そのカウントを画面に表示するサーブレット「SessionCountServlet?」を作成して下さい。
画面イメージ
count.html
サーブレットの初期化・終了処理 †
- web.xml
今までは、Webアプリケーションのデフォルトの設定でサーブレットを動かしてきましたが、通常のシステムではある程度カスタマイズするのが一般的です。
Tomcatでは、「web.xml」というXMLファイルでWebアプリケーションの定義を行います。
(このファイルは、「WEB-INF」ディレクトリの直下に配置します。)
このファイルでは、サーブレットの初期化パラメータや初期化のタイミング、サーブレットのURLマッピング、ウェルカムページなどを設定することができます。
- init()
サーブレットでは、init(ServletConfig? config) メソッドをオーバーライドすることにより、初期化処理を行うことができます。
この初期化処理は、サーブレットが最初に呼び出されたときに1回だけ呼び出されます。
ただし、定義ファイルの設定によっては、アプリケーションサーバの起動時に初期化処理を行わせることも可能です。
なお、定義ファイルから初期化パラメータを渡すこともできます。
初期化パラメータを取得するには、サーブレットコンフィグクラス(javax.servlet.ServletConfig?)の getInitParameter?(String name) メソッドを使用します。
- destroy()
サーブレットでは、destroy() メソッドをオーバーライドすることにより、終了処理を行うことができます。
この終了処理は、サーブレットが廃棄されるとき(アプリケーションサーバが停止するとき)に1回だけ呼び出されます。
サンプルソース:InitDestroyServlet?.java
package sample.servlet;
import javax.servlet.*;
import javax.servlet.http.*;
public class InitDestroyServlet extends HttpServlet {
@param @exception
@Override
public void init(ServletConfig config) throws ServletException {
String initParam1 = config.getInitParameter("InitParam1");
String initParam2 = config.getInitParameter("InitParam2");
System.out.println("*** InitDestroyServlet の初期化処理 ***");
System.out.println("InitParam1=" + initParam1);
System.out.println("InitParam2=" + initParam2);
}
public void destroy() {
System.out.println("*** InitDestroyServlet の終了処理 ***");
}
}
Cookieの読み書き †
- サーブレットでCookieは必要か?
サーブレットでは、セッションを使うことにより画面間の情報の共有を行うことができるので、Cookieを使用する必要性はあまりありません。
また、セッションでは任意のオブジェクトを扱えるのに比べて、Cookieでは文字列しか扱うことができません。
それに、Cookieはクライアントのブラウザが受け入れないかぎり使用することができません。
これらの理由から、サーブレットでCookieを使用することはまれです。
しかし、既存のシステムと連携する場合や、複数の会社やプロジェクトチームによって開発が行われる場合などは、セッションが使用できないのでCookieを使用することがあります。
- Cookieクラス
サーブレットでCookieを扱うにはCookieクラス(javax.servlet.http.Cookie)を使用します。
このクラスには、Cookieの名前・値といった基本情報のほかに、コメントやパス・ドメイン・最長存続期間・バージョンといったオプション情報のsetter, getter が用意されています。
- Cookieの取得
既存のCookieの取得は、リクエストクラス(javax.servlet.http.HttpServletRequest?)の getCookies() メソッドで行います。
ただし、このメソッドでは全てのCookieを配列で取得することしかできないので、目的のCookieを取得するには、下のサンプルソースのように自分でループをまわして名前の一致するCookieを検索する必要があります。
- Cookieの設定
Cookieを設定するには、まずCookieクラスのインスタンスを生成する際に、コンストラクタで名前と値を決定します。
そして、レスポンスクラス(javax.servlet.http.HttpServletResponse?)クラスの addCookie(Cookie cookie) メソッドに生成したCookieのインスタンスを渡すことにより、実際にレスポンスにCookieを追加します。
サンプルソース:CookieUseServlet?.java
package sample.servlet;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class CookieUseServlet extends HttpServlet {
@param @param @exception @exception
@Override
protected void service(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
res.setContentType("text/html; charset=Windows-31J");
PrintWriter out = res.getWriter();
out.println("<html>");
out.println("<head>");
out.println("<title>Cookieの使用</title>");
out.println("</head>");
out.println("<body>");
out.println("<center><h1>Cookieの使用</h1></center>");
out.println("<hr>");
Cookie[] cookies = req.getCookies();
Cookie testCookie = null;
if (cookies != null) {
for (int i = 0; i < cookies.length; i++) {
if (cookies[i].getName().equals("testCookie")) {
testCookie = cookies[i];
break;
}
}
}
if (testCookie == null) {
out.println("Cookie「testCookie」が見つかりませんでした");
Cookie cookie = new Cookie("testCookie", "CookieValue");
res.addCookie(cookie);
}
else {
out.println("testCookie=" + testCookie.getValue());
}
out.println("</body>");
out.println("</html>");
}
}
バイナリデータの出力 †
- サーブレットでバイナリデータを出力する必要性
サーブレットでは、通常のHTML以外にバイナリデータを出力することもできます。
例えば、サーバー上の任意のファイルをダウンロードしたり、画像データをファイルやデータベースから読み込んでそのままブラウザに出力することもできます。
この機能を使えば、ファイルがダウンロードされた回数をカウントしたり、ランダムに画像を切り替えたりすることもできます。
- 画像表示用サーブレット
以下に、画像用のサーブレット「ImageServlet?」のソースを示します。
このサーブレットは、画像ファイルを読み込んでその内容をバイナリデータとしてそのまま出力しています。
サンプルソース:ImageServlet?.java
package sample.servlet;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class ImageServlet extends HttpServlet {
public static final String IMAGE_FILE = "/temp/hoge.png";
@param @param @exception @exception
@Override
protected void service(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
BufferedInputStream in = new BufferedInputStream(new FileInputStream(
IMAGE_FILE));
BufferedOutputStream out = new BufferedOutputStream(res
.getOutputStream());
int c = -1;
while ((c = in.read()) != -1) {
out.write(c);
}
out.flush();
out.close();
in.close();
}
}
このサーブレットを呼び出すHTML「
callImageServlet.html」の内容は以下の通りです。
<img>タグのsrc属性に、画像ファイルを指定する代わりにサーブレットを指定しています。
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=Shift_JIS">
<title>画像表示サーブレットの呼び出し</title>
</head>
<body>
<center>
<h1>画像表示サーブレットの呼び出し</h1>
<hr>
<h2>これはサーブレットから出力された画像です</h2>
<img src="/kensyu/servlet/ImageServlet">
</center>
</body>
</html>
総合課題 †
Java勉強会カリキュラムに戻る