■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
                      2009年02月22日

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

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


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


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


それでは、前回のソース・コードを簡単に解説しておきましょう。

JStudySOAPプロジェクトのjp.co.flsi.lecture.webservice.hotelパッケージ
の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;

public class HotelSoapBindingImpl implements jp.co.flsi.lecture.webservice.hotel.Hotel{
   public jp.co.flsi.lecture.webservice.hotel.RoomInfo[] findRooms(jp.co.flsi.lecture.webservice.hotel.StayInfoInput stayInfo) throws java.rmi.RemoteException {
      Vector<RoomInfo> roomInfoVector = new Vector<RoomInfo>();
      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"));
                  roomInfo.setCapacity(rs.getInt("CAPACITY"));
                  roomInfo.setRatePerNight(rs.getInt("RATE"));
                  roomInfo.setRoomType(rs.getString("TYPE"));
                  if ("Y".equals(rs.getString("WEB"))) {
                     roomInfo.setWebBrowsable(true);
                  }
                  else {
                     roomInfo.setWebBrowsable(false);
                  }
                  roomInfoVector.add(roomInfo);
               }
               selectSql.close();
               conn.close();
            }
         }
      }
      catch(Exception e) {
         e.printStackTrace();
      }
      return roomInfoVector.toArray(new RoomInfo[roomInfoVector.size()]);
   }

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

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


まず、findRooms()メソッドとreserveRoom()メソッドの引数を見てみましょう。
これらの引数は元々、自動生成した時点では、in0という名前になっていました。
このように、自動生成された引数は無味乾燥な名前になってしまいますので、
分かりやすい名前(上の例では、それぞれstayInfoとroomReserveにしている)に
変更しておきます。


あとは、

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

の行を見てみましょう。
roomInfoVectorは

Vector<RoomInfo> roomInfoVector = new Vector<RoomInfo>();

で宣言している通り、Vector型であり、RoomInfo型のオブジェクトを要素として
入れる入れ物になっています。
しかし、findRooms()メソッドの戻り値はRoomInfo[]型、つまりRoomInfoの配列
なので、Vectorから配列に変換してからreturnする必要があります。

このような場合に使えるのが上記のtoArray()メソッドです。このメソッドの
引数にRoomInfoの配列を指定してやると、このメソッドはVectorから配列への
変換を行った上で、その結果の配列を戻り値として返してくれます。
このとき、引数に指定した配列に、Vectorの中の全要素が収納できた場合は
その配列が返され、引数に指定した配列に収納し切れない場合は、同じ型の
別の配列が作られて、それが返されます。したがって、上記の例のようにして
Vectorの中の要素数と一致させたサイズの配列を引数に指定しておくと効率が
いいのです。


他の部分は説明しなくてもわかりますね。



では、これに対応したクライアント側(R社側)のプログラムを作ってみましょう。


とりあえず、R社(旅行会社)がH社(ホテル)からWSDLファイル(Hotel.wsdl)を
受け取ったという場面を想定して、そこから作業を始めることにします。


R社側では、受け取ったWSDLファイルからスタブを自動生成し、それを使って
クライアントのプログラムを作成します。

まず最初にWSDLファイルからスタブを自動生成するためのAntのビルド・ファイル
を作成しましょう。
vol.136のときに作成した作業用のディレクトリー

C:\ant_work2

を今回も使うこととし、そこに予めWSDLファイル(Hotel.wsdl)を入れてある
ものとします。
(本来は、H社側のコンピューターとR社側のコンピューターは別物ですが、
ここでは、PCが1台しかない人のために、同一のPCをそのまま使うこととし、
vol.136で作成した上記ディレクトリーとその中に入っているWSDLファイルを
そのまま再利用することにします。
本来ならば、このディレクトリーも別の(R社側の)コンピューターに別途
作成し、そこにビルド・ファイルをコピーしておく必要があることを頭に
入れておいて下さい。また、これ以後の作業は、本来ならば、その別のコン
ピューターで行うことになります。)


