Revised: 7th/July/2002
HTTP は stateless と呼ばれる、要求/応答がワンセットごとに独立するように設計されています。これによって、途中でネットワークが切断されても、次回の通信に全く影響を及ぼしません。
要求1: | こんにちは。菅井です。 |
---|---|
応答1: | いらっしゃい、菅井さん。 |
要求2: | ハッピーですか? |
応答2: | いえ、あまり。ところで、お名前は? |
これがステートレスと言うことです。サーバ側では、最初の要求/応答と、二回目の要求/応答が完全に独立しており、セッションになりません。一連の要求/応答をトラッキングするには、その上に乗るアプリケーション側で実装する必要があります。
セッションは同一ユーザからの一連のアクセスを指し、セッション管理はユーザを識別する仕組みです。前回のアクセス時の状況を保持して、次回以降のアクセス時にユーザを識別した処理ができます。
例えば、次のような、一連の処理を継続して処理するために使われます。
ログイン認証から、ログアウトするまで、サーバ側では同一ユーザからのアクセスであることを認識して特定する必要があります。これを行うのがセッション管理です。
セッションは、cookie や URL への埋め込みによって実現しますが、一般に煩雑な作業になります。 Java では、セッション管理の仕組みを言語仕様として持っており、実装方法に無自覚でいられます。 HttpSession
インタフェースを使うと、サーブレット・コンテナが自動的にセッション管理を行ってくれます。
面倒ですが、これも練習です。今回もドキュメント・ルートを作ってみましょう。
server.xml
ドキュメント・ルートを Tomcat に認識させるには、Tomcat がインストールされたディレクトリの、 conf/server.xml
になります。
202 行目付近に次のようなコードがあります。
<!-- Tomcat Manager Context --> <Context path="/manager" docBase="manager" debug="0" privileged="true"></Context>
このコードの次の行から、次のようなコードを記述します。
<!-- Session Examples Context --> <Context path="/session" docBase="C:/java/session" debug="0" reloadable="true" crossContext="true"/>
ドキュメント・ルート C:\java\session
が、コンテキスト・ルート /session
にマッピングされました。 localhost の場合は、 URL http://localhost:8080/session
からアクセスできるようになりました。
server.xml
で設定したドキュメント・ルートに、次のディレクトリ構造を作成します。
![]() |
図: C:\java\session 以下のディレクトリ構造 |
web.xml
ここでは、サーブレットに別名をつけて、アクセスするための URL パターンにマッピングしておきます。この情報はドキュメント・ルート C:\java\sessionDemo
の WEB-INF\web.xml
に記述します。
今から、サーブレット SessionDemo
を作成し、ドキュメント・ルートの WEB-INF\classes
に配置します。このサーブレットに、session
という別名を与えて、 URL http://localhost:8080/session/demo
からアクセスできるように設定します。
C:\java\sessionDemo\WEB-INF\web.xml
を次のように作成します。
<?xml version="1.0" encoding="Shift_JIS"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <!-- サーブレット名 --> <servlet> <servlet-name>session</servlet-name> <servlet-class>SessionDemo</servlet-class> </servlet> <!-- URL パターン --> <servlet-mapping> <servlet-name>session</servlet-name> <url-pattern>/demo</url-pattern> </servlet-mapping> </web-app>
![]() |
図: web.xml の配備 |
ドキュメント・ルート C:\java\sessionDemo
の WEB-INF\classes
に次のようなコードを用意してコンパイルしておきます。
![]() |
図: SessionDemo.java の配備 |
SessionDemo.java
:
import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class SessionDemo extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPerform(request, response); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPerform(request, response); } public void doPerform(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /* セッションの開始 * セッション情報を調べて、存在しなければ作成 */ HttpSession session = request.getSession(true); int cnt; String msg, id; // セッションIDの取得 id = session.getId(); try { // セッションに格納された値の取得 String cntStr = (String)session.getAttribute("counter"); cnt = Integer.parseInt(cntStr); msg = cnt + " 回目のお越しです!"; } catch (NumberFormatException e) { cnt = 1; msg = "はじめまして!"; } cnt++; // セッションに値を格納 session.setAttribute("counter", Integer.toString(cnt)); // 応答文字コードのセット response.setContentType("text/html; charset=Shift_JIS"); // 出力ストリームの取得 PrintWriter out = response.getWriter(); out.println("<html><head>\n" + "<meta http-equiv=\"content-type\" content=\"text/html; charset=Shift_JIS\" />\n" + "<title>セッション・デモ</title>\n" + "</head><body>\n" + "<h1>セッション・デモ</h1>\n" + "<p>" + msg + "</p>\n" + "<p>サーブレットコンテナが割り振ったセッションID:" + id + "</p>\n" + "</body></html>\n"); } }
本稿では、以前 CLASSPATH にサーブレット・パッケージのアーカイブをセットしているので、次のようにコンパイルできます:
C:\>cd C:\java\session\WEB-INF\classes C:\java\session\WEB-INF\classes>javac SessionDemo.java C:\java\session\WEB-INF\classes>
CLASSPATH を設定していない方は、 -classpath
フラッグで、Tomcat のインストール・ディレクトリ内の common\lib\servlet.jar
を設定する必要があります。デフォルトでは C:\Program Files\Apache Tomcat 4.0\common\lib\servlet.jar
です。
このサーブレットは、 server.xml
によって、コンテキスト・ルート /session
にマップされており、更に、 web.xml
によって、 URL パターン /demo
にマップされています。マシン・リソースを http://localhost:8080
で識別すると、このサーブレットは http://localhost:8080/session/demo
でアクセスできます。
server.xml
と web.xml
の更新は、 Tomcat の再起動によって反映します。開始/停止は Tomcat アイコンから実施します。
![]() |
図: Tomcat アイコン |
![]() |
図: Tomcat の開始 |
Tomcat が正常に開始したら、ブラウザで次の URL を要求します。
http://localhost:8080/session/demo
![]() |
図:最初の要求 |
![]() |
図:二回目以降の要求 |
HttpSession
によって、セッションがどのように管理されているか確認します。
最初のアクセス時に HttpSession
オブジェクトが作成され、セッション ID が割り振られます。クライアントには、 HTTP ヘッダに埋め込まれる形で cookie が送付されて ID が保持されます。二回目以降のアクセス時には、クライアントから送られた cookie に記述されたセッション ID から、特定の HttpSession
オブジェクトが識別されて、前回の要求/応答に継続した処理が可能になります。
一般に、 Cookie には文字列だけが保持され、サーバにアクセスする度に、保持している全ての Cookie が送信されます。HttpSession
では、作成する Cookie にはセッション ID だけを格納します。無関係のサーバに送信された Cookie が不正に取得されても、記述されているのは ID だけなので無価値となり、クライアントにとっては安心です。
一方、セッション ID で識別される HttpSession
には任意のオブジェクトが Object
型で保持することが可能です。キーワードを指定してオブジェクトを格納/取り出しが可能です。
クライアントによっては、 Cookie を拒否するように設定しています。非明示的にサーバにローカル・ディスク内の情報を送信される事を嫌うためです。事実、 Cookie を利用した不正アクセスは後を絶ちません。
このようなユーザには、上記の HttpSession
による処理に加えて、 URL に情報を付加するようにします。これを行うのが、 HttpServletResponse
オブジェクトの encodeURL()
メソッドです。これを実装しておけば、 Cookie が利用できる場合はなにもせず、利用できない場合だけ引数の URL を加工して、セッション ID を付加した URL を返します。
String nextURL = response.encodeURL("./secondServlet"); out.println("<a href=\"" + nextURL + "\">次のページ</a>");
JSP では、 HttpSession
オブジェクトは、暗黙オブジェクト session
として作成されています。ここには、 session
スコープのオブジェクトが保持されます。