広告

■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
                      2010年04月25日

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

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


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


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


前回までで、インターネット・ショッピングのアプリケーションの処理の
基本的な流れの実装は終わりましたので、今回からは副次的な流れのプログ
ラミングにはいっていきます。


まずは、これまでに出てきた

--------------------------------------------------------
(U-7) 注文のためのWebページが開く。
[order.jsp]

(U-8) (会員登録しない一時的な顧客の場合)顧客の住所・氏名・連絡先を
入力し、支払い方法(クレジット・カード払い/銀行振り込み)を選択し、
「注文内容の確認」ボタンを押す。
[confirmorder.do]→[ConfirmOrderAction.java]

(U-9) 注文確認のためのWebページが開く。
[confirmOrder.jsp]
--------------------------------------------------------

という処理の流れの副次的な流れとして、

(a) これから会員登録する顧客の場合
(b) 既に会員登録済みの顧客の場合

を考えます。

まず(a)に対しては

--------------------------------------------------------
(U-7) 注文のためのWebページが開く。
[order.jsp]

(U-8a-1) (これから会員登録する顧客の場合)「新規に会員登録する」
という項目を選択する。支払い方法(クレジット・カード払い/銀行振り込み)
を選択し、「注文内容の確認」ボタン(「新規会員登録」ボタンも兼務)
を押す。
[confirmorder.do]→[ConfirmOrderAction.java]

(U-8a-2) 会員登録のためのWebページが開く。
[userReg.jsp]

(U-8a-3) 顧客の住所・氏名・連絡先・ユーザーID・パスワードを入力し、
「会員登録」ボタンを押す。
[userreg.do]→[UserRegistrationAction.java]

(U-9) 注文確認のためのWebページが開く。
[confirmOrder.jsp]
--------------------------------------------------------

という処理の流れを考えます。

ただし、上記のうち(U-8a-3)〜(U-9)に対しては、さらに下記のような
副次的な流れが考えられます。

--------------------------------------------------------
(U-8a-3) 顧客の住所・氏名・連絡先・ユーザーID・パスワードを入力し、
「会員登録」ボタンを押す。
[userreg.do]→[UserRegistrationAction.java]

(U-8a-3r-1) (ユーザーIDが既存のものと重複していた場合)会員登録の
ためのWebページが再度開く。このとき「重複エラー」を表示する。
[userReg.jsp]

(U-8a-3r-2) 顧客の住所・氏名・連絡先・ユーザーID・パスワードを入力し直し、
「会員登録」ボタンを押す。
[userreg.do]→[UserRegistrationAction.java]

(U-9) 注文確認のためのWebページが開く。
[confirmOrder.jsp]
--------------------------------------------------------



では、これらの処理の流れをプログラミングしていきましょう。



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


まず、order.jspに

(a) これから会員登録する顧客の場合
(b) 既に会員登録済みの顧客の場合

の選択肢のためのコードを加えます。


では、order.jspを下記のように編集しましょう。

--------------------------------------------------------
<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib uri="/tags/struts-bean" prefix="bean" %>
<%@ taglib uri="/tags/struts-logic" prefix="logic" %>
<%@ taglib uri="/tags/struts-html" prefix="html" %>
<%@ taglib uri="/tags/struts-nested" prefix="nested" %>

