corner imagecorner image
IDEPlatformPluginsDocs & SupportCommunityPartners
NetBeans E コマースのチュートリアル - セッションの管理

NetBeans E コマースのチュートリアル - セッションの管理

このページの内容は NetBeans IDE の version 6.8 および 6.9 が対象です

何らかのショッピングカート機能を提供するすべての E コマースアプリケーションでは、ユーザーが Web サイトの中でクリックしたときにユーザー固有のデータを覚える必要があります。開発者にとって残念ですが、インターネットの通信で使用される HTTP プロトコルはステートレスなプロトコルです。サーバーが受け取る各要求は、以前に受け取った要求とは無関係の独立した情報です。このため、顧客がボタンをクリックして自分のショッピングカードに項目を追加したら、アプリケーションはこのユーザーのカートの状態を更新するだけでなく、このアクションが、同時にサイトをブラウズしているユーザーのカートに影響しないようにする必要があります。

上記のシナリオを適切に処理するには、ユーザーがサイトにアクセスしている間、セッションを作成し、セッションを維持できるようにする機能を実装する必要があります。このために、すべての Java ベースの Web アプリケーションの基盤であるサーブレットテクノロジには HttpSession インタフェースが用意されています。また、セッションが維持されている間、アプリケーションが一時的にユーザーデータを格納できるようにする ShoppingCart および ShoppingCartItem というクラスを定義する必要があります。

このチュートリアルユニットでは、ほかの NetBeans E コマースのチュートリアルとは異なる方法を取ります。プロジェクトファイルを作成してそのプロジェクトにコピー&ペーストできるコードスニペットを提供するという手順に従うのではなく、このユニットの完成したプロジェクトのスナップショットを開き、IDE デバッガなどのツールを使用してコードを調べます。このプロセスの中で、HttpSession オブジェクトをコードに適用して、Web サイトへのアクセスごとに専用のセッションを作成する方法を学習します。また、スコープ指定された変数について、およびそれを Java クラスや JSP ページで使用する方法についても学習します。このユニットでは、セッションを維持するための HttpSession のデフォルト機構 (Cookie) についても説明し、ユーザーのブラウザで Cookie が無効になっている場合に必要となる手順を示します。ユニットの最後ではセッションタイムアウトについても触れ、要求をインターセプトしてセッションが存在するかどうかを確認する単純なフィルタを作成して、セッションタイムアウトを処理する方法を示します。

このチュートリアルで構築するアプリケーションのライブデモを、「NetBeans E コマースのチュートリアルのデモアプリケーション」で確認できます。



ソフトウェアまたはリソース 必須バージョン
NetBeans IDE Java バンドル版、6.8 または 6.9
Java Development Kit (JDK) version 6
GlassFish サーバー v3 または Open Source Edition 3.0.1
MySQL データベースサーバー version 5.1
AffableBean プロジェクト スナップショット 5

注:

  • NetBeans IDE が正常に動作するには、JDK (Java Development Kit) が必要です。上記に一覧表示されているいずれのリソースもインストールされていない場合は、最初に JDK をダウンロードしてインストールするようにしてください。
  • NetBeans IDE の Java バンドル版には、このチュートリアルで構築するアプリケーションに必要な Java Web および EE テクノロジが含まれています。
  • NetBeans IDE の Java バンドル版には、このチュートリアルに必要な GlassFish サーバーも含まれています。GlassFish サーバーを別個にダウンロードすることもできますが、NetBeans ダウンロードに付属するバージョンを使用すると、IDE に自動的に登録されるので便利です。
  • このチュートリアルユニットは、以前のユニットを完了させていなくても進めることができます。そのために、データベースの準備や、IDE、GlassFish、および MySQL 間の接続の確立について説明した設定手順を参照してください。

セッションデータの処理

アプリケーションは、HttpSession オブジェクトを使用してユーザーセッションを管理できます。ユーザー固有のデータを HttpSession オブジェクトにバインドして、あとでこのデータにアクセスできます。バインドとアクセスの両方のアクションは、Java クラスから行うことも、EL 式のセッションスコープ指定された変数から行うことも可能です。

HttpSession オブジェクトの操作

AffableBean アプリケーションは HttpSession オブジェクトを使用して、複数の要求にわたってユーザーを識別します。HttpSession オブジェクトを取得するには、特定の要求で次のように getSession() を使用します。

HttpSession session = request.getSession();

その要求のセッションオブジェクトがまだ存在しない場合、このメソッドは新しいセッションを作成して返します。

セッションオブジェクトは、要求間でデータを渡すための輸送手段として使用できます。オブジェクトをセッションにバインドするには、setAttribute メソッドを使用します。同様に、オブジェクトをセッションから取得するには、getAttribute を使用します。たとえば AffableBean アプリケーションでは、次の方法でユーザーのショッピングカートが作成され、ユーザーセッションにバインドされます。

ShoppingCart cart = new ShoppingCart();
session.setAttribute("cart", cart);

セッションからカートを取得するために、次のように getAttribute メソッドが適用されます。

cart = (ShoppingCart) session.getAttribute("cart");

JSP ページでは、セッションにバインドされているオブジェクトに EL 式を使用してアクセスできます。上記の例を続けて使用します。「cart」という名前の ShoppingCart オブジェクトがセッションにバインドされている場合、次の EL 式を使用することでこのオブジェクトにアクセスできます。

${cart}

しかし、ShoppingCart オブジェクト自体にアクセスしてもほとんど意味がありません。本当に必要なのは、オブジェクトに格納されている値にアクセスするための方法です。プロジェクトのスナップショットで新しい ShoppingCart クラスを調べると、次のプロパティーが含まれていることがわかります。

  • double total
  • int numberOfItems
  • List<String, ShoppingCartItem> items

プロパティーに対応する取得メソッドがあれば、EL 式で単純なドット表記法を使用して個々のプロパティーの値にアクセスできます。cart.jsp ページを調べると、次のように、ちょうどこの方法で numberOfItems の値にアクセスしているのが確認できます。

<p>Your shopping cart contains ${cart.numberOfItems} items.</p>

上記の items リストのような複数の値を含むプロパティーからデータを抽出するために、cart.jsp ページでは次のように <c:forEach> ループを使用しています。

<c:forEach var="cartItem" items="${cart.items}" varStatus="iter">

  <c:set var="product" value="${cartItem.product}"/>

    <tr class="${((iter.index % 2) == 0) ? 'lightBlue' : 'white'}">
        <td>
            <img src="${initParam.productImagePath}${product.name}.png"
                 alt="${product.name}">
        </td>

        <td>${product.name}</td>

        <td>
            &euro; ${cartItem.total}
            <br>
            <span class="smallText">( &euro; ${product.price} / unit )</span>
        </td>
        ...
    </tr>

</c:forEach>

