広告

■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
                      2010年03月21日

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

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


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


========================================================
◆ 01.はじめに
========================================================


vol.190で作成したValueLogStringMakerですが、その後、プログラム・
ミスが見つかりましたので、こっそりとバックナンバーのvol.190上で
修正してあります。(当メールマガジンは実際の開発プロジェクトと
異なり、作成したプログラムにじゅうぶんなテストを行っていません。
その代わりに不具合が見つかった時点で適時修正しております。)

バックナンバーのvol.190を確認し、どこをどう修正したのか、なぜ
修正する必要があるのか考えてみて下さい。

バックナンバーはこちら↓
http://www.flsi.co.jp/Java_text/



========================================================
◆ 02.Strutsのアプリケーション開発
========================================================


さて、前回までに作成したプログラムで動作確認すると、たとえば次のような
操作をすると結果がおかしくなりますね。
(Eclipseでサーバーを始動し、WebブラウザーでitemSelect.jspのWebページを
開くところまで完了しているものとします。)

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

(2) 「ショッピング・カートの中のリスト」のWebページが開いたら、
「トップ・ページ(商品検索のページ)に戻る」のリンクをクリックして
「商品の検索」のWebページに戻ります。
 
(3) 「商品の検索」のWebページが開いたら、再度「商品検索」ボタンをクリック
して、次の「商品のリスト」のWebページでは、先ほどと同じ商品番号F0002の選択欄
にチェック・マークを入れて「買物かごに入れる」ボタンをクリックしましょう。
 
(4) 「ショッピング・カートの中のリスト」のWebページが開いたら、すべてのF0002
の購入個数を0にして、「これらの商品を購入する」ボタンをクリックしましょう。

F0002をすべて個数0にしたのだから、カートから無くならないといけないはず
なのに、次の「購入する商品」のWebページでは、F0002の商品は無くならずに
購入個数1でリストされますね。
これはおかしいですね。


どうしてこういう結果になってしまうかというと、OrderクラスのremoveOrderItem()
メソッドに不具合があるからです。

現在、OrderクラスのremoveOrderItem()メソッドのソース・コードは

--------------------------------------------------------
public void removeOrderItem(OrderItem anOrderItem) {
   for (int i = 0; i < this.orderItems.size(); i++) {
      if (this.orderItems.get(i).getItemNum().equals(anOrderItem.getItemNum())) {
         this.orderItems.remove(i);
      }
   }
}
--------------------------------------------------------

というふうになっていますが、これだと例えば、orderItemsに入っている要素の中に

インデックス0が商品番号F0002
インデックス1が商品番号F0002

というようにF0002が複数存在していたとすると、ループの最初の処理(インデックス0
が対象になっている処理)のとき、F0002が引数のOrderItemオブジェクトの商品番号と
一致するので
this.orderItems.remove(i);
を実行され、インデックス0の要素が削除されます。すると、インデックス1の要素が
繰り上がって(削除されてできた空きを埋めるために)インデックス0の要素になり
ます。
すると、続いてループの繰り返しの処理でインデックス1に対して処理しようとすると、
以前のインデックス1の要素はインデックス0に変わってしまっているので、二つ目の
F0002の要素は処理されない、つまり削除されないことになります。

というわけで、このロジックには欠陥があるのです。


この問題は、ループで処理するインデックスを大きいほうから0のほうへ逆順に
処理すれば解決します。

つまり、OrderクラスのremoveOrderItem()メソッドのソース・コードを

--------------------------------------------------------
public void removeOrderItem(OrderItem anOrderItem) {
   for (int i = this.orderItems.size() - 1; i >= 0; i--) {
      if (this.orderItems.get(i).getItemNum().equals(anOrderItem.getItemNum())) {
         this.orderItems.remove(i);
      }
   }
}
--------------------------------------------------------

というふうに修正すればいいのです。



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


さて、もう一つ気になることがありますね。

現実のお店だと、例えばミカン1個が既にカートに入っている状態でさらにミカン1個
をカートに追加した場合は、普通は、「ミカン2個がカートに入っている」という言い方
をするのが自然でしょう。「ミカン1個とミカン1個がカートに入っている」なんて
言い方は普通しないですね。

これと同様に、カートの中にF0002が複数行リストされるのは何か不自然ですね。問題と
までは言えなくても、ちょっと見苦しいです。

