レベル: 初級 Graham Glass (graham-glass@mindspring.com), CEO/Chief Architect, The Mind Electric
2001年 2月 本稿では、Web Services Description Language (WSDL) について取り上げます。このWSDLは、WebServiceの機能や保管場所や起動方法といったプロパティーを指定するためのXML文法です。さらに、IBMWSDL Toolkitも紹介しましょう。これは、WSDLからスタブを生成して、Web Servicesアプリケーションの作成を簡略化するためのツールキットです。
進化と革命という観点からWeb Servicesテクノロジーに切り込むこの連載も、今回で4回目になりました。3回目の記事(参考文献を参照) では、Simple Object Access Protocol (SOAP) の水面下の働きについて説明しました。今回は、WebServiceのコア・プロパティーを記述するための標準的な方法であるWSDLについて解説することになっています。さらに、WSDLを活用して開発プロセスをスピードアップするためのツールもいくつか紹介しましょう。
ツールとインストール作業
今回使用する新しいツールが2つあります。
-
IBM WSTK 2.1: このIBM Web Services Toolkit 2.1 (参考文献を参照) には、Apache SOAP、WSDL生成プログラム、Universal Description, Discoveryand Integration (UDDI) クライアントが含まれています。このシリーズでは、これまでもApacheSOAP 2.0を使ってきましたので、今後もApache SOAP 2.0をSOAPサーバーとして使用することになりますが、今回からはさらにWSTKのWSDL生成プログラムも使用することします。
-
IBM WSDL 1.1 Toolkit - このIBM Web Services Description Language Toolkit (参考文献を参照) では、WSDLからクライアントとサーバーのスタブを生成します。このツールキットのコードは、
wsdl.jar というパッケージになっており、WSTKのbsf.jar (Bean Scripting Framework) ファイルとxalan.jar (XMLスタイルシート・プロセッサー) ファイルを使用します。
これらのツールをダウンロードしてインストールしたら、wsdl.jar、bsf.jar、 xalan.jar がCLASSPATHに入っていることを確認してください。その時点で、最初のWSDL対応プログラムを構築する準備ができたことになります。
WSDLの概要
Web Servicesの背後には、アプリケーションが将来的には、ネットワーク対応のサービスの集合として構築されるという発想があります。機能的に等価なサービスが2つあるとして、標準的かつ中立的な方法でネットワークに対してそれぞれのサービスの告知ができるとすれば、理論的には、アプリケーションの構築の際に、価格やパフォーマンスといった基準に照らして、これらの競合する代替サービスの間で選択ができるということになります。さらに、マシン間でコピーできるように設定したサービスがあれば、そのような便利なサービスをローカル・ストレージにコピーすることによって、特定のコンピューター(またはクラスター) で実行するアプリケーションのパフォーマンスを向上させることができます。
少し考えてみれば、このような仕組みは、人間の労働市場の仕組みとよく似ています。就職情報関連のサイトや会社では、労働者と雇用主との間の仲介サービスを提供し、そのような仲介プロセスを円滑にするために、履歴書や職種説明書を活用しています。適当な組み合わせが見つかれば、当事者間で条件の詰めが行われていきます。合意に達した時点で、労働者は雇用主の拠点に移転するか、インターネットなどの遠隔通信手段を活用して仕事をするようになります。
Web Services Description Languageは、XML版の履歴書のようなものです。要するに、WebServiceの機能や保管場所や起動方法を記述するための手段です。百聞は一見にしかずですから、Xmethodsサイト(参考文献を参照) に置かれているCurrent ExchangeというサービスのWSDLを実際に見てみましょう。
http://www.xmethods.net/sd_ibm/CurrencyExchangeService.wsdl
にアクセスすると、そのサービスのおおまかな説明が表示されます。そのWSDLURLのフィールドをクリックすると、リスト1 のようなWSDLコードが表示されます。
XMethodsサイトにアクセスすると、それぞれのWSDLにつき2つのバージョンが表示されますが、そのうちの1つがIBMWSDLに固有のバージョンになっています。なぜそのようになっているかと言えば、IBMWSDL Toolkitにはバグがあって、他のツールキットからWSDLを処理できなくなっているからです。この点は間もなく修正されるはずですが、とりあえず本稿のためのバージョンをXMethodsとの共同作業で作ってみました。
では、WSDL文書の各部分を見ていきましょう。まずは<definitions> セクションです。
<definitions>
<definitions> 要素には、1つ以上のサービスの定義を含めます。ただし、ほとんどの場合は、1つのWSDLファイルで1つのサービスを定義します。<definitions> タグの後には、次のような属性宣言を入れるのが普通です。
-
name: この属性では、サービスの全体的な目的を示すような名前を指定しますが、省略してもかまいません。
-
targetNamespace: この属性では、サービスの情報のための論理的な名前空間を定義します。個々のサービスに対して固有の名前空間を指定するのが普通です。この属性については、後から詳しく取り上げます。
-
xmlns:tns: この名前空間は、多くのWSDLファイルでは使われていません (このサンプルでも使っていません)が、しばらくすれば定着するはずです。使う場合は、targetNamespace の値に設定されます。この属性については、後から詳しく取り上げます。
-
xmlns:soap とxmlns:xsd: これらは、SOAP固有の情報とデータ型を指定するためにWSDL文書で後から使用される標準的な名前空間定義です。
-
xmlns: WSDL文書のデフォルトの名前空間は、http://schemas.xmlsoap.org/wsdl/ に設定されます。<definitions>、<message>、<service> といったWSDLタグはすべて、この名前空間に入っています。
<definitions> 内には、3つの概念上のセクションがあります。
-
<message> と<portType>: サービスが提供する操作の種類。
-
<binding>: 操作を起動する方法。
-
<service>: サービスの保管場所。
さらに、サービスで複合データ型を使用するのであれば、<message> セクションの直前に、任意指定の <types> セクションでそのデータ型を定義する必要があります。今回の簡単なサンプルでは、プリミティブ引数型しか使っていないので、<types> セクションを含めていません。
各セクションをさらに詳しく見てみましょう。
<message> と <portType>
<message> は、サービスとそのサービスを起動する側との間でやり取りされる個々の情報に対応します。通常の往復のリモート・メソッド呼び出しは、2つのメッセージとしてモデル化されます。つまり、要求用のメッセージと応答用のメッセージです。各<message> にはゼロ個以上の部分を含めることができ、各部分には1つの名前と1つのデータ型(任意指定) を含めることができます。WSDLによってオブジェクトを記述する場合は、各部分がメソッド呼び出しの引数に対応します。メソッドによってvoid が戻される場合は、応答が空のメッセージになります。
<portType> は、1つ以上の操作からなる各操作セットに対応します。<operation> では、特定の入力/出力メッセージ・シーケンスを定義します。各入力/出力のメッセージ属性は、前に定義した<message> の名前に対応していなければなりません。操作で入力だけを指定する場合は、一方向の操作になります。入力の後に出力を指定した場合は、応答請求操作になって、1つの入力が通知になります。WSDLによってオブジェクトを記述する場合は、各<operation> がメソッドに対応し、各<portType> がJavaインターフェースまたはクラスに対応します。
このサンプルでは、getRate 操作が入力としてgetRateRequest メッセージを受け取り、出力としてgetRateResponse メッセージを戻すようになっています。
<binding>
<binding> は、SOAPやCORBAといった特定のプロトコルを使って実装した<portType> に対応します。バインディングのデータ型属性は、前に定義した<portType> の名前に対応していなければなりません。WSDLはプロトコルを選ばない仕様なので、SOAP、CORBA、DCOMだけでなく、他の標準プロトコル用のバインディングも指定できます。サービスで複数のプロトコルをサポートする場合は、WSDLの中に、サポートしているプロトコルごとに<binding> を含める必要があります。
このサンプルでは、<binding> セクションが、標準のSOAPエンコードを使ったRPC-over-HTTP通信になっています。また、このサンプルのsoapAction 設定 (前回の記事を参照) が空のストリングに設定されており、サービスのURIが"urn:xmethods-CurrencyExchange" に設定されていることにも注目してください。
<service>
<service> は、ポートの集合としてモデル化されます。<port> は、特定のエンドポイントの特定のバインディングの可用性を表します。ポートのバインディング属性は、前に定義した<binding> の名前に対応していなければなりません。
このサンプルでは、<service> が、Xmethods WebサイトのCurrentExchangeBinding バインディングによってアクセスできるという設定になっています。
<documentation>
WSDLの各要素では、任意指定の<documentation> 要素を宣言して、その要素の情報を人間が読める形式で含めることができます。このサンプルでは、 <service> だけに<documentation> 要素を含めています。一般には、個々の操作をはじめ、その他の要素にも<documentation> 要素を含めるのが普通です。
WSDLを使ってクライアント・スタブを生成する
WSDLには、サービスのインターフェースに関する詳しい記述が含まれているので、WSDLからスタブを作成して、サービスへのアクセスを簡略化することができます。
IBM WSDL Toolkitでは、Apache SOAP用のスタブを作成できます。その一例として、Xmethodsに置かれているCurrencyExchangeサービスを起動するためのクライアント・スタブを作成してみましょう。まず、今回のソフトウェアにかかわるあらゆるファイルを入れるための\demo3 ディレクトリーを作成します。それから、ブラウザーで「ファイル」→「名前を付けて保存」をクリックして、このディレクトリーにサンプルのCurrentExchangeWSDLファイルを保管します。
さらに、次のコマンドを入力して、クライアント・スタブを作成します。
\demo3> java com.ibm.wsdl.Main -in CurrencyExchangeService.wsdl
|
このコマンドによって、CurrencyExchangePortTypeProxy.java というクライアント・スタブ・クラスが生成されます (リスト2)。「unable to load JDK compiler」というメッセージが表示されたら、無視してください。このスタブは、手作業でコンパイルするからです。
お気づきのとおり、このクライアント・スタブは、前回の記事で使ったコードとそっくりです。クライアント・プログラムでは、このプロキシー・クラスを使って、通常のJavaオブジェクトと同じようにしてWebServiceにアクセスできるようになります (リスト3 を参照)。
リスト3: プロキシー・クライアント・クラス
public class Client1
{
public static void main( String[] args ) throws Exception
{
CurrencyExchangePortTypeProxy exchange = new CurrencyExchangePortTypeProxy();
float rate = exchange.getRate( "USA", "japan" );
System.out.println( "rate = " + rate );
}
} |
これらのファイルをコンパイルして実行した場合は、図1 の出力が表示されるはずです。
図1:CurrencyExchangePortTypeProxy.java の出力
WSDLを生成する
IBM WSTKやMicrosoft .NET Studioをはじめとするほとんどのベンダー・ツールキットには、コンポーネントからWSDLを自動的に生成するための機能が用意されています。WSTKを使ってサービスからWSDLを生成する方法を示すために、リスト4 のような気象情報サービスを取り上げてみましょう。
リスト4: コンポーネントからWSDLを生成する
public class Weather
{
public float getTemp( String zipcode )
{
System.out.println( "getTemp( " + zipcode + " )" );
return 56;
}
public void setTemp( String zipcode, float temp )
{
System.out.println( "setTemp( " + zipcode + ", " + temp + " )" );
}
} |
このクラスのWSDLを作成するには、コンパイルした後で、\demo3 ディレクトリーからWSTKのserviceWizard を開始します。そのためには、まず\wstk-2.1\bin がPATH設定に入っていることを確認してから、次のコマンドを実行します。
図2 のような出力ウィンドウが表示されます。
図2: Web Service作成ツール
「Next」をクリックすると、クラスの名前とCLASSPATHを入力するための画面が表示されます。その他のフィールドにはデフォルト値が設定されており、このサンプルでは、デフォルト値をそのまま使います。図3 は、各フィールドの値を示したものです。
図3: Web Service作成ツールのWSDL情報
「Next」をクリックすると、WSDLによって公開するメソッドを選択するための画面が表示されます。図4 で示されているメソッドをすべて選択状態にします。
図4: 選択したメソッドをWSDLで公開する
最後に、これまでの設定の要約が表示されます (図5)。「Finish」をクリックすると、処理が実行されます。
図5: Web Service作成ツールの設定の要約
こうして、最初のWSDLファイルを作成できました。というよりも、実際には、2つのファイルを作成したことになります。
-
Weather_Service-interface.wsdl: このファイルには、WSDL記述の<message>、 <portType>、<binding> の各部分が含まれています。要するに、Web Serviceのインターフェースの記述です(リスト5 を参照)。
-
Weather_Service-impl.wsdl: このファイルには、WSDL記述の<service> の部分が含まれており、Weather_Service-interface.wsdlをインポートすることになります(リスト6 を参照)。
このようにして、インターフェースの記述と実装の記述を切り離しておくのは良い方法です。理論的には、1つの*interface.wsdl ファイルに対して多数の *impl.wsdl ファイルを作成することも可能ですし、UDDI (次回の記事で取り上げます) などのレジストリーを対象にして、特定のインターフェース記述の実装を検索することもできます。
ここで面白い問題があります。<binding> セクションは、本当にWSDLのインターフェース・ファイルに入れてよいのでしょうか。実際には、実装ファイルに入れたほうがよいのではないでしょうか。確かに、<binding> セクションは実装ファイルに入れるべきだという考え方にも一理あります。これは、SOAPやCORBAといった特定のバインディングに固有のセクションだからです。将来的に、この点に関する規則が標準仕様の中に盛り込まれるかどうかは、時間がたってみなければわかりません。
リスト5:Weather_Service-interface.wsdl
<?xml version="1.0" encoding="UTF-8"?>
<definitions name="Weather_Service-interface"
targetNamespace="http://www.weatherservice.com/Weather-interface"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://www.weatherservice.com/Weather"
xmlns:xsd="http://www.w3.org/1999/XMLSchema">
<message
name="IngetTempRequest">
<part name="meth1_inType1"
type="xsd:string"/>
</message>
<message
name="OutgetTempResponse">
<part name="meth1_outType"
type="xsd:float"/>
</message>
<message
name="InsetTempRequest">
<part name="meth2_inType1"
type="xsd:string"/>
<part name="meth2_inType2"
type="xsd:float"/>
</message>
<portType
name="Weather_Service">
<operation name="getTemp">
<input
message="IngetTempRequest"/>
<output
message="OutgetTempResponse"/>
</operation>
<operation
name="setTemp">
<input
message="InsetTempRequest"/>
</operation>
</portType>
<binding
name="Weather_ServiceBinding"
type="Weather_Service">
<soap:binding style="rpc"
transport="http://schemas.xmlsoap.org/soap/http"/>
<operation
name="getTemp">
<soap:operation
soapAction="urn:weather-service"/>
<input>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="urn:weather-service"
use="encoded"/>
</input>
<output>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="urn:weather-service"
use="encoded"/>
</output>
</operation>
<operation
name="setTemp">
<soap:operation
soapAction="urn:weather-service"/>
<input>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="urn:weather-service" use="encoded"/>
</input>
</operation>
</binding>
</definitions> |
リスト6:Weather_Service-impl.wsdl
<?xml version="1.0" encoding="UTF-8"?>
<definitions name="Weather_Service"
targetNamespace="http://www.weatherservice.com/Weather"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://www.weatherservice.com/Weather"
xmlns:xsd="http://www.w3.org/1999/XMLSchema">
<service
name="Weather_Service">
<documentation>IBM WSTK 2.0 generated service definition file</documentation>
<port
binding="Weather_ServiceBinding"
name="Weather_ServicePort">
<soap:address
location="http://localhost:8080/soap/servlet/rpcrouter/"/>
</port>
</service><import
location="http://localhost:8080/wsdl/Weather_Service-interface.wsdl"
namespace="http://www.weatherservice.com/Weather-interface">
</import>
</definitions> |
WSTK WSDLファイルからスタブを生成する
WSTK WSDLファイルからスタブを作成する作業は少し面倒です。Weather_Service.impl のインポート・ステートメントでは、WSDLスタブ生成プログラムが、インターフェース・ファイルの位置に対してHTTPGetを実行することになっているからです。その位置とは、この場合は、http://localhost:8080/wsdl/Weather_Service-interface.wsdl になります。Tomcatがこのファイルを読み込むには、Tomcatのルート・フォルダー$TOMCAT_HOME\webapps\ROOT に\wsdl ディレクトリーを作成し、そのディレクトリーに .wsdlファイルをすべてコピーしなければなりません。さらに、Tomcatを\demo3 ディレクトリーで実行しているという前提で、\demo3 ディレクトリー内から次のコマンドを入力します。
\demo3> java com.ibm.wsdl.Main -in Weather_Service-impl.wsdl |
Tomcatが、\wsdl ディレクトリーからWeather_Service-interface.wsdl ファイルを読み込むと、\demo3 にWeather_ServiceProxy.java というスタブ・クラスが生成されることになります。リスト7 は、ローカルのTomcatサーバーに置かれている気象情報サービスにアクセスするためのテスト・クライアント・プログラムです。
リスト7: Weather Web Serviceのテスト・コード
public class Client2
{
public static void main( String[] args ) throws Exception
{
Weather_ServiceProxy weather = new Weather_ServiceProxy();
float temp = weather.getTemp( "75248" );
System.out.println( "temp = " + temp );
weather.setTemp( "75248", 84 );
}
} |
このプログラムを実行するには、\demo3 内のすべてのJavaファイルをコンパイルして、Apacheの展開画面から urn:weather-service というURNでWeather Web Serviceを展開し、Clientプログラムを実行します。図6 は、展開画面のフィールドの値を示したものです。
図6: Weather Web Serviceの実行
このプログラムを実行すると、クライアントには、getTemp() 呼び出しの戻り値が表示され、Tomcatウィンドウには、送られてくるsetTemp() 呼び出しが表示されます。
ターゲット名前空間
WSDLファイルでは他のWSDLファイルをインポートできるので、名前競合の可能性が常に存在します。したがって、最新のWSDLファイルでは、<definitions> セクションにtargetNamespace 属性と xml:tns 属性を定義するようになっています。targetNamespace には、そのWSDLの固有のURLを設定します (普通は元のWSDLファイルの名前)。そのような機能があるWSDL生成プログラムは、名前の競合を回避するために、セクション間の参照の範囲をtns: で限定します。たとえば、リスト8 では、操作宣言の中のtns: 接頭部によって、メッセージの範囲が特定のWSDLファイルに明示的に限定されています。
リスト8:tns: 接頭部の使い方
<portType name = "CurrencyExchangePortType">
<operation name = "getRate">
<input message = "tns:getRateRequest" name = "getRate"/>
<output message = "tns:getRateResponse" name = "getRateResponse"/>
</operation>
</portType>
|
しばらくすれば、すべてのWSDLツールキットがこの方法を採用することになるでしょう。
次回の予告
次回の記事では、Web Serviceを告知して、他のWeb Serviceから使えるようにする方法を見てみましょう。つまり、UDDI(Universal Description, Discovery and Integration) を使った告知方法です。リリースされたばかりのIBMUDDI4Jツールキットを使って、UDDIリポジトリーにWeb Serviceを公開したり、UDDIリポジトリー内のWebServiceにバインドしたりする方法を学びましょう。
参考文献 -
第1回の記事では、ピアツーピア分散ネットワークに対応するために、Web Servicesアプリケーションを構築することの利点と課題について解説しています。
-
第2回の記事では、Web Servicesを開発する方法を段階的に説明しています。必要なツール、ツールのインストール方法、コードの記述方法、WebServicesの展開方法についても取り上げています。
-
第3回の記事では、ネットワーク上でのSOAPのメッセージ交換の仕組みと機能について説明しています。
- IBMWSDL Toolkit をalphaWorksからダウンロードしてください。
- IBMWeb Services Toolkit をalphaWorksからダウンロードしてください。
- Xmethods.netには、公開されているWeb Servicesのディレクトリーがあります。本稿では、その中の1つを使用しています。
著者について  | |  | Graham Glass氏はThe Mind Electric の創設者、CEO、主任設計士で、大規模分散コンピューティングの構築を専門としています。インターネットの進化は頭脳の進化を反映し、人々や企業が効率的にネットワークを形成するのを 助けるアーキテクチャーは、人々の頭脳をネットワークで結ぶアーキテクチャーにヒントを与えると 氏は考えています。
The Mind Electricの創設以前は、ダラスに本拠を置く、企業間統合に取り組むObjectSpace の会長、CTO、共同創設者でした。ObjectSpaceでは、Voyager製品ラインの設計者兼主任開発者として
分散コンピューティング、JGL Javaコレクション・ライブラリー、さらにクロス・プラットフォームのC++ ツールキットを手がけました。1996年にErnst and Young Entrepeneur年間賞を 受賞したのをはじめ、VoyagerおよびJGLの産業界の賞をいくつも受賞しています。
Graham Glass氏はまた、最先端テクノロジーのトレーニングを提供するObjectLesson社の創設者でした。UNIXおよびSTLに関するPrentice Hallの2冊の書籍を著作し、新しいテクノロジーに関する熱意にあふれた分かりやすい話をする 講演者としても知られています。
University of Southamptonで 数学およびコンピューター・サイエンスのBSc、University of Texasでコンピューター・サイエンスのMS、およびHaberdashers' Aske's Schoolで 英国 "O" レベルと "A" レベルをそれぞれ取得しました。産業界に身を転じる以前は、UTDで上級講師としてUNIX、C、C++、Smalltalk、およびプログラム言語を教えました。連絡先はgraham-glass@mindspring.com です。 |
記事の評価
|