まず、Eclipseを起動し、以下の手順でビルド・ファイルを作成してください。
(vol.136と同じような作業になります。)

(1) パッケージ・エクスプローラーの中のJStudySoapClient(プロジェクト)を
右クリックし、「新規」→「ファイル」を選択します。

(2) 「新規ファイル」ウインドウにおいて「ファイル名」に
build.xml
と入力し、「終了」ボタンをクリックします。

(3) build.xmlファイルが開くので、下記のような内容を入力します。

--------------------------------------------------------
<?xml version="1.0" encoding="UTF-8"?>
 <project name="WebServices Stub" 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="wsdl2java">
     <axis-wsdl2java
      output="${sources.dir}"
      url="${local.wsdl}" >
     </axis-wsdl2java>
   </target>
 </project>
--------------------------------------------------------

vol.136のときと違って、

      serverside="true"

という属性の記述がないことに注意してください。
これは、サーバー用のソース・コードを生成するのではなく、クライアント用の
スタブを生成するためです。(serverside属性のデフォルト値は"false"です。)


(4) パッケージ・エクスプローラーの中のJStudySoapClient(プロジェクト)を右クリッ
クし、「新規」→「ファイル」を選択します。

(5) 「新規ファイル」ウインドウにおいて「ファイル名」に
build.properties
と入力し、「終了」ボタンをクリックします。

(6) build.propertiesファイルが開く(そのままだと、ビルド・プロパティー・エディターが開くので、
その下部にある「build.properties」タブをクリックするか、または、いったんファイルを閉じてから
パッケージ・エクスプローラーの中のbuild.propertiesを右クリックし、「アプリケーションから開く」
→「テキスト・エディター」を選択する。)ので、下記のような内容を入力します。
--------------------------------------------------------
work.dir=C:/ant_work2
sources.dir=C:/JavaWorks/JStudySoapClient/src
axis.dir=C:/Tomcat6.0/webapps/axis/WEB-INF
local.wsdl=${work.dir}/Hotel.wsdl
axisservlet=axis/services/AdminService
--------------------------------------------------------


以上のファイルの入力が終わったら、保管(Ctrl + S)しておきましょう。


続いて、下記のようにしてAntを実行します。

(1) パッケージ・エクスプローラー内で、先ほど作成したbuild.xmlを右クリックし、
「実行」→「2 Ant実行」を選択します。

(2) Antが実行され、コンソールにメッセージが表示されます。最終的に

BUILD SUCCESSFUL
Total time: 5 seconds

のようなメッセージが表示されればOKです。
(ただし、Total timeの数字は環境によって異なります。)

(3) ワークスペース内のJStudySoapClientプロジェクト内にJavaのソース・ファイル
が生成されているはずなのですが、これをパッケージ・エクスプローラー内
に表示させるためには、パッケージ・エクスプローラー内のJStudySoapClientを
右クリックし「更新」を選択します。

jp.co.flsi.lecture.webservice.hotelというパッケージができて、その配下に

Facilities.java
Hotel.java
HotelService.java
HotelServiceLocator.java
HotelSoapBindingStub.java
RoomInfo.java
RoomReserveInfo.java
StayInfoInput.java

が生成されていますね。

では、このスタブを使って、次のようにクライアントのプログラムを作りましょう。

(1) JStudySoapClient(プロジェクト)配下のjp.co.flsi.lecture.webservice.hotel
(パッケージ)を右クリックし、「新規」→「クラス」を選択します。

(2) 「名前」欄に

HotelClient

と入力し、「終了」ボタンをクリックします。

(3) HotelClient.javaファイルが開いたら、下記のようにソース・コードを入力
しましょう。

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

import java.net.MalformedURLException;
import java.net.URL;
import java.rmi.RemoteException;

import javax.xml.rpc.ServiceException;


