■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
                      2008年02月24日

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

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


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


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


さて、だんだんと何をやっているのか、わからなくなってきた人も
いるのではないでしょうか。
私も、何をやっているのか、わからなくなってきました(おいおい)。

...というのは冗談ですが、プログラムのファイルがどんどんと
増えてきてごちゃごちゃしてきましたので、念のために、これまで
に作成してきた各プログラムの流れを頭の中で整理したうえで、
次のプログラミングにはいっていくことにしましょう。


ショッピングの通常の手順に合わせて、これまでのプログラムの流れ
を整理すると、次のようになります。

--------------------------------------------------------
(0) 最初のページを呼び出す。
[WebブラウザーにURL(http://localhost:8080/JStudy2/itemSelect.html)を入力する]
 → itemSelect.html

(1) キーワード(商品名の一部)やカテゴリーを入力して商品を検索する。
「送信」ボタンをクリックする
 → ItemServlet.java → itemList.jsp

(2) 商品のリストが表示されるので、買いたい商品を選択する(= ショッ
ピング・カートに入れる)。(ショッピング・カートは以後ときどき
カートと略します。)
「選択した商品をカートに入れる」ボタンをクリックする
 → OrderItemServlet.java → cart.jsp

(3) カートの中身が表示されるので、必要に応じて購入個数を変更する。
「選択した商品を購入する」ボタンをクリックする
 → OrderProcessServlet.java → order.jsp
[例外:<<カートのオブジェクトが消失していた場合>>
 → OrderProcessServlet.java → cartmissing.html]
--------------------------------------------------------

前回は、(3)の場面において例外に対処するためのプログラミングを行いまし
たが、上記の(3)の例外以外にもさまざまな場面で例外が発生する可能性が
あります。
これらの例外については、あとでまとめて対処方法を説明いたします。



さて、(3)のあとは、以下のような処理が行えるようにプログラミングして
いきましょう。(ただし例外の処理は、あと回し)

--------------------------------------------------------
(4) 購入(注文)する内容が表示され、ユーザーIDとパスワードが要求される
ので入力する。
「最終購入確認画面に進む」ボタンをクリックする
 → ConfirmOrderServlet.java → confirmOrder.jsp

(5) 商品の発送先(= ユーザーの住所・氏名)が表示され、購入(注文)を
確定するかどうか聞いてくる。
「購入(注文確定)する」ボタンをクリックする
 → CompleteOrderServlet.java → completeOrder.jsp

(6) 「注文を受け付けました。」のメッセージが表示される。
(これで終わり。)
--------------------------------------------------------



ではこれから、ConfirmOrderServletのプログラミングにとりかかりたいと
思います。
このサーブレットでは、(3)のorder.jspのWebページに入力されたユーザーID
からデータベースのCUSTOMERテーブルを検索し、住所と氏名を取り出します。

なお、パスワードや住所・氏名などの個人情報をそのままインターネット上に
流すとハッカーなどに読み取られる危険性がありますから、通常はSSL
(Secure Socket Layer)やTLS(Transport Layer Security)というプロト
コルを使って暗号化するのですが、ここら辺の話をするとなると、随分時間
がかかってしまいます。(公開鍵暗号方式(public key cryptosystem)とか
電子署名(digital signature)とか電子証明書(digital certificate)とか
認証局(CA = Certificate Authority)とかの話をしなければならなくなり
ます。)

しかも、こういったセキュリティーのお話は大変重要なので、あとでまとめて
じゅうぶん時間をかけてお話したいと思います。

というわけで、今のところは、セキュリティー関連の部分は省略します。
パスワードも形式的に入力するだけにしておきます。
(あとでセキュリティーのお話のときにパスワード関連の部分も一緒に
まとめてプログラミングします。)


はい。では、ひとまずユーザーID(= 顧客番号)からデータベースのCUSTOMER
テーブルを検索し、住所と氏名を取り出すようにするために、
CustomerDbManagerクラスを次のように修正しておきましょう。
--------------------------------------------------------
package jp.co.flsi.lecture.webapp.db;

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

import jp.co.flsi.lecture.webapp.entity.Customer;

public class CustomerDbManager extends DbManager {
   private Vector<Customer> customerList = new Vector<Customer>();
   private String selectAllSql = "SELECT * FROM CUSTOMER";
   private String selectNumSql = "SELECT * FROM CUSTOMER WHERE NUM = ?";

   public Vector<Customer> getCustomerList() {
      return customerList;
   }

   public void selectAllData() {
      try {
         connect();
         Statement selectSql = conn.createStatement();
         ResultSet rs = selectSql.executeQuery(selectAllSql);
         while (rs.next()) {
            Customer aCustomer = new Customer();
            aCustomer.setNum(rs.getString("NUM"));
            aCustomer.setName(rs.getString("NAME"));
            aCustomer.setZipCode(rs.getString("ZIPCODE"));
            aCustomer.setAddress(rs.getString("ADDRESS"));
            getCustomerList().addElement(aCustomer);
         }
         selectSql.close();
         disconnect();
      }
      catch (SQLException exception) {
         exception.printStackTrace();
      }
   }

   public void selectData(String customerNum) {
      try {
         connect();
         PreparedStatement selectPs = null;
         ResultSet rs;
         if (customerNum == null) {
            selectPs = conn.prepareStatement(selectAllSql);
            rs = selectPs.executeQuery(selectAllSql);
         }
         else {
            selectPs = conn.prepareStatement(selectNumSql);
            selectPs.setString(1, customerNum);
            rs = selectPs.executeQuery();
         }
         while (rs.next()) {
            Customer aCustomer = new Customer();
            aCustomer.setNum(rs.getString("NUM"));
            aCustomer.setName(rs.getString("NAME"));
            aCustomer.setZipCode(rs.getString("ZIPCODE"));
            aCustomer.setAddress(rs.getString("ADDRESS"));
            getCustomerList().addElement(aCustomer);
         }
         selectPs.close();
         disconnect();
      }
      catch (SQLException exception) {
         exception.printStackTrace();
      }
   }

}
--------------------------------------------------------
これも説明は不要ですね。
基本的にはselectData()というメソッドが追加されただけですね。
あと、リテラルで記述していたSQL文を変数に変えたという点も以前
と異なっていますが、本質的な違いではありません。



では続いて、次のような内容のConfirmOrderServletクラスを作成して
ください。

このクラスは、正しいユーザーID(CUSTOMERテーブルのNUMカラムに
登録されている顧客番号)が入力されたら、顧客の住所と氏名を商品の
送付先として表示するJSP(confirmOrder.jsp)を呼び出します。
--------------------------------------------------------
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 {
               Customer customer = customerDb.getCustomerList().firstElement();
               session.setAttribute("CUSTOMER",customer);
               getServletContext().getRequestDispatcher("/confirmOrder.jsp").forward(req,res);

            }
         }
      }
   }

}
--------------------------------------------------------
このソース・コードでは、カートがnullのときやユーザーIDが不正のときに、
それぞれordermissing.htmlやwrongUser.jspを呼び出すようにコーディング
していますが、これらのエラー関連のファイルは、あとでまとめて作成する
ことにします。