というわけで、例えばF0002を複数回カートに追加した場合は、それらを一つの行に
まとめて、購入個数を追加した分だけ増やすようにしたほうがいいですね。


こういった扱い方をできるようにするために、OrderクラスのOrderItemを追加する
メソッド、すなわちaddOrderItem()メソッドを少し修正しましょう。

つまり、Order(注文)オブジェクトに商品(OrderItem)を追加するときは、すでに
注文の中に同じ商品が含まれている場合にはそのOrderItemオブジェクトの注文個数だけ
を追加し、まだ注文の中に同じ商品が含まれていない場合にはそのOrderItemオブジェク
トを新規に追加するというふうにOrderクラスのaddOrderItem()メソッドを修正します。


はい、では下記のようにOrderクラスのaddOrderItem()メソッドを修正しましょう。

--------------------------------------------------------
public void addOrderItem(OrderItem anOrderItem) {
   for (int i = 0; i < this.orderItems.size(); i++) {
      if (this.orderItems.get(i).getItemNum().equals(anOrderItem.getItemNum())) {
         int totalQuantity = this.orderItems.get(i).getOrderQuantity() + anOrderItem.getOrderQuantity();
         this.orderItems.get(i).setOrderQuantity(totalQuantity);
         return;
      }
   }
   this.orderItems.add(anOrderItem);
}
--------------------------------------------------------

(ちなみに、最初からaddOrderItem()メソッドをこのようにコーディングしてあれば、
removeOrderItem()メソッドのほうは以前のままでも問題は発生しないですね。)



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


念のため、以上の修正をしたOrderクラスの全ソース・コードを下記に提示します。

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

import java.io.Serializable;
import java.util.Date;
import java.util.Vector;

public class Order implements Serializable {
   private int orderNumber;
   private Date orderDate;
   private String userType;
   private String userId;
   private int payMethod;
   private boolean payment;
   private short delivery;
   private Vector<OrderItem> orderItems;
   private boolean orderComplete;

   public Order() {
      setOrderNumber(0);        // 0は未設定を意味するものとする。
      setPayMethod(0);          // デフォルトはクレジット・カード払い
      setPayment(false);        // デフォルトは未払い。
      setDelivery((short)0);    // デフォルトは未配達。
      setOrderComplete(false);  // デフォルトは注文前
      orderItems = new Vector<OrderItem>();
   }

   public void setOrderNumber(int orderNumber) {
      this.orderNumber = orderNumber;
   }

   public int getOrderNumber() {
      return orderNumber;
   }

   public void setOrderDate(Date orderDate) {
      this.orderDate = orderDate;
   }

   public Date getOrderDate() {
      return orderDate;
   }

   public void setUserType(String userType) {
      this.userType = userType;
   }

   public String getUserType() {
      return userType;
   }

   public void setUserId(String userId) {
      this.userId = userId;
   }

   public String getUserId() {
      return userId;
   }

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

   public int getPayMethod() {
      return payMethod;
   }

   public void setPayment(boolean payment) {
      this.payment = payment;
   }

   public boolean isPayment() {
      return payment;
   }

   public void setDelivery(short delivery) {
      this.delivery = delivery;
   }

   public short getDelivery() {
      return delivery;
   }

   public void setOrderItems(Vector<OrderItem> orderItems) {
      this.orderItems = orderItems;
   }

   public Vector<OrderItem> getOrderItems() {
      return orderItems;
   }

   public void addOrderItem(OrderItem anOrderItem) {
      for (int i = 0; i < this.orderItems.size(); i++) {
         if (this.orderItems.get(i).getItemNum().equals(anOrderItem.getItemNum())) {
            int totalQuantity = this.orderItems.get(i).getOrderQuantity() + anOrderItem.getOrderQuantity();
            this.orderItems.get(i).setOrderQuantity(totalQuantity);
            return;
         }
      }
      this.orderItems.add(anOrderItem);
   }

   public void removeOrderItem(OrderItem anOrderItem) {
      for (int i = this.orderItems.size() - 1; i >= 0; i--) {
         if (this.orderItems.get(i).getItemNum().equals(anOrderItem.getItemNum())) {
            this.orderItems.remove(i);
         }
      }
   }

   public void setOrderComplete(boolean orderComplete) {
      this.orderComplete = orderComplete;
   }

