Javaに関する様々な情報をご紹介します。

Javaに関する様々な情報をご紹介します。
評価

0

HttpSession.isNew()について

http://www.javaroad.jp/servletjsp/sj_servlet6.htm
のページを見て勉強中です。
isNewの挙動が知りたくて以下のようなサーブレットを作り動作させました。
ボタンをクリックすると正しくカウントアップしますが、その後にアドレスバーをクリックしてEnterで再表示させると別セッションとして認識しているようです。ただ、IDは同じになっていました。
その後、ボタンをクリックすると先ほど(アドレスバー操作以前)のカウント値から継続してカウントアップされています。
F5でリロードすると再送信メッセージは出ますが、カウントアップされていました。

1)アドレスバーをクリックした時、(IDが同じなのに)なぜ同一セッションと認識できないのでしょうか。
2)アドレスバーをクリックした時に同一のセッションとして認識させるにはどの様にすれば良いのでしょうか。

環境:
tomcat 6.0.20
JDK 1.6.0_18
IE8


public class SessionServlet2 extends HttpServlet {
  private final String attrCnt = "cnt";

  public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
    func(req, res);
  }

  public void doPost(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException{
    func(req, res);
  }

  private void func(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException{
    String sCnt = "0";
    long lCnt = 0;
    String sId;

    HttpSession hs1 = req.getSession();
    sId = hs1.getId();

    PrintWriter out = res.getWriter();
    out.println("<HTML>");
    out.println("<BODY>");

    if (hs1.isNew()) {
      hs1.setAttribute(attrCnt, sCnt);
      out.println("New Page<br>");
    } else {
      sCnt = (String)hs1.getAttribute(attrCnt);
      if(sCnt == null){
        sCnt = "0";
        hs1.setAttribute(attrCnt, sCnt);
      }
      else{
        lCnt = Long.parseLong(sCnt)+1;
        if(lCnt < 100){
          sCnt = Long.toString(lCnt);
          hs1.setAttribute(attrCnt, sCnt);
        }
      }
    }
    out.println("Count:" + sCnt + "<br>");
    out.println("Id:" + sId + "<br>");

    out.println("<form method=\"post\" action=\"./SessionServlet\">");
    out.println("<input type=\"submit\" value=\"SessionManagement\">");
    out.println("</form>");

    out.println("</BODY>");
    out.println("</HTML>");
  }
}

18

回答

82164

閲覧

18件の回答

評価

0

スレ主さんの言う「同一セッション」は、仕様とはちょっと違うな。

ぶっちゃけていうと、スレ主さんの疑問は、
セッションが同一であるかどうかとは無関係です。
そもそも、HttpSession#isNew()は、セッションが同一であるかどうかを判定するものではない。

APIドキュメントを読みましょう。書いてあります。

http://mergedoc.sourceforge.jp/tomcat-servletapi-5-ja/javax/servlet/http/HttpSession.html#isNew%28%29

評価

0

不良社員さん、レスありがとうございます。

> ぶっちゃけていうと、スレ主さんの疑問は、セッションが同一であるかどうかとは無関係です。
>
その通りみたいです。試しにisNew() 関係なしに「hs1.getAttribute(attrCnt)」としても値の取得はできないようです。
アドレスバーを操作した時も継続してカウントアップする様にしたいのですが、どのようにすればよいのでしょうか。

> そもそも、HttpSession#isNew()は、セッションが同一であるかどうかを判定するものではない。
>
http://www.javaroad.jp/servletjsp/sj_servlet6.htm
のページのisNewの説明を読んでいたのですが、勘違いしてしまったようです。
「初回アクセス、クライアントがセッションを管理しているかの状態を返す=同一セッションか判定?」みたいに思ってしまいました。
ブラウザのアクセスバーでEnterを押すと、初回アクセス(クライアントがセッション管理していない状態?)と認識されてしまうと言うことでしょうか。

評価

0

>アドレスバーを操作した時も継続してカウントアップする様にしたいのですが、どのようにすればよいのでしょうか。

アドレスバーの操作じゃ、クッキーは送られないから、セッションでの対応は無理だろ。
サーバー側で、セッションIDごとに、カウントを保存するか、
カウントをパラメータに貼り付けておいて、
サーバー側でカウントアップしてリダイレクトってところか。
リダイレクトする場合は、ループにならないよう、
フラグ処理が必要だな。・・・面倒くさい。

>ブラウザのアクセスバーでEnterを押すと、初回アクセス(クライアントがセッション管理していない状態?)と認識されてしまうと言うことでしょうか。

APIドキュメントのURLまで貼ったのに。
読んでもいないのかよ。

初回かどうかなんぞ、人間の都合にすぎない。
isNew()の返り値は、この場合は、クッキーがついてないので、
セッションがすっぴんだ、といっているだけ。

評価

0

追加。

>フラグ処理が必要だな。・・・面倒くさい。

コレは、もしやるとしたらこういうことを考える必要があるだろうな、
ということを書いただけ。
こうすれば可能、と書いた訳じゃないので、あしからず。:-p

評価

0

不良社員さん、再度のレスありがとうございます。

> アドレスバーの操作じゃ、クッキーは送られないから、セッションでの対応は無理だろ。
> サーバー側で、セッションIDごとに、カウントを保存するか、
> カウントをパラメータに貼り付けておいて、
> サーバー側でカウントアップしてリダイレクトってところか。
> リダイレクトする場合は、ループにならないよう、
> フラグ処理が必要だな。・・・面倒くさい。
>
処理方法を教えて頂きありがとうございます。
今すぐにプログラムとして組むことができません(どのようにプログラムすれば良いか思いつかない)が、両方できるように頑張ってみます。

> APIドキュメントのURLまで貼ったのに。
> 読んでもいないのかよ。
>
APIドキュメントは読ませて貰っていましたが、「アドレスバーの操作じゃ、クッキーは送られない」というこは知らなかったので、もう一つの「クライアントがセッション管理していない状態」なのかと思っていました。

評価

0

ん? 勘違いがあったような。

カウント数は、クッキーじゃなくて、
セッションに貼り付けてあるんだよな。

じゃ、isNew()を使わなければ、解決するんじゃないの。

評価

0

>サーバー側で、セッションIDごとに、カウントを保存する
>
DBがないのでサーバー側にテキストファイルを作り、「作成日時、ID、カウント値」を出力、「作成日時、ID」から「カウント値」を検索するようなプログラムを作成しましたが、アドレスバーでEnterした時はやはり駄目でした。
その後、現在日時を表示するようにして試した結果、アドレスバーでEnterすると時間が、初回の開いた時間になっておりました。
結果としてキャッシュが効いているのが問題でした。IEのオプションの「全般」→「閲覧履歴」→「インターネット一時ファイル」の設定を「webサイト〜」に変更するとアドレスバーでEnterしても問題なく動作することがわかりました。

> isNew()を使わなければ、解決するんじゃないの
>
キャッシュ設定を変更後であれば、アドレスバーでEnterしても isNew() は false になるようです。

不良社員 さん最後までお付き合い頂きありがとうございました。

評価

0

>「作成日時、ID」から「カウント値」を検索するようなプログラムを作成しました

俺なら「ID」で「カウント値、最終更新日時」を検索するようにするけどな。

>キャッシュ設定を変更後であれば、

もし本番環境でこんな仕様を採用したとして、
ユーザー全員に、んなことさせられると思ってるの?

つか、isNew()を使わない、の意味わかってる?
判定なんぞ削除して、セッションから値をとってみなよ。

だいたい、isNew()そのものが、必要のないメソッドなんだし。

評価

0

>つか、isNew()を使わない、の意味わかってる?
>判定なんぞ削除して、セッションから値をとってみなよ。
>
これについては以下のように変更して実験しましたがやはり駄目でした。何か認識が間違っているのでしょうか。

> if (hs1.isNew()) {
>   略
> }
>

---- ここから
    if(hs1.getAttribute(attrCnt) != null){
      sCnt = (String)hs1.getAttribute(attrCnt);
    }
    else{
      hs1.setAttribute(attrCnt, sCnt);
    }
    lCnt = Long.parseLong(sCnt)+1;
    if(lCnt < 100){
      sCnt = Long.toString(lCnt);
      hs1.setAttribute(attrCnt, sCnt);
    }
    out.println("Now:" + (new java.util.Date()) + "<br>");
---- ここまで
と変更しました。

実験にあたり、日時を表示するようにしているのですが、
初回:Tue Jan 26 16:13:26 JST 2010
二回:Tue Jan 26 16:13:36 JST 2010
三回:Tue Jan 26 16:13:46 JST 2010
<ここでアドレスバーでEnter>
      Tue Jan 26 16:13:26 JST 2010
となっていました。

評価

0

>駄目でした。何か認識が間違っているのでしょうか。

最初のアクセス時に、cntがない、ってしかられてんでしょ。
初期値をスマートに用意するのは、
コーディングにおける、キモのひとつだと思うんだがどうだろう。

こんな感じでどう?

  long lcnt = 0;
  HttpSession hs1 = null;

  try {
    hs1 = req.getSession();
    lcnt = Long.parseLong(hs1.getAttribute(attrCnt));
    lcnt++;
  } catch( RuntimeException e ) {
    //初期設定は、こっちでやるのだ。
    1.hs1がnullなら、セッションを生成する。
    2.lcntが0なら、1を代入する。
  }
  //カウンタは、忘れないように更新しないとね。
  ht1.setAttribute(attrCnt, String.toString(lcnt));
  んでから、画面を描画する。

評価

0

http://www.javaroad.jp/servletjsp/sj_servlet6.htm

(1).      クライアントが初めてServletプログラムにアクセスした際、セッションIDが生成されます。
(2).     生成されたセッションIDはクライアントに返され保管されます。
(3).     再びクライアントがServletプログラムにアクセスする際にセッションIDがクライアントから送信されます。
(4).     Servletプログラムでは送信されたセッションIDにより、同一クライアントかどうかを判断します。

アドレスバーをクリックした場合、セッションIDはサーバーに送信されますか?

評価

0

あ、ちょいとみすった。
スルーしてください。

評価

0

む、複合ショッピングサイトなんかで、
アドレスバーを直接叩いて店舗移動をしても、
カートの内容は失われることはないから、
「アドレスバーの操作では、クッキーは送付されない」ってのは、ウソなのかな。orz

スレ主さんの、カウンタに関する要件は、
俺のコードで満たせるはず・・・。

isNew()が何故Trueを返すのかは、不明であります。m(_ _)m

評価

0

不明とは書いたけど、
一連のトランザクション処理の途中で、
アドレスバーを直接操作したアクセスに対して、
isNew()がFalseを返してくれたら困る、という都合はある。w

評価

0

不良社員 さん、K さんレスありがとうございます。
2010-01-26 17:16で書いて頂いたコードを私の方に入れてみましたが、やはりアドレスバーでEnterすると駄目でした。(Count:1 となっていました。)

TCP Monitorというネットワーク通信を監視できるソフトがあるのですが、通信を覗いてみたところアドレスバーでEnterした時は、tomcatから応答が返っていないようでした。ボタンをクリックした時は正しく値を返していました。

tomcatを停止してブラウザ上に作成したpostするボタンをクリックするとエラーが出るのですが、その後、アドレスバーでEnterするとCount:1の状態で表示されていました。

アドレスバーでEnterした時にはサーブレットにアクセスされずにローカル側で勝手に処理されているように見えるのですが、どうなのでしょうか。

評価

0

それぞれのリクエストヘッダー覗いてみたら。

クライアントキャッシュを防ぎたいなら、レスポンスヘッダーとmetaタグ。
まあ従うかどうかはクライアント次第だけど。

質問者の疑問とは関係ないけど、

class Counter{
int count;
void inc(){ count++; }
int getCount(){ return count;}
}

こんなオブジェクトならnewとsetAttribute()は1回だけだし、parse()やtoString()も不要。

評価

0

$ さんありがとうございます。
以下のように追加したら思うように動作していました。

--- <meta タグ>
    out.println("<HEAD>");
    out.println("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=Shift_JIS\">");
    out.println("<meta http-equiv=\"Pragma\" content=\"no-cache\">");
    out.println("<meta http-equiv=\"Cache-Control\" content=\"no-cache\">");
    out.println("<meta http-equiv=\"Expires\" content=\"0\">");
    out.println("</HEAD>");

--- レスポンスヘッダ
debug用にヘッダ出力していましたが、それを削ったから変になっていますが。
    String element;
    boolean bCacheCtr = false;
    Enumeration headers = req.getHeaderNames();
    while (headers.hasMoreElements()) {
      element = (String)headers.nextElement();
      if(element.compareToIgnoreCase("cache-control") == 0){
        bCacheCtr = true;
      }
    }
    if(!bCacheCtr){
      res.addHeader("cache-control", "no-cache");
    }

評価

0

全部列挙して判定しなくたって、無条件でぶっ込めば上書きされた気がする。

質問から6ヶ月以上経過しているので、回答を書き込むことはできません。