広告

■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
                      2011年01月25日

    Java総合講座 - 初心者から達人へのパスポート
                  vol.215

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


========================================================
◆ 00.お知らせ(バックナンバーの閲覧に関して)
========================================================


-------------------------------------------------------
・現在、このメールマガジンは以下の2部構成になっています。
[1] 当初からのコース:vol.xxx(xxxは番号)が振られています。
   これは現在、中級レベルになっています。
[2] 2009年11月開講コース:xxx号(xxxは番号)が振られています。
   これは現在、初心者向けのレベルになっています。
・このメールマガジンは、画面を最大化して見てください。
小さな画面で見ていると、不適切な位置で行が切れてしまう
など、問題を起すことがあります。
・このメールマガジンに掲載されているソース・コード及び
文章は特に断らない限り、すべて筆者が著作権を所有してい
ます。また、これらのソース・コードは学習用のためだけに
提供しているものです。
-------------------------------------------------------


========================================================
◆ 01.Strutsのアプリケーション開発(プロジェクト:StrutsShop)
        --- Tiles ---
========================================================

これから作りこむもの(注文状況を管理する機能)は、お店側が使用するもの
です。(注文が確定した後にお店側が行う作業に必要なものです。)

これまで作ってきたものは、お客様(ショッピングする人)が商品を注文する
ためのアプリケーションでしたが、これから作りこむものはそれとは別扱いに
なりますので、本来ならば別のプロジェクトとして扱うことが望ましいでしょ
う。

しかし、実際には互いに関係しているし、作業や説明を簡略化したいという
意図もあるため、これまでと同じStrutsShopプロジェクトの中に作りこんで
いくことにします。


この注文状況を管理する機能とは、確定している注文をリストアップし、
それらの詳細情報を表示して注文の状況を確認し、状況に変化があったもの
についてはそれを登録する(たとえば支払いや配達が完了したものについて
は支払いや配達が完了したことを登録する)というものです。

なお、支払いも配達もともに完了している注文は、必要な作業がすべて
完了していることになりますが、このような注文についてもデータベース
からデータを削除せずに、当分の間はデータを保持しておきます。
それは、のちに問合せや返品などの要求があったときにデータが必要に
なるからです。



◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆


さて、現在のStrutsShopプロジェクトのアプリケーションでは、
支払い方法としてはクレジットカード払いか銀行振込を選択できる
ようにしていますが、クレジットカード払いの場合は注文確定時に
与信チェックが行われるのが普通です。

与信チェックというのは、そのクレジットカードが信用できるかどうかを
確認するもので、通常はクレジットカード会社のシステムに接続し、
クレジットカード番号や有効期限、名義などを入力して、そのクレジット
カードが使用できるかどうかを問い合わせることによって行われます。
(これはクレジットカード会社のシステムとインターネット・ショッピング
のシステムを連係させることによって自動的に行えます。)

このとき、そのクレジットカードに盗難届けが出ているとか、持ち主の預金口座
からの引き落としができなくてクレジットカード会社への支払いが滞っていて
ブラックリストに載っているなど、何らかの問題がある場合には、クレジット
カードが信用できないという結果が返ってきます。

その場合、Webアプリケーションは例えば「このクレジットカードは使用できない
ため、注文は受け付けられません」という旨のメッセージを返して注文を拒否する
ことになります。

┌補足─────────────────────────┐
与信チェックは普通のお店ではCAT(Card Authorization Terminal)
と呼ばれる機器にカードを挿入することによって行われます。
CATはネットワークを通してクレジットカード会社のシステム
に接続されており、この機器がカードの情報をシステムに送信
することによって与信チェックが行われます。
なお、与信チェックによってカードが信用できることがわかって
もカードの使用者が本当にカードの持ち主であるかどうかは確認
できません(もしかしたら盗んだカードかもしれない)ので、
カードの使用者に署名をさせ、それがカードの裏側に書かれた署名
と一致するかどうかを(店員が)照合することによって、カード
の持ち主であることを判定するというのが本来のルールなのです
が、日本ではこの照合はほとんど実施されていません。
一方、インターネット・ショッピングでは署名をチェックする
わけにはいきませんので、この署名の照合は行われません。
したがって、盗用されたカードやコピーされたカードが使われ
やすいなどの危険性があります。

