■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
                      2009年03月22日

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

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


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


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


さて、前回は、プロパティー・ファイルをclassesディレクトリーから
読み込ませるために

InputStream inputStream = getClass().getClassLoader().getResourceAsStream("runmode.properties");

というパターンでクラス・ローダーを呼び出して、そのクラス・ローダーに
ファイルをクラスパスから探させていました。
しかし、このコードはちょっと長ったらしいと感じたかもしれません。

実は、クラス・ローダー(ClassLoaderオブジェクト)だけでなく、Classオブジェクトも
getResourceAsStream()メソッドを持っています。
そして、ClassオブジェクトのgetClassLoader()メソッドを使ってClassLoaderオブジェ
クトを呼び出さなくても、ClassオブジェクトのgetResourceAsStream()メソッドを呼び
出すことによって、クラス・ローダーにファイルを探させることもできるのです。
つまり、

InputStream inputStream = getClass().getResourceAsStream("ファイル名");

というような指定もできるのです。この場合でも、クラス・ローダーがファイルを
クラスパスから探してくれます。しかもコードが短くなって読みやすいですね。

ただし、この場合ちょっと注意が必要で、

InputStream inputStream = getClass().getResourceAsStream("/runmode.properties");

のように、ファイル名(パス)の先頭にスラッシュ(/)を付けてやる必要があります。

これは何故かというと、ClassオブジェクトのgetResourceAsStream()メソッドの場合は
クラスを基点にしてファイルを探すために、そのクラスのパッケージ名をディレクトリー名
に変換したパスを頭につけてファイルを探そうとします。
例えば、HotelSoapBindingImplクラスの場合には、パッケージ名は
jp.co.flsi.lecture.webservice.hotel
なので、それを表すディレクトリー名を先頭につけたパス、つまり

"jp\co\flsi\lecture\webservice\hotel\runmode.properties"
(UNIXやLinuxの場合は"jp/co/flsi/lecture/webservice/hotel/runmode.properties")

というパスにしてファイルを探すことになります。
これでは、classesディレクトリーの直下に入っているrunmode.propertiesファイルを
見つけることはできません。
この場合は、

"/runmode.properties"

というように指定してやると、クラスパスの直下(classesディレクトリーの直下など)
に入っているrunmode.propertiesファイルを探してくれます。
(この先頭のスラッシュ(/)はルート(root = 根)のディレクトリーを意味するため、
ここではクラスパスの根元に相当するディレクトリーの直下に含まれるファイルを指定
したことになります。したがって、クラスパスの一つであるclassesディレクトリーの
直下からもrunmode.propertiesファイルが探されることになります。)



ところでプロパティー・ファイルの中に日本語の文字(漢字やひらがな等)を
入れたいときには注意が必要です。

