広告

■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
                      2010年11月28日

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

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


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


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

それでは、実際に例外に対する処理(handling)のコーディングを行って
いきましょう。

例外発生時にforwardの値を"systemerror"にし、systemerror.jspのWebページ
に進むように、struts-config.xmlファイルと各Javaファイルを編集していきます。
systemerror.jspファイルの作成も行います。



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

まず、forwardの値"systemerror"をsystemerror.jspに結びつけるように
(すなわちforwardの値が"systemerror"のときに、systemerror.jsp
のWebページに進むように)struts-config.xmlにマッピングの記述を
行っておきましょう。

(1) Eclipseを起動し、プロジェクト・エクスプローラーの中のStrutsShop配下の
WebContent配下のWEB-INF配下のstruts-config.xmlをダブルクリックしてエディター
(フロー・エディター)を開いて下さい。

(2) struts-config.xmlのエディター上で「ページ」のアイコンを白い領域に
貼り付けましょう。
(ここらへんの操作方法は、vol.183でお話していますので、忘れた人は復習して下さい。)

(3) /page1.jspという文字列を/systemerror.jspに書き換えて下さい。

(4) /itemlistのアイコンから/systemerror.jspのアイコンまで「進む」の矢印線を
引いて下さい。

(5) forward1の文字列をsystemerrorに書き換えて下さい。

以下同様に、

(6) /cartのアイコンから/systemerror.jspのアイコンまで「進む」の矢印線を
引いて下さい。

(7) /orderprocessのアイコンから/systemerror.jspのアイコンまで「進む」の矢印線を
引いて下さい。

(8) /confirmorderのアイコンから/systemerror.jspのアイコンまで「進む」の矢印線を
引いて下さい。

(9) /completeorderのアイコンから/systemerror.jspのアイコンまで「進む」の矢印線を
引いて下さい。

(10) /userregのアイコンから/systemerror.jspのアイコンまで「進む」の矢印線を
引いて下さい。

(11) forward1〜forward5の文字列をすべてsystemerrorに書き換えて下さい。

以上で、このエディターにおけるビジュアルな操作は終わりです。
いったん保管(Ctrl + S)しておきましょう。


なお、フロー・エディターの画面がごちゃごちゃし過ぎて操作しずらいと思う人は
ソース・エディター(エディターの下側の「ソース」タブをクリックする)で、
直接ソース・コードを編集しても構いません。
その場合は、ソース・コードを下記のように編集してみて下さい。


--------------------------------------------------------
<?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-bean name="orderForm" type="jp.co.flsi.lecture.struts.OrderForm"/>
    <form-bean name="confirmOrderForm" type="org.apache.struts.action.DynaActionForm"/>
    <form-bean name="userRegForm" type="jp.co.flsi.lecture.struts.UserRegForm"/>
  </form-beans>
  <global-exceptions>
  </global-exceptions>
  <global-forwards>
  </global-forwards>
  <action-mappings>
    <action path="/itemlist" name="itemSelectForm" type="jp.co.flsi.lecture.struts.ItemListAction" scope="request" input="/itemSelect.jsp">
      <forward name="success" path="/itemList.jsp"/>
      <forward name="systemerror" path="/systemerror.jsp"/>
    </action>
    <action path="/cart" name="itemListForm" type="jp.co.flsi.lecture.struts.CartAction" scope="request" input="/itemList.jsp">
      <forward name="success" path="/cart.jsp"/>
      <forward name="systemerror" path="/systemerror.jsp"/>
    </action>
    <action path="/orderprocess" name="cartForm" type="jp.co.flsi.lecture.struts.OrderProcessAction" scope="request" validate="true" input="/cart.jsp">
      <forward name="success" path="/order.jsp"/>
      <forward name="cartVacant" path="/cartVacantError.jsp"/>
      <forward name="systemerror" path="/systemerror.jsp"/>
    </action>
    <action path="/confirmorder" name="orderForm" type="jp.co.flsi.lecture.struts.ConfirmOrderAction" scope="request" input="/order.jsp">
      <forward name="success" path="/confirmOrder.jsp"/>
      <forward name="cartVacant" path="/cartVacantError.jsp"/>
      <forward name="userInsertError" path="/userInsertError.jsp"/>
      <forward name="newUser" path="/userReg.jsp"/>
      <forward name="useridInvalid" path="/useridInvalid.jsp"/>
      <forward name="passwordInvalid" path="/passwordInvalid.jsp"/>
      <forward name="systemerror" path="/systemerror.jsp"/>
    </action>
    <action path="/completeorder" name="confirmOrderForm" type="jp.co.flsi.lecture.struts.CompleteOrderAction" scope="request" input="/confirmOrder.jsp">
      <forward name="success" path="/completeOrder.jsp"/>
      <forward name="cartVacant" path="/cartVacantError.jsp"/>
      <forward name="userVacant" path="/userVacant.jsp"/>
      <forward name="tokenInvalid" path="/tokenInvalid.jsp"/>
      <forward name="systemerror" path="/systemerror.jsp"/>
    </action>
    <action path="/userreg" name="userRegForm" type="jp.co.flsi.lecture.struts.UserRegistrationAction" scope="request" input="/userReg.jsp">
      <forward name="success" path="/confirmOrder.jsp"/>
      <forward name="cartVacant" path="/cartVacantError.jsp"/>
      <forward name="userInsertError" path="/userInsertError.jsp"/>
      <forward name="systemerror" path="/systemerror.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>
