広告

■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
                      2010年03月14日

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

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


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


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


さて前回はcart.jspまでプログラミングして、その動作確認をしてみましたが、
そのcart.jspはまだ未完成でした。

今回は、vol.188のアプリケーションの処理の流れのうち(U-5)〜(U-7)までの
部分をプログラミングしていきますが、ここではcart.jspを完成させる作業
から始めていきます。

念のため、(U-5)〜(U-7)までの部分を抜粋し、再提示しておきます。

--------------------------------------------------------
(U-5) カートの中身を表示するWebページが開く。
[cart.jsp]

(U-6) 「注文に進む」ボタンを押す。
[orderprocess.do]→[OrderProcessAction.java]

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



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


では、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">
         <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>
--------------------------------------------------------

上記のソース・コードでは、購入個数の入力域を作成するためにHTMLの<input type="text">
タグを使い、

<input type="text" name="<bean:write name="item" property="itemNum" />"
 value="<bean:write name="item" property="orderQuantity" />" size="1">

というふうにコーディングしていることに注意して下さい。
特にname属性にアイテム(item)の商品番号(itemNum)の値を指定していること
に注目して下さい。

どうしてこのような指定をしているのかというと、このWebページでは、この入力域
を含む行が複数できるため、サーバー側に購入個数のデータが送られたときに、それ
らがそれぞれどの商品に対する個数なのか識別できるようにしておかなければ困る
からです。
ここでは、購入個数のデータをvalue属性の値としてサーバー側に送付する際に、
name属性の値として商品番号を送るようにすることによって、各購入個数のデータ
がそれぞれどの商品に属するものか識別できるようにしているのです。
(なお、これらの情報は後でActionクラス(具体的にはOrderProcessAction)で処理
します。)

これをStrutsの<html:text>タグを使って記述することはできません。



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


さて、これで、FORM(Strutsでは<html:form>タグで記述する)が組み込まれました
ので、それに対応するActionFormオブジェクトが必要になります。
このActionFormオブジェクトをcartFormという名前で用意することにしましょう。

cartFormは、下記のようにして、struts-config.xmlに登録することにしましょう。

まず、struts-config.xmlの中の

--------------------------------------------------------
  <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-beans>
--------------------------------------------------------

の部分を
--------------------------------------------------------
  <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="org.apache.struts.action.DynaActionForm">
       <!-- <form-property name="" type="" /> -->
    </form-bean>
  </form-beans>
--------------------------------------------------------

というように編集しましょう。ここでcartFormのクラスとしてはDynaActionFormという
クラスを使っていることに注意して下さい。

実は、このようにDynaActionFormというクラスを指定すると、自分でActionFormのサブ
クラスをプログラミングしなくてもStrutsが自動的にActionFormオブジェクトを用意して
くれることになっており、プログラミングの手間が省けて楽ができてしまうのです。

しかも、そのプロパティーも
<form-property name="プロパティー名" type="プロパティーの型(クラス名)" />
という指定(上記のソース・コードでは<!--と-->で囲むことによってコメントにして
いて実際には指定していない)によって、簡単に指定することができます(フィールドも
setterメソッドやgetterメソッドも自分でコーディングする必要がない)。

今回のアプリケーションではこのcartFormにはプロパティーは使用しないので、プロパ
ティーは指定していません。必要なデータはsessionスコープのBeanに読み書きしている
のでActionFormのプロパティーは使用しなくて済んでいるのです。


このように単純にプロパティーの読み書きだけする、あるいはプロパティーの読み書き
さえしない、簡単なActionFormの場合は、DynaActionFormで済ませると楽です。

(ただし、ActionFormオブジェクトに特殊な処理を組み込みたい場合は、DynaActionForm
を使わずに(前回まで行っていたように)自分でActionFormのサブクラスをプログラミ
ングする必要があります。)



続いて、struts-config.xmlの中の

--------------------------------------------------------
    <action path="/orderprocess" type="jp.co.flsi.lecture.struts.OrderProcessAction"
     input="/cart.jsp">
      <forward name="success" path="/order.jsp"/>
    </action>
--------------------------------------------------------

の部分を

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

というように編集して下さい。
つまり、cart.jspのFORMにcartFormを対応付けます。



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


では、次にOrderProcessActionクラスをコーディングしましょう。

OrderProcessAction.javaのエディターを開いて下さい。(Eclipseのプロジェクト・
エクスプローラー内のStrutsShop配下の「Javaリソース:src」配下の
jp.co.flsi.lecture.struts配下のOrderProcessAction.javaをダブルクリックする。)

OrderProcessAction.javaを下記のように編集しましょう。

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