それに対して、最近では銀行のキャッシュ・カードを利用して、
買物の支払いを銀行から直接の引き落としで行うデビットカード
というものが使われるようになってきましたが、デビットカード
の場合はパスワード(暗証番号)の入力が必要という点でクレ
ジットカードよりも安全性が高くなっています。

ただし、デビットカードはまだあまり普及していません。

一方、クレジットカードに対してもSET(Secure Electronic
Transaction)という、インターネット上で安全にクレジットカー
ドを使用できるシステムも開発されていますが、ほとんど普及
していません。

現在のところは、盗難などによるカードの不正使用に対しては、
クレジットカード会社が加入している保険によって対処されて
いるというのが現状です。
└───────────────────────────┘


通常のインターネット・ショッピングでは、お客様からの支払いが完了した
ときに、商品の配達へと進むのが普通ですが、クレジットカード払いが選択
されている場合は、カードの与信チェックの結果がOKであればクレジット
カード会社から支払いが保証される(クレジットカード会社が立替払いを
する)ので、支払いが完了したのと同等に扱うことができます。

しかし、現在作成している学習用のアプリケーションではクレジットカード会社
のシステムに接続することは不可能のため、この与信チェックの部分の実装は
割愛し、クレジットカード払いが選択されている場合には無条件に支払いが完了
したものとして処理することにします。(すなわち、即座に配達の作業へと進む
ことになります。)


一方、銀行振込の場合は料金先払いで取引するのが普通ですので、銀行振込が
選択されている場合は、いったん注文は確定するけれど、料金の振込が行われ
ない限り商品の配達は行わず、一定の期間内に料金の振込が行われないと、
注文を取り消すことにします。



というわけで、Tilesを使ったアプリケーションを作る前に、これまで作って
きたアプリケーションについても以下のような変更を行っておきましょう。

(1) クレジットカード払いの場合は、注文確定時に自動的に、料金支払い
(PAYMENT)を1(=支払い済み)にします。



それから、新しく作成するアプリケーション(Tilesを使って開発するアプリ
ケーション)としては、以下の機能を作り込んでいきましょう。

(2) 確定した注文については、状況をリスト表示し、料金支払い状況や配達状況
をいつでも確認/変更できるようにします。

(3) 支払い済みになっていて、未配達の注文については、リスト上で「配達待ち」
の旨を表示するようにします。(「配達待ち」になっている注文を見た店員は
商品を梱包し、運送会社に引き渡す作業を行います。)

(4) (商品を梱包した段階で、店員が配達の状態を「配達中」に変更すること
とし)配達の状態が「配達中」になったときには、商品(ITEMテーブル)の在庫数
(QUANTITY)を自動的に該当数分減らします。

なお、現実のアプリケーションでは、商品の在庫数は注文の確定の時点で引き当て
(その注文のために確保しておく商品の個数分だけ在庫数から仮に引き算しておく
ことによって、他の注文に横取りされないようにしておく)をしておき、実際に
倉庫から取り出された時点で真の在庫数から差し引くというような2段階の処理
をするのが普通ですが、ここではそういった細かい処理は割愛します。

(5) 注文日から1ヶ月以上経っても支払いが済んでいないものはリスト上に「取り消し」
の表示をします。



◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆


それでは、まず(1)の実装ですが、CompleteOrderActionのソース・コードを
下記のように変更しましょう。(jp.co.flsi.lecture.strutsパッケージの
CompleteOrderAction.javaを編集します。)

--------------------------------------------------------
package jp.co.flsi.lecture.struts;

import java.util.Vector;

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

import jp.co.flsi.lecture.reflect.ValueLogStringMaker;
import jp.co.flsi.lecture.struts.db.Order;
import jp.co.flsi.lecture.struts.db.OrderDbManager;
import jp.co.flsi.lecture.struts.db.OrderItem;
import jp.co.flsi.lecture.struts.db.StruShopDbException;
import jp.co.flsi.lecture.struts.db.User;

import org.apache.log4j.Logger;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

public class CompleteOrderAction extends Action {
   private static Logger logger = Logger.getLogger(CompleteOrderAction.class);

