Webサービスの基軸を構成するのは、SOAP、WSDL、UDDIの3つのテクノロジだ。この連載では、Webサービスの基本的な知識を身に付けるために、この3つのテクノロジの背景と仕様、機能などを分かりやすく解説していく。(編集局)
前回「WSDL:Webサービスのインターフェイス情報」では、Webサービスのインターフェイス情報を記述するWSDL文書の基本的な構造を紹介した。今回は、WSDL文書の中に含まれる抽象的な定義と具体的な定義との関係や、WSDLの要素間の関係に注目しつつ、WSDL文書を構成するそれぞれの要素を解説していく。
まずは、前回紹介したWSDL文書の構造をもう一度思い出していただこう。
WSDLの最上位要素はwsdl:definitions要素であるが、その下位要素としてwsdl:types要素、wsdl:message要素、wsdl:portType要素(以上、抽象的な定義)、wsdl:binding要素、wsdl:service要素(以上、具体的な定義)が記述される。今回は抽象的な定義に該当する要素を中心に解説する。
wsdl:definitions要素は、WSDL文書の最上位要素だ。Webサービスを定義するすべての要素は、wsdl:definitions要素の子孫要素になる。
wsdl:definitions要素には、name属性とtargetNamespace属性の2つの属性を指定できる。WSDL仕様ではname属性に何を指定すべきなのかを限定していないが、実際のWSDL文書を見ると、そのWSDL文書によってインターフェイスを記述しているWebサービス名を指定することが多いようだ。一方、targetNamespace属性は、WSDL文書の対象名前空間URIを明示するために使用される。WSDL文書の対象名前空間URIとは、そのWSDL文書内で定義される名前(後述するメッセージ名やポートタイプ名など)が属する名前空間を識別するためのURIのことだ。
wsdl:definitions要素の例をリスト1に示す。このリストでは、Webサービスの名前として「Estimate」が、またこのWSDL文書の対象名前空間URIとして「http://unitec-denki.utj.co.jp/schema/2003/」が指定されている。
<wsdl:definitions name="Estimate" targetNamespace="http://unitec-denki.utj.co.jp/schema/2003/" xmlns=……> ……Webサービスのインターフェイス情報の定義…… </wsdl:definitions>
wsdl:definitions要素では、WSDL文書全体で使用する名前空間を宣言しておく。WSDL文書で使われることの多い名前空間と、本連載で使う接頭辞を併せて以下の表1に示す。
本連載で使用する 名前空間接頭辞 |
名前空間URI | 定義 | |
---|---|---|---|
wsdl | http://schemas.xmlsoap.org/wsdl/ | WSDL名前空間 | |
wsdlsoap | http://schemas.xmlsoap.org/wsdl/soap/ | SOAPバインディングのための WSDL拡張性要素のための名前空間 | |
soapenv | http://schemas.xmlsoap.org/soap/envelope/ | SOAP 1.1で定義されたエンベロープ名前空間 | |
soapenc | http://schemas.xmlsoap.org/soap/encoding/ | SOAP 1.1で定義されたエンコーディング名前空間 | |
xsd | http://www.w3.org/2001/XMLSchema | XML Schemaで定義されたスキーマ名前空間 | |
xsi | http://www.w3.org/2001/XMLSchema-instance | XML Schemaで定義されたインスタンス名前空間 | |
表1 WSDL文書で使われることの多い名前空間 |
この記事の解説で使用するサンプルWSDL文書では、表1に加えて、WSDL文書の対象名前空間(名前空間URIは「http://unitec-denki.utj.co.jp/schema/2003/」)を示す名前空間接頭辞として“impl”を使うことにする。
WSDL文書では、Webサービスのインターフェイスを抽象的に定義しておき、それを具体的なインターフェイスとして定義し直すという、2段構えのインターフェイス記述を行うことを前回解説した。これは、抽象的に定義した部分だけを分離して、通信プロトコルやネットワークアドレスの異なる別のWebサービスの定義に再利用するための工夫だ。
●インターフェイスを抽象的に定義する
Webサービスを利用するための出入り口のことをWSDLではポートと呼ぶ。WSDLでは、ポートやポートを通してやりとりされるメッセージのフォーマットを、まず抽象的に定義する。抽象的に定義するとは、ポートやメッセージを、特定の通信プロトコルやネットワークアドレスに依存しない形式で定義することだ。
●抽象的なインターフェイスを具体的に定義し直す
続いてWSDLでは、抽象的に定義されたメッセージやポートに具体的な通信プロトコルやネットワークアドレスをバインドすることによって具体的に定義し直す。さらに、関連するポートをまとめたものをサービスとして定義する。
これらの定義は、具体的にはWSDL文書内の各要素によって行われる。順に見ていこう。
(1)メッセージのフォーマットを定義する(wsdl:message要素)
Webサービスにおいてやりとりされるメッセージには、サービス要求のためにWebサービスが受信する入力メッセージや、サービス結果を提供するためにWebサービスが送信する出力メッセージや、エラー情報を返すためにWebサービスが送信するフォルトメッセージがある。
wsdl:message要素は、それらのメッセージのフォーマットを抽象的に定義する。ただし、Webサービスがやりとりするメッセージといっても、wsdl:message要素で定義するメッセージフォーマットとは、SOAPヘッダを含むSOAPメッセージ全体のことではなく、SOAPメッセージのペイロード、すなわちSOAP本体(Body)の内容のことだ。SOAP本体については、本連載の第2回「SOAPという封筒の内部構造」を参照してほしい。
wsdl:message要素が定義する入出力メッセージやフォルトメッセージは、それぞれ0個以上のパート(part)と呼ぶデータの集まりで構成される。WebサービスによってRPC(Remote Procedure Call)を実現する場合、入力メッセージにはいくつかのパラメータが埋め込まれることになるが、ここで考えているパートとは、個々のパラメータに相当するデータのことだ。
パートは、wsdl:message要素の子として出現するwsdl:part要素によって記述する。wsdl:part要素は、具体的にはパートの型を定義する。例えば、以下のメソッドをWebサービスとして公開するとしよう。
int getPrice(String Code, int Value)
このメソッドを利用するためにWebサービスへ送られる要求メッセージ(すなわちWebサービスの入力メッセージ)をWSDLで記述するとリスト3のようになる。
<wsdl:message name="getPriceRequest"> <wsdl:part name="Code" type="xsd:string"/> <wsdl:part name="Value" type="xsd:int"/> </wsdl:message>
リスト3を見ると、wsdl:message要素の子として出現するwsdl:part要素が、メソッドのパラメータ(CodeとValue)のデータの型を、XML Schemaのビルトインデータ型を使って定義していることが分かるだろう。
wsdl:message要素で定義した抽象メッセージは、後で参照できるようにメッセージ名を付けておくことが必要だ。wsdl:message要素のname属性はメッセージ名を指定する属性だ。リスト3の場合、メッセージ名として「getPriceRequest」という名前が指定されている。
(2)型を定義する(wsdl:types要素)
パートの型として、ユーザーが独自に定義する型を指定したいことがある。その場合、どこか別のところで型を定義して名前を付けておき、その名前をwsdl:part要素で参照する。wsdl:definitions要素の子として出現するwsdl:types要素は、型や要素を定義するために使用する。
WSDL仕様では、相互運用性を確保するため、types要素における型の定義をXML Schemaを使って行うことを推奨しているが、それ以外の型定義システムを使用することも許されている。以下に示すリストは、wsdl:types要素で定義した型を使ってメッセージのフォーマットを定義した例だ。
リスト4を見ると、getPriceRequestというメッセージを構成するgetPriceParmというパートがwsdl:types要素で定義された型(xsd1:EstimaeType)を使用していることが分かるだろう。
ここまで説明してきたwsdl:types要素、wsdl:message要素、wsdl:part要素の関係を整理すると図2のようになる。
wsdl:message要素とwsdl:types要素は、あくまでデータフォーマットを抽象的に定義しているだけだ。従って、XML Schemaを使ってXML文書の要素に対してデータ型を定義するとしても、実際にやりとりされるメッセージがXML形式になるとは限らない。実際のデータフォーマットは、最終的にメッセージがどんなプロトコルにバインドされるかによって決まる(WSDLはさまざまなプロトコルに対するインターフェイスを記述できる)。例えばSOAPプロトコルにバインドされればメッセージはSOAPのエンコーディング規則に従ってXML形式にエンコーディングされるが、HTTP GETプロトコルにバインドされればメッセージはURLエンコードデータでWebサービスに送られることになる。
WSDLではこうしたプロトコルに対する柔軟性を備えている。そのため、メッセージのデータフォーマットを抽象的に定義しておくことでメッセージの定義をさまざまなプロトコルにバインドでき、メッセージのフォーマット定義の再利用が可能になる。
要素の説明を続けよう。
(3)抽象的なポートを定義する(wsdl:portType要素)
WSDLは、メソッドに相当するものを操作(operation)と呼ぶ。wsdl:portType要素の子として出現するwsdl:operation要素は、抽象的な操作を記述するために使用される。操作は、その操作を呼び出すための入力メッセージと、処理結果を返すための出力メッセージ、エラー情報を通知するためのフォルトメッセージという3種類のメッセージで構成される。
wsdl:operation要素の子として出現するwsdl:input要素とwsdl:output要素とwsdl:fault要素は、それぞれのメッセージを記述するために使用する。それぞれのメッセージがどんなフォーマットになるかは、wsdl:message要素で定義したフォーマットを参照することによって指定する。
関連する抽象的な操作の集合をポートタイプと呼ぶ。wsdl:portType要素は、ポートタイプを定義する要素だ。ポートタイプは、Java言語におけるインターフェイスに相当するもので、インターフェイスがいくつかのメソッドを持つようにポートタイプは0個以上の操作を持つことができる。
ここまで説明してきたwsdl:portType要素やwsdl:operation要素の関係を整理すると図3のようになる。
wsdl:operation要素において、wsdl:input要素やwsdl:output要素の出現の仕方によって、さまざまなパターンの操作を表現できる。
例えば各支店から本部サーバに月間報告書を送りつけるだけで、何の応答も求めない場合のように、クライアントからWebサービスにメッセージを一方的に送信する「一方向操作」を考えてみよう(図4)。
この場合、クライアントからの入力メッセージを受信するだけの操作となるので、wsdl:operation要素の子としてwsdl:input要素が1個だけ出現するリスト5のような記述になる。
<wsdl:operation name="MonthlyReport"> <wsdl:input message="impl:MonthlyReport"/> </wsdl:operation>
wsdl:input要素のmessage属性には、特定のメッセージ名(前述したwsdl:message要素のname属性値のこと)を指定してメッセージのフォーマットを参照する。リスト5では、「impl:MonthlyReport」というメッセージ名を持つ抽象的なメッセージフォーマットを指定している。
次に、商品コードと商品数を取引先サーバに渡すと見積価格が返される場合のように、Webサービスがクライアントからのメッセージを受信し、対応する応答メッセージを送信する「要求/応答操作」を考えてみよう(図5)。
応答/要求操作のためのwsdl:operation要素の構造は、一方向操作の場合よりも少し複雑になる。wsdl:operation要素の子要素を整理すると以下のようになる。
(1)Webサービスがクライアントから受け取る入力メッセージを記述するwsdl:input要素
(2)Webサービスがクライアントへ送信する出力メッセージを記述するwsdl:output要素
(3)必須ではないがWebサービスでエラーが発生したときにエラー情報をクライアントへ送信するフォルトメッセージを記述する、0個以上のwsdl:fault要素
wsdl:operation要素はリスト6に示す記述になるだろう。
<wsdl:portType name="Estimate"> <wsdl:operation name="getPrice" parameterOrder="Code Value"> <wsdl:input message="impl:getPriceRequest" /> <wsdl:output message="impl:getPriceResponse" /> <wsdl:fault message=……> …… </wsdl:operation> </wsdl:portType>
リスト6において、wsdl:input要素は「getPriceRequest」というメッセージ名を持つメッセージフォーマットを、wsdl:output要素は「getPriceeResponse」というメッセージ名を持つメッセージフォーマットを参照している。
ところで、リスト6を見るとwsdl:operation要素にparameterOrder属性が付与されていることが分かる。parameterOrder属性は、wsdl:operation要素がRPCの操作を記述する場合にパラメータの順序を示すための属性だ。parameterOrder属性は、1個の空白で区切ったパート名(パート名はwsdl:part要素のname属性で指定されている)をパラメータの順序どおりに並べて指定する。リスト6の場合、wsdl:operation要素が記述するRPC関数のパラメータは、「Code」と「Value」の順番に並ぶことを示している。
ここまで述べた操作は抽象的に定義された操作だ。従って、メッセージの実際の通信方法は、どんなプロトコルにバインドするかに依存する。例えば、要求/応答操作であっても、HTTP要求/応答を使って単一の通信で送受信を実現する方法もあるだろうし、2つのSMTPメッセージを使って独立した通信で送受信を実現する方法もあるだろう。WSDLでは、具体的なプロトコルを別の場所で定義し、それを抽象的な定義にバインドするという仕組みをとっている。
今回はWSDLにおける抽象的な定義の部分を中心に見てきた。次回は、ここまでの定義を、Webサービスで利用する具体的なインターフェイスに再定義する方法を解説しよう。
Copyright © ITmedia, Inc. All Rights Reserved.