   public boolean isOrderComplete() {
      return orderComplete;
   }

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



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


あと「ショッピング・カートの中のリスト」のWebページにおいて、購入個数欄に
漢数字を入力したとか(そんな人はいないとは思うが)、何らかの数字以外の文字
を入力してしまった場合には、正常な処理はできませんが、ユーザー側(Webブラ
ウザー側)にはそのことが明示されませんね。
このままでは、何か誤解をするユーザーもいるかもしれません。(例えば
「ノートを五個注文したはずなのに来ない」とか言う人もいるかも知れません。)
(現在のところ、ログには例外が記録されるが、Webブラウザー上にはエラー・メッ
セージが表示されないので、ユーザーが見過ごしてしまう恐れがある。)

こういう場合は、ちゃんとWebブラウザー上にエラー・メッセージを返すべきですね。

というわけで、エラー・メッセージを返すように修正しましょう。


以前vol.184〜vol.185で、Validatorプラグインを使って入力データを検証する方法
をお話しましたが、そのときは検証の対象がActionFormオブジェクト(Formビーン)
のプロパティーだったので、検証のプログラムを書かずに済みました。
しかし、今回は検証の対象がActionFormオブジェクトのプロパティーではないため、
このやり方は通用しません。

代わりに、今回はJavaのプログラミングで検証を行います。

また、vol.185では英文のエラー・メッセージを返していましたが、今回は、英文
ではなく日本文のエラー・メッセージを返す方法を紹介します。
そのために、MessageResources.propertiesファイルの中に日本文のエラー・メッセージ
を入れることにします。

ただし、MessageResources.propertiesファイルの中に日本文のエラー・メッセージ
を入れるにはUnicodeエスケープで指定する必要があることに注意して下さい。
このことは、既にvol.145でrunmode.propertiesと名付けたプロパティー・ファイルを
使って説明済みなので、今回は説明は省略し、作業だけを淡々と実施することにしま
しょう。



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


今回の入力データの検証のためには、以下のような作業が必要になります。

(V-1) JSPのファイルの中のエラー・メッセージを表示したい場所に
<html:errors />タグを書き込む。

(V-2) ActionFormのサブクラスを作成し、その中にvalidate()メソッド(vol.106参照)
を実装する。

(V-3) struts-config.xmlファイルを編集し、<form-bean>タグの記述を修正する
(今回の場合は修正になるが、まだ該当する<form-bean>タグの記述をしていな
かった場合は新規に<form-bean>タグを記述する)。
また、該当する<action>タグにvalidate="true"を指定する。

(V-4) MessageResources.propertiesファイルにエラー・メッセージを記述する。
(今回は日本語(ただしUnicodeエスケープ)にする。)



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


では、まずは(V-1)の作業から行っていきましょう。

cart.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="/orderprocess">
<html:errors />
<!-- <font color="#ff0000"><b><html:errors property="required" /></b></font> -->
<!-- <font color="#ff00ff"><b><html:errors property="invalid" /></b></font> -->
<table border="1" width="100%">
  <tbody>
    <tr>
      <th>商品番号</th>
      <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(); %>
    <bean:define id="orderItemList" name="ORDER" property="orderItems" type="Vector<OrderItem>" scope="session" />
<logic:iterate id="item" 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><img src="<%= homebase %><bean:write name="item" property="image" />"></td>
<td align="center"><input type="text" name="<bean:write name="item" property="itemNum" />"
     value="<bean:write name="item" property="orderQuantity" />" size="1"></td>
    </tr>
</logic:iterate>
  </tbody>
</table>
<br>
複数購入したい商品につきましては、「購入個数」欄の数字を書き換えて下さい。<br>
なお、購入したくない商品につきましては「購入個数」欄を0にして下さい。<br>
購入手続きに進むには、下の「これらの商品を購入する」ボタンをクリックして下さい。<br>
<br>
<html:submit property="submit" value="これらの商品を購入する" />
</html:form>
<br>
<html:link page="/itemSelect.jsp">トップ・ページ(商品検索のページ)に戻る</html:link>
</body>
</html:html>
--------------------------------------------------------

変更した所は、15行目〜17行目に

<html:errors />
<!-- <font color="#ff0000"><b><html:errors property="required" /></b></font> -->
<!-- <font color="#ff00ff"><b><html:errors property="invalid" /></b></font> -->

という行を追加したことだけですね。

このうち<!--と-->で囲まれている行はコメントになっているわけですが、これは
参考までにコメントとして書いておいたものです。
この行は、<html:errors />タグによって表示するエラー・メッセージを<b>と</b>で
囲むことによって太字にし、さらに<font color="xxxxx">と</font>で囲むことに
よって文字に色をつけています。
それから、propertyという属性は<html:errors />タグが複数あるときにそれぞれ
を区別するためにつけるもので、あとで説明します。

これら(文字の色など)については、あとで、MessageResources.propertiesファイ
ルに記述するのでJSP内に記述する必要はないのですが、例えばメッセージの内容ごと
に色を変えたいとき(例えば上記のように"required"のメッセージについては赤色にし、
"invalid"のメッセージについては紫色にするとか)や、メッセージを表示する場所
が複数あって、その場所ごとに色を変えたいといった場合には、上記のようにproperty
属性を指定した<html:errors />タグと<font>タグの組み合わせをJSPの中に記述する
という方法があります(この場合はMessageResources.propertiesファイルの中には
<font>タグは記述しない)。



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


では、続いて(V-2)の作業を行いましょう。

(1) プロジェクト・エクスプローラー内でjp.co.flsi.lecture.strutsを右クリック
し、「新規」→「その他」を選択します。

(2) 「新規」ウインドウにおいて「Amateras」配下の「Struts」配下の
「Struts ActionForm」を選択し、「次へ」ボタンをクリックします。

(3) 「名前」欄に

CartForm

と入力し、「完了」ボタンをクリックします。

(4) CartForm.javaのエディターが開いたら、下記のように編集しましょう。

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

import java.io.UnsupportedEncodingException;
import java.util.Enumeration;

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

   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 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(">>>>>");
         Enumeration<String> paramNames = (Enumeration<String>) request.getParameterNames();
         while(paramNames.hasMoreElements()) {
            String aParamName = paramNames.nextElement();
            String aParam = (String)request.getParameter(aParamName);
            if (!"submit".equals(aParamName)) {  // パラメター名がsubmitの場合はsubmitボタンなので除外する。
               if ("".equals(aParam)) {  // 入力データが空文字の場合、エラーを設定する。
                  logger.info("Invalid input data: <<<<<" + stringMaker.getValues("aParam", aParam));
                  logger.info(">>>>>");
//                  errors.add("required", new ActionMessage("errors.order.quantity.required"));
                  errors.add(null, new ActionMessage("errors.order.quantity.required"));
               }
               else if (!aParam.matches("[0-9]+")) {  // 入力データが数字でない場合、エラーを設定する。
                  logger.info("Invalid input data: <<<<<" + stringMaker.getValues("aParam", aParam));
                  logger.info(">>>>>");
//                  errors.add("invalid", new ActionMessage("errors.order.quantity.integer"));
                  errors.add(null, new ActionMessage("errors.order.quantity.integer"));
               }
            }
         }
      }
      catch (Throwable e) {
         logger.error(e, e);
      }
      finally {
         logger.info("End ...............");
      }
      logger.info("Method return: <<<<<" + stringMaker.getValues("errors", errors));
      logger.info(">>>>>");
      return (errors);
   }

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

