相互運用可能な Web ベースの分散アプリケーションというアイデアは、新しいものではありません。例を挙げると、B2B オンライン e コマースが脚光を浴びるかなり前から、EDI (電子データ交換) 市場の必要性が存在していました。そして、B2B 市場の拡大により、相互運用性が EDI の最も重要な要件となっています。
適当なオンライン電子市場を例に考えてみましょう。そこには多くの企業が存在し、それぞれが独自の "サービス" ("Web サービス" と呼ぶことにします) を提供しています。今日の e コマースには、企業がパートナー候補の提供するサービスを自動的に発見できるメカニズムは存在しません。いわゆる次世代ドットコムは、まさにこの自動発見のためのメカニズムを提供することになります。
このような次世代ドットコム企業には、自分が提供するサービスつまり Web サービスを記述できるソリューションが必要です。もっと具体的に言うと、次のような質問に対する答えを記述できるフォーマットつまりある種の文法が必要なのです。
- オンライン・ビジネスではどのようなサービスが提供されているのか?
- どうすればビジネス・サービスを呼び出すことができるのか?
- ビジネス・サービスを呼び出したユーザーはサービスにどのような情報を示す必要があるか?
- ユーザーはどのような方法で必要な情報を提示するのか?
- サービスはどのような形式でユーザーに情報を送り返すのか?
心配する必要はありません。WSDL がこれらすべてを行うためのメカニズムを提供してくれます。
WSDL の動作方法を理解しやすいよう、最初に、WSDL で SOAP と HTTP がどのように動作するのかを説明します。WSDL の目的は、Web サービスを "記述する" ことです。企業は、WSDL ファイルを交換することで相手のサービスを理解します。パートナーのサービスの内容がわかり、それを呼び出したいときは、SOAP の出番です。サービスは、SOAP を使ってアクセスするオブジェクトと考えることができます。
普通、パートナー候補との通信には、インターネットまたは e-mail を使用します。ご存じのとおり、インターネットは HTTP を使用し、e-mail の通信には SMTP が使われるので、HTTP と SMTP は、SOAP に対する "トランスポート・サービス・プロバイダー" の有力な候補になります。
次に、Web サービスに対する WSDL を記述するプロセスを見ていきます。目標は、既存の Web サービスを公開することです。次のような状況が考えられます。
- 既存のサービスがあり (Web サイトなど)、その機能を公開したい。
- WSDL があり、決定済みの公開内容に従って Web サーバー側のロジックを実装したい。(あまりないシナリオのように思うかもしれませんが、UDDI の指紋というアイデアにより、このようなシナリオは十分に可能性があります。UDDI については、このシリーズの第 4 回で採り上げます。)
- Web サイトも WSDL インターフェースも何もない状態から始める。
この記事では、このような可能性のいずれかまたはすべてが考慮されています。
WSDL のオーサリングを 4つの簡単なステップに分けることにします。これらのステップに従って作業を進めれば、Web サービスを配置する準備が整います。
サンプルのプロジェクトとして、携帯電話販売会社のサービス・インターフェースを構築します (サービスの名前は MobilePhoneService にします)。この会社が販売している携帯電話にはいくつかのモデルがあるので、この会社の Web サービスのバックエンド・データ・ストレージには、model number と price という 2つの列を持つテーブルが含まれています。WSDL 自体から焦点がそれないようにするため、テーブルは単純にしておきます。WSDL を使って公開するサービスには、2つのメソッドがあります。
- getListOfModels ()
- getPrice (modelNumber)
GetListOfModels メソッドからは文字列の配列が返り、各文字列は、携帯電話のモデル番号を表しています。GetPrice は、モデル番号を受け取り、その価格を返します。WSDL は、操作としてこれらのメソッドを呼び出します。それでは、"WSDL インターフェース・ファイル" の構築を始めましょう。
すべての WSDL ファイルのルート要素は <definitions> です。この要素では、サービスを完全に記述する必要があります。まず最初に、<definitions> 要素内にさまざまなネーム・スペースを宣言します。WSDL、SOAP、および XSD (XML Schema Definition) という 3 種類の外部ネーム・スペースを指定する必要があります。これとは別に、MobilePhoneService を参照する TNS というネーム・スペースがあります。つまり、TNS (targetNamespace の略) には、MobilePhoneService に対して特別に定義されるすべての要素と属性の名前が含まれます。ただし、ほとんどの WSDL オーサリングで使用する主要なネーム・スペースは WSDL です。他のネーム・スペースの有用性については、このシリーズで使用するときに説明します。
ネーム・スペースについて一言注意しておきます。WSDL ではネーム・スペースの概念が広範囲に使われています。W3C の公式 Web サイトで、ネーム・スペースについての理解を深めることをお勧めします (参考文献を参照)。ネーム・スペースは無限の柔軟性を備えており、これこそ電子データ交換用のポータブルなフォーマットで必要とされる特徴であることから、WSDL はネーム・スペースという考え方を実装しています。
<definitions> 要素には 1つまたは複数の <portType> 要素が含まれ、各 <portType> 要素は、実際には、公開しようとする "操作" の集合を表しています。あるいは、1つの <portType> 要素は、メソッドをクラスに論理的に分類することと考えることもできます。たとえば、サプライ・チェーン・マネージメントのソリューションにおいて顧客と業者の両方と対話する必要がある場合は、普通、それぞれと個別に対話するための機能を定義します。つまり、顧客用に <portType> を 1つ定義し、業者用に <portType> を 1つ定義します。完成した WSDL ファイルがサービスの集合となるよう、個々の<portType> をサービスと呼ぶ必要があります。
個々のサービスに名前を付ける必要があります。この例では、サービスは 1つだけです。したがって、<portType> 要素も 1つです。携帯電話販売サービスに名前を割り当てるには、<portType> 要素の name 属性を使用する必要があります。
各サービスには、いくつかのメソッドつまり "操作" が含まれる場合があります。WSDL では、<operation> 要素を使って操作を参照します。サンプル・アプリケーションでは、getListOfModels および getPrice という 2つのメソッドを公開します。したがって、2つの <operation> 要素を使い、それぞれで name を指定する必要があります。サンプルでは、<operation> 要素の name 属性を使って、各操作の名前を割り当てています。
この段階で、WSDL ファイルはリスト 1 のようになります。
リスト 1: 操作の定義
<?xml version="1.0" encoding="UTF-8" ?> <definitions name="MobilePhoneService" targetNamespace="www.mobilephoneservice.com/MobilePhoneService-interface" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://www.mobilephoneservice.com/MobilePhoneService" xmlns:xsd="http://www.w3.org/1999/XMLSchema"> <portType name="MobilePhoneService_port"> <operation name="getListOfModels "> ....... ....... </operation> <operation name="getPrice"> ....... ....... </operation> </portType> </definitions> |
操作 (つまりメソッド) を定義したなら、次に、メソッドに送るパラメーターとメソッドから返るパラメーターを指定する必要があります。WSDL の用語では、パラメーターはすべて "メッセージ" と呼ばれます。メッセージを送信し、結果としてリターン・メッセージを受け取ると考えればよいでしょう。メソッドの呼び出しは、着信メッセージに対する応答のリターン・"メッセージ" を準備するために行われる操作です。
第 1 のステップでは 2つの操作を公開したことを思い出してください。1 番目の操作 getListOfModels は、パラメーターを受け取らず、文字列の配列を返します。各文字列は、携帯電話のモデル番号を表しています。したがって、文字列の配列を含む <message> 要素を定義する必要があります。
リスト 2 でさまざまな <message> 要素を見てみましょう。最初の <message> 要素の name 属性は ListOfPhoneModels (このメッセージの論理名) であり、models という名前の <part> 要素が 1つ指定されています。これは、ListOfPhoneModels が単一パートのメッセージであり、唯一存在するパートの名前が "models" であることを意味しています。1つのメッセージにはいくつでもパートを指定できます。ただし、各パートには異なる名前を割り当てて、一意に識別できるようにする必要があります。
<part> 要素のもう 1つの属性として type を指定しています。この "type" 属性は、C++ や Java におけるデータ型と考えることができます。models のデータ型として tns:Vector を指定しています (ルートの <definitions> 要素で指定したネーム・スペースの中に tns があったことを思い出してください)。これは、MobilePhoneService ネーム・スペースを示しています。つまり、WSDL のオーサリングでは独自のネーム・スペースを作成できます。これに対しては、"なぜ" および "どのようにして" という 2つの論理的な疑問が生じるでしょう。
なぜ という疑問に対しては、getListOfModels 操作で返される文字列配列を例にして答えることにします。WSDL では、XSD (XML Schema Definition) で定義されているいくつかのプリミティブ・データ型 (int、float、long、short、byte、string、Boolean など) が使われており、それらを直接使用することもできるし、それらのプリミティブ・データ型をベースに複合データ型を作成しメッセージで使用することもできます。このような理由から、複合データ型を参照するために独自のネーム・スペースを定義する必要があるのです。この例の場合は、"文字列の配列" に対する複合データ型を作成する必要があります。
次に、どのようにして という疑問に対して答えます。ネーム・スペースを作成するには XSD を使用します。そのため、<types> 要素の中で xsd:complexType 要素を使って、Vector という名前のデータ型を定義しました。Vector では、String (項目データ) と Integer (項目数) の 2つのプリミティブ・データ型が使われています。これによって、Vector はネーム・スペースの一部になり、tns というエイリアスを使って参照できます。
同様にして、PhoneModel および PhoneModelPrice という 2つのメッセージを定義しています (リスト 2 を参照)。これらの 2つのメッセージでは xsd ネーム・スペースの String プリミティブ・データ型だけを使用しているので、これらのメッセージを使用するために複合データ型を別に定義する必要はありません。
<message> 要素を作成する過程で、メッセージが入力パラメーターなのか戻り値なのかを指定していないことに気付いたと思います。これは、<portType> 要素内の <operation> 要素で処理します。したがって、リスト 2 を見るとわかるように、2つの操作のそれぞれに <input> 要素と <output> 要素を追加してあります。各 <input> 要素では、名前でメッセージを参照し、その操作を呼び出すときに渡すパラメーターとしてメッセージを扱っています。<output> 要素でも同様の方法でメッセージを参照し、操作の呼び出しの戻り値としてメッセージを扱っています。
リスト 2 は、ここまでの解説を 1つのフレームにまとめたものです。
これまでは、操作とメッセージを抽象的に定義し、実装の詳細には触れませんでした。実際、WSDL の仕事は、Web サービスを定義つまり記述した後、外部のフレームワークへの参照を示して、WSDL のユーザーがサービスの実装に到達する方法を定義することです。このようなフレームワークは、WSDL の抽象的な定義とその実装の間の "バインディング" と考えることができます。
現時点で最も一般的な "バインディング" 技法は、SOAP (Simple Object Access Protocol) を使うものです。WSDL では、Web サービスの本当の実装にアクセスできる SOAP サーバーを指定します。その後、ユーザーを WSDL ファイルから実装に導くのは、すべて SOAP の役割になります。SOAP についてはこのシリーズの次回の記事で採りあげるので、ここでは SOAP の詳細には踏み込まず、WSDL のオーサリングにだけ注目します。
WSDL オーサリングの 3 番目のステップは、WSDL ファイルと SOAP のバインディングのプロセスを記述することです。<definitions> 要素の中で <binding> 要素を指定します。<binding> 要素では、name と type を指定する必要があります。name ではそのバインディングを識別する名前を指定し、type ではそのバインディングに関連付ける portType (操作の集合) を指定します。リスト 3 を見ると、<portType> 要素の name 属性が <binding> 要素の type 属性の値と一致していることがわかります。
<binding> 要素には、バインディングのために使用する外部テクノロジーの宣言が含まれています。SOAP を使用するので、ここでは SOAP のネーム・スペースを使用します。WSDL の用語では、外部ネーム・スペースの使用は、extensibility (拡張性) 要素と呼ばれます。
リスト 3 には、空の <soap:binding/> 要素があります。この要素の目的は、バインディングおよびトランスポートのサービスとして SOAP を使用することを宣言することです。
<soap:binding> 要素には、<style> と <transport> という 2つの属性があります。<style> は省略可能な属性で、そのバインディング内での操作の特性を記述するために使用します。<transport> 属性では、そのバインディングで使用する下位レベルのトランスポート・サービスとして HTTP を指定します。
SOAP クライアントは、WSDL ファイルから SOAP の構造を読み取って、もう一方の端にある SOAP サーバーとの協調動作を行うので、"相互運用性" について十分に注意する必要があります。相互運用性の詳細については、シリーズの第 3 回で検討する予定です。
空の <soap:binding/> 要素の後には、WSDL の <operation> 要素が 2つ指定されており、それぞれがステップ 1 で定義した操作に対応しています。各 <operation> 要素では、個々の操作のバインディングに関する詳細が指定されています。したがって、<soap:operation/> という名前の別の extensibility 要素を使用しています。これも、拡張機能が使われる操作に関係する空の要素です。この <soap:operation/> 要素では <soapAction/> 属性が指定されており、SOAP クライアントはこの属性の値を使って SOAP リクエストを作成します。
ステップ 2 で定義した getListOfModels 操作には出力だけがあり、入力がないことを思い出してください。したがって、この操作に対しては <output> 要素を指定する必要があります。この出力には <soap:body/> 要素が含まれており、これもやはり、その値が使用される出力に関係する空の要素です。SOAP クライアントでは、SOAP リクエストを作成するためにこの情報が必要です。<soap:body/> の <namespace> 属性の値は、SOAP サーバーに配置する "サービス" の名前に対応していなければなりません。SOAP サーバーへのサービスの配置については、次回の記事で解説します。
ステップ 3 もほぼ終わりです。この操作の後に次の操作をコピーすれば、リスト 3 の完成です。
サービスの "インターフェース" を完全に記述した WSDL ファイルが完成しました。最後のステップとして、WSDL ファイルのサマリーを作成する必要があります。WSDL では、このサマリーのことを "インプリメンテーション"・ファイルと呼び、UDDI レジストリーで Web サービスを公開するときに使用します。UDDI レジストリーでの Web サービスの公開については、シリーズの第 4 回で触れます。リスト 4 で WSDL インプリメンテーション・ファイルの例を見てください。インプリメンテーション・ファイルには、主に、次のような特徴があります。
- ルートの
<definitions>要素は、リスト 3 (WSDL インターフェース・ファイル) のものと同じです。ただし、リスト 4 (インプリメンテーション・ファイル) の場合はtargetNamespace属性の指定が異なり、インプリメンテーション・ファイルを参照しています。 - リスト 3 のインターフェース・ファイル (ファイル名は MobilePhoneService-interface.wsdl) とそのネーム・スペースを参照する
<import>要素があります。 - このサービスに対する論理的な
name属性を含む<service>タグがあります。<service>要素には、リスト 3 で作成した SOAP バインディングを参照する<port>要素が含まれています。
IBM の Web Services ToolKit (WSTK) を使用した WSDL オーサリング
Web サービスを配置する準備が完全に整いました。これまでは、emacs のような簡単なテキスト・エディタを使って手作業でファイルを作成する方法を見てきました。IBM の WSTK のような Web サービス・オーサリング・ツールを使って同じファイルを生成することもできます。ツールキットへのリンクおよびこの記事で触れているその他の参考文献については、参考文献を参照してください。
WSTK を使えば、ウィザードによるプロセスを通して、これらのファイルを生成できます。上記のチュートリアルで紹介したものと同じ 2つのメソッドに対する WSDL ファイルを生成し、WSTK のファイルとリスト 3 およびリスト 4 のファイルを比較してみてください。
以下のような違いがあることがわかります。
- WSTK は、論理的な公式に従ってすべての
name属性を作成します。作成手順の例では、自分でわかりやすい名前を使用しました。 - WSTK では、入力のない操作であっても、少なくとも 1つの
inputタグが必ず生成されます。listAllPhoneModels操作にはinput要素はありませんでしたが、同じファイルを WSTK で生成すると、このメソッドに対しても空のinput要素が作成されます。 - WSTK では、手作業で作成した 2つのファイルに加えて第 3 のファイルが作成されます。この 3 番目のファイルは、SOAP エンジンがサービスを配置する際に使用する SOAP 配置ディスクリプターです。サービスの配置については、このシリーズの後の記事で解説する予定です。
今回の記事では、手作業による WSDL オーサリングでインターフェース・ファイルとインプリメンテーション・ファイルを作成する手順を示し、IBM の Web Services ToolKit で生成されるファイルと比較しました。次回は、この WSDL サービスを SOAP サーバーに配置する方法について説明します。
- Web Services Description Language (WSDL) 1.1 の仕様、および XSD やネーム・スペースの技術文書を含むその他のすべての XML 関連公式仕様を参照するには、W3C の公式 Web サイトにアクセスしてください。
- この記事で使用した Web Services ToolKit (WSTK) をダウンロードするには、IBM のalphaWorks Web サイトにアクセスしてください。
- Building Web Services: Making Sense of XML, SOAP, WSDL, and UDDI (Steve Graham、Simeon Simeonov、Toufic Boubez、Glen Daniels、Doug Davis、Yuichi Nakamura、Ryo Neyama 共著、Sams publishing、2001年) は、Web サービス技術のあらゆる分野の著者たちによって書かれた新しい書籍です。
- WebServicesArchitect.com で Web サービスについての優れた記事を読んでください。