ShoppingCartItemproduct プロパティーは、カート項目の製品タイプを識別します。上記のループでは、最初に product 変数を ${cartItem.product} の式に設定することで、これを利用しています。そのあと、この変数を使用して、名前や価格などの製品に関する情報を取得しています。

Web アプリケーションでのスコープ指定された変数の操作

JSP/サーブレットテクノロジを扱う場合、アプリケーションのレルム内で使用できる 4 つのスコープオブジェクトがあります。JSP テクノロジには、サーブレット API によって定義されるクラスにアクセスできる「暗黙オブジェクト」が実装されています。

スコープ 定義 サーブレットクラス JSP 暗黙オブジェクト
アプリケーション Web アプリケーションのグローバルメモリー javax.servlet.ServletContext applicationScope
セッション ユーザーセッションに固有のデータ javax.servlet.http.HttpSession sessionScope
要求 個々のサーバー要求に固有のデータ javax.servlet.HttpServletRequest requestScope
ページ 単一のページ (JSP のみ) のコンテキストだけで有効なデータ [n/a] pageScope

エディタでプロジェクトの category.jsp ファイルを開くと、EL 式に ${categories}${selectedCategory}、および ${categoryProducts} などのさまざまなスコープ指定された変数が含まれているのが確認できます。${categories} 変数はアプリケーションスコープ指定されており、次のように ControllerServletinit メソッドで設定されています。

// store category list in servlet context
getServletContext().setAttribute("categories", categoryFacade.findAll());

ほかの ${selectedCategory}${categoryProducts} の 2 つは、ControllerServlet からアプリケーションのセッションスコープに置かれています。例:

// place selected category in session scope
session.setAttribute("selectedCategory", selectedCategory);

注: 前のチュートリアルユニットから続けている場合、${selectedCategory}${categoryProducts} はもともと要求スコープ内に置かれていたことに気付くかもしれません。前のユニットではこれで問題ありませんでしたが、ここではユーザーがカテゴリページで「add to cart」ボタンをクリックしたらどうなるかを考えてください。サーバーは、現在表示されているカテゴリページを返すことによって、addToCart 要求に応答します。したがって、選択されたカテゴリに関係する selectedCategorycategoryProducts を知る必要があります。この情報は、要求ごとに確立するのではなく、複数の要求にまたがって保持し、必要なときにアクセスできるように、category 要求からセッションスコープに置きます。また、カートページによって提供される機能を調べます。(機能についてはあとで説明します。)「continue shopping」ボタンを押すと、ユーザーは前に表示されていたカテゴリに戻ります。再度 selectedCategory 変数と categoryProducts 変数が要求されます。

EL 式でスコープ指定された変数を参照する場合、(異なるスコープに同じ名前の 2 つの変数がないと仮定して) 変数のスコープを指定する必要はありません。JSP エンジンは 4 つすべてのスコープを調べて、最初に一致した変数を返します。たとえば、category.jsp にある次の式を見てください。

${categoryProducts}

これは、次の式の短縮形です。

${sessionScope.categoryProducts}
詳細については、次のリソースを参照してください。

Java デバッガによるセッションデータの確認