   public ActionForward execute(ActionMapping mapping, ActionForm form,
         HttpServletRequest request, HttpServletResponse response) {
      logger.info("Start ...............");
      if (isTokenValid(request, true)) {
         OrderDbManager orderDb = new OrderDbManager();
         try {
            ValueLogStringMaker stringMaker = new ValueLogStringMaker();
            logger.info("Method parameter: <<<<<" + stringMaker.getValues("mapping", mapping));
            logger.info(">>>>>");
            logger.info("Method parameter: <<<<<" + stringMaker.getValues("form", form));
            logger.info(">>>>>");
            logger.info("Method parameter: <<<<<" + stringMaker.getValues("request", request));
            logger.info(">>>>>");
            logger.info("Method parameter: <<<<<" + stringMaker.getValues("response", response));
            logger.info(">>>>>");
            HttpSession session = request.getSession();
            Order anOrder = (Order)session.getAttribute("ORDER");
            if (anOrder == null) {
               logger.info("Method return: <<<<< cartVacant (1)");
               logger.info(">>>>>");
               return mapping.findForward("cartVacant");
            }
            // クレジットカード払い(0)の場合は、注文確定時に自動的に、料金支払いを支払い済みにします。
            anOrder.setOrderComplete(true); // 注文確定
            if (anOrder.getPayMethod() == 0) { // クレジットカード払いの場合
               anOrder.setPayment(true); // 料金支払いを支払い済みに変更
            }
            Vector<OrderItem> orderItemList = anOrder.getOrderItems();
            if (orderItemList == null) {
               logger.info("Method return: <<<<< cartVacant (2)");
               logger.info(">>>>>");
               return mapping.findForward("cartVacant");
            }
            User user = (User)session.getAttribute("USER");
            if (user == null) {
               logger.info("Method return: <<<<< userVacant");
               logger.info(">>>>>");
               return mapping.findForward("userVacant");
            }
            orderDb.connect();
            orderDb.insertData(anOrder);
         } catch (StruShopDbException e) {
            logger.error(e, e);
         }
         catch (Throwable e) {
            logger.error(e, e);
         }
         finally {
            try {
               orderDb.disconnect();
            }
            catch (Exception e) {
               logger.info("Disconnect failed or there is no connection.");
            }
            logger.info("End ...............");
         }
         logger.info("Method return: <<<<< success");
         logger.info(">>>>>");
         return mapping.findForward("success");
      }
      else {
         return mapping.findForward("tokenInvalid");
      }
   }

}
--------------------------------------------------------

変更(追加)した行にはコメントを入れておきましたので、説明しなくても
分かると思います。



それから、OrderDbManagerクラスのinsertData()メソッドも修正が必要ですね。
このソースコードのうち

         insHeaderPs.setInt(6, 0);  // 未払い

という行を

         if (anOrder.isPayment()) {  // paymentの値がtrueなら
            insHeaderPs.setInt(6, 1);  // 支払い済み
         }
         else { // paymentの値がfalseなら
            insHeaderPs.setInt(6, 0);  // 未払い
         }

というように書き換える必要があります。

というわけで、OrderDbManagerクラスのソース・コードを下記のように変更し
ましょう。。(jp.co.flsi.lecture.struts.dbパッケージの
OrderDbManager.javaを編集します。)

--------------------------------------------------------
package jp.co.flsi.lecture.struts.db;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Date;

import jp.co.flsi.lecture.reflect.ValueLogStringMaker;
import org.apache.log4j.Logger;

public class OrderDbManager extends DbManager  {
   private static Logger logger = Logger.getLogger(OrderDbManager.class);
   private String selectMaxONumSql = "SELECT MAX(O_NUM) FROM ORDERHEADER";
   private String insHeaderSql = "INSERT INTO ORDERHEADER  VALUES (?, ?, ?, ?, ?, ?, ?)";
   private String insItemSql = "INSERT INTO ORDERITEM  VALUES (?, ?, ?, ?)";