このファイルのエンコーディングは基本的にはASCII(正確に言うとASCIIを拡張
したISO 8859-1という文字コード)なので、日本語をShift_JISやUTF-8などで
エンコーディングしても正しく読み取ってもらえません。
という訳で、プロパティー・ファイルはASCII(ISO 8859-1)の文字だけで記述すること
が望ましいです。(プロパティー・ファイルは通常、何らかの設定(configuration
= 構成)情報を記述するために使いますから、日本語の文字を使う必要性はあまり
ないはずです。
(なお、設定(構成)情報を入れておくファイルを「コンフィギュレーション・
ファイル(configuration file)」または、「コンフィグレーション・ファイル」、
略して「コンフィグ・ファイル」と呼ぶことがあります。)

しかし、日本語の文字でもUnicodeエスケープで指定してやればプロパティー・ファ
イルに入れることができます。

┌補足─────────────────────────┐
Unicodeエスケープは、漢字のようにUnicodeには含まれるがASCII
には無い文字や特殊な文字をASCIIのファイル内に指定できるよう
にしたものです。
\uNNNNという形式(ただしNは16進数)で4桁の(16進数の)数字
(= 文字コード)を指定することにより漢字などの文字も指定
することができます。
ただし、\は本来はバックスラッシュ(スラッシュ(/)が左下
から右上へ引かれた線で描かれる記号なのに対し、バックスラッ
シュはその逆方向の(左上から右下へ引かれた)線で描かれる記号)
ですが、日本のPCではバックスラッシュの代わりに\の記号が割り
当てられているため、\で表記します。
└───────────────────────────┘

漢字をUnicodeエスケープで記述するのは大変だと思うかも知れませんが、JDKには
native2asciiというツールが用意されており、このツールを使えばShift_JISなど
からUnicodeエスケープへの変換が簡単にできてしまいます。
たとえば、

native2ascii -encoding Shift_JIS runmode.properties.SJIS runmode.properties

というようにコマンドを実行すると、Shift_JISで書かれたrunmode.properties.SJIS
ファイルがUnicodeエスケープに変換されてrunmode.propertiesファイルに書き出され
ます。


また、Antにもnative2asciiタスクが用意されており、

<native2ascii encoding="Shift_JIS" src="input_directory" dest="output_directory"
   includes="**/*.properties" />

のような指定で、Shift_JISからUnicodeエスケープに変換することができます。
ここで、"input_directory"はShift_JISで書かれたプロパティー・ファイルが存在する
ディレクトリーの名前で、"output_directory"はUnicodeエスケープへ変換した後の
ファイルを出力するディレクトリーの名前です。また、includes="**/*.properties"
は、src属性に指定したディレクトリー下の(そのすべてのサブディレクトリーも含めて)
拡張子が.properties(つまりプロパティー・ファイル)のすべてのファイルを変換対象
にすることを意味します。
詳しくは、

http://ant.apache.org/manual/OptionalTasks/native2ascii.html

を参照して下さい。


┌補足─────────────────────────┐
上記の他に、Properties Editorというプラグインを使って、
プロパティー・ファイルに日本語文字を入力する方法もあります。
Properties Editorはプロパティー・ファイル専用のエディター
です。これを使えば、native2asciiを使わなくても、Eclipse上で
Shift_JISで入力した文字が自動的にUnicodeエスケープに変換さ
れます。
興味のある人は、
http://www.eclipseplugincentral.com/Web_Links-index-req-viewlink-cid-206.html
あるいは、
http://propedit.sourceforge.jp/
などを参照して下さい。
└───────────────────────────┘


ではAntのnative2asciiタスクを試してみましょう。
Shift_JISで日本語の文字を書き込んだプロパティー・ファイルを、
以前作成した作業用フォルダー

C:\ant_work2

に入れておいて、これをUnicodeエスケープに変換したファイルをTomcatの
classesフォルダーに書き込むべく、Antのビルド・ファイルを編集すること
にしましょう。


(1) まず、C:\ant_work2フォルダーに下記のような内容のプロパティー・ファイルを
作成し、runmode.propertiesというファイル名で保管しておきましょう。(以前の
プロパティー・ファイルと同じファイル名ですが、保管するディレクトリーが異な
ることに注意して下さい。)

--------------------------------------------------------
secure=no
test=yes
# The next key is only for test.
test.letters=これはテストです。
--------------------------------------------------------

ちなみに、上記の3行目のように行の先頭に#を入れるとコメント行になります。
つまり、何らかの説明文を書き込みたいときは、#から始めればいいのですが、
こういったコメントも日本語の文字で書いてしまうと、Unicodeエスケープに変換
した後は読めなくなってしまいますから、なるべく英語(ASCII)で書いておいた
ほうがいいでしょう。


(2) 次に、Eclipseを起動して、JStudySOAPプロジェクトの中のbuild.propertiesファ
イルを下記のように編集することによって、先ほどのC:\ant_work2を登録しておき
ましょう。

--------------------------------------------------------
sjis.dir=C:/ant_work2
work.dir=C:/JavaWorks/JStudySOAP
classes.dir=C:/JavaWorks/JStudySOAP/bin
sources.dir=C:/JavaWorks/JStudySOAP/src
axis.dir=C:/Tomcat6.0/webapps/axis/WEB-INF
local.wsdl=${work.dir}/Hotel.wsdl
axisservlet=axis/services/AdminService
wsdd.dir=${sources.dir}/jp/co/flsi/lecture/webservice/hotel
wsdd.deployfile=${wsdd.dir}/deploy.wsdd
wsdd.undeployfile=${wsdd.dir}/undeploy.wsdd
--------------------------------------------------------

これは、たんに1行目が追加されただけですね。


(3) 次に、JStudySOAPプロジェクトの中のbuild.xmlファイルを下記のように
編集しましょう。

--------------------------------------------------------
<?xml version="1.0" encoding="UTF-8"?>
 <project name="WebServices Deploy" basedir="." default="wsdl2java">
   <property file="build.properties" />

   <path id="axis.classpath">
     <fileset dir="${axis.dir}/lib">
        <include name="**/*.jar" />
      </fileset>
   </path>
  
   <taskdef resource="axis-tasks.properties" classpathref="axis.classpath" />
  
   <target name="java2wsdl">
     <axis-java2wsdl
      classname="jp.co.flsi.lecture.soap.hotelinterface.Hotel"
      location="http://localhost:8080/axis/services/Hotel"
      namespace="http://hotel.webservice.lecture.flsi.co.jp/"
      output="${local.wsdl}" >
        <classpath>
          <pathelement path="${classes.dir}"/>
        </classpath>
     </axis-java2wsdl>
   </target>

   <target name="wsdl2java" depends="java2wsdl">
     <axis-wsdl2java
      output="${sources.dir}"
      serverside="true"
      url="${local.wsdl}" >
     </axis-wsdl2java>
   </target>

    <target name="jar">
      <jar destfile="${work.dir}/hotel.jar"
        basedir="${classes.dir}"
        includes="**/**"
      />
    </target>

    <target name="copy"  depends="jar">
      <copy todir="${axis.dir}/lib">
        <fileset dir="${work.dir}">
          <include name="**/hotel.jar"/>
        </fileset>
      </copy>
      <native2ascii encoding="Shift_JIS" src="${sjis.dir}"
       dest="${axis.dir}/classes" includes="**/*.properties" />
    </target>

    <target name="deploy" depends="copy">
      <axis-admin port="8080" hostname="localhost" failonerror="true"
       servletpath="${axisservlet}" debug="true" xmlfile="${wsdd.deployfile}" />
    </target>

    <target name="undeploy">
      <axis-admin port="8080" hostname="localhost" failonerror="true"
       servletpath="${axisservlet}" debug="true" xmlfile="${wsdd.undeployfile}" />
    </target>
 </project>

--------------------------------------------------------

"copy"ターゲットにnative2asciiタスクを追加しただけですね。
native2asciiタスクはコピー作業とは違いますが、手間を省くために新しいターゲット
は用意せず、ちょっと手を抜いて"copy"ターゲットに含めました。
きちんとターゲットを分けておきたい人は、自分で編集し直して下さい。


(4) 先ほどプロパティー・ファイルに書き込んだ日本語の文字列がちゃんと
読み取れることを確認するために、HotelSoapBindingImpl.javaを下記のように
編集しましょう。

--------------------------------------------------------
/**
 * 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.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Properties;
import java.util.Vector;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;

import org.apache.log4j.Logger;

public class HotelSoapBindingImpl implements jp.co.flsi.lecture.webservice.hotel.Hotel{
  
   Logger logger = Logger.getLogger(HotelSoapBindingImpl.class);
  
   public jp.co.flsi.lecture.webservice.hotel.RoomInfo[] findRooms(jp.co.flsi.lecture.webservice.hotel.StayInfoInput stayInfo) throws java.rmi.RemoteException {
      logger.info("HotelSoapBindingImpl.findRooms()を開始します。 ...............");
      Vector<RoomInfo> roomInfoVector = new Vector<RoomInfo>();

      InputStream inputStream = getClass().getResourceAsStream("/runmode.properties");
      if(inputStream != null) {
         Properties properties = new Properties(); 
         try {
            properties.load(inputStream);
         } catch (IOException e1) {
            logger.error(e1);
         }
         String testValue = properties.getProperty("test");
         if (testValue != null && testValue.equals("yes")) {
            RoomInfo roomInfo0 = new RoomInfo();
            roomInfo0.setRoomNum(100);
            roomInfo0.setCapacity(2);
            roomInfo0.setRatePerNight(20000);
            roomInfo0.setRoomType("SINGLE");
            roomInfo0.setWebBrowsable(true);
            roomInfoVector.add(roomInfo0);
            String testLettersValue = properties.getProperty("test.letters");
            if (testLettersValue != null) {
               logger.info(testLettersValue);
            }
            return roomInfoVector.toArray(new RoomInfo[roomInfoVector.size()]);
         }
      }

      try{
         Context initCtx = new InitialContext();
         if(initCtx == null) throw new Exception("Error: InitialContext could not be generated!");
         DataSource ds = (DataSource) initCtx.lookup("java:comp/env/jdbc/HOTELDB");
         if (ds != null) {
            Connection conn = ds.getConnection();
            if(conn != null)  {
               Statement selectSql = conn.createStatement();
               ResultSet rs = selectSql.executeQuery("SELECT * FROM ROOMINFO");
               while (rs.next()) {
                  RoomInfo roomInfo = new RoomInfo();
                  roomInfo.setRoomNum(rs.getInt("ROOMNUM"));
                  logger.info("ROOMNUM = " + rs.getInt("ROOMNUM"));
                  roomInfo.setCapacity(rs.getInt("CAPACITY"));
                  roomInfo.setRatePerNight(rs.getInt("RATE"));
                  roomInfo.setRoomType(rs.getString("TYPE"));
                  if ("Y".equals(rs.getString("WEB"))) {
                     roomInfo.setWebBrowsable(true);
                     logger.debug("roomInfo.setWebBrowsable(true);の行が実行されました。");
                  }
                  else {
                     roomInfo.setWebBrowsable(false);
                     logger.debug("roomInfo.setWebBrowsable(false);の行が実行されました。");
                  }
                  roomInfoVector.add(roomInfo);
               }
               selectSql.close();
               conn.close();
            }
         }
      }
      catch(Exception e) {
         logger.error(e);
      }
      finally {
         logger.info("HotelSoapBindingImpl.findRooms()を終了します。 ...............");
      }
      return roomInfoVector.toArray(new RoomInfo[roomInfoVector.size()]);
   }

   public boolean reserveRoom(jp.co.flsi.lecture.webservice.hotel.RoomReserveInfo roomReserve) throws java.rmi.RemoteException {
      return false;
   }

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

つまり、runmode.propertiesファイルの中のキー"test.letters"に対する値をログに
書き出すわけです。こうすれば、"これはテストです。"というメッセージがログに
書き出されるはずですね。

なお、ログの出力量を減らすために、C:\Tomcat6.0\webapps\axis\WEB-INF\classesフォ
ルダーのlog4j.propertiesファイルの中身は、下記のようにして、ログのレベルをINFO
に戻しておきましょう。

--------------------------------------------------------
log4j.rootLogger=INFO, A1
log4j.appender.A1=org.apache.log4j.FileAppender
log4j.appender.A1.file=logs/stdout.log
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=*%-5p [%d] %c.%M: %m%n
--------------------------------------------------------

なお、ログのファイル名も以前とは変えておきました。


(5) では、前回(あるいは前々回)と同じく、再度、デプロイを行い、再度HotelClientを
実行してみて下さい。(予めTomcatを起動しておくことをお忘れなく。)


実行が終わったら、まず、

C:\Tomcat6.0\webapps\axis\WEB-INF\classes

の中に入っているrunmode.propertiesの中身を見てみましょう。

--------------------------------------------------------
secure=no
test=yes
# The next key is only for test.
test.letters=\u3053\u308c\u306f\u30c6\u30b9\u30c8\u3067\u3059\u3002
--------------------------------------------------------

のような内容になっていますね。つまり、キー"test.letters"に対する値(Shift_JISで
日本語の文字列を入れていた所)がUnicodeエスケープになっていることがわかります。

続いて、

D:\Tomcat6.0\logs

の中のstdout.logファイルを(NoEditorなどで)開いて見てみましょう。最後のほうに

これはテストです。

という文字列があるはずですから、探してみてください。

ありますね。これで、Unicodeエスケープに変換されていた日本語の文字列がちゃんと
読み取られていたことがわかりますね。




ところで、Webサービスを実行するたびに毎回プロパティー・ファイルを読み込むの
では、コンピューターに余計な負荷がかかることになります。
何度も、同じものを読み込むのは無駄な負荷ということになります
メモリーからデータを読むのに比べて、ハードディスクからデータ(ファイル)
を読むのははるかに時間がかかり、アプリケーションのパフォーマンス(=処理性能)
低下の原因になりますので、頻繁に行わないようにしなければなりません。

こういう場合は、シングルトン(Singleton)というデザイン・パターンを利用すれば
最初に一回プロパティー・ファイルを読み込むだけで済みます。

シングルトンとは、インスタンスを一つだけしか生成できないオブジェクトを意味し、
すでに生成されたインスタンスが存在するときは、新たにインスタンスを生成すること
はできなくて、既存のインスタンスを使い続けるしかない、という仕組みを持った
クラスを意味します。

このシングルトンのクラスでPropertiesオブジェクトをカプセル化し、プロパティー・
ファイルを読み込む操作をインスタンスの生成時に一回だけ行うようにすれば、
最初に一回だけプロパティー・ファイルを読み込むという仕組みができあがります。


シングルトンは、コンストラクターをprivateにすることによって隠蔽し、代わりに
インスタンスを取得するためのpublicなstaticメソッドを用意して、そのメソッド
ではインスタンスが既存の場合はそのインスタンスを返し、インスタンスが存在し
ないときはコンストラクターを呼び出してインスタンスを生成し、それを返すという
仕組みによって、単一のインスタンスを維持します。


では実際に、シングルトンのクラスを作ってみましょう。

JStudySOAPプロジェクトのjp.co.flsi.lecture.webservice.hotelパッケージ
の中にRunModePropertiesというクラスを作り、下記のような内容にしておきま
しょう。

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

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

import org.apache.log4j.Logger;

public class RunModeProperties extends Properties {

   private static RunModeProperties instance = null;
   private static Logger logger = Logger.getLogger(RunModeProperties.class);

   private RunModeProperties() {
      InputStream inputStream = getClass().getResourceAsStream("/runmode.properties");
      if(inputStream != null) {
         try {
            super.load(inputStream);
         } catch (IOException e1) {
            logger.error(e1);
         }
      }
      logger.info("インスタンスを生成しました。 ...............");
   }
  
   public static RunModeProperties getInstance() {
      if (instance != null) {
         logger.info("インスタンスは既に存在します。 ...............");
      }
      else {
         logger.info("インスタンスが存在しないので生成します。 ...............");
         instance = new RunModeProperties();
      }
      return instance;
   }
}
--------------------------------------------------------

このクラスはPropertiesクラスのサブクラスにしていることに注意しましょう。
このことによって、このクラスはPropertiesクラスの機能を受け継ぐことになります。

コンストラクターRunModeProperties()はprivateにすることによって隠蔽し、その代わりに
getInstance()メソッドがpublicなメソッドとしてインスタンスを返す仕事をしていますね。
そして、インスタンスが既存の場合はそれを返し、インスタンスが存在しないときは
コンストラクターRunModeProperties()を呼び出してインスタンスを生成させています。
そして、コンストラクターRunModeProperties()によって、プロパティー・ファイル
runmode.propertiesを読み込むようにしています。

こうしておけば、最初にこのクラスのインスタンスが生成されたときだけプロパティー・
ファイルが読み込まれることになります。


では、このRunModePropertiesクラスを使ってプロパティー・ファイルを読み込むように
HotelSoapBindingImplを変更してみましょう。
HotelSoapBindingImpl.javaを下記のように編集して下さい。

--------------------------------------------------------
/**
 * 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.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Vector;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;

import org.apache.log4j.Logger;

public class HotelSoapBindingImpl implements jp.co.flsi.lecture.webservice.hotel.Hotel{
  
   Logger logger = Logger.getLogger(HotelSoapBindingImpl.class);
  
   public jp.co.flsi.lecture.webservice.hotel.RoomInfo[] findRooms(jp.co.flsi.lecture.webservice.hotel.StayInfoInput stayInfo) throws java.rmi.RemoteException {
      logger.info("HotelSoapBindingImpl.findRooms()を開始します。 ...............");
      Vector<RoomInfo> roomInfoVector = new Vector<RoomInfo>();

      RunModeProperties properties = RunModeProperties.getInstance();
      String testValue = properties.getProperty("test");
      if (testValue != null && testValue.equals("yes")) {
         RoomInfo roomInfo0 = new RoomInfo();
         roomInfo0.setRoomNum(100);
         roomInfo0.setCapacity(2);
         roomInfo0.setRatePerNight(20000);
         roomInfo0.setRoomType("SINGLE");
         roomInfo0.setWebBrowsable(true);
         roomInfoVector.add(roomInfo0);
         String testLettersValue = properties.getProperty("test.letters");
         if (testLettersValue != null) {
            logger.info(testLettersValue);
         }
         return roomInfoVector.toArray(new RoomInfo[roomInfoVector.size()]);
      }

      try{
         Context initCtx = new InitialContext();
         if(initCtx == null) throw new Exception("Error: InitialContext could not be generated!");
         DataSource ds = (DataSource) initCtx.lookup("java:comp/env/jdbc/HOTELDB");
         if (ds != null) {
            Connection conn = ds.getConnection();
            if(conn != null)  {
               Statement selectSql = conn.createStatement();
               ResultSet rs = selectSql.executeQuery("SELECT * FROM ROOMINFO");
               while (rs.next()) {
                  RoomInfo roomInfo = new RoomInfo();
                  roomInfo.setRoomNum(rs.getInt("ROOMNUM"));
                  logger.info("ROOMNUM = " + rs.getInt("ROOMNUM"));
                  roomInfo.setCapacity(rs.getInt("CAPACITY"));
                  roomInfo.setRatePerNight(rs.getInt("RATE"));
                  roomInfo.setRoomType(rs.getString("TYPE"));
                  if ("Y".equals(rs.getString("WEB"))) {
                     roomInfo.setWebBrowsable(true);
                     logger.debug("roomInfo.setWebBrowsable(true);の行が実行されました。");
                  }
                  else {
                     roomInfo.setWebBrowsable(false);
                     logger.debug("roomInfo.setWebBrowsable(false);の行が実行されました。");
                  }
                  roomInfoVector.add(roomInfo);
               }
               selectSql.close();
               conn.close();
            }
         }
      }
      catch(Exception e) {
         logger.error(e);
      }
      finally {
         logger.info("HotelSoapBindingImpl.findRooms()を終了します。 ...............");
      }
      return roomInfoVector.toArray(new RoomInfo[roomInfoVector.size()]);
   }

   public boolean reserveRoom(jp.co.flsi.lecture.webservice.hotel.RoomReserveInfo roomReserve) throws java.rmi.RemoteException {
      return false;
   }

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

どの部分が変更されたか、わかりますね。



では、再度、デプロイを行い、再度HotelClientを実行してみて下さい。
その後、HotelClientを何回か実行しておきましょう。(何回実行しても
プロパティー・ファイルは最初の1回しか読み取られないことを確認する
ためです。)

実行が終わったら、

D:\Tomcat6.0\logs

の中のstdout.logファイルを開いて見てみましょう。

インスタンスを生成しました。 ...............

は一回だけ出力され、あとは

インスタンスは既に存在します。 ...............

ばかりが出力されていて、インスタンスの生成は最初の一回しか行われて
いないことがわかりますね。
RunModePropertiesのソース・コードを見てみると、インスタンスを生成した
ときにしかrunmode.propertiesファイルの読み込みは行っていないので、
プロパティー・ファイルの読み込みは最初の一回しか行われていないこと
がわかります。




(次回に続く)


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



================================================
◆ 02.前回の演習問題の答
======================================================

下記のいずれかの編集を行うといいですね。

(1) runmode.propertiesファイルの中でtestのキーに対する値をyes以外にする。
例えば、runmode.propertiesファイルの中身を下記のように編集します。
--------------------------------------------------------
secure=yes
test=no
--------------------------------------------------------

(2) runmode.propertiesファイルからtestのキーの行を取り除く。
あるいは、testのキーの行をコメント行にする(行の先頭に#を挿入する)。
例えば、runmode.propertiesファイルの中身を下記のように編集します。
--------------------------------------------------------
secure=yes
#test=yes
--------------------------------------------------------

(3) runmode.propertiesファイル自体を無くす。
つまり、runmode.propertiesファイルを削除(あるいはファイル名を変更)します。



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