アプリケーションが実行時にどのように動作するかを調べます。IDE のデバッガを使用してコードを段階的に実行し、HttpSession がどのように作成されるのか、また、あとで取得できるようにほかのオブジェクトをセッションスコープに置く方法を調べます。

  1. このチュートリアルユニットのプロジェクトスナップショットを IDE で開きます。「プロジェクトを開く」(「プロジェクトを開く」ボタン) ボタンをクリックし、ウィザードを使用して、このプロジェクトをダウンロードしたコンピュータ上の場所に移動します。前のチュートリアルユニットから続けている場合は、このプロジェクトスナップショットに ShoppingCart クラスと ShoppingCartItem クラスを含む新しい cart パッケージが含まれています。また、次のファイルも変更されています。
    • WEB-INF/web.xml
    • css/affablebean.css
    • WEB-INF/jspf/header.jspf
    • WEB-INF/jspf/footer.jspf
    • WEB-INF/view/cart.jsp
    • WEB-INF/view/category.jsp
    • WEB-INF/view/checkout.jsp
    • controller/ControllerServlet
  2. プロジェクトを実行 (「プロジェクトを実行」ボタン) して、使用しているデータベースとアプリケーションサーバーで適切に構成されていることを確認します。

    プロジェクトの実行時にエラーが発生した場合は、データベースの準備や、IDE、GlassFish、および MySQL 間の接続の確立について説明した設定手順をもう一度確認してください。

  3. ブラウザでアプリケーションの機能をテストします。前のチュートリアルユニットから直接継続している場合は、次のような機能拡張に気付くでしょう。

    カテゴリページ

    • はじめて「add to cart」をクリックするとショッピングカートが有効になり、「proceed to checkout」ウィジェットがヘッダーに表示されます。
    • 「add to cart」をクリックすると、ヘッダーにあるショッピングカートウィジェットのカート項目の数が更新されます。
    • 「view cart」をクリックすると、カートページが表示されます。
    • 「proceed to checkout」をクリックすると、チェックアウトページが表示されます。
    カテゴリページのブラウザの画像

    カートページ

    • 「clear cart」をクリックすると、ショッピングカートの項目が空になります。
    • 「continue shopping」をクリックすると、前に表示されていたカテゴリページに戻ります。
    • 「proceed to checkout」をクリックすると、チェックアウトページが表示されます。
    • 項目の「quantity」フィールドに 1 から 99 までの数字を入力してから「update」をクリックすると、項目の合計と小計が再計算されます。
    • 項目の「quantity」フィールドに 0 を入力してから「update」をクリックすると、表示された表からその項目が削除されます。
    カートページのブラウザの画像

    チェックアウトページ

    • 「view cart」をクリックすると、カートページが表示されます。
    • 「submit purchase」をクリックすると、(ユーザー固有のデータなしで) 確認ページが表示されます。
    チェックアウトページのブラウザの画像
  4. 「ファイルに移動」ダイアログを使用して、エディタで ControllerServlet を開きます。Alt-Shift-O (Mac の場合は Ctrl-Shift-O) を押してから、ダイアログで「Controller」と入力して「了解」をクリックします。
    「ファイルに移動」ダイアログ
  5. HttpSession オブジェクトを作成する行 (150 行目) の doPost メソッドにブレークポイントを設定します。ブレークポイントを設定するには、エディタの左マージンをクリックします。
    エディタで設定されたブレークポイント

    エディタの行番号表示を切り替えるには、左マージンを右クリックして「行番号を表示」を選択します。

  6. デバッガを実行します。IDE のメインツールバーにある「プロジェクトをデバッグ」(「プロジェクトをデバッグ」ボタン) ボタンをクリックします。GlassFish サーバーが起動 (すでに実行中の場合は再起動) し、そのデバッグポート番号でソケットを開きます。アプリケーションの開始ページがブラウザで開きます。

    デバッグポート番号は、「サーバー」ウィンドウ (「ツール」>「サーバー」) から表示および変更できます。使用しているサーバーの「Java」タブを選択します。「デバッグ設定」の下の「使用するアドレス」にポート番号を指定します。

  7. アプリケーションの開始ページがブラウザに表示されたら、いずれかのカテゴリ画像をクリックしてカテゴリページに移動します。「add to cart」ボタンをクリックすると、次のようにサーバーに addToCart 要求が送信されることを思い出してください。
    <form action="addToCart" method="post">
    ページビューおよびコントローラサーブレットの準備」で説明したように、ControllerServletdoPost メソッドは、/addToCart の URL パターンの要求を処理します。このため、ユーザーが「add to cart」ボタンをクリックすると doPost メソッドが呼び出されることを想定できます。
  8. カテゴリページで、いずれかのカテゴリの「add to cart」をクリックします。IDE に戻ると、デバッガがブレークポイントで中断されていることがわかります。
    エディタのブレークポイントで中断されたデバッガ
  9. getSession() への呼び出しにカーソルを置き、Ctrl- スペースキーを押して Javadoc ドキュメントを呼び出します。
    エディタに表示された Javadoc ドキュメント
    ドキュメントによると、getSession() は現時点で要求に関連付けられている HttpSession を返し、セッションが存在しない場合、このメソッドは新しいセッションオブジェクトを作成します。

  10. session 変数の上にカーソルを移動します。デバッガは、それが実行しようとしている行で中断されています。getSession() によって返される値はまだ session 変数に保存されておらず、ポップアップには「"session" は、現在のコンテキスト内で既知の変数ではありません。」と表示されます。
    エディタに表示されたデバッガのポップアップ
  11. エディタの上にあるデバッガツールバーの「ステップオーバー」(「ステップオーバー」ボタン) ボタンをクリックします。この行が実行され、デバッガはファイルの次の行に進みます。
  12. 再度 session 変数の上にカーソルを移動します。今度は、session 変数に現在設定されている値を確認します。
    エディタに表示されたデバッガのポップアップ

    NetBeans 6.9 では、ポップアップでグレーのポインタ (「ステップオーバー」ボタン) をクリックすると、強調表示された要素に含まれている変数の値の一覧を展開できます。

  13. 「ステップオーバー」(「ステップオーバー」ボタン) ボタン (F8、Mac の場合は fn-F8) をクリックして if 文 (154 行目) に達します。ブラウザで「add to cart」ボタンをクリックしたばかりなので、userPath.equals("/addToCart") の式は true として評価されるはずです。
  14. Ctrl を押しながらマウスでクリックして userPath.equals("/addToCart") の式を強調表示します。今度は、強調表示した式の値を示すポップアップが表示されます。
    ポップアップに表示された評価された式
  15. F8 (Mac の場合は fn-F8) を押して次の行 (158 行目) に進みます。このアプリケーションは、ユーザーがはじめてカートに項目を追加するときにだけユーザーセッションの ShoppingCart オブジェクトを作成するように設計されています。このデバッグセッションで addToCart 要求が受け取られたのはこれが最初であるため、cart オブジェクトは null と等しいと想定できます。
    ポップアップに表示された評価された式
  16. F8 (Mac の場合は fn-F8) を押して次の行 (160 行目) に進みます。次に、ShoppingCart オブジェクトが作成される 160 行目で、「ステップイン」(「ステップイン」ボタン) ボタンをクリックします。呼び出されるメソッドにデバッガがステップインします。この場合、直接 ShoppingCart のコンストラクタに移動します。
    ポップアップに表示された評価された式
  17. Ctrl-Tab を押して ControllerServlet に戻ります。IDE によって 160 行目に「呼び出しスタック」(「呼び出しスタック」バッジ) バッジが表示されます。これは、現在デバッガが呼び出しスタックの上位にあるいずれかのメソッドで中断されていることを示しています。

    Alt-Shift-3 (Mac の場合は Ctrl-Shift-3) を押すと IDE の「呼び出しスタック」ウィンドウが開きます。

  18. F8 (Mac の場合は fn-F8) を押して、コードの実行を進めます。デバッガが ShoppingCart コンストラクタを完了すると、ControllerServlet に戻ります。

    ControllerServlet の 161 行目では、新しく作成された cart オブジェクトをセッションにバインドします。
    session.setAttribute("cart", cart);
    これを確認するには、デバッガの「変数」ウィンドウを開きます。「ウィンドウ」>「デバッグ」>「変数」を選択するか、Alt-Shift-1 (Mac の場合は Ctrl-Shift-1) キーを押します。
    「変数」ウィンドウ
    「session」>「session」>「attributes」ノードを展開すると、セッションにバインドされているオブジェクトを表示できます。上記の画像では、現時点でセッションにバインドされている (強調表示された) 2 つの項目があります。これらは、それぞれ ControllerServlet の 83 行目と 89 行目でインスタンス化された selectedCategorycategoryProducts です。これらの項目は両方とも、以前カテゴリ画像をクリックして ControllerServlet がカテゴリページの要求を処理したときにバインドされました。
  19. F8 (Mac の場合は fn-F8) を押して 161 行目を実行します。cart オブジェクトはセッションにバインドされており、「変数」ウィンドウは変更を反映して更新されます。「変数」ウィンドウでは、現在、セッションに 3 つの属性が含まれていることを確認できます。3 つ目の変数は、新しく初期化された ShoppingCart オブジェクト (次で強調表示) です。
    「変数」ウィンドウ

    これまでは、「変数」ウィンドウに一覧表示されたセッションが HttpSession セッションを示していることを「証明」しませんでした。前述のように、HttpSession は実際にはインタフェースであるため、HttpSession オブジェクト (セッションオブジェクト) について説明する場合、実際には HttpSession インタフェースを実装するオブジェクトを指しています。「変数」ウィンドウで「session」の上にカーソルを移動すると、この変数が HttpSession オブジェクトを表していることを示すポップアップが表示されます。表示されているように、StandardSessionFacade 型は、GlassFish が HttpSession インタフェースを実装するために使用する内部クラスです。Tomcat に詳しいユーザーが「値」列に表示された「org.apache.catalina」のパスに戸惑うのは、GlassFish Web/サーブレットコンテナが実は Apache Tomcat コンテナの派生クラスであるためです。

    新しい ShoppingCart がセッションに追加され、要求の処理が続行されます。「add to cart」機能の実装を完成させるために、次のアクションが取られます。
    • 選択された製品の ID が要求から取得される (165 行目)
    • ID を使用して Product オブジェクトが作成される (169 行目)
    • product を使用して新しい ShoppingCartItem が作成される (170 行目)
    • ShoppingCartItemShoppingCartitems リストに追加される (170 行目)
  20. 上記で一覧表示された 4 つのアクションを意識しながら、F8 (Mac の場合は fn-F8) を押して、コードの実行を進めます。デバッガが 170 行目で中断したら一時停止します。
  21. セッションにウォッチを作成します。これによって、次の手順で addItem メソッドにステップインするときに、セッションに含まれている値を表示できるようになります。「変数」ウィンドウでセッションを右クリックして、「固定ウォッチを作成」を選択します。
    「変数」ウィンドウに表示されたメニューを右クリック

    または、エディタ内の session 変数にカーソルを置いてから、右クリックして「新規ウォッチ」を選択します。「新規ウォッチ」ダイアログでは、アプリケーションのデバッグ時に継続的に監視する変数または式を指定できます。(式の場合は、最初に式を強調表示してから、右クリックして「新規ウォッチ」を選択します。)
    「新規ウォッチ」ダイアログ

    session 変数とそれに含まれるすべての変数の新しいウォッチが作成されます。ウォッチは、「ウォッチ」ウィンドウ (「ウィンドウ」>「デバッグ」>「ウォッチ」) に表示させるか、「変数」ウィンドウの左マージンにある「ウォッチ」(「ウォッチ」ボタン) ボタンを切り替えて「変数ウィンドウ」の一番上の行に表示させることができます。

    コードを段階的に実行しながら、デバッガで変数を監視できるようになります。これは、たとえば特定の変数の値をたどる場合 (そして各手順で「変数」ウィンドウに示される全リストから選択しなくても済むようにする場合) や、調べる必要のある変数が含まれていないクラスに一時的にステップインする場合に役立ちます。
  22. 「ステップイン」(「ステップイン」ボタン) ボタンをクリックして、ShoppingCartaddItem メソッドにステップインします。
  23. 53 行目まで addItem メソッドを段階的に実行します。Javadoc に記述されているとおり、addItemShoppingCartitems リストに ShoppingCartItem を追加します。指定された product の項目がすでにショッピングカートリストに存在する場合、その項目の数量が増加します。」
  24. (上記の手順 21 で) ウォッチを作成した session 変数を調べます。51 行目の items.add(scItem) 文によって、ShoppingCartitems リストに新しい ShoppingCartItem が追加されました。これは、セッションに含まれている 3 つ目の属性 (cart 変数) を調べるとわかります。
    「変数」ウィンドウ
    この段階で、要求のために HttpSession が作成される方法、ShoppingCart オブジェクトが作成されてセッションに接続される方法、および ShoppingCartItem がユーザーの製品選択に基づいて作成され、ShoppingCartitems のリストに追加される方法を確認できます。残っているアクションは、category.jsp ビューへの要求の転送だけです。
  25. エディタで JSP フラグメント (header.jspf) を開き、86 行目にブレークポイントを設定します。この行には、カート項目の数を表示する、ショッピングカートウィジェット内の EL 文が含まれています。
    JSP ページに設定されたブレークポイント
  26. デバッガツールバーの「続行」(「続行」ボタン) ボタンをクリックします。デバッガは実行が完了するか、別のブレークポイントに達するまで続行されます。この場合、デバッガはヘッダーの JSP フラグメントの 86 行目で中断されます。

    注: JSP ページでデバッガを中断させるには、ブレークポイントを設定する必要があります。たとえば、ControllerServlet が要求を適切なビューに転送したとき、デバッガは JSP ページ内で自動的に中断されません。

  27. まだ開いていない場合は「変数」ウィンドウを開きます (Alt-Shift-1、Mac の場合は Ctrl-Shift-1)。Java クラスとは異なり、JSP ページではデバッガで変数や式の上にカーソルを移動してもツールチップは表示されません。ただし、コードを段階的に実行しながら、「変数」ウィンドウで変数の値を判定できます。では、${cart.numberOfItems} の値はどこにあるでしょうか。
  28. 「変数」ウィンドウで、「暗黙的なオブジェクト」>「pageContext」>「session」>「session」>「attributes」ノードを展開します。これによって、前に ControllerServlet をデバッグしていたときのように、セッションオブジェクトにアクセスできるようになります。上記の手順 21 でウォッチを作成したセッションは、実は同じオブジェクトを指しています。ここで、${cart.numberOfItems} の値が「1」と等しいことを確認できます。
    「変数」ウィンドウ

    「変数」ウィンドウなどの IDE のウィンドウは、いずれもウィンドウのヘッダーを右クリックしてから「ウィンドウを最大化」(Shift-Esc) を選択することで最大化できます。

    デバッガで、pageContext の暗黙オブジェクトにアクセスできるようになります。pageContext は JSP ページのコンテキストを示しており、HttpServletRequestHttpSession、および ServletContext オブジェクトなどのさまざまなオブジェクトへの直接的なアクセスを提供します。詳細については、「Java EE 5 チュートリアル: 暗黙オブジェクト」を参照してください。
  29. 「セッションを終了」(「セッションを終了」ボタン) ボタンをクリックします。ランタイムが実行を完了し、デバッグセッションが終了します。ブラウザにカテゴリページが完全に描画され、ページヘッダーにあるショッピングカートウィジェットに 1 つの項目が含まれているのが確認できます。