--------------------------------------------------------

このうち、<action-mappings>の中の各<action>の中の

      <forward name="systemerror" path="/systemerror.jsp"/>

という行(複数ある)が今回編集(追加)する行です。



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


続いて、systemerror.jspファイルを作成しましょう。

struts-config.xmlのフロー・エディター上で/systemerror.jspのアイコンに警告の
マーク(ビックリ・マークの入った橙色の三角形のマーク)がついていますが、
これはファイルがまだ存在しないからなので、このアイコンをダブル・クリックして
下さい。
すると、例によって「Struts JSPファイル」ウインドウが開きますので、「完了」ボタン
をクリックします。

すると、systemerror.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>
      <font color="#ff0000">
      まことに申し訳ございません。<br>
      現在システムがダウンしています。<br>
      数時間程度で回復する予定ですので、数時間のちに再度<br>
      初めから操作をやり直してみて下さい。<br>
      あるいは、お急ぎの場合はTel:123-456-7890までお問合せ下さい。<br>
      </font>
      <br>
      <br>
      <html:link page="/itemSelect.jsp">トップ・ページ(商品検索のページ)に戻る</html:link>
   </body>
</html:html>
--------------------------------------------------------

編集し終わったら保管(Ctrl + S)しましょう。



では、struts-config.xmlファイルもsystemerror.jspファイルも作業が終わり
ましたので、エディターを閉じて下さい。



現時点では、まだJavaファイルのほうのコーディングは行っていませんが
とりあえず、ここで再度ItemListActionTestのJUnitテストを実行して
みて下さい。(JUnitテストの実行前にはMySQLを停止しておくこと。)
すると、今度は、「JUnit」タブ配下のテスト結果には

junit.framework.AssertionFailedError: was expectiong '/systemerror.jsp' but received '/itemList.jsp'

というエラー・メッセージが表示されますね。前回のエラー・メッセージとは異なります。

前回のエラーはstruts-config.xmlファイルの中にforwardの値"systemerror"に対する
マッピングの指定が無かったためにおこっているのですが、今回はstruts-config.xml
ファイルにちゃんとマッピングの指定を行っているので前回のメッセージは出ません。

代わって今度は、「systemerror.jspが期待されているのに実際にはitemList.jspに
進んだ(を受け取った)」というメッセージになっています。
こちらのエラー・メッセージに対処するには、Javaファイルのほうを修正する必要が
あります。



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

という訳で、続いてJavaファイルのほうのコーディングを行っていきましょう。


まず、前回RDBMSへの接続(connect)に失敗してjava.net.ConnectException
を投げていたDbManagerのソース・ファイルを開いてみましょう。
(Eclipseのプロジェクト・エクスプローラーからStrutsShop(プロジェクト)
の配下の「Javaリソース: src」の配下のjp.co.flsi.lecture.struts.db(パッケージ)
の配下のDbManager.javaをダブル・クリックして開く。)

この中のconnect()メソッドのソースコードは下記のようになっています。

