Struts1 [セッション管理]

セッションとは、同一クライアントからの一連のリクエストからなる接続単位です。Tomcat によって実現された セッション管理のメカニズムを用いる事で、よりクライアントを意識した動的コンテンツを実現する事が可能になります。struts [validator]のサンプル(bbs02.war) をベースに、実装してゆきます。bbs04 として使います。今回のサンプル(bbs04.war)
更新日 2016-02-13

セッションについて

セッションとは、クライアントとサーバーにおける継続したコミュニケーションの単位を意味します。セッションはそれぞれのクライアントの最初のアクセス より生じ、一定時間以上のアクセス延滞を持って終了となります。
従って個々のリクエストがどのクライアントから送られてきたかサーバー側で識別したり、 クライアント固有の情報をサーバーに保持させる事も不可能です。従って、既存のHTTP アーキテクチャーの上でウェブアプリを作る場合、 常にクライアント側でデータを保持させる必要がありました。
これに対しTomcat は、同一クライアントからの複数のリクエストをセッションという接続単位に識別・分類できます。そしてセッションとデータを 関連付けてサーバー上に保持できるのです。
これを上記の例で説明するならば、店員が顧客毎の買い物カゴを用意してくれてる状況でして、こちらは 純粋に商品の追加を指示するだけです。店員は顧客の顔を覚えているので、他人のカゴと間違えもせず追加してくれます。一定時間リクエストがこなければ タイムアウトとなり、店員はカゴをリセットし、顔も忘れるという訳です。

セッション管理の仕組み

HTTP (Hyper Text Transfer Protocol)は、(クライアントからの)リクエストと、それに対する(サーバーからの)レスポンスの1度限りのやり取りで 完結する極めてシンプルな仕組みを提供します。リクエストとレスポンスに含む事ができるCookie を利用します。最初のリクエストに対するレスポンスに、 サーバー側で動的に作成した一意な値(セッションID)をCookie に忍ばせて返します。以降のリクエストのCookie 中にその値があるかどうかで判別しています。 またCookie を無効にしてあるブラウザに対してはURL にjsessionid というパスパラメータ形式のセッションIDを沿える事でフォローしています。 そして実際の実装においても、html:link やhtml:rewrite、logic:redirect といったstruts タグを用いる事でセッション管理を意識する必要は全くありません。
Tomcat のセッション維持用のクッキーは、ブラウザが閉じられるまで有効です。

入力されたデータをセッションが無効になるまで保持し続ける。

アクションフォームはリクエストスコープなので、せっかく受け取ったリクエストパラメータもあっけなく消えてしまいます。 ショッピングカートのようにセッション間保持するには、セッションスコープに割り当てたインスタンスに頼る必要があります。 ちなみにセッションそのものはbbs.jsp のpage ディレクティブでsession 属性にfalse としない限りデフォルトで有効となります。
方法としては2つあります。アクションクラス内で実際にString インスタンスをnew してセッションスコープに関連付ける方法。もう一つはbbs.jsp 側で jsp:useBean タグを用いてインスタンスを生成する方法です。比較として両方実装してみます。

bbs.jsp を変更する。

bean:write を変更してsession_strA ならびにsession_strB の二つを書き出します。どちらもセッションスコープから 探し出します。session_strB に関してはjsp:useBean にてインスタンス化する事を明示します。sesstion_strA は アクションクラス内でインスタンス化させます。
<%@page contentType="text/html; charset=Shift_JIS" pageEncoding="Shift_JIS" %>
<%@ taglib uri="/tags/struts-html" prefix="html" %>
<%@ taglib uri="/tags/struts-bean" prefix="bean" %>

<jsp:useBean id="session_strB" class="java.lang.String" scope="session" />

<html:html locale="true">
<head>
	<title>bbs.jsp</title>
</head>
<body>
	<div>
		<html:form action="/bbs" method="POST">
			<html:text property="str" size="20" />
			<html:submit property="submit" value="実行" />
		</html:form>
	</div>
	<pre><bean:write name="bbs_dvf" property="str" scope="request" /></pre>
- (session_strA)
	<pre><bean:write name="session_strA" scope="session" ignore="true" /></pre>
- (session_strB)
	<pre><bean:write name="session_strB" scope="session" /></pre>
</body>
</html:html>
session_strA インスタンスは、一番最初にbbs.jsp をリクエストした段階では存在しないのですから、その場合例外で無く無視させる方向でignore 属性を true に設定しておきます。

アクションクラスの変更

HttpServletRequest::getSession() メソッドを使ってセッションオブジェクトを取得できます。セッションスコープの インスタンスはコレを経由して取得できるので試みます。取得できなければ存在しない訳で、さっそくnew します。(session_strB については jsp:useBean でインスタンス化されるのでnew の必要は無い)
入力データ文字列と改行をセッションインスタンスに追加します。
// Compile command. You must be current file's directry.
// javac -classpath "../lib/struts.jar;../../../../common/lib/servlet-api.jar;"
 -d "." bbsAction.java

package mypackage;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.DynaActionForm;
import org.apache.struts.validator.DynaValidatorForm;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;                  // 追加

public class bbsAction extends Action
{
    public ActionForward execute
    (
        ActionMapping mapping,
        ActionForm form,
        HttpServletRequest req,
        HttpServletResponse res
    )
    throws Exception
    {
        // セッションオブジェクトを取得
        HttpSession ses = req.getSession();

        // アクションフォームにアクセスできます。
        DynaValidatorForm myForm = (DynaValidatorForm)form;   // 修正

		String strLogout = "logout";
        String str = (String)myForm.get( "str" );       // 修正
		if (str.equals(strLogout))
		{
			ses.invalidate();
		}
		else
		{
			{
				String session_str = (String)ses.getAttribute( "session_strA" );
				if (session_str == null)
				{
					session_str = new String();
				}
				session_str += str + ";";
				ses.setAttribute( "session_strA", session_str);
			}

			{
				String session_str = (String)ses.getAttribute( "session_strB" );
				if (session_str != null)
				{
					session_str += str + ";";
					ses.setAttribute( "session_strB", session_str);
				}
			}
		}

        return mapping.findForward( "success" );
    }
}
インスタンスの値を変更したらsetAttribute() を使って再設定しないといけません。getAttribute で 渡されるのは値のコピーだからです。
ちなみにlogout と入力して送信するとセッションは消去されます。もちろんbbs.jsp を返す時に自動的に新規作成はされるでしょうけど。 session_strA もsession_strB もセッションスコープでしか生存しえません。

動作テスト

入力を続ける度にセッションスコープのString インスタンス(session_strA ならびにsession_strB) に追記されてれば成功です。 logout と送信して、変化を確かめて下さい。