import java.util.Enumeration;
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.OrderItem;

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

   public ActionForward execute(ActionMapping mapping, ActionForm form,
         HttpServletRequest request, HttpServletResponse response) {
      logger.info("Start ...............");
      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");
         }

         Enumeration<String> paramNames = (Enumeration<String>) request.getParameterNames();
         // Webページ上の購入対象の商品がカートの中に入っていることのチェック用フラグ
         boolean orderItemFoundInCart = false;  // チェック用フラグ: falseは「カートにない」を意味する
         while(paramNames.hasMoreElements()) {
            String aParamName = paramNames.nextElement();
            OrderItem anOrderItem = new OrderItem();
            for (OrderItem oi : anOrder.getOrderItems()) {
               if(aParamName.equals(oi.getItemNum())) {  // そのアイテム番号がカートの中に見つかったら
                  orderItemFoundInCart = true;  // チェック用フラグを「カートにある」にセット
                  anOrderItem.setItemNum(oi.getItemNum());
                  anOrderItem.setItemName(oi.getItemName());
                  anOrderItem.setPrice(oi.getPrice());
                  anOrderItem.setImage(oi.getImage());
                  anOrderItem.setCatName(oi.getCatName());
                  anOrderItem.setOrderQuantity(Integer.parseInt((String)request.getParameter(aParamName)));
                  break;  // そのアイテム番号に対する処理が終わったのでループを脱出
               }
            }
            if (orderItemFoundInCart) {  // そのアイテムが「カートにある」場合
               anOrder.removeOrderItem(anOrderItem);  // そのアイテムの古い注文情報をいったん取り除く
               if (anOrderItem.getOrderQuantity() > 0) {  // 注文個数が0でなければ
                  anOrder.addOrderItem(anOrderItem);  // カートに入れなおす
               }
               orderItemFoundInCart = false;  // チェック用フラグを「カートにない」にリセット
            }
         }

         if (anOrder.getOrderItems().size() < 1) {  // カートの中が空になった場合
            logger.info("Method return: <<<<< cartVacant (3)");
            logger.info(">>>>>");
            return mapping.findForward("cartVacant");
         }
      }
      catch (Throwable e) {
         logger.error(e, e);
      }
      finally {
         logger.info("End ...............");
      }
      logger.info("Method return: <<<<< success");
      logger.info(">>>>>");
      return mapping.findForward("success");
   }

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

ここで、

Enumeration<String> paramNames = (Enumeration<String>) request.getParameterNames();

という行について若干解説しておきます。
このgetParameterNames()というメソッドはvol.089で説明した通りですが、今回は
Enumerationの入れ物に入るオブジェクトの型を<String>というふうに明記している
点がvol.089と異なっています。これに合わせて、右辺にキャストを入れていますが、
このような複雑なキャスト(Enumeration<String>)は例によってコンパイラーでは
妥当性がチェックできないため警告(Eclipseでは橙色のマーク)が出ますが、これ
は無視してよいものです。

残りのコードは適所にコメントも入れておきましたので、自力で理解できるでしょう。

それから、今回のクラスでは、ログ出力のうち、メソッドの戻り値の情報を書き出し
ている所(logger.info("Method return: <<<<< ・・・という行)を今までと違った
書き方にしていますね。これは実は、今まで無意味な出力をしていた(mappingに
何も設定を行っていないのにmappingの中身をログ出力しようとしていた)という
ミスに今頃気がついたからです。
まあ、ご愛嬌ということで、気になる人は今までのプログラムも自分で修正して
下さい。



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


では、続いてorder.jspをコーディングしましょう。

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>
         <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>
      <br>
      <html:link page="/itemSelect.jsp">トップ・ページ(商品検索のページ)に戻る</html:link>
   </body>
</html:html>
--------------------------------------------------------

本来ならば、合計金額(上のJSPの中のtotalPrice)の計算はAction側で行って(専用の
Beanを用意して計算させて)、注文のオブジェクトに記憶すべきですが、ここでは手抜きし
て(本当は、StrutsのJSPの中にJavaのソース・コードを組み込むやり方を例示するために)、
このJSP内にJavaのソース・コードを組み込んであります。

このとき上のソース・コードの中の

<% totalPrice += item.getPrice() * item.getOrderQuantity(); %>

という行のように、Strutsのタグの中で定義した変数(ここでは<logic:iterate>タグの
id="item"という属性の指定で定義したitem)をJavaのソース・コードの中で使用する
ことができるのですが、その場合、

<logic:iterate id="item" type="OrderItem" name="orderItemList" scope="page">

の行のように、その変数の型を(type="OrderItem"というふうに)明示する必要がある
ことに注意して下さい。

ここら辺は、ちゃんとAction側で(Beanで)処理を行っておれば、シンプルなJSPで
済んでしまうわけです。



では、動作確認をしてみましょう。

(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の商品
のほうは購入個数を0にし、F0002の商品のほうは購入個数を3にして、
「これらの商品を購入する」ボタンをクリックしましょう。

すると、次の「購入する商品」のWebページでは、ちゃんとF0001の商品はなくなり、
F0002の商品が購入個数3でリストされ、合計金額も合っていますね。


ところが、実はこれまで作成してきたプログラムには欠陥があり、操作の仕方によって
は間違った結果を生じます。

さて、どんな欠陥があるでしょうか?また、どう対処すればいいでしょうか?

答えは、次回に。



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


(次回に続く)


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



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