広告

■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
                      2010年02月21日

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

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


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


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


さて、前回までで、(U-1)〜(U-3)まで(vol.188参照)の部分をコーディングし、
動作を確認しました。

このとき、一番最初に開くitemSelect.jspのWebページにおいて、カテゴリーの欄
がありましたが、カテゴリーをユーザーに手入力させることに違和感を感じた人
もいるのではないでしょうか。

そもそもカテゴリーはデータベース上に、決まった小数のもの(具体的には
食品、雑貨、文具の3つ)が登録されているだけです(vol.189参照)。

それなら、予めカテゴリー名のリストを画面上に表示しておいて、そのリストから
選択させるような形式にしてあげたほうが親切で簡単ではないか、と思った人も
いることと思います。


そこで、itemSelect.jspのWebページにおいて、カテゴリーをリストから選択
する形式に変更してみましょう。

リストするカテゴリー名としては、データベースから取り出したものを使用する
こととします。
(データベースとは関係なく直接JSPファイル上にカテゴリー名を書いておく
という方法もありますが、そうすると、カテゴリー名に変更があった場合に
データベースとJSPファイルの複数個所で重複して修正が必要になり、煩雑
かつ修正ミスの原因にもなりかねません。というわけで、このようなやり方
は好ましくありません。)

ただし、itemSelect.jspのWebページを表示するたびに毎回データベースから
読み取るのでは、コンピューター資源の無駄使いになってしまいますので、
最初に一回読み取ったら、その後はそれらをずっと使い続けることにします。


さて、どのように変更すればいいと思いますか?
説明にはいる前に、自分で一寸考えてみて下さい。



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


では、典型的なやり方をお話しましょう。


まず、ActionFormのreset()メソッドは、対応するFORMを含むJSPのWebページが
クライアント(Webブラウザー)のコンピューターに送られる前に実行される
ということを頭に入れておいて下さい。

そうすると、このreset()メソッドの中で、データベースからカテゴリー名のリスト
を取り出す作業を行っておき、取り出したリストをWebページ(JSPファイル)の
FORM上に組み込んでおけばいいわけです。
そうすれば、そのWebページがWebブラウザー上に表示されるときには、カテゴリー名
のリストが表示されることになります。

ただし、カテゴリー名のリストはデータベースから1回取り出したら、以後はそれを
ずっと使い続けるような仕組みにしなければなりません。

このようなときに使えるのがapplicationスコープです。
1回取り出したカテゴリー名のリストは、applicationスコープに保管しておけば、
以後そのWebアプリケーション内でずっと保持し続けられますので、ずっと使い
続けることができます。


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

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

import java.io.UnsupportedEncodingException;
import java.util.Vector;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;

import jp.co.flsi.lecture.struts.db.Category;
import jp.co.flsi.lecture.struts.db.CategoryDbManager;
import jp.co.flsi.lecture.struts.db.StruShopDbException;

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

public class ItemSelectForm extends ActionForm {
   private static Logger logger = Logger.getLogger(ItemSelectForm.class);
   private String keyword = null;
//   private String category = null;
   private String categoryNum = null;

   public void reset(ActionMapping mapping, HttpServletRequest request) {
      logger.info("Start ...............");
      CategoryDbManager dbManager = new CategoryDbManager();
      try {
         request.setCharacterEncoding("UTF-8");
         ServletContext serv = getServlet().getServletContext();
         Vector<Category> categoryList = (Vector<Category>) serv.getAttribute("categoryList");
         if (categoryList == null) {
            dbManager.connect();
            categoryList = dbManager.getDataByName("");
            serv.setAttribute("categoryList", categoryList);
         }
      } catch (UnsupportedEncodingException e) {
         logger.error(e, e);
      } catch (StruShopDbException e) {
         logger.error(e, e);
      }
      finally {
         try {
            dbManager.disconnect();
         }
         catch (Exception e) {
            logger.info("Disconnect failed or there is no connection.");
         }
         logger.info("End ...............");
      }
   }

   public void setKeyword(String keyword) {
      this.keyword = keyword;
   }

   public String getKeyword() {
      return keyword;
   }
//   public void setCategory(String category) {
//      this.category = category;
//   }
//   public String getCategory() {
//      return category;
//   }

   public void setCategoryNum(String categoryNum) {
      this.categoryNum = categoryNum;
   }