IDE デバッガは、プロジェクトが想定どおりに動作しない場合の検査用としてだけでなく、コードに詳しくなるためのツールとしても便利であることがわかるでしょう。ほかにも、デバッガツールバーには次のような便利なボタンがあります。

  • (「ステップアウト」ボタン) ステップアウト: 現在のメソッド呼び出しをステップアウトします。呼び出しスタックの最上位のメソッド呼び出しを実行して削除します。
  • (「カーソルまで実行」ボタン) カーソルまで実行: カーソルが置かれている行まで実行します。
  • (「コードの変更を適用」ボタン) コードの変更を適用: ファイルを編集してからこのボタンを押すと、ファイルが再コンパイルされ、変更がデバッグセッションに反映されます。
  • (「式をステップオーバー」ボタン) 式をステップオーバー: 式のメソッド呼び出しごとの入力パラメータおよび結果の出力値を表示できるようにします。「局所変数」ウィンドウで、前のメソッドの出力値と次のメソッドの入力パラメータを検査できます。次のメソッド呼び出しが存在しない場合、「式をステップオーバー」は「ステップオーバー」(「ステップオーバー」ボタン) コマンドと同様に機能します。

セッション追跡オプションの確認

クライアントとサーバー間のセッションを追跡するための慣習的な方法が 3 つあります。圧倒的に多く使用されているのは Cookie です。URL 書き換えは、Cookie がサポートされていないか無効になっている場合に適用できます。隠しフォームフィールドも、複数の要求にわたって「状態を維持する」ための手段として使用できますが、これらはフォーム内での使用に制限されます。

AffableBean プロジェクトでは、カテゴリとカートの両方のページに隠しフィールドメソッドの例が含まれています。製品項目用に表示する「add to cart」および「update」ボタンには、ボタンがクリックされると製品 ID をサーバーに渡す隠しフィールドが含まれています。エディタで cart.jsp ページを開くと、<form> タグに隠しフィールドが含まれているのが確認できます。