このvalidate()というメソッドが、WebページのFORMから入力された入力データを
検証するためのメソッドです。(以前vol.106でもお話しました。)

入力データがActionFormオブジェクトのプロパティーになっている場合は、
プロパティーの値を直接検証すればいいのですが、今回はそうなっていないため、
request(HttpServletRequestオブジェクト)からデータを取り出して検証している
ことに注意して下さい。

validate()というメソッドは、ActionErrorsという、エラー・メッセージを保持する
オブジェクトを戻り値として返すことになっており、データを検証した結果、エラー
が判明した場合には、ActionErrorsのadd()メソッドを使ってActionErrorsオブジェクト
にエラー・メッセージを設定しておきます。
このadd()メソッドは上記のソース・コードにあるように(コメントにしてあるが)

errors.add("required", new ActionMessage("errors.order.quantity.required"));

といった形式で2つの引数を指定します。

ここで、第一引数は、先ほどのcart.jspの中で(今回はコメントで提示した)
<html:errors property="required" />というふうに指定したproperty属性の値を
指定します。
このように、JSP内に<html:errors />タグが複数ある場合は、property属性を指定する
ことによってそれぞれを区別し、ActionErrorsオブジェクトのadd()メソッドの第一引数
でそのproperty属性の値を明記することによって、どちらの<html:errors />タグにメッ
セージを書き出したいのかを示すのです。
<html:errors />タグ一つしかなく、property属性が指定されていない場合は、第一引数
にはnullもしくはActionMessages.GLOBAL_MESSAGEもしくは適当な文字列を指定しておき
ます。(<html:errors />タグにproperty属性が指定されていないので第一引数に文字列
を指定しても使われないから何でもよいが、nullのほうがわかりやすくてよい。)