   public String getCategoryNum() {
      return categoryNum;
   }

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


上記のソース・コードのうち

ServletContext serv = getServlet().getServletContext();

によって取り出したserv(ServletContextオブジェクト)に対して

serv.getAttribute("categoryList");



serv.setAttribute("categoryList", categoryList);

を実行していることに注意してください。
これらが、applicationスコープからオブジェクト(bean)を取り出したり、
保管したりする操作(定石的なやり方)です。

上記のソースコードでは、すでにapplicationスコープにカテゴリーのリストを
保管済みの場合は

Vector<Category> categoryList = (Vector<Category>) serv.getAttribute("categoryList");

という行でそれを取り出して使用し、まだ保管されていない場合は代わりに
categoryListにnullが返ってくるので、その場合は

categoryList = dbManager.getDataByName("");

という行でデータベースにアクセスしてカテゴリーのリストを取り出し、categoryList
に代入したあと、applicationスコープに保管しています。

(データベースの処理をしているところは、いつものパターンなので説明を
省略します。)



なお、上記のソース・コードでは、プロパティーの名前をcategoryから
categoryNumに変更していることにも注意して下さい。これは、あとでJSP側
を変更するときにWebページからカテゴリー名ではなくカテゴリーの番号を
受け取るように変えるために、それに対応して改名したものです。

┌補足─────────────────────────┐
「わざわざプロパティー名を変える必要はないんじゃないか。
カテゴリー番号でもそのままcategoryというプロパティー名を
使えばいいじゃないか。」と思うかもしれませんが、オブジェ
クト指向プログラミングにおいては、名前はとても大切で、その
意味にふさわしい名前にすべきなのです。

その意味を的確に表現する名前にしておくことは、プログラム
がわかりやすくなるだけでなく、プログラミング・ミスの減少
にもつながります。

そして、プロパティーの名前を変えるということは、そのsetter
やgetterメソッドの名前も変えることを意味します。
上記のソース・コードの中で変数名、setterメソッド名、getter
メソッド名すべて変えていることに注意して下さい。(比較でき
るように以前のプロパティーに関する行はコメント(//)にして
残してあります。)

このように、扱う対象をカテゴリー名からカテゴリー番号に変更
したことに対応してプロパティー名を変更しておくと、それを
参照している他のプログラムが影響を受ける場所も明確になり
ます。
たとえば、ItemListActionクラスの中でこのプロパティーを参
照していたところがエラー(コンパイラーがエラーを見つけて
くれる)になるので、変更すべきところが明確になりますね。
└───────────────────────────┘


なお、このソースコードをEclipseに入力すると、

Vector<Category> categoryList = (Vector<Category>) serv.getAttribute("categoryList");

の行に警告のマーク(橙色のアンダーラインがつく)が付くことと思います。

これは、以前もお話したように(Vector<Category>)というような複雑なキャストの
妥当性はコンパイラーではチェックできないからです。
というわけで、この警告は(本人が正しいコーディングをしていると確信して
いれば)気にしなくていいものです。



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


続いて、itemSelect.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="/itemlist">
         キーワード(商品名の一部)やカテゴリーを入力し、「商品検索」ボタンをクリックしてください。
         <br>
         <br>
         キーワード:<html:text property="keyword" size="20" />
         <br>
         カテゴリー:
         <html:select property="categoryNum">
            <html:options collection="categoryList" property="num" labelProperty="name" />
         </html:select>
         <br>
         <br>
         <html:submit property="submit" value="商品検索" />
      </html:form>
   </body>
</html:html>
--------------------------------------------------------


どこを編集したかというと、

<html:text property="category" size="20" />

だったところを

<html:select property="categoryNum">
   <html:options collection="categoryList" property="num" labelProperty="name" />
</html:select>

に書き換えていますね。

この部分も新しく出てきたタグなので解説しておきましょう。

まず、<html:select>というタグは、vol.178に出てきた<select>タグに相当する
Struts専用のタグで、</html:select>との間に囲まれたオプションをリストして
表示するものです。
そのオプションはvol.178では<option>というタグを複数並べてリストしていました
が、それに相当するStruts専用のタグとして、ここでは<html:options>というタグ
を使用しています。(このようにStrutsを使うと随分と簡単なコードで済んでしまう
ことが分かりますね。)

具体的には、

<html:options collection="categoryList" property="num" labelProperty="name" />

という指定をしていますが、ここではcollectionという属性に、先ほどItemSelectForm
の中でapplicationスコープに保管しておいた"categoryList"を指定していることに注意
して下さい。このcategoryListはVector型のコレクションでしたから、このオブジェ
クトは複数の要素を含みます。
html:optionsタグは、指定されたコレクションの複数の要素をリストするという機能を
持っているので、optionsというようにoptionの複数形のタグ名になっているのです。

あと、property属性とlabelProperty属性には、そのコレクションの要素のプロパティー
を指定します。
このうちproperty属性に指定したものが値として使われ(Webブラウザー上でユーザー
がその項目を選択すると、サーバー側へはそのproperty属性に指定された値が送られる
ことになる)、labelProperty属性に指定したものは実際にWebブラウザー上でリストの
中に表示されるものになります。

categoryListの中の要素はCategory型のオブジェクトで、Categoryにはnumという
プロパティー(getNum()メソッドで取得できる)とnameというプロパティー(getName()
メソッドで取得できる)がありましたね。これらを上記のproperty属性とlabelProperty
属性に指定しているわけです。

さて、先ほどの<html:select>タグのproperty属性には"categoryNum"という値が指定
されていましたが、このcategoryNumがItemSelectFormのcategoryNumプロパティー
を表します。
そして、このリストの中で選択された項目のnumプロパティー(Categoryオブジェクト
のnumプロパティー)の値がこのcategoryNumプロパティーの値としてItemSelectForm
オブジェクトにセットされることになるのです。

(文章だけで説明すると頭が混乱しそうですが、各ソース・ファイルを見比べていけば
用意に理解できることと思います。)



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


では、今度は、このItemSelectFormのデータを受け取って処理するItemListAction
のほうも変更しましょう。
ItemListActionのエディターを開いてみると、先ほどItemSelectFormの中で行った変更
に対応してエラーになっている部分がありますね。その部分を修正しておきましょう。

下記のように編集して下さい。

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

import java.util.Vector;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jp.co.flsi.lecture.reflect.ValueLogStringMaker;
import jp.co.flsi.lecture.struts.db.Item;
import jp.co.flsi.lecture.struts.db.ItemDbManager;
import jp.co.flsi.lecture.struts.db.StruShopDbException;
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 ItemListAction extends Action {
   private static Logger logger = Logger.getLogger(ItemListAction.class);

   public ActionForward execute(ActionMapping mapping, ActionForm form,
         HttpServletRequest request, HttpServletResponse response) {
      logger.info("Start ...............");
      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(">>>>>");
      ItemSelectForm itemSelectForm = (ItemSelectForm) form;
      ItemDbManager itemDbManager = new ItemDbManager();
      try {
         itemDbManager.connect();
         Vector<Item> itemList = null;
         itemList = itemDbManager.getDataByItemNameAndCatNum(itemSelectForm.getKeyword(), itemSelectForm.getCategoryNum());

         request.setAttribute("itemList", itemList);
      } catch (StruShopDbException e) {
         logger.error(e, e);
      }
      catch (Throwable e) {
         logger.error(e, e);
      }
      finally {
         try {
            itemDbManager.disconnect();
         }
         catch (Exception e) {
            logger.info("Disconnect failed or there is no connection.");
         }
         logger.info("End ...............");
      }
      logger.info("Method return: <<<<<" + stringMaker.getValues("mapping", mapping));
      logger.info(">>>>>");
      return mapping.findForward("success");
   }

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

どこをどう修正したか分かりますね。

ヒント:引数が変わると、その引数を使用するメソッドもその引数にふさわしいものに
なっているかどうか確認する必要があります。
ここでは変更後の引数も以前と同じStringという型なので、その引数を使用するメソッド
がエラーになることはありませんが、メソッド名がその引数にふさわしい名前かどうか
見てみるとメソッドも変更する必要があることがわかります。



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


では、各ファイルの修正&保管が終わったら、再度このWebアプリケーション
(Strutsのアプリケーション)を実行してみましょう。

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

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

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

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

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

を入力しましょう。


どうですか。「カテゴリー」欄が選択式(三角形ボタンをクリックするとリストが
表示されますね)に変わった以外は、前回と同様に動作しますね。

これで、「カテゴリー」欄の入力が楽になりました。



では続いて、(U-3)〜(U-5)までの部分をコーディングしましょう。



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


(次回に続く)


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



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