<form action="updateCart" method="post">
    <input type="hidden"
           name="productId"
           value="${product.id}">
    ...
</form>

このように、製品 ID は要求パラメータとして送信されます。この製品 ID は、ユーザーのカート内の項目の数量を変更する必要があるときに、項目を識別するためにサーバーによって使用されます。

サーブレット API は、セッションを管理するための高水準な機構を提供します。基本的にこの機構では、要求と応答のサイクルごとにクライアントとサーバー間で Cookie を作成して渡します。クライアントのブラウザで Cookie が使用できない場合、サーブレットエンジンは自動的に URL 書き換えに戻ります。次の 2 つの課題でこの機能を示します。

HTTP モニターによるクライアントとサーバー間の通信の確認

デフォルトでは、サーブレットエンジンは、Cookie を使用して要求間のセッションを維持および識別します。セッションオブジェクトごとにランダムな英数字が生成され、一意の識別子として使用されます。この識別子は、「JSESSIONID」Cookie としてクライアントに渡されます。クライアントが要求を作成すると、サーブレットエンジンは JSESSIONID Cookie の値を読み込み、その要求が属しているセッションを判定します。

これを示すために、IDE の HTTP モニターと連携してデバッガを使用します。

  1. まず、使用しているサーバーの HTTP モニターを有効にします。「ツール」>「サーバー」を選択します。「サーバー」ウィンドウの左の列で、使用しているサーバー (GlassFish) を選択します。次にメインの列で、「HTTP モニターを有効化」オプションを選択します。
    「サーバー」ウィンドウ
  2. サーバーがすでに実行されている場合は再起動する必要があります。しかし、ここではデバッガを使用する予定であり、デバッガを実行すると異なるポートで通信するためにサーバーが再起動されるため、単に IDE のメインツールバーにある「プロジェクトをデバッグ」(「プロジェクトをデバッグ」ボタン) ボタンをクリックします。サーバーが再起動し、デバッグセッションが開始して、アプリケーションの開始ページがブラウザで開きます。HTTP モニターが IDE の最下部の領域に表示されます。
    HTTP モニター
  3. 上記の画像で示したように、左の列の AffableBean レコードをクリックします。左の列でレコードを選択すると、右の (メインの) 列が更新され、対応するデータが表示されます。上記の画像の「要求」タブには、要求された URI (/AffableBean/) および HTTP メソッド (GET) が表示され、さらに要求と一緒に送信されたクエリー文字列はなかったことが示されています。
  4. 「セッション」タブを選択します。「この要求の結果、セッションが作成されました」という文が表示されています。これは、サーバーが応答として JSESSIONID Cookie の Set-Cookie ヘッダーを送信したためです。また、新しいセッション ID が「セッションプロパティー」の下に表示されています。あとで示すように、セッション ID は JSESSIONID Cookie の値です。
    HTTP モニター - 「セッション」タブ
    サイトの開始ページの要求からセッションオブジェクトが作成された方法について疑問に思うかもしれません。結局、ControllerServlet/AffableBean/ の最初の要求を処理しておらず、この要求が getSession() の影響を受ける機会はどこにもありません。それとも、これが行われたのでしょうか。JSP ページは、配備時にサーブレットにコンパイルされることを思い出してください。サーバーにプロジェクトを配備すれば、実際に IDE を使用して、サーバー上の JSP のコンパイルされたサーブレットを表示できます。
  5. 「プロジェクト」ウィンドウで index.jsp ファイルを右クリックし、「サーブレットを表示」を選択します。エディタで index_jsp.java ファイルが開きます。これは、index.jsp ページから自動的にコンパイルされたサーブレットです。
  6. このファイルで getSession を検索します。Ctrl-F (Mac の場合は ⌘-F) を押し、検索バーで「getSession」と入力してから Enter を押します。

    Ctrl-F (Mac の場合は ⌘-F) は、「編集」>「検索」のキーボードショートカットです。

    getSession メソッドが表示されたエディタ
    実は、getSession メソッドは呼び出されます。これが起こる理由は、JSP ページにはデフォルトで pageContext.session の暗黙オブジェクトが含まれるためです。この動作を無効にするには、JSP ファイルの最初に次の指令を追加します。
    <%@page session="false" %>
    こうすると、コンパイルされたサーブレットの getSession メソッドが削除されます。

    サーバー上のコンパイルされたサーブレットの場所を見つけるには、エディタの上にあるサーブレット名のタブ上にカーソルを移動します。ポップアップに、コンピュータ上のファイルへのパスが表示されます。

  7. ブラウザで、カテゴリを選択してからカートに項目を追加します。IDE に戻ります。以前に設定した ControllerServlet のブレークポイント (150 行目) でデバッガは中断されます。すべてのブレークポイントは、セッション間で記憶されます。ブレークポイントを削除するには、エディタの左マージンにあるブレークポイント (ブレークポイントバッジ) バッジをクリックします。しかし、このプロジェクトにはすでに複数のブレークポイントが設定されているため、「ウィンドウ」>「デバッグ」>「ブレークポイント」でデバッガの「ブレークポイント」ウィンドウを開きます。
    「ブレークポイント」ウィンドウ
    「ブレークポイント」ウィンドウから、IDE で開いているプロジェクトで設定されたすべてのブレークポイントを表示してアクションを呼び出せます。
  8. header.jspf に設定されたブレークポイントを右クリックして、「削除」を選択します。そのあと、ControllerServlet に設定されたブレークポイントを右クリックして、「無効」を選択します (この課題の後半で再度有効にします)。
  9. 「続行」(「続行」ボタン) ボタンをクリックします。要求の実行が終了し、カートに項目が 1 つ追加されたカテゴリページがブラウザに表示されます。
  10. HTTP モニターの左の列で addToCart 要求を検索して選択すると、メインの列に詳細が表示されます。

    「昇順ソート」(「昇順ソート」ボタン) ボタンをクリックすると、もっとも最近のレコードが最上部に表示されます。


    「要求」タブの下で、要求された URI (/AffableBean/addToCart)、HTTP メソッド (POST)、および要求されたパラメータ (productId および submit) を確認してください。
    HTTP モニター - 「要求」タブ
  11. 「Cookie」タブを選択します。ここでは、JSESSIONID という名前の Cookie が存在し、クライアントからサーバーに送信されたことを確認できます。Cookie の値は、「セッション」タブの下に表示されたセッション ID と同じです。
    HTTP モニター - 「Cookie」タブ
    同様に、「ヘッダー」タブをクリックすると Cookie が表示されます。これは、「Cookie」がクライアントによって送信された要求ヘッダーであるためです。
    HTTP モニター - 「Cookie」タブ

    要求および応答ヘッダーの詳細については、ウィキペディアの「HTTP ヘッダーの一覧」を参照してください。

  12. 「セッション」タブを選択します。「この要求の前にセッションが存在しました」という文が表示されています。また、cart 属性が「要求後のセッション属性」の下に表示されています。addToCart 要求がはじめて処理されるときに cart オブジェクトがセッションにバインドされるため、これは理にかなっています。
    HTTP モニター - 「セッション」タブ

    以降のいくつかの手順では、「変数」ウィンドウでセッション ID および JSESSIONID Cookie を確認します。
  13. 以前に ControllerServlet に設定したブレークポイントを再度有効にします。Alt-Shift-5 (Mac の場合は Ctrl-Shift-5) を押して「ブレークポイント」ウィンドウを開き、ブレークポイントエントリの横にあるチェックボックスをクリックして、再度有効にします。
  14. ブラウザで、一覧表示された製品のうちの 1 つで「add to cart」ボタンをクリックします。
  15. IDE に切り替えると、デバッガが ControllerServlet に設定されたブレークポイントで中断されています。「ステップオーバー」(「ステップオーバー」ボタン) ボタンをクリックして、session 変数をセッションオブジェクトに割り当てます。
  16. 「変数」ウィンドウを開き (Alt-Shift-1、Mac の場合は Ctrl-Shift-1)、「session」>「session」を展開します。セッション ID が「id」変数の値として表示されます。
  17. JSESSIONID Cookie を見つけるために、通常は HttpServletRequestgetCookies メソッドを呼び出すことで、サーブレットから Cookie にアクセスできることを思い出してください。したがって request オブジェクトを、「request」>「継承」>「request」>「request」>「継承」>「cookies」と展開します。これで cookies ArrayList が表示されます。リストを展開すると JSESSIONID Cookie があり、この値がセッション ID になります。
  18. 「セッションを終了」(「セッションを終了」ボタン) ボタンをクリックして、デバッグセッションを終了します。

