■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
                      2009年07月27日

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

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


[このメールマガジンは、画面を最大化して見てください。]


========================================================
◆ 01.SOAPのアプリケーション(Webサービス)
========================================================


さて、以前vol.147でHotelDbExceptionという当アプリケーション専用の例外クラス
を作成しました。
これは文字通り、ホテルのデータベース処理における異常を報告するために使用
しています。

このデータベース処理に異常があるときは、ホテルの運営にも深刻な影響を与える
可能性があるため、ただちに対処する必要があります。

そしてこの例外は、どのクラスで発生したとしても最終的にはHotelSoapBindingImpl
クラスまで伝えられる(途中のメソッドではthrows HotelDbExceptionを指定する
ことによって、最終的にはHotelSoapBindingImplでcatchできる)ようにしてあります。

このように、HotelSoapBindingImplで一括してHotelDbExceptionを捕捉(catch)できる
ようにしておけば、この異常な状態を一括してオペレーターやシステム管理者などに
即時に報告して、担当者に対処を求めることができます。

今回は、このやり方をお話しましょう。

ただし、本格的なシステムであれば、システム監視用のコンピューターが別途用意され
ていて、そのコンピューターに異常な状態を報告するメッセージが寄せられるところで
しょうが、今回はもっと安直な方法を取ります。

今回取る方法は、電子メール(E-mail)の形式でメッセージを送るという方法です。
宛先のメール・アドレスをシステム管理者の携帯電話のメール・アドレスなどに
しておけば、HotelDbExceptionが発生したときに直ちにシステム管理者の携帯電話
にメッセージが伝えられることになります。
あとは、メッセージを受けたシステム管理者が対処(システム管理者がログを調べて
問題を分析&対処)することになります。


では、まず最初に電子メールでメッセージを送信するために使用するAPIをインス
トールしておきましょう。

JavaMailというのがそのAPIです。これは下記URLからダウンロードできます。

http://java.sun.com/products/javamail/downloads/index.html

ダウンロードが終わったら、そのZIPファイル(javamail-1.4.2.zipというファイル名
になっているはず)を解凍して下さい。
解凍してできたフォルダーの中にmail.jarというJARファイルがあるはずですので、
それを

C:\Tomcat6.0\lib

の中にコピーしておきましょう。

JavaMailを使用するためにはJAF(JavaBeans Activation Framework)のAPIが前提と
なっていますので、これもインストールしておきましょう。これは下記URLから
ダウンロードできます。

http://java.sun.com/javase/technologies/desktop/javabeans/jaf/downloads/index.html

ダウンロードが終わったら、そのZIPファイル(jaf-1_1_1.zipというファイル名
になっているはず)を解凍して下さい。
解凍してできたフォルダーの中にactivation.jarというJARファイルがあるはずです
ので、それを

C:\Tomcat6.0\lib

の中にコピーしておきましょう。

これらのJARファイルをEclipseのJStudySOAPプロジェクトのクラスパス(ビルド・パス)
にも追加しておきましょう。

(1) パッケージ・エクスプローラーの中のJStudySOAP(プロジェクト)を右クリックし、
「ビルド・パス」→「外部アーカイブの追加」を選択します。

(2) 「JARの選択」ウインドウにおいて

C:\Tomcat6.0\lib

の中を開き(「ファイルの場所」で選択)、(先ほどコピーしておいた)mail.jarと
activation.jarを選択して「開く」ボタンをクリックします。



それでは、HotelSoapBindingImplを下記のように変更してみましょう。

--------------------------------------------------------
/**
 * HotelSoapBindingImpl.java
 *
 * このファイルはWSDLから自動生成されました / [en]-(This file was auto-generated from WSDL)
 * Apache Axis 1.4 Apr 22, 2006 (06:55:48 PDT) WSDL2Java生成器によって / [en]-(by the Apache Axis 1.4 Apr 22, 2006 (06:55:48 PDT) WSDL2Java emitter.)
 */

package jp.co.flsi.lecture.webservice.hotel;

import java.rmi.RemoteException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Properties;
import java.util.Vector;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

import jp.co.flsi.lecture.webservice.hotel.db.BookingDbManager;
import jp.co.flsi.lecture.webservice.hotel.db.HotelDbException;
import jp.co.flsi.lecture.webservice.hotel.db.RoomDbManager;

import org.apache.log4j.Logger;

public class HotelSoapBindingImpl implements Hotel{

   private static Logger logger = Logger.getLogger(HotelSoapBindingImpl.class);