なお、顧客番号はCUSTOMERテーブルのプライマリー・キーになっていますから、
一つの顧客番号に対してはCUSTOMERテーブルのデータは1行しかありえません。
したがって、35行目の

Customer customer = customerDb.getCustomerList().firstElement();

においては、VectorのfirstElement()メソッドを呼び出すことによって先頭の
データ(これしかない)だけを取り出すようにしています。

あとの部分は、説明しなくても理解できることと思います。


では、このサーブレットを登録すべく、web.xmlを下記のように編集しま
しょう。
--------------------------------------------------------
<?xml version="1.0" encoding="ISO-8859-1"?>

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
   version="2.5">

    <servlet>
        <servlet-name>TestServlet</servlet-name>
        <servlet-class>jp.flsi.lecture.servlet.TestServlet</servlet-class>
    </servlet>

    <servlet>
        <servlet-name>TestServlet2</servlet-name>
        <servlet-class>jp.flsi.lecture.servlet.TestServlet2</servlet-class>
    </servlet>

    <servlet>
        <servlet-name>AgeCalcServlet</servlet-name>
        <servlet-class>jp.co.flsi.lecture.webapp.servlet.AgeCalcServlet</servlet-class>
    </servlet>

    <servlet>
        <servlet-name>CustomerServlet</servlet-name>
        <servlet-class>jp.co.flsi.lecture.webapp.servlet.CustomerServlet</servlet-class>
    </servlet>

    <servlet>
        <servlet-name>ItemServlet</servlet-name>
        <servlet-class>jp.co.flsi.lecture.webapp.servlet.ItemServlet</servlet-class>
    </servlet>

    <servlet>
        <servlet-name>OrderItemServlet</servlet-name>
        <servlet-class>jp.co.flsi.lecture.webapp.servlet.OrderItemServlet</servlet-class>
    </servlet>

    <servlet>
        <servlet-name>OrderProcessServlet</servlet-name>
        <servlet-class>jp.co.flsi.lecture.webapp.servlet.OrderProcessServlet</servlet-class>
    </servlet>

    <servlet>
        <servlet-name>ConfirmOrderServlet</servlet-name>
        <servlet-class>jp.co.flsi.lecture.webapp.servlet.ConfirmOrderServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>TestServlet</servlet-name>
        <url-pattern>/servlet/test</url-pattern>
    </servlet-mapping>

    <servlet-mapping>
        <servlet-name>TestServlet2</servlet-name>
        <url-pattern>/servlet/test2</url-pattern>
    </servlet-mapping>
   
    <servlet-mapping>
        <servlet-name>AgeCalcServlet</servlet-name>
        <url-pattern>/servlet/age</url-pattern>
    </servlet-mapping>

    <servlet-mapping>
        <servlet-name>CustomerServlet</servlet-name>
        <url-pattern>/servlet/customers</url-pattern>
    </servlet-mapping>

    <servlet-mapping>
        <servlet-name>ItemServlet</servlet-name>
        <url-pattern>/servlet/items</url-pattern>
    </servlet-mapping>

    <servlet-mapping>
        <servlet-name>OrderItemServlet</servlet-name>
        <url-pattern>/servlet/orderitems</url-pattern>
    </servlet-mapping>

    <servlet-mapping>
        <servlet-name>OrderProcessServlet</servlet-name>
        <url-pattern>/servlet/orderprocess</url-pattern>
    </servlet-mapping>

    <servlet-mapping>
        <servlet-name>ConfirmOrderServlet</servlet-name>
        <url-pattern>/servlet/confirmorder</url-pattern>
    </servlet-mapping>
   