URL 書き換えによるセッションの維持

前述のように、サーブレットエンジンはクライアントブラウザで Cookie がサポートされているかどうかを検出し、サポートされていない場合は、セッションを維持する手段を URL 書き換えに切り替えます。これはすべて、クライアントにとって透過的に行われます。開発者にとっては、このプロセスは完全に透過的なわけではありません。

Cookie が無効になっている場合に必ず URL 書き換えができるようにアプリケーションを実装する必要があります。このために、アプリケーション内でサーブレットによって返されるすべての URL に対して、応答の encodeURL メソッドを呼び出すようにします。このようにすると、Cookie が使用できない場合にはセッション ID が URL に付加されるようになります。これを行わないと、URL を変更せずに返すことになります。

たとえば、ブラウザが次のような AffableBean の 3 つ目のカテゴリ (bakery) の要求を送信します: category?3。サーバーは次のように、セッション ID を URL に含めて応答します。

/AffableBean/category;jsessionid=364b636d75d90a6e4d0085119990?3

前述のように、アプリケーションのサーブレットによって返されるすべての URL をエンコードする必要があります。JSP ページはサーブレットにコンパイルされることを思い出してください。JSP ページでどのように URL をエンコードするのでしょうか。JSTL の <c:url> タグがこの機能を果たします。以下の課題で、問題と解決法を示します。

  1. ブラウザの Cookie を一時的に無効にします。Firefox を使用している場合、「ツール」>「オプション」(Mac の場合は「Firefox」>「設定」) を選択します。表示されたウィンドウで「プライバシー」タブを選択してから、表示されたドロップダウンで「記憶させる履歴を詳細設定する」を選択します。「サイトから送られてきた Cookie を保存する」オプションを選択解除します。
    Firefox「設定」ウィンドウ
  2. AffableBean プロジェクトを実行します。開始ページが表示されたら、カテゴリをクリックしてからカートに項目を追加してみてください。現状ではアプリケーションがまったく機能しません。
    壊れたカテゴリページ
    以前と同様に、サーバーはセッションを生成して、そのセッションにオブジェクトをバインドします。このようにして、選択されたカテゴリおよび製品をカテゴリページに表示できます。しかし、サーバーは JSESSIONID Cookie の設定に失敗しました。したがって、クライアントが 2 つ目の要求を作成したとき (ユーザーが「add to cart」をクリックしたとき)、サーバーには要求が属しているセッションを識別する方法がありません。このため、以前にセッションで設定された selectedCategorycategoryProducts などの属性をいずれも見つけることができません。このような理由により、応答の描画にはこれらの属性によって指定される情報が欠如しています。
  3. エディタでプロジェクトの category.jsp ページを開きます。「add to cart」ボタンを実装する行 (58 行目) を見つけます。<form> 要素の action 属性によって、サーバーに送信される要求が決まります。
    <form action="addToCart" method="post">
  4. 要求を変更して、<c:url> タグを通して渡されるようにします。
    <form action="<c:url value='addToCart'/>" method="post">
  5. Ctrl-S (Mac の場合は ⌘-S) を押して、ファイルへの変更を保存します。IDE は、デフォルトで有効な「保存時に配備」機能が備わっていることを思い出してください。これによって、保存された変更はすべて自動的にサーバーに配備されます。
  6. ブラウザで異なるカテゴリを選択して、新しく変更されたカテゴリページをアプリケーションに描画させます。
  7. このページのソースコードを調べます。Firefox では Ctrl-U (Mac の場合は ⌘-U) を押します。各製品の「add to cart」ボタンに、URL に付加されたセッション ID が一緒に表示されています。
    <form action="addToCart;jsessionid=4188657e21d72f364e0782136dde" method="post">
  8. いずれかの項目で「add to cart」ボタンをクリックします。サーバーが、要求が属しているセッションを判定して、適切に応答を描画できるようになっていることを確認できます。
  9. 次に進む前に、ブラウザで Cookie を再度有効にしてください。

前述のように、アプリケーション内でユーザーがクリックできるすべてのリンクは、その応答が何らかの形式のセッション関連のデータを必要とする場合、適切にエンコードされる必要があります。上記の例ほど実装が単純ではない場合もあります。たとえば、cart.jsp で使用されている「clear cart」ウィジェットは、現時点ではリンクがクリックされると clear パラメータを true に設定します。

<%-- clear cart widget --%>
<c:if test="${!empty cart && cart.numberOfItems != 0}">
    <a href="viewCart?clear=true" class="bubble hMargin">clear cart</a>
</c:if>

<c:url> タグは、次の方法で URL に適用できます。

<%-- clear cart widget --%>
<c:if test="${!empty cart && cart.numberOfItems != 0}">

    <c:url var="url" value="viewCart">
        <c:param name="clear" value="true"/>
    </c:url>

    <a href="${url}" class="bubble hMargin">clear cart</a>
</c:if>