   public RoomInfo[] findRooms(StayInfoInput stayInfo) throws RemoteException {
      logger.info("Start ...............");
      Vector<RoomInfo> roomInfoVector = null;
      RoomDbManager roomDbManager = new RoomDbManager();

      try {
         String startDate = stayInfo.getStartDate();
         if (startDate.length() != 8) {
            logger.info("Invalid parameter: startDate(= " + startDate
                  + ") format not correct: must be 8 letters");
            throw new RemoteException("Invalid parameter: startDate(= " + startDate
                  + ") is invalid: must be 8 letters");
         }
         SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
         GregorianCalendar stayDate = new GregorianCalendar();
         stayDate.setTime(dateFormat.parse(startDate));
         if (stayDate.get(Calendar.YEAR) != Integer.parseInt(startDate.substring(0, 4)) ||
               (stayDate.get(Calendar.MONTH) + 1) != Integer.parseInt(startDate.substring(4, 6)) ||
               stayDate.get(Calendar.DAY_OF_MONTH) != Integer.parseInt(startDate.substring(6))) {
            logger.info("Invalid parameter: startDate(= " + startDate
                  + ") is not correct: impossible date");
            throw new RemoteException("Invalid parameter: startDate(= " + startDate
                  + ") is not correct: impossible date");
         }
         int numOfNights = stayInfo.getNumOfNights();
         int numOfLodgers = stayInfo.getNumOfLodgers();
         if (numOfNights < 1 || numOfLodgers < 1) {
            logger.info("Invalid parameter: numOfNights (" + numOfNights + ") or numOfLodgers ("
                  + numOfLodgers + ") is invalid.");
            throw new RemoteException("Invalid parameter: numOfNights (" + numOfNights
                  + ") or numOfLodgers (" + numOfLodgers + ") is invalid.");
         }

         roomDbManager.connect();
         roomInfoVector = roomDbManager.getData(numOfLodgers, stayInfo.getMinRatePerNight(),
               stayInfo.getMaxRatePerNight());
         int roomInfoSize = roomInfoVector.size();
         if (roomInfoSize > 0) {
            for (int roomIndex = roomInfoSize - 1; roomIndex > -1; roomIndex--) {
               RoomInfo roomInfo = roomInfoVector.get(roomIndex);
               stayDate.setTime(dateFormat.parse(startDate));  // 宿泊日の初日にリセット
               int roomNum = roomInfo.getRoomNum();
               for (int i = 0; i < numOfNights; i++) {
                  if (roomDbManager.isBooked(roomNum, stayDate)) {
                     roomInfoVector.remove(roomIndex);
                     break;
                  }
                  stayDate.add(Calendar.DAY_OF_MONTH, 1);
               }
            }
         }
      } catch (HotelDbException e) {
         logger.error(e, e);
      } catch (ParseException e) {
         logger.info(e);
         throw new RemoteException("ParseException occured (startDate format not correct)", e);
      }
      catch (RemoteException e) {
         logger.info(e);
         throw e;
      }
      catch (Throwable e) {
         logger.error(e, e);
      }
      finally {
         try {
            roomDbManager.disconnect();
         }
         catch (Exception e) {
            logger.info("Disconnect failed or there is no connection.");
         }
         logger.info("End ...............");
      }

      return roomInfoVector.toArray(new RoomInfo[roomInfoVector.size()]);
   }