<html:html>
   <head>
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
      <title>購入手続き</title>
   </head>
   <body bgcolor="#77ffff" text="#fa5a00">
      <h1>購入する商品</h1>
      <html:form method="POST" action="/confirmorder">
         <table border="1" width="100%">
           <tbody>
             <tr>
               <th>商品番号</th>
               <th>商品名</th>
               <th>価格</th>
               <th>カテゴリー</th>
               <th>購入個数</th>
             </tr>
             <%@ page import="java.util.Vector" %>
             <%@ page import="jp.co.flsi.lecture.struts.db.OrderItem" %>
             <% String homebase = request.getContextPath(); %>
            <% int totalPrice = 0; %>
             <bean:define id="orderItemList" name="ORDER" property="orderItems" type="Vector<OrderItem>" scope="session" />
            <logic:iterate id="item" type="OrderItem" name="orderItemList" scope="page">
               <tr>
                  <td><bean:write name="item" property="itemNum" /></td>
                  <td><bean:write name="item" property="itemName" /></td>
                  <td><bean:write name="item" property="price" />円</td>
                  <td><bean:write name="item" property="catName" /></td>
                  <td align="center"><bean:write name="item" property="orderQuantity" /></td>
                </tr>
                <% totalPrice += item.getPrice() * item.getOrderQuantity(); %>
            </logic:iterate>
           </tbody>
         </table>
         <br>
         <table border="1">
             <tr>
               <th>合計金額:</th>
               <th><%= totalPrice %>円</th>
             </tr>
         </table>
         <br>
         <br>
         お支払い方法を選択してください。
         <table border="0">
             <tr>
               <th>お支払い方法:</th>
               <td>
               <html:select property="payMethod" value="0" size="2">
                 <html:option value="0">クレジット・カード払い</html:option>
                 <html:option value="1">銀行振り込み</html:option>
               </html:select>
              </td>
             </tr>
         </table>
         <br>
         <br>
         <html:errors property="membership" />
         <html:radio property="membership" value="new"/>
         新規に会員登録する
         <br>
         <br>
         <html:radio property="membership" value="yes"/>
         会員登録済み(ユーザーIDとパスワードを入力して下さい。)
         <br>
         <html:errors property="userid" />
         <html:errors property="password" />
         <table border="0">
             <tr>
               <th>ユーザーID:</th>
               <td><html:text property="userid" size="7" /></td>
             </tr>
             <tr>
               <th>パスワード:</th>
               <td><html:password property="password" size="10" redisplay="false" /></td>
             </tr>
         </table>
         <br>
         <html:radio property="membership" value="no"/>
         会員登録せずに購入する(下の各欄にお客様のお名前・ご住所等をご入力ください。)
         <br>
         <html:errors property="name" />
         <html:errors property="zipcode" />
         <html:errors property="address" />
         <html:errors property="telNo" />
         <html:errors property="emailAddress" />
         <table border="0">
             <tr>
               <th>お名前:</th>
               <td><html:text property="name" size="20" /></td>
             </tr>
             <tr>
               <th>郵便番号:</th>
               <td><html:text property="zipcode" size="8" /></td>
             </tr>
             <tr>
               <th>ご住所:</th>
               <td><html:text property="address" size="100" /></td>
             </tr>
             <tr>
               <th>お電話番号:</th>
               <td><html:text property="telNo" size="12" /></td>
             </tr>
             <tr>
               <th>Eメール・アドレス:</th>
               <td><html:text property="emailAddress" size="30" /></td>
             </tr>
         </table>
         <br>
         <br>
         <html:submit property="submit" value="最終購入確認または新規会員登録の画面に進む" />
      </html:form>
      <br>
      <html:link page="/itemSelect.jsp">トップ・ページ(商品検索のページ)に戻る</html:link>
   </body>
</html:html>
--------------------------------------------------------

ここで新たに<html:radio>タグと<html:password>タグが登場しました。

<html:radio>タグは見てわかる通り、ラジオ・ボタンのタグです。
ここでは、property属性に"membership"という値を指定した<html:radio>タグ
が2つ書かれていますが、そうするとこのどちらか一方だけをオンにする(クリッ
クするとマークが付く)ことができます。そして、オンになったほうのvalue属性
の値がサーバーに送られることになります。

<html:password>タグは見てわかる通り、パスワードを入力させるためのタグです。
<html:text>タグと同じように文字列の入力ができますが、<html:text>タグと違っ
て文字列はアスタリスク(*)に変換されて表示されます。
このタグのredisplayという属性は、予め値が設定されているときにそれを表示
するかどうかを指定するもので(ただし画面上はアスタリスクで表示されるので
文字列は見えない)、redisplay="false"を指定すると表示されません。
このWebページでは、パスワードを初めて入力させるので、redisplay="false"を
指定しています。