   public boolean insertData(Order anOrder) throws StruShopDbException {
      logger.info("Start ...............");
      ValueLogStringMaker stringMaker = new ValueLogStringMaker();
      logger.info("Method parameter: <<<<<" + stringMaker.getValues("user", anOrder));
      logger.info(">>>>>");
      try {
         conn.setAutoCommit(false);
         Statement selectSt = conn.createStatement();
         PreparedStatement insHeaderPs = conn.prepareStatement(insHeaderSql);
         PreparedStatement insItemPs = conn.prepareStatement(insItemSql);
         ResultSet rs = selectSt.executeQuery(selectMaxONumSql);
         int newOrderNumber = 1;
         if (rs.next()) newOrderNumber = rs.getInt(1) + 1;
         insHeaderPs.setInt(1, newOrderNumber);
         insHeaderPs.setDate(2, new java.sql.Date((new Date()).getTime()));
         insHeaderPs.setString(3, anOrder.getUserType());
         insHeaderPs.setString(4, anOrder.getUserId());
         insHeaderPs.setInt(5, anOrder.getPayMethod());
         if (anOrder.isPayment()) {  // paymentの値がtrueなら
            insHeaderPs.setInt(6, 1);  // 支払い済み
         }
         else { // paymentの値がfalseなら
            insHeaderPs.setInt(6, 0);  // 未払い
         }
         insHeaderPs.setInt(7, 0);  // 未配達
         insHeaderPs.executeUpdate();
         int seqNum = 1;
         for (OrderItem oi : anOrder.getOrderItems()) {
            insItemPs.setInt(1, newOrderNumber);
            insItemPs.setInt(2, seqNum);
            insItemPs.setString(3, oi.getItemNum());
            insItemPs.setInt(4, oi.getOrderQuantity());
            insItemPs.executeUpdate();
            seqNum++;
         }
         selectSt.close();
         insHeaderPs.close();
         insItemPs.close();
         conn.commit();
      }
      catch (SQLException e) {
         try {
            conn.rollback();
            logger.info("Rollback executed.");
         }
         catch (Exception ex) {
            logger.info("Rollback failed.", ex);
         }
         logger.error(e, e);
         throw new StruShopDbException("Error: insertData() failed!", e);
      }
      catch (Throwable e) {
         try {
            conn.rollback();
            logger.info("Rollback executed.");
         }
         catch (Exception ex) {
            logger.info("Rollback failed.", ex);
         }
         logger.error(e, e);
         return false;
      }
      finally {
         logger.info("End ...............");
      }
      logger.info("Method return: <<<<< true");
      logger.info(">>>>>");
      return true;
   }

}
--------------------------------------------------------



◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆


では、一度テストをしてみましょう。

(1) 「サーバー」ビューの中の「ローカル・ホストのTomcat v5.5サーバー」を
右クリックし、「開始」を選択します。しばらくして、

ローカル・ホストのTomcat v5.5サーバー[始動済み,同期済み]

というように、後ろに「始動済み」の表示が出たら、Tomcatの起動が完了しています。

(2) Webブラウザー(Internet Explorer)を起動して、URL

http://localhost:8080/StrutsShop/itemSelect.jsp

を入力しましょう。

(3) 「商品の検索」のWebページが開いたら、そのまま「商品検索」ボタンをクリック
して、次の「商品のリスト」のWebページでは全ての選択欄にチェックマークを入れて
「買物かごに入れる」ボタンをクリックしましょう。

(4) 「ショッピング・カートの中のリスト」のWebページが開いたら、そのまま
「これらの商品を購入する」ボタンをクリックしましょう。

(5) 「購入する商品」のWebページが開いたら、「お支払い方法」欄は
「クレジット・カード払い」のままにし、「会員登録済み」を選択(その左側
の丸の中をクリック)し、「ユーザーID」欄には、以前会員登録した
user01
を入力し、「パスワード」欄には、以前登録した正しいパスワード
pass01
を入力し、「最終購入確認または新規会員登録の画面に進む」ボタンを
クリックしましょう。

(6) 購入(注文)内容の確認のWebページが開いたら、、「購入(注文確定)する」ボタン
をクリックして下さい。

最後に注文完了のWebページ(「下記のご注文を受け付けました。」のWebページ)が
開くことを確認して下さい。


その後、EclipseのDBViewerでORDERHEADERテーブルを表示して、先ほどの注文データ
(O_DATEカラムが本日の日付になっている行を確認すればよい)のPAYMENTカラムが
1になっていることを確認して下さい。
(DBViewerの操作方法を忘れた人は、vol.084などを復習のこと。)

同様にして、銀行振り込みを選択した場合もテストしてみて下さい。



◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆


では、続いて(2)〜(4)の機能を作りこんでいきましょう。これにはTilesを使って
いきます。



(次回に続く)



┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
★ホームページ:
      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) 2011 Future Lifestyle Inc. 不許無断複製