■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
                      2008年03月23日

    楽しいJava講座 - 初心者から達人へのパスポート
                  vol.096

                                セルゲイ・ランダウ
 バックナンバー: http://www.flsi.co.jp/Java_text/
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■



[このメールマガジンは、画面を最大化して見てください。]


========================================================
◆ 01.Tomcatのアプリケーション開発
========================================================


では、前回整理した前提条件について考察し、前提条件が満たされていない
ときの対処をプログラミングしていきましょう。
このうち、オプションになっているものは、条件が満たされて
いなくてもかまわないものなので、無視します。

そうすると、(操作1)と(操作2)は無視できます。

では、(操作3)はどうでしょうか。

(前回、「カートのオブジェクトの中に商品(OrderItem)オブジェクトが
が入っていること。」という前提条件を書き忘れていたので、それを追記した
ものを下記に提示します。(注文する商品がないのに購入手続きへ進めるわけ
にはいきませんので、この前提条件は必要です。))

--------------------------------------------------------
(操作3) 購入個数の変更
----------------------------------
■入力ページ:
・[cartページ]
■前提条件:
・[cartページ]において商品の購入個数が変更されていること(オプション)
・[cartページ]において「選択した商品を購入する」ボタンをクリック
すること。
・セッション・オブジェクトの中にカートのオブジェクト
が入っていること。
<<条件が満たされていない場合→エラー・メッセージ:cartmissing.html>>
・購入対象の商品 = 購入個数の変更対象になる商品(OrderItem)オブジェクトが
既にカートのオブジェクトの中に入っていること。
また、カートのオブジェクトの中に商品(OrderItem)オブジェクトが
が入っていること(カートが空でないこと)。
<<条件が満たされていない場合→エラー・メッセージ:orderiteminvalid.html>>
■事後条件:
・セッション・オブジェクトの中にカートのオブジェクト
が入っていること。
■結果ページ:
・[orderページ]
----------------------------------
 [cartページ]→ OrderProcessServlet.java → order.jsp
----------------------------------


(操作3)の前提条件のうち、セッション・オブジェクトの中にカートのオブジェクト
が入っていない場合にcartmissing.htmlのWebページでエラー・メッセージを返す
ことについては、すでにvol.092で実装しました。

しかし、セッション・オブジェクトの中にショッピング・カートのオブジェクト
が入っている場合でも、次のような問題が生じる場合があります。

一度ショッピング・カートの中から商品を削除(購入個数を0にする)した後、
Webブラウザーの「戻る」ボタンをクリックするなどして商品を削除する前の
ページに戻った場合、ショッピング・カートのオブジェクト内ではその商品は
無くなっているのに、Webページ上ではその商品はあることになります。
つまり、Webページ上の内容とセッション・オブジェクトにはいっているショッ
ピング・カートのオブジェクトの内容に食い違いが生じることになります。

このように食い違いが生じた場合には、Webページ上の情報でセッション・オブ
ジェクトにはいっているショッピング・カートを書き換える(目に見えている
Webページの情報のほうを優先する)というようにしてもいいのですが、当メール
マガジンでは、この状態をはっきりとわかるようにするために、エラーとして
メッセージを返すことにします。

そのため、
--------------------------------------------------------
・購入対象の商品 = 購入個数の変更対象になる商品(OrderItem)オブジェクトが
既にカートのオブジェクトの中に入っていること。
--------------------------------------------------------
という前提条件にして、この前提条件が満たされていないときはエラー・メッセー
ジとしてorderiteminvalid.htmlというWebページを返すことにしています。


では、この前提条件が満たされていないときにorderiteminvalid.htmlを返すよう
にするために、OrderProcessServletを下記のように編集しましょう。
--------------------------------------------------------
package jp.co.flsi.lecture.webapp.servlet;

import java.io.IOException;
import java.util.Enumeration;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import jp.co.flsi.lecture.webapp.entity.Order;
import jp.co.flsi.lecture.webapp.entity.OrderItem;