なお、パスワードは通常、2回入力させておいて、それらが一致しているかどうか
検証する(画面上ではアスタリスクしか表示されないので、入力間違いがあっても
分からないため、2回入力させて検証する)のが普通ですが、ここでは省略します。


あと、<html:errors>タグを追加したことも頭に入れておいて下さい。



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


上記のJSPによって、新たにmembership、userid、passwordというプロパティーが
追加されましたので、これに合わせてOrderFormクラスのほうも変更しましょう。
このOrderFormクラスでは、入力データの検証も行うことにします。

では、OrderForm.javaファイルを下記のように編集して下さい。

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

import java.io.UnsupportedEncodingException;
import javax.servlet.http.HttpServletRequest;
import jp.co.flsi.lecture.reflect.ValueLogStringMaker;
import org.apache.log4j.Logger;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionMessage;

public class OrderForm extends ActionForm {
   private static Logger logger = Logger.getLogger(OrderForm.class);
   private String name = null;
   private String zipcode = null;
   private String address = null;
   private String telNo = null;
   private String emailAddress = null;
   private String payMethod = null;
   private String membership = null;
   private String userid = null;
   private String password = null;

   public void reset(ActionMapping mapping, HttpServletRequest request) {
      logger.info("Start ...............");
      try {
         request.setCharacterEncoding("UTF-8");
      } catch (UnsupportedEncodingException e) {
         logger.error(e, e);
      }
      finally {
         logger.info("End ...............");
      }
   }

   public void setName(String name) {
      this.name = name;
   }

   public String getName() {
      return name;
   }

   public void setZipcode(String zipcode) {
      this.zipcode = zipcode;
   }

   public String getZipcode() {
      return zipcode;
   }

   public void setAddress(String address) {
      this.address = address;
   }

   public String getAddress() {
      return address;
   }

   public void setTelNo(String telNo) {
      this.telNo = telNo;
   }

   public String getTelNo() {
      return telNo;
   }

   public void setEmailAddress(String emailAddress) {
      this.emailAddress = emailAddress;
   }

   public String getEmailAddress() {
      return emailAddress;
   }

   public void setPayMethod(String payMethod) {
      this.payMethod = payMethod;
   }

   public String getPayMethod() {
      return payMethod;
   }

   public void setMembership(String membership) {
      this.membership = membership;
   }

   public String getMembership() {
      return membership;
   }

   public void setUserid(String userid) {
      this.userid = userid;
   }

   public String getUserid() {
      return userid;
   }

   public void setPassword(String password) {
      this.password = password;
   }

   public String getPassword() {
      return password;
   }

   public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) {
      logger.info("Start ...............");
      ValueLogStringMaker stringMaker = new ValueLogStringMaker();
      ActionErrors errors = new ActionErrors();
      try {
         logger.info("Method parameter: <<<<<" + stringMaker.getValues("mapping", mapping));
         logger.info(">>>>>");
         logger.info("Method parameter: <<<<<" + stringMaker.getValues("request", request));
         logger.info(">>>>>");

         if (membership.equals("yes")) {
            if (userid.equals("")) {
               errors.add("userid", new ActionMessage("errors.userid.required"));
            }
            if (password.equals("")) {
               errors.add("password", new ActionMessage("errors.password.required"));
            }
         }
         else if (membership.equals("no")) {
            if (name.equals("")) {
               errors.add("name", new ActionMessage("errors.name.required"));
            }
            if (zipcode.equals("")) {
               errors.add("zipcode", new ActionMessage("errors.zipcode.required"));
            }
            if (address.equals("")) {
               errors.add("address", new ActionMessage("errors.address.required"));
            }
            if (telNo.equals("")) {
               errors.add("telNo", new ActionMessage("errors.telNo.required"));
            }
            if (emailAddress.equals("")) {
               errors.add("emailAddress", new ActionMessage("errors.emailAddress.required"));
            }
         }
         else  if (!membership.equals("new")) {
            errors.add("membership", new ActionMessage("errors.membership.mustselected"));
         }
      }
      catch (Throwable e) {
         logger.error(e, e);
      }
      finally {
         logger.info("End ...............");
      }
      logger.info("Method return: <<<<<" + stringMaker.getValues("errors", errors));
      logger.info(">>>>>");
      return (errors);
   }

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