</web-app>
--------------------------------------------------------



では、続いて次のようにconfirmOrder.jspを作成しましょう。
--------------------------------------------------------
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="Shift_JIS" %>

<HTML>
<HEAD>
<TITLE>購入(注文)内容の確認</TITLE>
</HEAD>
<BODY bgcolor="#77ffff" text="#fa5a00">
<H1>購入する商品:</H1>
<FORM action="completeorder" method="POST">
<TABLE border="1" width="100%">
  <TBODY>
    <TR>
      <TH>商品番号</TH>
      <TH>商品名</TH>
      <TH>価格</TH>
      <TH>カテゴリー</TH>
      <TH>購入個数</TH>
    </TR>

<%@ page import="jp.co.flsi.lecture.webapp.entity.*" %>
<% int totalPrice = 0; %>
<jsp:useBean class="jp.co.flsi.lecture.webapp.entity.Order" id="ORDER" scope="session" />
<%    OrderItem anOrderItem;
      for (int i = ORDER.getOrderItems().size() - 1; i >= 0 ; i--) {
         anOrderItem = ORDER.getOrderItems().elementAt(i); %>
         <TR>
            <TD><%= anOrderItem.getNum() %></TD>
            <TD><%= anOrderItem.getName() %></TD>
            <TD><%= anOrderItem.getPrice() %>円</TD>
            <TD><%= anOrderItem.getCategory() %></TD>
            <TD align="right"><%= anOrderItem.getOrderQuantity() %></TD>
          </TR>
<%         totalPrice += anOrderItem.getPrice() * anOrderItem.getOrderQuantity();
      } %>

  </TBODY>
</TABLE>
<BR>
<TABLE border="1">
    <TR>
      <TH>合計金額:</TH>
      <TH><%= totalPrice %>円</TH>
    </TR>
</TABLE>
<BR>
<BR>
<H1>商品の送付先:</H1>
<jsp:useBean class="jp.co.flsi.lecture.webapp.entity.Customer" id="CUSTOMER" scope="session" />
<TABLE border="1">
    <TR>
      <TH>郵便番号</TH>
      <TH>住所</TH>
      <TH>氏名</TH>
    </TR>
    <TR>
      <TD><%= CUSTOMER.getZipCode() %></TD>
      <TD><%= CUSTOMER.getAddress() %></TD>
      <TD><%= CUSTOMER.getName() %></TD>
    </TR>
</TABLE>
<BR>
<BR>
この内容で購入(注文)を確定するには、下の「購入(注文確定)する」ボタンを押してください。
<BR>
<FONT color="#ff0000"><STRONG>
なお、「購入(注文確定)する」ボタンを押すと注文が確定してしまい、取り消しはできませんのでご注意ください。
</STRONG></FONT>
<BR>
<BR>
<INPUT TYPE="submit" NAME="completeOrder" VALUE="購入(注文確定)する">
</FORM>
</BODY>
</HTML>
--------------------------------------------------------

このソース・コードの中で

<FONT color="#ff0000">......</FORM>

のFONTはフォントの種類を指定するためのタグで、そのcolor="#ff0000"
という属性はフォントの色をRGB指定で赤色にするものです。また、

<STRONG>......</STRONG>

のSTRONGは文字を強調表示することを指定するためのタグです。

残りのソース・コードは説明しなくても理解できますね。



ここまでできたところで、一度テストしてみましょう。

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

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

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

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

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

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

(5) 次のページに商品の送付先として顧客の郵便番号、住所、氏名が
表示されていることを確認してください。


どうですか?うまくいきましたか。

これ以降の部分は次回以降にプログラミングします。



(次回に続く)


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

何か、わからないところがありましたら、下記の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. 不許無断複製