clear=true パラメータは、<c:url> タグ間に <c:param> タグを追加することで設定されます。「url」という名前の変数が <c:url> の var 属性を使用して設定されたあと、var${url} 式を使用して HTML アンカータグでアクセスされます。

スナップショット 6 をダウンロードして調べれば、プロジェクトのすべてのリンクがどのようにエンコードされているかを確認できます。

URL 書き換えは、追跡メソッドとして Cookie が使用できない場合にのみ使用する必要があります。セッション ID がブラウザのアドレスバーだけでなく、ログ、ブックマーク、リファラヘッダー、およびキャッシュされた HTML に表示されるため、通常は URL 書き換えは次善手段と見なされています。また、受け取る要求ごとに URL からセッション ID を抽出して、既存のセッションと組み合わせるためのサーバーの追加手順が必要なため、サーバー側リソースも多く必要になります。


セッションタイムアウトの処理

セッションの時間間隔の設定

サーバーがセッションを維持する時間間隔の最大値について考慮する必要があります。Web サイトのトラフィックが多くなると、多数のセッションによってサーバーのメモリー容量が使い果たされる可能性があります。このため、使用されていないセッションを削除できるように、時間間隔を短くできます。一方で、セッションが短すぎると使い勝手が悪くなり、Web サイトの背後にあるビジネスに悪影響を与える可能性があります。これは避ける必要があります。AffableBean アプリケーションの例で考えると、ユーザーがショッピングカートに多くの項目を入れてからチェックアウトに進むとします。そのあと、クレジットカードの詳細を入力する必要があることがわかり、財布を探しにいきます。クレジットカードを持ってコンピュータに戻ってから、チェックアウトフォームに入力して「submit」をクリックします。しかしこの間に、このユーザーのセッションはサーバーで時間切れになっていました。ショッピングカートは空になり、ユーザーはホームページにリダイレクトされます。このユーザーは、再度時間をかけて同じプロセスを実行するでしょうか。

次の手順では、AffableBean プロジェクトのセッションタイムアウトの間隔を 10 分に設定する方法を示します。言うまでもなく実際の間隔は最終的に、サーバーリソース、業務におけるアプリケーションの目標、および Web サイトの需要によって決まります。

  1. エディタでアプリケーションの配備記述子を開きます。Alt-Shift-O (Mac の場合は Ctrl-Shift-O) を押して、IDE の「ファイルに移動」ダイアログを使用します。「web」と入力してから「了解」をクリックします。
    「ファイルに移動」ダイアログ
    エディタの「XML」ビューに web.xml ファイルが表示されます。NetBeans が web.xml ファイルに提供しているテンプレートでは、デフォルトとして 30 分が設定されています。
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
  2. 「一般」タブをクリックして、「セッションタイムアウト」フィールドに「10」と入力します。
    web.xml ファイルの「一般」タブ
  3. ファイルを保存します (Ctrl-S、Mac の場合は ⌘-S)。

    「XML」ビューに戻ると、<session-timeout> 要素が更新されていることを確認できます。
    <session-config>
        <session-timeout>10</session-timeout>
    </session-config>

注: 代わりに、<session-timeout> 要素をすべて削除し、GlassFish 固有の配備記述子 (sun-web.xml) で session-properties 要素を編集することもできます。これを行うと、サーバーの Web モジュールにあるすべてのアプリケーションのグローバルタイムアウトが設定されます。詳細は、「Oracle GlassFish Server 3.0.1 アプリケーション開発ガイド: セッションの作成および管理」を参照してください。

プログラムによるセッションタイムアウトの処理