上のソース・コードの中で各errors.add()の第一引数が先ほどのorder.jspの中の
各<html:errors>タグのproperty属性の値に対応していることを確認して下さい。



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


では、これらの各errors.add()の第二引数に合わせて、MessageResources.properties
にメッセージを追加しておきましょう。

(1) プロジェクト・エクスプローラーのStrutsShop配下の「Javaリソース: src」配下
のMessageResources.propertiesを右クリックし、「アプリケーションから開く」→
「Limyプロパティー・エディター」を選択します。

(2) MessageResources.propertiesのエディターが開いたら、
MessageResources.propertiesファイルの最後に次のような行を追加して下さい。

--------------------------------------------------------
errors.userid.required=ユーザーIDを入力する必要があります。
errors.password.required=パスワードを入力する必要があります。
errors.name.required=お名前を入力する必要があります。
errors.zipcode.required=郵便番号を入力する必要があります。
errors.address.required=ご住所を入力する必要があります。
errors.telNo.required=お電話番号を入力する必要があります。
errors.emailAddress.required=Eメール・アドレスを入力する必要があります。
errors.membership.mustselected=会員登録に関する項目(「会員登録済み」など)を選択する必要があります。
--------------------------------------------------------

念のため、MessageResources.propertiesファイルの中身全体を下記に表示します。

--------------------------------------------------------
# -- standard errors --
errors.header=<font color="#ff0000"><b>
errors.prefix=
errors.suffix=<br><br>
errors.footer=</b></font>
# -- validator --
errors.invalid={0} is invalid.
errors.maxlength={0} can not be greater than {1} characters.
errors.minlength={0} can not be less than {1} characters.
errors.range={0} is not in the range {1} through {2}.
errors.required={0} is required.
errors.byte={0} must be an byte.
errors.date={0} is not a date.
errors.double={0} must be an double.
errors.float={0} must be an float.
errors.integer={0} must be an integer.
errors.long={0} must be an long.
errors.short={0} must be an short.
errors.creditcard={0} is not a valid credit card number.
errors.email={0} is an invalid e-mail address.
# -- other --
errors.cancel=Operation cancelled.
errors.detail={0}
errors.general=The process did not complete. Details should follow.
errors.token=Request could not be completed. Operation is not in sequence.
# -- welcome --
welcome.title=Struts Blank Application
welcome.heading=Welcome!
welcome.message=To get started on your own application, copy the struts-blank.war to a new WAR file using the name for your application. Place it in your container's "webapp" folder (or equivalent), and let your container auto-deploy the application. Edit the skeleton configuration files as needed, restart your container, and you are on your way! (You can find the application.properties file with this message in the /WEB-INF/src/java/resources folder.)
# -- StrutsShop order --
errors.order.quantity.required=購入個数に空文字が入力されましたが、受け付けられません。
errors.order.quantity.integer=購入個数に数字以外の文字が入力されましたが、受け付けられません。
errors.userid.required=ユーザーIDを入力する必要があります。
errors.password.required=パスワードを入力する必要があります。
errors.name.required=お名前を入力する必要があります。
errors.zipcode.required=郵便番号を入力する必要があります。
errors.address.required=ご住所を入力する必要があります。
errors.telNo.required=お電話番号を入力する必要があります。
errors.emailAddress.required=Eメール・アドレスを入力する必要があります。
errors.membership.mustselected=会員登録に関する項目(「会員登録済み」など)を選択する必要があります。
--------------------------------------------------------