   public boolean reserveRoom(RoomReserveInfo roomReserve) throws RemoteException {
      logger.info("Start ...............");
      BookingDbManager bookingDbManager = new BookingDbManager();

      try {
         String startDate = roomReserve.getStartDate();
         if (startDate.length() != 8) {
            logger.info("Invalid parameter: startDate(= " + startDate
                  + ") format not correct: must be 8 letters");
            throw new RemoteException("Invalid parameter: startDate(= " + startDate
                  + ") is invalid: must be 8 letters");
         }
         SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
         GregorianCalendar startStayDate = new GregorianCalendar();
         startStayDate.setTime(dateFormat.parse(startDate));
         if (startStayDate.get(Calendar.YEAR) != Integer.parseInt(startDate.substring(0, 4)) ||
               (startStayDate.get(Calendar.MONTH) + 1) != Integer.parseInt(startDate.substring(4, 6)) ||
               startStayDate.get(Calendar.DAY_OF_MONTH) != Integer.parseInt(startDate.substring(6))) {
            logger.info("Invalid parameter: startDate(= " + startDate
                  + ") is not correct: impossible date");
            throw new RemoteException("Invalid parameter: startDate(= " + startDate
                  + ") is not correct: impossible date");
         }
         int numOfNights = roomReserve.getNumOfNights();
         int numOfLodgers = roomReserve.getNumOfLodgers();
         if (numOfNights < 1 || numOfLodgers < 1) {
            logger.info("Invalid parameter: numOfNights (" + numOfNights + ") or numOfLodgers ("
                  + numOfLodgers + ") is invalid.");
            throw new RemoteException("Invalid parameter: numOfNights (" + numOfNights
                  + ") or numOfLodgers (" + numOfLodgers + ") is invalid.");
         }
         bookingDbManager.connect();
         bookingDbManager.insertData(roomReserve.getRoomNum(), roomReserve.getName(),
               roomReserve.getAddress(), roomReserve.getTelNo(),
               startStayDate, numOfNights, numOfLodgers);
      } catch (HotelDbException e) {
         logger.error(e, e);
         // 以下は、電子メールで例外を通知するための処理
          Properties props = new Properties();
          props.put("mail.smtp.host", "smtp.sirimasen.co.jp"); // メール・サーバー
          props.put("mail.smtp.port", "25"); // ポート番号
          props.put("mail.smtp.auth", "true");
          Session session = Session.getInstance(props, null);
          try {
              MimeMessage message = new MimeMessage(session);
              message.setFrom(new InternetAddress("soushinsha@sirimasen.co.jp")); // 送信者メール・アドレス
              InternetAddress[] to = { new InternetAddress("jushinsha@wakarimasen.co.jp") }; // 宛先メール・アドレス(配列で複数指定可)
              message.setRecipients(Message.RecipientType.TO, to);
              message.setSubject("[Emergency] Hotel DB Exception!!!"); // メールのサブジェクト(件名)
              message.setText("Hotel DB Exception occurred. Please check logs!\n"); // メールの本文
              Transport transport = session.getTransport("smtp");
              transport.connect("smtp.sirimasen.co.jp", "sirimasenuserid", "sirimasenpassword"); // host, userid, password
              transport.sendMessage(message, to);         
          } catch (MessagingException mex) {
            logger.error(mex, mex);
          }
         
         return false;
      } catch (ParseException e) {
         logger.info(e);
         throw new RemoteException("ParseException occured (startDate format not correct)", e);
      }
      catch (RemoteException e) {
         logger.info(e);
         throw e;
      }
      catch (Throwable e) {
         logger.error(e, e);
         return false;
      }
      finally {
         try {
            bookingDbManager.disconnect();
         }
         catch (Exception e) {
            logger.info("Disconnect failed or there is no connection.");
         }
         logger.info("End ...............");
      }
      return true;
   }

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

メール送信を行うソース・コードの例として、少し行を追加しただけですが、
追加したところは、

         // 以下は、電子メールで例外を通知するための処理

の行から

         return false;

の上の行までです。これが、電子メールを送信するためのコードです。
上のソース・コードでは、仮のメール・アドレスやユーザーID(アカウント名)、
パスワードなど、ウソの値を指定してありますが、実際にはこれらを、ちゃんと使える
まともな値に置き換える必要があります。
メーラー(電子メール・ソフト)の設定を自分でやったことのある人なら、何をどの
ように置き換えればいいか直ぐわかるでしょうから、説明は省略します。


なお、上のソース・コードでは電子メールを送信するためのコードをベタで書き込み
ましたが、本来は別のクラスにしておくべきでしょうし、メール・アドレスやユーザーID
などの設定値はプログラムの中に直接組み込むのではなく、vol.144などで紹介した方法
を使ってプロパティー・ファイルで設定しておくべきでしょう。
これらは、読者への課題としておきます。



(次回に続く)


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


========================================================
◆ 02.編集後記
========================================================

当メールマガジンを書き始めてから、あっと言う間に3年も経ってしまいました。
(歳を取ると、年月の流れが速い!)
3年も経つと、以前記載していたURLが陳腐化して使えなくなるなど、
ITの世界はどんどんと変化してしまいます。

というわけで、バックナンバーで学習している読者のために、
今までの記事をすべて見直してバックナンバーを修正して
おこうかとも思ったのですが、忙しくてなかなか暇がないので
結局、断念してしまいました。すみません。

とりあえず、今後の切れ目のいいとき(おそらく、現在執筆中のSOAPアプリケーション
の話が完結した時点で)Eclipseなどのツール類を最新のものにバージョンアップし、
その上で、記事を初級者向けのコーナーと上級者向けのコーナーの二部構成にして、
初級者向けのコーナーでは最新の環境で最初から学べるようにしていく予定です。
そのときには、記事も整理し直されて学習しやすいものに変わっていくと思います。
ご期待ください。



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