public class HotelClient {
   public static void main(String[] args) throws RemoteException, ServiceException {
      HotelService locator = new HotelServiceLocator();
     
      Hotel hotel = null;
      String url = "http://localhost:8080/axis/services/Hotel";
      try {
         hotel = locator.getHotel(new URL(url));
      } catch (MalformedURLException e) {
         e.printStackTrace();
      }
      StayInfoInput stayInfo = null;
      RoomInfo[] roomInfos = hotel.findRooms(stayInfo);
      for (RoomInfo roomInfo : roomInfos) {
          System.out.println("==============================================");
          System.out.println("Room number : " + roomInfo.getRoomNum());
          System.out.println("Room capacity : " + roomInfo.getCapacity());
          System.out.println("Rate per night : " + roomInfo.getRatePerNight());
          System.out.println("Room type : " + roomInfo.getRoomType());
          System.out.println("Web browsable : " + roomInfo.isWebBrowsable());
      }
      System.out.println("==============================================");
   }
}
--------------------------------------------------------

上記のソース・コードの中のurlを"http://localhost:8080/axis/services/Hotel"に
しているのは、WSDLファイルの中に記述されているものをそのままコピーしたものです
が、このコンピューターがサーバー側のコンピューターとは別物である場合は、もち
ろんlocalhostではなく、正しいIPアドレスまたはドメイン名(ホスト名)を指定し
てやる必要があります。



さて、以上で、H社側とR社側でそれぞれテスト可能なサーバーとクライアントの
プログラムができたので、実行してみることにしましょう(なお、これらは、
データベースにアクセスできることを検証するためのプログラムに過ぎず、
本来のアプリケーションの機能はまだ実装していません)。

ただし、まだサーバー側のデプロイを行っていないので、先にデプロイを行って
おかないことには実行はできません。



というわけで、サーバー側(Webサービス)のデプロイを行っておきましょう。

そのために、サーバー側のビルド・ファイルを以下のように編集しましょう。
(これはvol.137の演習問題に対する回答例でもあります。)


JStudySOAP(プロジェクト)配下のビルド・ファイル(build.xml)を次のように
編集しましょう。(以前のファイルにjar、copy、deploy、undeployという4つの
ターゲットを追加します。)

--------------------------------------------------------
<?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>
   </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>
--------------------------------------------------------

また、build.propertiesファイルを次のように編集しましょう。(wsdd.dirに注意して
下さい。)

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

ここで、ビルド・ファイルのprojectタグのdefault属性が以前のままであることに注意
してください。
つまり、このビルド・ファイルをデフォルトで実行した場合は、以前と同じように
wsdl2javaのターゲットまでしか実行されません。(wsdl2javaのターゲットの実行と
デプロイの実行を一まとめに行いたくないのでこのようにしておきます。)
したがって、デプロイを実行するためには、Antの実行時に今回追加したdeployターゲッ
トを明示的に指定してやる必要があります。

では、そのデプロイをやってみましょう。

まず、Tomcatを起動しておいてください。

(1) JStudySOAP配下のbuild.xmlを右クリックし、「実行」→「3 Antビルド...」を
選択します。

(2) 表形式でターゲットがリストされていますから、このうちdeployにチェック・
マークを入れ、他のターゲットはチェック・マークを外して、「実行」ボタンを
クリックします。

(3) Antが実行され、コンソールにメッセージが表示されます。最終的に

BUILD SUCCESSFUL
Total time: 5 seconds

のようなメッセージが表示されればOKです。
(ただし、Total timeの数字は環境によって異なります。)



これで、デプロイが完了しましたので、先ほど作成したHotelClientを実行して
みて下さい。そして、コンソールに表示される結果を、実際のデータベースの
ROOMINFOテーブルに入っているデータと比較して、同じ内容になっていること
を確認してください。

データベースからデータが正しく取り出されていることがわかりますね。



(次回に続く)


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



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