また、第二引数にはActionMessageというクラスのオブジェクトを指定します。

このActionMessageはメッセージを表現するクラスですが、ここではその場でnew演算子
によってインスタンス生成しています。
このとき、コンストラクターの引数に指定しているのは、MessageResources.properties
ファイル内のキー(プロパティーの名前)です。実際のメッセージはそのキーに対する値
(プロパティーの値)としてMessageResources.propertiesファイルの中に入れておき
ます。これについては、またあとで説明します。

なお、このJavaのソース・コード内で直接メッセージをActionMessageオブジェクトに
設定することもできますが、メッセージをJavaのソース・コードで固定してしまうこと
は好ましくないので、通常は外部のファイル(MessageResources.properties)に保管
します。


ところで、ActionErrorsという複数形の名前になっていることからわかるように、この
ActionErrorsオブジェクトには(add()メソッドを複数回実行することによって)複数の
エラー・メッセージを追加することができます。

これに対応して、さきほどのJSPの中の

<html:errors />

というタグも複数形になっており、これらの複数のエラー・メッセージを表示できる
ことを意味しているのです。



それから、

else if (!aParam.matches("[0-9]+")) {  // 入力データが数字でない場合、エラーを設定する。

という行では、入力された購入個数のデータが数(1個以上の0〜9の数字)であることを
正規表現を使って検証しています。
正規表現については、vol.149やvol.152でも触れましたが、とても重要な概念なので
後ほど特集号を作って詳しくお話しすることにします。

ここでは、[0-9]が0から9までの数字1文字を表現し、その後ろの+がその[0-9]で表現
される文字を1個以上何個でも表現することを意味している、という程度に理解して
おいて下さい。
そうすると、このaParamの値が数字だけで構成されている場合はこのmatches()メソッド
はtrueを返しますが、そうでない場合は、falseを返します。(空文字でもfalseを返す
が、空文字は既にその上のif文で処理済みなので、ここのif文は通らない。)


なお、requestにはいっているパラメター名はHTMLのFORMの中にはいっている各<input>タグ
のname属性の値ですから、JSPのファイルを見てもわかりにくいと思います。これは
実際に動作テストをして、ショッピング・カートの中のリストのWebページを開いた
ときにその(Internet Explorerの)画面上で右クリック→「ソースの表示」を選択
することによって、HTMLのソースを表示させればすぐにわかります。


残りのソース・コードは、説明しなくても自力で理解できることでしょう。



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


続いて(V-3)の作業を行いましょう。

struts-config.xmlファイルを下記のように編集しましょう。

--------------------------------------------------------
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN" "http://struts.apache.org/dtds/struts-config_1_2.dtd">
<struts-config>
  <data-sources>
  </data-sources>
  <form-beans>
    <form-bean name="itemSelectForm" type="jp.co.flsi.lecture.struts.ItemSelectForm" />
    <form-bean name="itemListForm" type="jp.co.flsi.lecture.struts.ItemListForm" />
    <form-bean name="cartForm" type="jp.co.flsi.lecture.struts.CartForm" />
  </form-beans>
  <global-exceptions>
  </global-exceptions>
  <global-forwards>
  </global-forwards>
  <action-mappings>
    <action path="/itemlist" type="jp.co.flsi.lecture.struts.ItemListAction"
     name="itemSelectForm" scope="request" input="/itemSelect.jsp">
      <forward name="success" path="/itemList.jsp"/>
    </action>
    <action path="/cart" type="jp.co.flsi.lecture.struts.CartAction"
     name="itemListForm" scope="request" input="/itemList.jsp">
      <forward name="success" path="/cart.jsp"/>
    </action>
    <action path="/orderprocess" type="jp.co.flsi.lecture.struts.OrderProcessAction"
     name="cartForm" scope="request" input="/cart.jsp" validate="true">
      <forward name="success" path="/order.jsp"/>
    </action>
    <action path="/confirmorder" type="jp.co.flsi.lecture.struts.ConfirmOrderAction"
     input="/order.jsp">
      <forward name="success" path="/confirmOrder.jsp"/>
    </action>
    <action path="/completeorder" type="jp.co.flsi.lecture.struts.CompleteOrderAction"
     input="/confirmOrder.jsp">
      <forward name="success" path="/completeOrder.jsp"/>
    </action>
  </action-mappings>
  <controller processorClass="org.apache.struts.tiles.TilesRequestProcessor"/>
  <message-resources parameter="MessageResources"/>
  <plug-in className="org.apache.struts.tiles.TilesPlugin">
    <set-property property="definitions-config" value="/WEB-INF/tiles-defs.xml"/>
    <set-property property="moduleAware" value="true"/>
  </plug-in>
  <plug-in className="org.apache.struts.validator.ValidatorPlugIn">
    <set-property property="pathnames" value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"/>
  </plug-in>
</struts-config>
--------------------------------------------------------

変更したのは、9行目の<form-bean>タグを

<form-bean name="cartForm" type="jp.co.flsi.lecture.struts.CartForm" />

のようにtype属性を書き換えたことと、24行目〜27行目

<action path="/orderprocess" type="jp.co.flsi.lecture.struts.OrderProcessAction"
 name="cartForm" scope="request" input="/cart.jsp" validate="true">
  <forward name="success" path="/order.jsp"/>
</action>

のうち、25行目にvalidate="true"を追加したことです。



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


続いて(V-4)の作業を行いましょう。

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

(2) 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.header=<font color="#ff0000"><b>
errors.prefix=
errors.suffix=<br><br>
errors.footer=</b></font>

という4行と、最後のほうの

# -- StrutsShop order --
errors.order.quantity.required=購入個数に空文字が入力されましたが、受け付けられません。
errors.order.quantity.integer=購入個数に数字以外の文字が入力されましたが、受け付けられません。

という3行(これは追加した行)だけです。


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

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



なお、国際的なプログラム(英語環境のコンピューターでは英語で表示し、日本語環境
のコンピューターでは日本語で表示し、中国語環境のコンピューターでは中国語で表示
するというふうに多言語が使えるプログラム)にしたい場合は、MessageResources.properties
ファイルを言語ごとにファイル名を変えて別々に用意することもできますが、詳しいこと
はまた後ほどお話いたします。


ここで、このMessageResources.propertiesの中身について少し解説しておきます。

まず、先頭のほうにある

errors.header=<font color="#ff0000"><b>
errors.prefix=
errors.suffix=<br><br>
errors.footer=</b></font>

という行は、JSPの中に記述した<html:errors />に対して、JSPからHTMLへの
変換時にエラー・メッセージの前後に組み込まれるものです。

先ほどCartForm.javaの説明をしたときに、ActionErrorsオブジェクトには
エラー・メッセージを複数追加できるという話をしましたが、その複数の
エラー・メッセージの一つ一つに対して、errors.prefixの値(ここでは何も
指定していないが)が前に、またerrors.suffixの値(ここでは<br><br>)が
後ろに組み込まれます。
また、複数のエラー・メッセージの全体の前と後ろにそれぞれerrors.headerの
値(ここでは<font color="#ff0000"><b>)とerrors.footerの値(ここでは
</b></font>)が組み込まれることになります。


また、最後のほうの

errors.order.quantity.required=購入個数に空文字が入力されましたが、受け付けられません。
errors.order.quantity.integer=購入個数に数字以外の文字が入力されましたが、受け付けられません。

は、先ほどCartForm.javaの中で、

errors.add(null, new ActionMessage("errors.order.quantity.required"));



errors.add(null, new ActionMessage("errors.order.quantity.integer"));

の中のActionMessageのコンストラクターの引数として指定したキーとその値
を記述したものです。



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


以上で必要な作業は完了しましたので、動作確認してみましょう。

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

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

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

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

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

を入力しましょう。

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

(4) 「ショッピング・カートの中のリスト」のWebページが開いたら、F0001の商品
のほうは購入個数を空文字(数字を削除する)にし、F0002の商品のほうは購入個数
を「五」を入力して「これらの商品を購入する」ボタンをクリックしましょう。

すると、再度「ショッピング・カートの中のリスト」のWebページが開いて、

--------------------------------------------------------
購入個数に数字以外の文字が入力されましたが、受け付けられません。

購入個数に空文字が入力されましたが、受け付けられません。
--------------------------------------------------------

というエラー・メッセージが表示されますね。これでOKです。



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


(次回に続く)


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



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


広告