これらのメッセージは、vol.196に書いたように、Unicodeエスケープに変換しておく
必要がありますが、このLimyプロパティー・エディターというEclipseのプラグイン
が自動的に変換してくれますので単純に保管(Ctrl + S)するだけで大丈夫です。
(プロパティー・エディター上では普通の日本語文字で表示されますがファイルの内部
はUnicodeエスケープに変換されています。)

(Limyのプラグインは現在使用している「Pleiades All in One 日本語ディストリビューション」
に予めインストールされているものです。)



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


では続いて、ConfirmOrderActionクラスを編集しましょう。
ConfirmOrderAction.javaファイルを下記のように変更して下さい。

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

import java.text.DecimalFormat;
import java.text.NumberFormat;
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.security.PasswordEncryption;
import jp.co.flsi.lecture.struts.db.Order;
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 jp.co.flsi.lecture.struts.db.UserDbManager;

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 ConfirmOrderAction extends Action {
   private static Logger logger = Logger.getLogger(ConfirmOrderAction.class);

   public ActionForward execute(ActionMapping mapping, ActionForm form,
         HttpServletRequest request, HttpServletResponse response) {
      logger.info("Start ...............");
      UserDbManager userDbManager = new UserDbManager();
      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");
         }
         Vector<OrderItem> orderItemList = anOrder.getOrderItems();
         if (orderItemList == null) {
            logger.info("Method return: <<<<< cartVacant (2)");
            logger.info(">>>>>");
            return mapping.findForward("cartVacant");
         }
         OrderForm orderForm = (OrderForm) form;
         anOrder.setPayMethod(Integer.parseInt(orderForm.getPayMethod()));
         // 新規に会員登録する場合:
         if (orderForm.getMembership().equals("new")) {
            logger.info("Method return: <<<<< newUser");
            logger.info(">>>>>");
            return mapping.findForward("newUser");
         }
         User user = null;
         userDbManager.connect();
         // 既に会員登録済みの場合:
         if (orderForm.getMembership().equals("yes")) {
            user = userDbManager.getDataByUserTypeAndUserid("R", orderForm.getUserid());
            //         (会員登録した顧客のユーザー・タイプは"R")
            // 入力されたユーザーIDに該当するユーザーがデータベースに見つからない場合:
            if (user.getUserid().equals("")) {
               logger.info("Method return: <<<<< useridInvalid");
               logger.info(">>>>>");
               return mapping.findForward("useridInvalid");
            }
            // 入力されたパスワードが正しくない場合:
            if (!PasswordEncryption.checkWithEncryptedPassword(orderForm.getPassword(), user.getPassword())) {
               logger.info("Method return: <<<<< passwordInvalid");
               logger.info(">>>>>");
               return mapping.findForward("passwordInvalid");
            }
         }
         else { // 会員登録せずに購入する場合:
            user = new User();
            String maxUserid = userDbManager.getMaxTemporaryUserid();
            user.setUserType("T");       // 一時的顧客のユーザー・タイプは"T"
            NumberFormat formatter = new DecimalFormat("0000000");
            user.setUserid(formatter.format(Integer.parseInt(maxUserid) + 1));
            user.setPassword(PasswordEncryption.encrypt("NONE"));
            user.setName(orderForm.getName());
            user.setZipcode(orderForm.getZipcode());
            user.setAddress(orderForm.getAddress());
            user.setTelno(orderForm.getTelNo());
            user.setEmail(orderForm.getEmailAddress());
            if (!userDbManager.insertData(user)) {
               logger.info("Method return: <<<<< userInsertError");
               logger.info(">>>>>");
               return mapping.findForward("userInsertError");
            }
         }
         anOrder.setUserType(user.getUserType());
         anOrder.setUserId(user.getUserid());
         session.setAttribute("USER", user);
      } catch (StruShopDbException e) {
         logger.error(e, e);
      }
      catch (Throwable e) {
         logger.error(e, e);
      }
      finally {
         try {
            userDbManager.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");
   }

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



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


(次回に続く)


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



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