--------------------------------------------------------
   public void connect() throws StruShopDbException {
      logger.info("Start ...............");
      try{
         if (conn == null || conn.isClosed()) {

            if ("junit".equals(System.getProperty("ut"))) {
                  Class.forName("com.mysql.jdbc.Driver");
                  conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/STRUSHOP", "root", "rootpass");
                  conn.setAutoCommit(false);
               return;
            }

            Context initCtx = new InitialContext();
            if(initCtx == null) throw new StruShopDbException("Error: InitialContext could not be generated!");
            DataSource ds = (DataSource) initCtx.lookup("java:comp/env/jdbc/STRUSHOP");
            if (ds != null) {
               conn = ds.getConnection();
               if(conn == null) throw new StruShopDbException("Error: Connection does not exist!");
               else logger.info("Connection has been gotten.");
            }
            else {
               throw new StruShopDbException("Error: DataSource does not exist!");
            }
         }
      } catch (NamingException e) {
         logger.error(e, e);
         throw new StruShopDbException("Error: Connect() failed!", e);
      } catch (SQLException e) {
         logger.error(e, e);
         throw new StruShopDbException("Error: Connect() failed!", e);
      } catch (StruShopDbException e) {
         logger.error(e, e);
         throw new StruShopDbException("Error: Connect() failed!", e);
      }
      catch (Throwable e) {
         logger.error(e, e);
      }
      finally {
         logger.info("End ...............");
      }
   }
--------------------------------------------------------

この中のcatchブロックのうち、NamingExceptionとSQLExceptionとStruShopDbException
に対するものは、ログ出力を行った後StruShopDbExceptionを投げる(throw)ように
コーディングされています。(ちなみにRDBMSへの接続に失敗すると結局データベース
への接続に失敗することになりますが、DriverManagerのgetConnection()メソッドは
データベースへの接続に失敗するとSQLExceptionを投げることになっています。
(詳しくは、APIのリファレンス
http://java.sun.com/javase/ja/6/docs/ja/api/index.htmlを参照のこと。)
したがって、少なくともSQLExceptionに対するcatchブロックに関係するところを編集
すれば、今回の用は足ります。そこで、このStruShopDbExceptionに注目します。)

ところがconnect()メソッド自体には

   public void connect() throws StruShopDbException {

のようにthrows StruShopDbExceptionという記述がありますから、
これらのStruShopDbExceptionはconnect()メソッドの呼び出し元に投げ出され、そこで
処理されることになります。

そこで、このconnect()メソッドの呼び出し元を調べてみましょう。

そのためには、Eclipseのエディターの中でconnect()という文字列ののどこかに
カーソルを入れ、メニュー・バーの「ナビゲート」→「呼び出し階層を開く」を
選択(代わりにCtrl+Alt+Hキーを押してもよい)します。

すると、下のほうに「呼び出し階層」ビュー(「呼び出し階層」というタブの
配下に表示される呼び出し階層のリスト)が表示されます。

このリストを見ると、connect()メソッドの配下にexcute()メソッドが複数並んで
いますが、これは、それらの各excute()メソッドの中でconnect()メソッドが
呼び出されていることを意味します。
これらの各excute()メソッドの行の右側(右のほうにスクロールして見る)には
それぞれ

: ActionForward - jp.co.flsi.lecture.struts.CartAction
: ActionForward - jp.co.flsi.lecture.struts.CompleteOrderAction



: ActionForward - jp.co.flsi.lecture.struts.ItemListAction

といった項目が表示されていますが、このうちActionForwardはこのexcute()メソッド
の戻り値の型を示しており、またその後ろの(パッケージ名のついた)クラス名
はこのexcute()メソッドを含むクラスの名前を示しています。

このうち、ItemListActionクラスのexcute()メソッドの行をダブル・クリックしてみて
下さい。

すると、ItemListActionクラスのエディターが開き、その中のexcute()メソッド
の中のconnect()メソッドを呼び出している部分がハイライト(色が反転)されます。
Eclipseではこのようにして、メソッドの呼び出し元とその呼び出している部分を容易に
調べることができるのです。

さて、このハイライトされたconnect()の部分を囲んでいるtryブロックとその下にある
catchブロックに注目して下さい。

--------------------------------------------------------
      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);
      }
--------------------------------------------------------

というようになっていますから、このcatch (StruShopDbException e)のブロックで
先ほどのconnect()メソッドから投げ出されたStruShopDbExceptionが処理されること
がわかります。

これらのcatchブロックの中でforwardの値を"systemerror"にすればよいのです。

そのためには、ItemListActionクラスのソース・コードを下記のように書き換えて
みて下さい。

--------------------------------------------------------
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) {
      String forward = "success";
      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);
         forward = "systemerror";
      }
      catch (Throwable e) {
         logger.error(e, e);
         forward = "systemerror";
      }
      finally {
         try {
            itemDbManager.disconnect();
         }
         catch (Exception e) {
            logger.info("Disconnect failed or there is no connection.");
         }
         logger.info("End ...............");
      }
      logger.info("Method return: <<<<< " + forward);
      logger.info(">>>>>");
      return mapping.findForward(forward);
   }

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


この状態で再度ItemListActionTestのJUnitテストを実行してみると
今度はテスト結果は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. 不許無断複製