アプリケーションがセッションに依存している場合、タイムアウトしたセッションや識別できないセッションに対する要求を受け取ったときにも正常に処理できるようにする必要があります。AffableBean アプリケーションでこれを実現するには、ControllerServlet に送信された要求をインターセプトする単純なフィルタを作成します。このフィルタはセッションが存在するかどうかを調べて、存在しなければ要求をサイトの開始ページに転送します。

  1. まず、ユーザーがサイトにアクセスしている途中でセッションがタイムアウトする場合に起こる問題を調べます。一時的に、セッションタイムアウトの間隔を 1 分に再設定します。Web 配備記述子 (web.xml) を開き、<session-timeout> タグの間に「1」を入力します。
    <session-config>
        <session-timeout>1</session-timeout>
    </session-config>
  2. AffableBean プロジェクトを実行します。ブラウザでカテゴリページをクリックして、項目をいくつかカートに追加してから「view cart」をクリックします。
    ショッピングカートの項目が表示されたカートページ
  3. 少なくとも 1 分間待ちます。
  4. カートページに表示された項目のうち、1 つの項目の数量を更新します (1 から 99 までのいずれかの数字を入力できます)。「update」をクリックします。サーバーから HTTP ステータス 500 のメッセージが返されます。
    ブラウザに表示された GlassFish エラーレポート
  5. IDE で GlassFish サーバーログを調べます。「出力」ウィンドウ (Ctrl-4、Mac の場合 ⌘-4) を開き、「GlassFish Server 3」タブを選択します。ログの最下部までスクロールして、エラーのスタックトレースを調べます。
    ブラウザに表示された GlassFish エラーレポート
    サーバーログに、ControllerServlet の 184 行目で NullPointerException が発生したことが示されています。「出力」ウィンドウには、例外が発生した行へのリンクが表示されます。
  6. リンクをクリックします。ControllerServlet の 184 行目に直接移動します。エディタの左マージンにあるエラーバッジの上にカーソルを置くと、例外を説明するツールチップが表示されます。
    エディタに表示されたエラーバッジおよびツールチップ
    要求を受け取る前にセッションがすでに期限切れになっていたため、サーブレットエンジンは、対応するセッションに要求を関連付けることができませんでした。このため、cart オブジェクトを見つけられませんでした (151 行目)。最終的に 184 行目で、null と等しい変数のメソッドをエンジンが呼び出そうとしたときに例外が発生しました。

    これで問題を特定できたので、フィルタを実装して修正しましょう。
  7. IDE のツールバーにある「新規ファイル」(「新規ファイル」ボタン) ボタンをクリックします。または、Ctrl-N (Mac の場合は ⌘-N) を押します。
  8. Web」カテゴリから「フィルタ」を選択し、「次へ」をクリックします。
  9. フィルタに「SessionTimeoutFilter」という名前を付けます。「パッケージ」フィールドに「filter」と入力して、フィルタクラスが作成時に新しいパッケージに配置されるようにします。
  10. 「次へ」をクリックします。デフォルトの設定を受け入れ、「完了」をクリックします。SessionTimeoutFilter のテンプレートが生成され、エディタで開きます。

    注: NetBeans 6.9 の時点では、ウィザードを使用して Web 配備記述子に登録されていないサーブレットへのマッピングを設定できません (ControllerServlet@WebServlet 注釈を使用して登録されています)。このため、生成されたコードを次の手順で変更します。

  11. @WebFilter 注釈署名を次のように変更します。
    @WebFilter(servletNames = {"Controller"})
    public class SessionTimeoutFilter implements Filter {
    これによって、ControllerServlet が処理するすべての要求をインターセプトするようにフィルタが設定されます (または、urlPatterns 属性を保持して、ControllerServlet が処理するすべてのパターンをリストすることもできます)。

    サーブレットの @WebServlet 注釈署名に指定されているように、「Controller」は ControllerServlet の名前です。また、フィルタクラスの名前がデフォルトで使用されているため、filterName 属性は削除しています。

    IDE のフィルタテンプレート自体に、調べる価値のある有用なコードが多く含まれています。ただし、そのほとんどはここでの目的には不要なものです。どのフィルタクラスも、次の 3 つのメソッドを定義する Filter インタフェースを実装する必要があります。
    • init: フィルタが初期化されてから使用が開始されるまでの間に、任意のアクションを実行します。
    • destroy: 使用を停止してフィルタを削除します。このメソッドは、任意のクリーンアップ処理を実行するためにも使用できます。
    • doFilter: フィルタがインターセプトする要求ごとの処理の実行に使用されます。

    Filter インタフェースに関するドキュメントを参照するには、「Javadoc 索引検索」を使用します。Shift-F1 (Mac の場合は fn-Shift-F1) を押してから、検索フィールドに「Filter」と入力して Enter を押します。「Interface in javax.servlet」エントリを選択します。索引検索ツールの下部の区画に、Javadoc ドキュメントが表示されます。

  12. SessionTimeoutFilter の本文を、次の内容に置き換えます。
    @WebFilter(servletNames = {"Controller"})
    public class SessionTimeoutFilter implements Filter {
    
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                throws IOException, ServletException {
    
            HttpServletRequest req = (HttpServletRequest) request;
    
            HttpSession session = req.getSession(false);
    
            // if session doesn't exist, forward user to welcome page
            if (session == null) {
                try {
                    req.getRequestDispatcher("/index.jsp").forward(request, response);
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
                return;
            }
    
            chain.doFilter(request, response);
        }
    
        public void init(FilterConfig filterConfig) throws ServletException {}
    
        public void destroy() {}
    
    }
  13. Ctrl-Shift-I (Mac の場合は ⌘-Shift-I) を押してインポート文を修正します (HttpServletRequest および HttpSession のためのインポートの追加が必要)。また、エディタのヒントを使用して initdestroy、および doFilter メソッドに @Override 注釈を追加します。

    以降の手順では、プロジェクトでデバッガを実行して doFilter メソッドを段階的に実行し、要求が既存のセッションにバインドされているかどうかをこのメソッドが判定する方法を確認します。
  14. 「ブレークポイント」ウィンドウを開き (Alt-Shift-5、Mac の場合は Ctrl-Shift-5)、既存のブレークポイントが設定されていないことを確認します。ブレークポイントを削除するには、ブレークポイントを右クリックして「削除」を選択します。(前述の課題「HTTP モニターによるクライアントとサーバー間の通信の確認」を完了した場合は、ControllerServlet に未処理のブレークポイントが設定されている可能性があります。)
  15. デバッガを実行します。IDE のメインツールバーにある「プロジェクトをデバッグ」(「プロジェクトをデバッグ」ボタン) ボタンをクリックします。
  16. ブラウザに開始ページが表示されたら、カテゴリを選択してからショッピングカートにいくつか項目を追加します。
  17. SessionTimeoutFilterdoFilter メソッドで、セッションへのアクセスを試行する行 (32 行目) にブレークポイントを設定します。
    エディタで設定されたブレークポイント
  18. ブラウザで「view cart」ボタンをクリックします。IDE に切り替えると、デバッガがブレークポイントで中断されていることがわかります。

    その時点でセッションオブジェクトが存在しない場合、getSession() は新しいセッションオブジェクトを作成することを思い出してください。ここでは、オブジェクトが見つからなくても新規作成を行わない getSession(false) を使用します。つまり、セッションが存在しない場合、メソッドは null を返します。
  19. 「ステップオーバー」(「ステップオーバー」ボタン) ボタンをクリックしてから、session 変数の上にカーソルを移動します。前回の要求が送信されてから 1 分が経過していなければ、変数が StandardSessionFacade に割り当てられていることを確認できます。これは、要求のセッションオブジェクトを表します。
    セッションオブジェクトに割り当てられた「session」変数を表示するポップアップ
  20. 要求が処理されるまで、メソッドを段階的に実行し続けます。sessionnull と等しくないため、if 文をスキップすると、chain.doFilter は要求を ControllerServlet に転送します (44 行目)。
  21. ブラウザで、1 分間が過ぎたことを確認してから、カートの製品項目のうちの 1 つの数量を更新します。この手順は、この課題の最初の方で実行してステータス 500 のメッセージが返されたときと同じ手順です。ここでは、ControllerServlet に送信された要求をフィルタがインターセプトするようになったので、セッションタイムアウトが起きるとどうなるかを確認しましょう。
  22. 「update」をクリックしてから IDE に切り替えると、フィルタに設定されたブレークポイントでデバッガが再度中断されています。
  23. req.getSession(false) の式を強調表示してから、この上にカーソルを移動します。ここでは、セッションがすでに期限切れになっているため、式が null と等しいことが確認できます。
    「null」と等しい「session」変数を表示するポップアップ
  24. 続けてコードを段階的に実行します。ここでは、session 変数が null と等しくなっているため、35 行目の if 文が処理され、要求が /index.jsp に転送されます。デバッガが実行を完了すると、ブラウザにサイトの開始ページが表示されます。
  25. 「セッションを終了」(「セッションを終了」ボタン) ボタンをクリックして、デバッグセッションを終了します。
  26. プロジェクトの web.xml ファイルを開き、セッションタイムアウトの間隔を 10 分に戻します。
    <session-config>
        <session-timeout>10</session-timeout>
    </session-config>
  27. ファイルを保存 (Ctrl-S、Mac の場合は ⌘-S) します。

スナップショット 6 は、このチュートリアルユニットのプロジェクトの完成版を示しています。最後に、セッション管理に関する 1 つのトピックについて説明します。セッションオブジェクトで invalidate メソッドを呼び出すことで、セッションを明示的に終了させることができます。セッションが不要になったら、サーバーが使用するメモリーを節約するために、そのセッションは削除するようにしてください。次のユニット「ビジネスロジックの取引の統合」を完了すると、顧客の注文を正常に処理したときに ControllerServletinvalidate メソッドを使用してどのようにユーザーの cart オブジェクトを破棄し、セッションを終了するかがわかります。

// if order processed successfully send user to confirmation page
if (orderId != 0) {

    // dissociate shopping cart from session
    cart = null;

    // end session
    session.invalidate();

    ...
}

これは、プロジェクトスナップショット 8 (およびそれ以降のスナップショット) に示されています。


関連項目