public class OrderProcessServlet extends HttpServlet {
   public void doPost(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException {
      req.setCharacterEncoding("Shift_JIS");
      HttpSession session = req.getSession();
      Order anOrder = (Order)session.getAttribute("ORDER");
      if (anOrder == null) {
         getServletContext().getRequestDispatcher("/cartmissing.html").forward(req,res);
      }
      else {
         Enumeration paramNames = req.getParameterNames();
         // Webページ上の購入対象の商品がカートの中に入っていることのチェック用フラグ
         boolean orderItemFoundInCart = false;  // falseは「カートにない」を意味する
         while(paramNames.hasMoreElements()) {
            String aParamName = (String)paramNames.nextElement();
            if (!aParamName.equals("putIntoOrder")) {
               OrderItem anOrderItem = new OrderItem();
               orderItemFoundInCart = false;  // チェック用フラグを「カートにない」にリセット
               for (OrderItem oi : anOrder.getOrderItems()) {
                  if(aParamName.substring(0, 5).equals(oi.getNum())) {
                     orderItemFoundInCart = true;  // チェック用フラグを「カートにある」にセット
                     anOrderItem.setNum(oi.getNum());
                     anOrderItem.setName(oi.getName());
                     anOrderItem.setPrice(oi.getPrice());
                     anOrderItem.setImage(oi.getImage());
                     anOrderItem.setCategory(oi.getCategory());
                     anOrderItem.setOrderQuantity(Integer.parseInt((String)req.getParameter(aParamName)));
                  }
               }
               if (!orderItemFoundInCart) {
                  break;
               }
               else {
                  anOrder.removeOrderItem(anOrderItem);
                  if (anOrderItem.getOrderQuantity() > 0) {
                     anOrder.addOrderItem(anOrderItem);
                  }
                  if (anOrder.getOrderItems().size() < 1) {
                     orderItemFoundInCart = false;
                  }
               }
            }
         }
         if (!orderItemFoundInCart) {
            getServletContext().getRequestDispatcher("/orderiteminvalid.html").forward(req,res);
         }
         else {
            session.setAttribute("ORDER",anOrder);
            getServletContext().getRequestDispatcher("/order.jsp").forward(req,res);
         }
      }
   }

   public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException {
      getServletContext().getRequestDispatcher("/bookmarkinvalid.html").forward(req,res);
   }

}
--------------------------------------------------------
ここでは、新たにorderItemFoundInCartという変数を用意して、Webページにリスト
されている商品がカートのオブジェクトの中に見つかった場合にはorderItemFoundInCart
の値をtrueにし、見つからなかった場合にはfalseのままにして、Webページを返す
時点でorderItemFoundInCartがfalseのときには、order.jspの代わりに
orderiteminvalid.htmlを呼び出すように編集を加えています。
また上記のソース・コードでは、カートの中が空っぽの場合にもorderItemFoundInCart
がfalseになりますので、「カートのオブジェクトの中に商品(OrderItem)オブジェ
クトがが入っていること(カートが空でないこと)。」という前提条件が満たされて
いないときにもorderiteminvalid.htmlが呼び出されることになります。

なお、orderItemFoundInCartの値がfalseのときは、Webページにリストされている
商品がカートのオブジェクトの中に見つからないことを意味し、trueのときは見つ
かったことを意味しますが、このように何かの判断のために使用する変数(主に
trueかfalseといった二者択一の値を保持する場合が多い)を「フラグ(flag)」と呼ぶ
ことがあります。これは、旗(flag)が信号として使われていたこと(旗を揚げるか
揚げないかで合図を送ったこと)に由来します。


では、エラー・メッセージを返すためのWebページとして、次のような
orderiteminvalid.htmlを作成しておきましょう。
(itemSelect.htmlと同じ場所に作成してください。)
--------------------------------------------------------
<HTML>
<HEAD>
<TITLE>カート・データ不正</TITLE>
</HEAD>
<BODY bgcolor="#77ffff" text="#fa5a00">
<H1>カート・データ不正</H1>
現在のショッピング・カートの情報とWebページの情報に整合性がないか、もしくは
<BR>
ショッピング・カートの中に商品がないため、購入手続きに進むことができません。
<BR>
まことに申し訳ありませんが、商品の検索から再度やり直してください。
<BR>
<BR>
<A href="http://localhost:8080/JStudy2/itemSelect.html">
商品の検索に戻るには、ここをクリック
</A>
<BR>
</BODY>
</HTML>
--------------------------------------------------------

では、このWebページがちゃんと表示されるか、テストしてみましょう。


EclipseでTomcatを起動し、Webブラウザーを起動して、下記のURLを
入力してください。

http://localhost:8080/JStudy2/itemSelect.html

(1) 「キーワード」欄、「カテゴリー」欄ともに何も入力せずに
「送信」ボタンをクリックしてみてください。

(2) 次のページ(商品リストのページ)でどれか2つの商品を選択(チェッ
ク・マークを入れる)[たとえば「製品001」と「製品002」を選択]し、
「選択した商品をカートに入れる」ボタンをクリックします。

(3) 選択した商品が次のページ(ショッピング・カートのページ)でリスト
されていることを確認し、このうちの一つの商品([たとえば「製品002」)
の「購入個数」を0に変更し、「選択した商品を購入する」ボタンをクリッ
クします。

(4) 次のページ(購入手続きのページ)で、「製品002」が無くなっている
(さきほど購入個数を0に変更していたので、カートから削除されている)
ことを確認し、Webブラウザーの「戻る」ボタンをクリックして(3)のページ
に戻ります。

(5) 今度は「製品002」の「購入個数」を10にして、「選択した商品を購入する」
ボタンをクリックします。

(6) Webページ上の商品がセッション・オブジェクト内のショッピング・カート
のものと食い違っているために、「カート・データ不正」のページが表示さ
れ、
--------------------------------------------------------
現在のショッピング・カートの情報とWebページの情報に整合性がないか、もしくは
ショッピング・カートの中に商品がないため、購入手続きに進むことができません。
まことに申し訳ありませんが、商品の検索から再度やり直してください。
--------------------------------------------------------
というメッセージが表示されますね。
これがorderiteminvalid.htmlのWebページです。



では、次に(操作4)の前提条件を見て見ましょう。

--------------------------------------------------------
(操作4) ユーザーIDに対応した住所・氏名の取り出し
----------------------------------
■入力ページ:
・[orderページ]
■前提条件:
・[orderページ]において正しいユーザーIDが入力されていること
<<条件が満たされていない場合→エラー・メッセージ:wrongUser.jsp>>
・[orderページ]において正しいパスワードが入力されていること
 (本来は必須だが、今のところオプションとしておく)
・[orderページ]において「最終購入確認画面に進む」ボタンをクリック
すること。
・セッション・オブジェクトの中にカートのオブジェクト
が入っていること。
<<条件が満たされていない場合→エラー・メッセージ:ordermissing.html>>
・カートのオブジェクトの中に商品(OrderItem)オブジェクトが
が入っていること。
<<条件が満たされていない場合→エラー・メッセージ:invalidorder.html>>
■事後条件:
・Customerオブジェクトがセッション・オブジェクトの中にはいっていること。
■結果ページ:
・[confirmOrderページ]:商品の発送先(= ユーザーの住所・氏名)が表示され、
購入(注文)を確定するかどうか聞いてくる。
----------------------------------
 [orderページ]→ ConfirmOrderServlet.java → confirmOrder.jsp
----------------------------------

まず
--------------------------------------------------------
・セッション・オブジェクトの中にカートのオブジェクト
が入っていること。
<<条件が満たされていない場合→エラー・メッセージ:ordermissing.html>>
--------------------------------------------------------
については、すでにvol.093で、カートの変数anOrderにオブジェクトを代入した
ときの値がnull(これはカートのオブジェクトが無く、変数が空であることを意味
する)のときにordermissing.htmlを呼び出すようにプログラミングしていました
が、ordermissing.html自体はまだ作成していませんでした。

というわけで、下記のようなordermissing.htmlのファイルを作成しましょう。
(これも同じくitemSelect.htmlと同じ場所に作成してください。)
--------------------------------------------------------
<HTML>
<HEAD>
<TITLE>注文エラー</TITLE>
</HEAD>
<BODY bgcolor="#77ffff" text="#fa5a00">
<H1>注文エラー</H1>
注文を確定すべきショッピング・カートが存在しないか、または消失しています。
<BR>
まことに申し訳ありませんが、商品の検索から再度やり直してください。
<BR>
<BR>
<A href="http://localhost:8080/JStudy2/itemSelect.html">
商品の検索に戻るには、ここをクリック
</A>
<BR>
</BODY>
</HTML>
--------------------------------------------------------


続いて、
--------------------------------------------------------
・[orderページ]において正しいユーザーIDが入力されていること
<<条件が満たされていない場合→エラー・メッセージ:wrongUser.jsp>>
--------------------------------------------------------
についても、wrongUser.jspの呼び出しは既にプログラミングしていましたが、
wrongUser.jsp自体はまだ作成しておりません。

というわけで、下記のようなwrongUser.jspのファイルを作成しましょう。
(これも同じくitemSelect.htmlと同じ場所に作成してください。)
--------------------------------------------------------
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="Shift_JIS" %>

<HTML>
<HEAD>
<TITLE>ユーザーIDエラー</TITLE>
</HEAD>
<BODY bgcolor="#77ffff" text="#fa5a00">
<H1>ユーザーIDエラー</H1>
<% String user = request.getParameter("userId"); %>
入力されたユーザーID(<%= user %>)またはパスワードは正しくありません。
<BR>
前のページに戻って、ユーザーID/パスワードの入力をやり直してください。
<BR>
<BR>
<FORM>
<INPUT TYPE="button" VALUE="前のページに戻る" onClick="history.back()">
</FORM>
</BODY>
</HTML>
--------------------------------------------------------

ここで、
--------------------------------------------------------
<INPUT TYPE="button" VALUE="前のページに戻る" onClick="history.back()">
--------------------------------------------------------
というコードが新しく出てきましたが、これは前のWebページに戻るためのボタン
を表示するコードです。Webブラウザーの「戻る」ボタンと同じ働きをします。

なお、このページではサーブレット(ConfirmOrderServlet)からuserIdの値を
引き継ぐために、HTMLファイルではなくJSPファイルにしています。

┌補足─────────────────────────┐
このuserIdの値は[orderページ]に入力されたものがrequestオブジェクトの
パラメーターとして保持され、サーブレット(ConfirmOrderServlet)から
wrongUser.jspにforward()
--------------------------------------------------------
getServletContext().getRequestDispatcher("/wrongUser.jsp").forward(req,res);
--------------------------------------------------------
されたものです。
サーブレットで
--------------------------------------------------------
String customerNum = req.getParameter("userId");
--------------------------------------------------------
というコードで取り出したの同じく、JSPでも
--------------------------------------------------------
<% String user = request.getParameter("userId"); %>
--------------------------------------------------------
という同様のJavaコードで取り出せます。
JSPでは、HttpServletRequestオブジェクトがrequestという変数で
表される決まりになっていることを思い出してください。
└───────────────────────────┘


では、このwrongUser.jspが正しく機能するかどうか、テストしてみましょう。

EclipseでTomcatを起動し、Webブラウザーを起動して、下記のURLを
入力してください。

http://localhost:8080/JStudy2/itemSelect.html

(1) 「キーワード」欄、「カテゴリー」欄ともに何も入力せずに
「送信」ボタンをクリックしてみてください。

(2) 次のページ(商品リストのページ)で商品をいくつか選択(チェッ
ク・マークを入れる)し、「選択した商品をカートに入れる」ボタン
をクリックします。

(3) 選択した商品が次のページ(ショッピング・カートのページ)で
リストされていることを確認し、「選択した商品を購入する」ボタン
をクリックします。

(4) 次のページ(購入手続きのページ)で、ユーザーIDに
aaaaa
などCUSTOMERテーブル上には存在しない文字列を入力し、パスワードには
tekitou
など適当な値を入力して(あるいは入力しなくてもよい)
「最終購入確認画面に進む」ボタンをクリックします。

(5) 次に「ユーザーIDエラー」のページが表示され、
--------------------------------------------------------
入力されたユーザーID(aaaaa)またはパスワードは正しくありません。
前のページに戻って、ユーザーID/パスワードの入力をやり直してください。
--------------------------------------------------------
というようなメッセージが表示されますね。
これがwrongUser.jspののWebページです。
これでOKですね。



では、最後の前提条件
--------------------------------------------------------
・カートのオブジェクトの中に商品(OrderItem)オブジェクトが
が入っていること。
<<条件が満たされていない場合→エラー・メッセージ:invalidorder.html>>
--------------------------------------------------------
を見てみましょう。

これは、カートの中に商品が入っていない場合には注文手続きをするわけには
いきませんので、エラー・メッセージを返させるようにするものです。

この部分はまだプログラミングしていなかったので、やっておきましょう。
ConfirmOrderServletを下記のように編集します。
--------------------------------------------------------
package jp.co.flsi.lecture.webapp.servlet;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import jp.co.flsi.lecture.webapp.db.CustomerDbManager;
import jp.co.flsi.lecture.webapp.entity.Customer;
import jp.co.flsi.lecture.webapp.entity.Order;

public class ConfirmOrderServlet extends HttpServlet {
   public void doPost(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException {
      req.setCharacterEncoding("Shift_JIS");
      HttpSession session = req.getSession();
      Order anOrder = (Order)session.getAttribute("ORDER");
      if (anOrder == null) {
         getServletContext().getRequestDispatcher("/ordermissing.html").forward(req,res);
      }
      else {
         String customerNum = req.getParameter("userId");
         if (customerNum == null) {
            getServletContext().getRequestDispatcher("/wrongUser.jsp").forward(req,res);
         }
         else {
            CustomerDbManager customerDb = new CustomerDbManager();
            customerDb.selectData(customerNum);
            if (customerDb.getCustomerList().size() < 1) {
               getServletContext().getRequestDispatcher("/wrongUser.jsp").forward(req,res);
            }
            else if (anOrder.getOrderItems().size() < 1) {
               getServletContext().getRequestDispatcher("/invalidorder.html").forward(req,res);
            }
            else {
               Customer customer = customerDb.getCustomerList().firstElement();
               session.setAttribute("CUSTOMER",customer);
               getServletContext().getRequestDispatcher("/confirmOrder.jsp").forward(req,res);

            }
         }
      }
   }

   public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException {
      getServletContext().getRequestDispatcher("/bookmarkinvalid.html").forward(req,res);
   }

}
--------------------------------------------------------
何が変わったかわかりますね。
--------------------------------------------------------
else if (anOrder.getOrderItems().size() < 1) {
   getServletContext().getRequestDispatcher("/invalidorder.html").forward(req,res);
}
--------------------------------------------------------
というコードが追加されただけです。


では、続いて下記のようなinvalidorder.htmlファイルを作成してください。
(itemSelect.htmlと同じ場所に作成してください。)
--------------------------------------------------------
<HTML>
<HEAD>
<TITLE>購入手続き不可</TITLE>
</HEAD>
<BODY bgcolor="#77ffff" text="#fa5a00">
<H1>購入手続き不可</H1>
ショッピング・カートの中に商品が入っていないなどの理由により、購入手続きを進めることができません。
<BR>
まことに申し訳ありませんが、商品の検索から再度やり直してください。
<BR>
<BR>
<A href="http://localhost:8080/JStudy2/itemSelect.html">
商品の検索に戻るには、ここをクリック
</A>
<BR>
</BODY>
</HTML>
--------------------------------------------------------



(次回に続く)


では、今日はここまでにします。

何か、わからないところがありましたら、下記のWebページまで質問を
お寄せください。



┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
★ホームページ:
      http://www.flsi.co.jp/Java_text/
★このメールマガジンは
     「まぐまぐ(http://www.mag2.com)」
 を利用して発行しています。
★バックナンバーは
      http://www.flsi.co.jp/Java_text/
 にあります。
★このメールマガジンの登録/解除は下記Webページでできます。
      http://www.mag2.com/m/0000193915.html
★このメールマガジンへの質問は下記Webページにて受け付けて
 います。わからない所がありましたら、どしどしと質問をお寄
 せください。
      http://www.flsi.co.jp/Java_text/
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

Copyright (C) 2008 Future Lifestyle Inc. 不許無断複製