連載 Webサービスのキホン(1)
Webサービスの主役、SOAP誕生の背景

Webサービスの基軸を構成するのは、SOAP、WSDL、UDDIの3つのテクノロジだ。この連載では、Webサービスの基本的な知識を身に付けるために、この3つのテクノロジの背景と仕様、機能などを分かりやすく解説していく。(編集局)


株式会社日本ユニテック
吉田 稔、青木 秀起
2002/11/19


Webサービスの中核技術〜SOAP/WSDL/UDDI

 Webサービスとは、インターネットの標準技術を使ってネットワーク上に分散したアプリケーションを連携させる技術のことだ。
あるいは、その技術によって連携させられるアプリケーションそのものをWebサービスと呼ぶこともある。

 Webサービスのことを「アプリケーション連携技術」とひと言で述べたが、実際のところWebサービスは単一の技術ではない。Webサービスは、メッセージ技術やセキュリティ技術やトランザクション管理技術など、実に広い範囲に及ぶ技術で構成されている複合的な技術だ。ただし、Webサービスを構成する技術の中にはまだ策定中のものが多い。さらに、重複する複数の技術が競合中で最終的にどれが生き残るのか分からないものさえある。

 Webサービスはまだ発展の途上にある技術なのだ。

 そこで、この連載では、Webサービスの中核をなす技術で、しかも仕様が比較的定まっている以下の3つの技術に話題を絞って解説する。

(1) Webサービスのためのメッセージ技術であるSOAP(Simple Object Access Protocol)
(2) Webサービスのためのインターフェイス記述技術であるWSDL(Web Services Description Language)
(3) Webサービスを公開・発見する技術であるUDDI(Universal Description,Discovery and Integration)

 Webサービスに対応した便利な開発ツールが数多く出回っているので、開発者はSOAP/WSDL/UDDI仕様の詳細を覚える必要はないだろう。
そこでこの連載では、仕様が策定された背景や、そのように定められた理由を説明することによって、仕様を覚えることではなく、理解することを
目標にして執筆を進めたいと思う。

 連載の皮切りとしてSOAP(Simple Object Access Protocol)を取り上げる。今回はSOAP規格が登場した背景を説明しよう。

SOAPが登場した背景

  Webサービスを利用するためには、クライアントからサーバにサービスの要求やパラメータを渡したり、クライアントから
サーバに処理結果を返したりすることが必要だ。Webサービスでは、HTTPやSMTPなどの通信プロトコル(下位プロトコルとも呼ぶ)
に乗せたXML形式のメッセージによって、それらのやり取りを実現する。

 つまりSOAPとは、Webサービスで使用されるメッセージのデータフォーマットや、メッセージの処理ルールを定めた通信規約のことだ(図1)。
またSOAP規格に準拠したXML形式のメッセージのことをSOAPメッセージと呼ぶ。

図1 SOAPはWebサービスを利用するためのメッセージ規格のこと

 SOAPという名前は「Simple Object Access Protocol」の略だ(注)。その名前が示すとおりSOAPは、元をただせばネットワーク上に存在している
オブジェクトにアクセスするためのプロトコルとして開発されたものだ。しかし、そのような分散したオブジェクトを統合する技術ならば、以前から分散オブジェクトがある。
では、SOAPが必要とされたのはなぜだろうか?

編注:SOAP 1.2からは、SOAPは何かの略ではなく「SOAP」という固有名詞とする、とのこと。

かつての分散オブジェクト技術には問題があった

 分散オブジェクト技術は、異なるコンピュータ上に分散したアプリケーションをオブジェクトとしてとらえ、オブジェクト間でメッセージを交換することによって分散処理を実現する。こうして、アプリケーションを利用するにあたって、動作しているプラットフォームやコンピュータの場所を隠蔽できるようになる。また、分散オブジェクト技術では、各オブジェクトのインターフェイスをIDL(Interface Description Language)という言語で記述する。IDL対応の開発ツールを使うことによって、オブジェクトのインターフェイスに対応するコードを容易に実装できるようになっている。

 分散オブジェクト技術としては、オブジェクト指向技術のための標準化団体OMG(Object Management Group)が策定したCORBA(Common Object Request Broker Architecture)や、マイクロソフトが策定したDCOM(Distributed COM)などがある。

 しかし、これまでの分散オブジェクト技術には問題点があった。1つ目の問題点は、異なるアーキテクチャによる分散オブジェクトシステムの相互接続が容易でないことだ。もちろん、CORBAで構築したシステム間や、DCOMで構築したシステム間なら、問題なく相互接続できる。しかし、CORBAの世界とDCOMの世界の通信を実現するには、両者のプロトコルを変換して2つの世界を橋渡しするための仕組みが必要になってしまうのだ(図2)。

図2 異なる分散オブジェクト技術の世界を接続するには橋渡しの仕組みが必要になる

 別の問題点は、分散オブジェクト技術で採用されているプロトコルが独自のプロトコルであることだ。たとえば、CORBAではIIOP(Internet Inter-ORB Protocol)というインターネットの世界では一般的ではないプロトコルが採用されている。そのため、同じ分散オブジェクト技術で構築したシステム同士でも、インターネット経由でメッセージを交換するためには、ファイアウォール設定の変更が求められることが多い(図3)。

図3 一般的でないプロトコルだとファイアウォールを通過できないことがある

 このように、CORBAやDCOMはイントラネットやLANなどの「閉じた」世界で使用するには向いているが、ファイアウォールを通過し、Web上のいろいろな通信プロトコルやプログラムに対応することは難しい。

 そこで、分散オブジェクト技術が普及するにつれて、特定の技術にロックされることなく、さまざまなプラットフォーム上で稼動するオブジェクトを利用したいという要望が出てきた。また、Web上に分散するオブジェクトを利用するためには、インターネットの標準技術を採用することが望ましいことも分かってきた。SOAPは、HTTPやXMLなどのインターネット標準技術を使うことによって、Web上の分散オブジェクトをプラットフォームの壁を越えて利用することを可能にするために開発されたプロトコルだ。

 SOAPはその名の示すとおり、“シンプル(Simple)な”プロトコルだ。CORBAやDCOMのようなトランザクション管理はできず、その分軽量だが、それゆえにWeb上の分散オブジェクトを「ゆるやかに」結合するのに適している。「ゆるやか」とは、システム上の大きな変更を必要とせず、たとえばCOMアプリケーションとCORBAアプリケーショ ンを接続することもできるということだ。つまり、CORBAやDCOMなどのような閉じた世界の密な結合から、SOAPによるプラットフォームに依存しないグローバルでゆるやかな結合へと変化することが求められ、登場したのがSOAPと言うことができる。

SOAPのこれまでの歩み

 SOAPが注目され始めたのは1999年のことだ。この年の9月、DevelopMentor、Microsoft、Userland Softwareの3社が策定したSOAPバージョン0.9が公開された。同年11月、この3社は、SOAPバージョン1.0をインターネット関連技術の標準化団体IETF(Internet Engineering Task Force)へ提出した。この時点のSOAPは、メッセージを運ぶ下位プロトコルとしてHTTPだけをサポートしていた。

 2000年5月、SOAPバージョン1.1(以下、SOAP1.1)は、XML関連技術の標準化団体W3C(World Wide Web Consortium)に技術ノートとして提出された。SOAP1.1は、HTTP以外のプロトコルもSOAPメッセージを運ぶ下位プロトコルとして使用できるようになった。さらに注目できる点はSOAP1.1の策定にIBMとLotusが加わったことだ。こうして、SOAPは特定のベンダに依存しない規格へと変化していった。

 前項で述べたとおり、SOAPは分散オブジェクト技術の発展の過程で登場した規格だ。だが、SOAPの登場は、単に既存の分散オブジェクト技術の改良にとどまらなかった。2000年9月には、Web上のSOAP対応のアプリケーションであるWebサービスを、公開したり検索したりするための規格UDDIバージョン1が発表された。また同じ月にWebサービスのインターフェイスの記述言語WSDLバージョン1.0が発表された。このようにSOAPの登場を皮切りにして、Webサービスという新しい世界の中核が次々と確立されていった。

 さまざまなベンダやオープンソース団体がSOAP1.1を実装したツールを提供するようになると、異なるツールで開発したシステム間でSOAPによる相互接続に不具合が生じることが分かってきた。SOAP1.1の仕様にあいまいな点があったため、仕様の解釈にゆらぎが生じたのだ。

 SOAPの仕様からあいまいさを排除するため、W3CはSOAPバージョン1.2(以下、SOAP1.2)を策定中だ。この記事を執筆している時点(2002年11月)でSOAP1.2は最終草案の段階に来ている。SOAP1.2が最終的な勧告になるのも間近だ。

 この記事を執筆している時点で稼動しているWebサービスや開発ツールのほとんどはSOAP1.1対応だろう。そこで、この記事ではSOAP1.1規格に沿って説明し、SOAP1.2で仕様が大きく変更されている見込みのところではその点を言及するにとどめる。SOAP1.2は最終草案の段階であり、最終勧告の仕様ではさらに変更される可能性が残っているからだ。

連載 Webサービスのキホン(2)
SOAPという封筒の内部構造

Webサービスの基軸を構成するのは、SOAP、WSDL、UDDIの3つのテクノロジだ。この連載では、Webサービスの基本的な知識を身に付けるために、この3つのテクノロジの背景と仕様、機能などを分かりやすく解説していく。(編集局)


株式会社日本ユニテック
吉田 稔、青木 秀起
2002/12/13


SOAPの内部構造

 SOAPは、本をただせばHTTPやXMLなどのインターネット標準技術を使うことによって、Web上の分散オブジェクトをプラットフォームの壁を越えて利用することを可能にするために開発されたプロトコルだ。本連載第1回「Webサービスの主役、SOAP誕生の背景」でも述べたが、そうした背景があるため、SOAP 1.1は、SOAPを“Simple Object Access Protocol”の略としている。しかしいまでは、SOAPはオブジェクトにアクセスするだけでなく、Webサービス間で交換されるメッセージについての汎用的なフレームワークとなった。そのためSOAP 1.2では、SOAPは何かの略ではなく「SOAP」という固有名詞として扱われている。

 さて、そのSOAPが次のような構造を持っていることはほとんどの方がご存じだろう。


図1 プロトコルバインディングされたSOAPメッセージの構造(分かりやすくするため、名前空間などの指定は省略してある)

 図1を見て分かるとおり、「SOAPメッセージ」と呼ばれるのは「SOAPエンベロープ」の部分であり、そこは「SOAPヘッダ」と「SOAP本体」の2つからなる。「SOAPエンベロープ」の前に付けられているのが「プロトコルバインディングヘッダ」で、SOAPメッセージを運ぶ下位の通信プロトコル(トランスポートプロトコルと呼ぶことにする)に関係する情報が記述される。各部分を簡単に解説しよう。

(1) プロトコルバインディングヘッダ
  実装するトランスポートプロトコルに依存するヘッダで、この中にプロトコルごとに定められているヘッダ情報が記述される。これを受け取ったサーバは、続くメッセージがSOAPであることを理解してSOAPに関する処理を実行する。
(2) SOAPエンベロープ
  Webサービス間で交換されるメッセージを記述する部分で、SOAPメッセージの一番外側を表す。SOAPエンベロープは、大きく分けてSOAPヘッダとSOAP本体の2つの部分に分かれる。
(3) SOAPヘッダ
  封筒のあて名書きに該当する部分で、実際のメッセージのヘッダ情報が記述される。SOAPメッセージの受信者がSOAP本体に記述された情報をだれ(どのサーバ)に渡し、どのように処理するべきなのか、といった情報が記述される。従ってSOAPを実装したアプリケーションに依存する部分をこのSOAPヘッダに記述することになる。SOAPヘッダは省略可能だ。
(4) SOAP本体
  封筒の中身に該当する部分で、メッセージの本文が記述される。実際に交換されるXMLデータが入る。具体的にはRPC呼び出しを行うメソッド名やその引数などの情報が記述される。SOAP本体は必須だ。

SOAPメッセージはXML形式

 ではなぜSOAPはこのような構造になったのだろうか? ある商品の見積もりを依頼するための簡単なSOAPメッセージを使って説明しよう(リスト1)。

POST /getEstimate HTTP/1.1
Host: www.utj.co.jp
Content-Type: application/soap+xml ; charset="utf-8"
Content-Length: nnnn

<?xml version="1.0" encoding="shift_jis"?>
<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Body>
    <m:getEstimate xmlns:m="http://www.unitec-denki.utj.co.jp/schema/2002/">
      <m:GoodsName>もぐらたたきキーボード</m:GoodsName>
      <m:Code>k-001</m:Code>
      <m:Value>100</m:Value>
      <m:DeliveryDate>2002-09-30</m:DeliveryDate>
    </m:getEstimate>
  </soapenv:Body>
</soapenv:Envelope>
リスト1 見積もり依頼のためのSOAPメッセージ

 リスト1からすぐに分かるように、SOAPエンベロープもSOAP本体に格納されるメッセージ本文も、データ形式としてXMLが採用されている。XMLは、システムによる自動処理が容易なうえ、仕様の変更にも柔軟に対応できる、インターネット標準のデータ形式だからだ。

 しかしXML形式のメッセージでよいのなら、相手に渡したいXML文書をHTTPなどでそのまま送ればよいと思う人もいるかもしれない。例えばリスト2のような見積もり依頼のXML文書をそのまま送ることが考えられる。

<?xml version="1.0" encoding="shift_jis"?>
<getEstimate>
  <GoodsName>もぐらたたきキーボード</GoodsName>
  <Code>k-001</Code>
  <Value>100</Value>
  <DeliveryDate>2003-01-01</DeliveryDate>
</getEstimate>
リスト2 XML文書をそのままメッセージにする場合

 しかし、この方法では問題が生じる。リスト2のメッセージフォーマットは、見積依頼としての単純なリクエストには使えても、在庫照会や送金確認などの厳密性を求めるやりとりには使えない。というのも1つのサーバが複数のメッセージを同時にやりとりすると、どのメッセージがどれに対する返事なのか、といったことが分からなくなったりするからだ。ここでメッセージにセッションIDなどを埋め込む領域などを用意しておけば、個々のメッセージを区別し、関連を確認することもできるようになる。

メッセージの中身は封筒の中に入れる

 上記の問題を解決するため、SOAPメッセージは、どんなケースでも共通して使える封筒のような要素でラッピングされた構造になっている。中身の構造は異なっていても、共通に使用できるEnvelope要素でラッピングすれば汎用性が高まるわけだ。また、封筒の部分に関連情報を記述することなどもできる。リスト2をラッピングするとリスト3のようになる。

<?xml version="1.0" encoding="shift_jis"?>
<Envelope>
<Body>
    <getEstimate>
      <GoodsName>もぐらたたきキーボード</GoodsName>
      <Code>k-001</Code>
      <Value>100</Value>
      <DeliveryDate>2002-09-30</DeliveryDate>
    </getEstimate>
  </Body>
</Envelope>
リスト3 送りたいメッセージ本文を、汎用的な要素(Envelope)でラッピングする

XML名前空間で、封筒と本文を区別

 一般にSOAP対応システムにおいて、受け取ったSOAPメッセージはまずSOAP専用のプロセッサで解析されてメッセージ本文が取り出される。続いてSOAPプロセッサは、取り出したメッセージ本文をサービスの提供を行なうアプリケーションに渡して、処理できるようにする。つまり、SOAPメッセージを構成する要素・属性のうち、「汎用的な封筒の要素・属性」と「サービス固有の要素・属性」の2つがあり、それぞれは異なるプログラムによって処理されるということだ。

 1つのXML文書中に、異なるプログラムによって処理される要素・属性が混在する場合、XML名前空間を使用して要素・属性を区別する。こうして要素名・属性名の衝突を防ぐ。

 同様にSOAPメッセージも、XML名前空間を使用することによって、SOAP規格が定めた封筒の要素・属性とアプリケーション固有の要素・属性を区別できるようにしている(リスト4)。

<?xml version="1.0" encoding="shift_jis"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Body>
    <m:getEstimate xmlns:m="http://www.unitec-denki.utj.co.jp/schema/2002/">
      <m:GoodsName>もぐらたたきキーボード</m:GoodsName>
      <m:Code>k-001</m:Code>
      <m:Value>100</m:Value>
      <m:DeliveryDate>2002-09-30</m:DeliveryDate>
    </m:getEstimate>
  </soapenv:Body>
</soapenv:Envelope>
リスト4 名前空間接頭辞を付与されたXMLメッセージ
http://schemas.xmlsoap.org/soap/envelope/は、SOAPの要素・属性の名前空間URI。 xmlns:m="http://www.unitec-denki.utj.co.jp/schema/2002/"は、見積もり依頼サービス固有の要素・属性の名前空間URI

封筒構造のメッセージとトランスポートプロトコルを切り離す

 リスト4を見て分かるとおり、SOAPメッセージ中には特定のトランスポートプロトコルに関する記述はない。SOAPが規定しているのは、やりとりされるデータをラッピングする部分のデータフォーマットだけであり、トランスポートプロトコルに左右されずに任意の構造のメッセージをやりとりできる。封筒の形式だけを規定し、その中に入れる書類の種類や運送方法にはこだわらないという発想だ。

 トランスポートプロトコルについての記述は、SOAPエンベロープの手前の「プロトコルバインディングヘッダ」部分に書かれる。例えば、次のように記述される。

POST /getEstimate HTTP/1.1
Host: www.utj.co.jp
Content-Type: application/soap+xml ; charset="utf-8"
Content-Length: nnnn

<?xml version="1.0" encoding="shift_jis"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Body>
    <m:getEstimate xmlns:m="http://www.unitec-denki.utj.co.jp/schema/2002/">
      <m:GoodsName>もぐらたたきキーボード</m:GoodsName>
      <m:Code>k-001</m:Code>
      <m:Value>100</m:Value>
      <m:DeliveryDate>2002-09-30</m:DeliveryDate>
    </m:getEstimate>
  </soapenv:Body>
</soapenv:Envelope>
リスト5 プロトコルバインディングヘッダ(濃い文字色)と、SOAPメッセージ(薄い文字色)

 上記はHTTPを使ってSOAPメッセージを運ぶ例だが、SOAPはHTTPしか利用できないわけではない。SMTPやFTPなどのプロトコルを使用することもできる。SOAP 1.2では、HTTPバインディングを標準的なものとして推奨するものの、ほかの選択も可能であることを示すため、SMTPバインディングを使用した電子メールの例も取り上げている。

 このように、SOAPは、トランスポートプロトコルに関する部分である「プロトコルバインディングヘッダ」と、SOAPメッセージを表す「SOAPエンベロープ」とが切り離されているため、トランスポートプロトコルに依存しないメッセージ交換が可能だ。

SOAPフォルトでエラーを返す

 SOAPでは、サーバ上で何らかのエラーが発生した場合、封筒構造のSOAP本体にSOAPフォルトと呼ばれるエラー情報を格納して、呼び出し元に返すこともできる。SOAPフォルトの例を見てみよう。

HTTP/1.1 500 Internal Server Error
Content-Type: text/xml; charset="utf-8"
Content-Length: nnnn

<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Body>

    <soapenv:Fault>
      <faultcode>soapenv::Server</faultcode>
      <faultstring>Server Error</faultstring>
      <detail>
        <e:myfaultdetails xmlns:e="http://www.utj.co.jp/faults">
          <message>My application didn't work</message>
          <errorcode>1001</errorcode>
        </e:myfaultdetails>
      </detail>
    </soapenv:Fault>
  </soapenv:Body>
</soapenv:Envelope>
リスト6 SOAPフォルトの記述例(濃い文字色のところ)

 リスト6にあるように、SOAPフォルトはFault要素を使用して、SOAP本体に記述する。この要素はSOAP本体中に一度しか記述することができない。また、Fault要素も、Envelope要素、Header要素、Body要素と同じ名前空間に属するため、名前空間接頭辞「soapenv」を用いて修飾する。表1に示すようにFault要素にはエラーの状態や詳細情報を記述するための子要素も定義されている。SOAP 1.1とSOAP 1.2では違いがあるので比較してみよう。

SOAP 1.1 SOAP 1.2 用途・用法
faultcode Code 違反コードを記述する
faultstring Reason 人間が読めるエラー解説を記述する
faultactor Node エラー発生元のURIを記述する
  Role エラー発生時点でのノードの役割を示すURIを記述する
detail Detail SOAP本体に関係するアプリケーション固有のエラー情報を記述する
表1 Fault要素の子要素

 SOAP 1.1では、Fault要素の子要素として4種類の要素が定義されていたが、SOAP 1.2では5種類の要素が定義されている。また、SOAP 1.2におけるSOAPフォルトでは、SOAPのエラーメッセージを運んでいることを認識させるため、Fault要素がBody要素の唯一の子要素でなければならないことが仕様書に明示されている。

連載 Webサービスのキホン(3)
SOAPヘッダの役割とSOAP-RPCの実現

Webサービスの基軸を構成するのは、SOAP、WSDL、UDDIの3つのテクノロジだ。この連載では、Webサービスの基本的な知識を身に付けるために、この3つのテクノロジの背景と仕様、機能などを分かりやすく解説していく。(編集局)


株式会社日本ユニテック
吉田 稔、青木 秀起
2003/1/17


SOAP拡張のかぎを握るヘッダ

 前回「SOAPという封筒の内部構造」では、SOAPの内部構造とメッセージ本体を中心に紹介した。今回は、SOAPのヘッダについて解説する。

 SOAPメッセージのヘッダには、SOAPメッセージを処理するアプリケーションが機能を拡張するために、独自に定義する要素・属性を格納できるようになっている。例えば、認証などのセキュリティ情報、トランザクション情報、セッション情報といった、SOAPボディでは直接扱うことのできない情報を記述することが考えられる。


図1
(第2回から再掲) プロトコルバインディングされたSOAPメッセージの構造(分かりやすくするため、名前空間などの指定は省略してある)

 要求/応答メッセージを例に、セッション情報が必要な理由を考えてみよう。要求/応答のセットをサポートしていない通信プロトコルを使用する場合や、要求メッセージと応答メッセージが別のセッションでやりとりされる場合、要求メッセージと応答メッセージの対応関係を取れなくなってしまう。


図2 要求/応答メッセージの実装例

SOAPヘッダでセッション管理

 そもそもSOAPは、一方向で1回限りのメッセージ送信が基本で、直前の通信状態を保持しない(すなわちステートレスな)プロトコルだ。しかしそれでは図2のような場合に、返信された応答メッセージを、それに対応する要求メッセージと関連付けることができない。この問題は、ヘッダ部分にメッセージIDを付加して対応関係を作ることによって解消できる。見積もり依頼サービスを要求するメッセージの例をご覧いただきたい。

<?xml version="1.0" encoding="shift_jis"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  
<soapenv:Header>
    <n:MsgHeader xmlns:n="http://unitec-denki.utj.co.jp/requestresponse">
      <n:MessageId>uuid:09111432-123b-5555-c787-2san15pip5e6</n:MessageId>
    </n:MsgHeader>
  </soapenv:Header>
  <soapenv:Body>
    …………
  </soapenv:Body>
</soapenv:Envelope>
リスト1 見積もり依頼サービスの要求メッセージ(色の濃い文字がSOAPヘッダ)

 このリストでは、見積もり依頼サービスのアプリケーションが独自に拡張したMsgHeader要素やMessageId要素が、メッセージIDを管理するためにSOAPヘッダの中で使用されている。対応する応答メッセージは、以下のようになる。

<?xml version="1.0" encoding="shift_jis"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  
<soapenv:Header>
    <n:MsgHeader xmlns:n="http://www.unitec-denki.utj.co.jp/schema/2002/">
      <n:MessageId>uuid:09111432-123b-2392-p423-9add28pon3j1</n:MessageId>
      <n:ResponseTo>uuid:09111432-123b-5555-c787-2san15pip5e6</n:ResponseTo>
    </n:MsgHeader>
  </soapenv:Header>
  <soapenv:Body>
    …………
  </soapenv:Body>
</soapenv:Envelope>
リスト2 見積もり依頼サービスの応答メッセージ(色の濃い文字がSOAPヘッダ)

 この応答メッセージでは、SOAPヘッダ内に埋め込まれたResponseTo要素に要求メッセージのメッセージIDが格納されている。このメッセージIDによって、これが先ほどの要求メッセージに対する応答メッセージであるということを識別できる。

 また、セキュリティ情報をヘッダ部分に格納することもできる。現在のところ、SOAPを利用したセキュアな通信の実現には、SSL/TLSやS/MIMEなどによって通信プロトコル上のセキュリティを確保する方法が一般的だが、ヘッダ部分にデジタル署名を組み込むことにより、コンテンツ自体を暗号化するとセキュリティをさらに強化できる。これが「SOAP Security Extensions: Digital Signature」(W3Cの技術ノート)と呼ばれる拡張機能だ。例えば、クレジットカード番号などが入った本体部分だけを暗号化し、暗号化アルゴリズムなど復号化に必要な情報をヘッダ部分に格納できる。

 このほかの拡張機能として、ファイル添付機能 (「SOAP 1.2 Attachment Feature」(W3Cワーキングドラフト))もある。この機能では、MIME構造を使って非XMLデータをSOAPメッセージに添付できる。

 SOAPの機能を拡張してさまざまな通信パターンに柔軟に対応する際に、拡張性に優れたSOAPヘッダは大きな役割を果たしている。現段階ではまだ策定中の仕様が多いが、ヘッダ機能の充実度がSOAPメッセージの使い勝手を左右するといっても過言ではないだろう。

リモートプロシージャコール(RPC)を表現する

 ここまで、SOAPの一方向メッセージや要求/応答メッセージを考えてきたが、SOAPの重要な応用分野がリモートプロシージャコール(Remote Procedure Call:RPC)だ。SOAPメッセージではRPCを表現する方法を定めており、リモートサーバ内のメソッドをあたかもローカルなメソッドのように呼び出すことができる。

 例として次のメソッドをSOAPを使ったRPCで呼び出すことを考えよう。

float GetLastTradePrice(String Symbol)

 これは、パラメータSymbolによって指定された銘柄の株価の終値を、浮動小数点で返すメソッドだ。SOAPではRPCを記述する約束事として、要求メッセージの要素名はRPCで呼び出すメソッド名と同じにすること、またパラメータはその子要素とすることが決まっている。従って、例として用いるメソッドの要求メッセージは以下のようになる。

<?xml version="1.0" encoding="shift_jis"?>
<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2001/12/soap-envelope">
  <soapenv:Header>
    ………
  </soapenv:Header>
  <soapenv:Body>
    <r:GetLastTradePrice ←メソッドから取られた要素名
soapenv:encodingStyle="http://www.w3.org/2001/12/soap-encoding"
xmlns:r="http://example-stock.utj.co.jp/2001/06/quotes">
      <r:Symbol>DEF</r:Symbol> ←パラメータ名から取られた要素名
    </r:GetLastTradePrice> ←メソッドから取られた要素名
  </soapenv:Body>
</soapenv:Envelope>
リスト3 SOAPによるRPC要求メッセージ 

 SOAP仕様によると、RPCの応答メッセージの要素名は、呼び出すメソッド名に“Response”という文字列を付けることが慣例になっている。上記のRPC呼び出しに対する応答メッセージは、以下のようになる。

<?xml version="1.0" encoding="shift_jis"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Header>
    ………
  </soapenv:Header>
  <soapenv:Body>
    <r:GetLastTradePriceResponse soapenv:encodingStyle="http://www.w3.org/2001/12/soap-encoding"
xmlns:r=http://example-stock.utj.co.jp/2001/06/quotes>
      <Price>34.5</Price>
    </r:GetLastTradePriceResponse>
  </soapenv:Body>
</soapenv:Envelope>
リスト4 SOAPによるRPC応答メッセージ  

 リスト4において、パラメータ名から取られたPrice要素の内容が戻り値として株価の終値を表している

 SOAP 1.2では戻り値の入る要素の要素名を指定するため、空ではない戻り値があるときに使用するresultという要素が追加された。この使い方は多少複雑だ。まず、xmlns:rpc=http://www.w3.org/2002/06/soap-rpcと名前空間の指定を行う。その名前空間接頭辞(リスト5ではrpc)で修飾されたrpc:result要素に「戻り値の入る任意の要素名」が格納される。そしてこの「戻り値の入る任意の要素名」の要素に「戻り値」が格納される。ただし、戻り値が空の場合はresult要素があってはならない。リスト5は、リスト4の一部にSOAP 1.2の仕様を反映した結果だ。

<r:GetLastTradePriceResponse soapenv:encodingStyle="http://www.w3.org/2001/12/soap-encoding"
xmlns:r=http://example-stock.utj.co.jp/2001/06/quotes
xmlns:rpc=http://www.w3.org/2002/12/soap-rpc>
  <rpc:result>r:price</rpc:result>
  <r:price>34.5</r:price>
リスト5 SOAP 1.2の場合のRPC応答メッセージ

中継サーバを経てSOAPメッセージが送られる場合

 SOAPメッセージは、最初にSOAPメッセージを送信するサーバから最終的なあて先のサーバに、ダイレクトに送られるとは限らない。途中でSOAPメッセージを受け取って、ログ情報などを書き込んでから最終的なあて先のサーバへ転送されることもある。このように最初のSOAP送信サーバと最終的なSOAP受信サーバの中間に存在して、SOAPメッセージを解析するサーバのことをSOAP仲介者(Intermediary)と呼ぶ


図3 SOAPメッセージを中継するサーバがあるケース

 SOAP仲介者はSOAPメッセージの最終的なあて先ではないので、一般にSOAP本体の情報を書き換えることはない。しかし、ログ情報などの付加的な情報をSOAPメッセージに付与することはあるだろう。SOAPヘッダは、このような付加的な情報を書き込む場所としても利用されることがある。

 SOAP仲介者を経由してSOAPメッセージが送られる場合、SOAP仲介者はSOAPメッセージのヘッダ情報をすべて理解する必要はない。例えば、メッセージの一部が暗号化されていて暗号化情報がSOAPヘッダに格納されていることがある。その場合、SOAPヘッダに格納された暗号化情報を使う必要があるのは最終的なSOAP受信サーバだけだ。

 従って、SOAPヘッダの特定の要素を処理する必要があるかどうかをSOAP仲介者に知らせる仕組みがないと、途中のサーバがヘッダ情報を処理できずにエラーを出してしまい、SOAPメッセージが最終的なSOAP受信サーバまで届かない可能性がある。


図4 特定のヘッダ処理ができないサーバがあるとメッセージが届かないことがある

 こうした問題を回避するため、SOAPヘッダ内の要素にactor属性(SOAP1.2ではactor属性ではなくrole属性を使って同様の指定を行う)という属性を付与して、どのSOAPサーバがその要素を処理すべきかを指定できる。例えば、ある途中のサーバがリスト6のようなSOAPメッセージを受信すると、actor属性に指定された“http:// www.unitec-denki.utj.co.jp/Gateway”というURIのサーバ以外は、r:Redirection要素を処理せず、SOAPメッセージをそのまま次のサーバに転送することになる。

<?xml version="1.0" encoding="shift_jis"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Header>
    <r:Redirection
xmlns:r="http://www.unitec-denki.utj.co.jp/ Redirection"
soapenv:actor="http://www.unitec-denki.utj.co.jp/Gateway">Business Dept.</r:Redirection>
  </soapenv:Header>
  <soapenv:Body>
    ………
  </soapenv:Body>
</soapenv:Envelope>
リスト6 actor属性の記述例  

 さらに、actor属性以外にも、サーバがSOAPヘッダ内のある要素を処理できない場合、エラーにするのか、気にせずに次のサーバに転送するのかを指定するmustUnderstand属性がSOAPには用意されている。

 mustUnderstand属性は、SOAP受信者がヘッダ項目を必ず処理しなければならないのか、処理が任意であるのかを指定する属性だ。要素の処理が必須である場合には“1”を、要素の処理が任意である場合には“0”(デフォルト値)を指定する(SOAP 1.2では、mustUnderstand属性の値は “true”(必須)または“false”(任意)に変更されている)。例えば、リスト7のように指定した場合、すべてのSOAP受信者はヘッダ項目を必ず解釈して処理しなければならない。

<?xml version="1.0" encoding="shift_jis"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Header>
    <r:Redirection
xmlns:r="http://www.unitec-denki.utj.co.jp/ Redirection"
soapenv:mustUnderstand="1">Business Dept.</r:Redirection>
  </soapenv:Header>
  <soapenv:Body>
    ………
  </soapenv:Body>
</soapenv:Envelope>
リスト7 mustUnderstand属性の記述例  

連載 Webサービスのキホン(4)
WSDL:Webサービスのインターフェイス情報

Webサービスの基軸を構成するのは、SOAP、WSDL、UDDIの3つのテクノロジだ。この連載では、Webサービスの基本的な知識を身に付けるために、この3つのテクノロジの背景と仕様、機能などを分かりやすく解説していく。(編集局)


株式会社日本ユニテック
吉田 稔、青木 秀起
2003/3/18


WSDLは何を記述している?

 あるアプリケーションがWebサービスとしてネットワーク上に公開されているとしよう。別のプログラムがそのWebサービスを利用するためには、次に挙げるWebサービスのインターフェイスに関する情報が必要になる。

そのWebサービスはどこにあるのか
そのWebサービスは、どんなフォーマットのメッセージを使って利用するのか
そのWebサービスは、どんな通信プロトコルを使ってメッセージをやり取りするのか

 Webサービスのインターフェイスを、人もプログラムも理解できるようにXML形式で記述するために開発された言語が、今回のテーマであるWSDL(Web Services Description Language)だ。CORBAのような分散オブジェクト技術では、インターフェイス記述言語としてIDL(Interface Description Language)が使用されている。Webサービスの世界におけるIDLに相当する言語がWSDLだ。

 当初、複数の言語がWebサービスのインターフェイス記述言語として提案されていた。マイクロソフトのSCL/SDL(SOAP Contract Language/Service Description Language)や、IBMのNASSL(Network Accessible Service Specification Language)がそうだ。しかし、2000年9月にそれらの言語はWSDLバージョン1.0に統一されて公開された。さらにその翌年の3月に、改訂されたバージョン1.1(以後、WSDL1.1と呼ぶ)がアリバ・IBM・マイクロソフトからW3Cへ提出され、(勧告ではなく)技術ノートとして公開された。本稿執筆段階で一般に実装されているWSDLは、WSDL1.1だ。

WSDLの用途

 WSDLで記述したXML文書のことをWSDL文書と呼ぶ。Webサービスのために、そのインターフェイスを記述したWSDL文書が用意されていることは、Webサービスの利用者に大きな利得となる。開発ツールにWSDL文書を読み込ませることによって、Webサービスを呼び出すモジュールのコードを自動生成できるようになるからだ。

 WebサービスにWSDL文書が用意されていることは、Webサービスの提供者にも利得となることがある。例えば、ある旅行代理店会社が自社のホテル予約アプリケーションをWebサービスとして公開するとしよう。ホテルの部屋の空きを照会したり予約を入れたりするといった業務は、どの旅行代理店でもほぼ共通した内容だ。このような場合、業界団体では、Webサービスとして公開するホテル予約サービスシステムのインターフェイスを標準化することが考えられる。業界団体は、標準化されたインターフェイスの仕様を記述したWSDL文書を作成し、それを一般に公開することになるだろう。自社のホテル予約システムを新たにWebサービス化する企業は、公開されているWSDL文書を開発ツールに読み込ませてインターフェイスを実装するコードを自動生成させるだけで済む。

 このようにWSDLは、Webサービスの開発の効率を飛躍的にアップさせる技術だ。またWSDLは、システムが発見したWebサービスと動的に接続することさえ可能にする技術だ。

WSDL文書の基本的な構造

 WSDLによる記述は、ごく簡単なSOAPメッセージのやりとりを表現する場合でも、かなり長くなる。そのため、WSDLの基本構造を知らないまま、いきなりWSDL文書を見ると難解に感じてしまうことがある。

 WSDL文書の基本構造を理解するうえで役立つポイントを、以下に3つ述べる。この3つのポイントを知っておくだけで、WSDL文書にずいぶん取り組みやすくなるはずだ。

(1)WSDL文書は抽象的な定義と具体的な定義の、2つのステップで構成されている

 WSDLは、大まかにいうと2つのステップに沿ってWebサービスのインターフェイスを定義する。

インターフェイスを抽象的に定義する
Webサービスを利用するための出入り口のことをWSDLではポートと呼ぶ。WSDLでは、ポートやポートを通してやりとりされるメッセージのフォーマットを、まず抽象的に定義する。抽象的に定義するとは、ポートやメッセージを、特定の通信プロトコルやネットワークアドレスに依存しない形式で定義することだ。
抽象的なインターフェイスを具体的に定義し直す
続いてWSDLでは、抽象的に定義されたメッセージやポートに具体的な通信プロトコルやネットワークアドレスをバインドすることによって具体的に定義し直す。さらに、関連するポートをまとめたものをサービスとして定義する。

 このようにWSDLでは、まずWebサービスのインターフェイスを抽象的に定義しておき、それを具体的なインターフェイスとして定義し直すという、2段構えのインターフェイス記述を行う。これは、抽象的に定義した部分だけを分離して、通信プロトコルやネットワークアドレスの異なる別のWebサービスの定義に再利用するための工夫なのだ。

(2)WSDL文書は中核要素と拡張性要素の2種類の要素で構成されている

 基本的にWSDL規格が定めているのは、メッセージやポートタイプなどWebサービスを抽象的に定義する要素・属性だけだ。従って、定義された抽象的なメッセージやポートを具体的な通信プロトコルやメッセージフォーマットにバインドするためには、プロトコル固有の情報を指定する要素・属性を追加する必要がある。WSDL規格では、WSDLの中核となる要素・属性に加えて、特定の通信プロトコルに依存する拡張性要素(extensibility element)を使用することによって、抽象的な定義に具体的な通信プロトコルをバインドすることにしている。

(3)WSDL文書は定義の積み重ねによって構成される

 WSDLでは、抽象的に定義してから具体的に定義し直すという2段構えであると述べた。しかし、実際の定義はさらに細かいステップに分かれていて、インターフェイスの最も基本的な単位からスタートし、それを包含する次の単位へと定義を進めていき、最終的にWebサービス全体を定義していく。つまり、WSDL文書は、各レベルにおける定義の積み重ねで構成されているわけだ。

WSDL文書を構成する主要な要素

  WSDL文書を構成する要素のうち、主要な要素を表1に示す。次回で詳しく説明するが、表1に挙げた要素はすべて、WSDL名前空間(名前空間URIは、「http://schemas.xmlsoap.org/wsdl/」)に属している。本連載では、WSDL名前空間の名前空間接頭辞としてwsdlを使用する。もちろんwsdl以外の文字列を使用しても構わない。

要素名 要素の説明
wsdl:definitions要素 WSDL文書の最上位要素
wsdl:types要素 メッセージのフォーマットを定義する際に使用する型を、抽象的に定義する。wsdl:types要素は省略可能だが、メッセージ定義にユーザー定義の型を使用する場合は、ここで型を定義しておく必要がある
wsdl:message要素 Webサービスで使用するメッセージのフォーマットを抽象的に定義する。wsdl:types要素で定義された型はここで使用する
wsdl:operation要素 入出力メッセージや、エラー情報を通知するために使用するフォルトメッセージのフォーマットとして、wsdl:message要素で定義されたフォーマットを割り当てる。入力と出力とフォルトメッセージ出力を行う処理の1単位である操作(operation)を抽象的に定義する
wsdl:portType要素 関連する操作をひとまとめにした抽象的なポートである、ポートタイプ(portType)を定義する
wsdl:binding要素 wsdl:portType要素で定義されたポートタイプ内の個々の抽象的な操作に、具体的な通信プロトコルをバインドする。ポートタイプの定義に通信プロトコルをバインドした定義のことをバインディング(binding)と呼ぶ。具体的な通信プロトコルへのバインドは、拡張性要素を使用して記述する
wsdl:port要素 wsdl:binding要素で定義されたバインディングに、通信エンドポイントのネットワークアドレスをバインドして具体的なポートを定義する。ネットワークアドレスのバインドは拡張性要素を使用して記述する
wsdl:service要素 wsdl:port要素で定義したポートのうち、関連するポートをひとまとめにしたサービスを定義する
表1 WSDL文書を構成する主要な要素

 表1の説明を上から順番に丹念に読んでいくと、1つ前の要素によって記述された定義が、次の要素による定義の記述に使用され、さらにそれが次の要素による定義の記述に使用されていることがお分かりいただけるだろう。

Java言語とWSDLの定義を比較してみる

 抽象的な定義を行う要素の働きは、表1を見ただけではピンとこないかもしれない。Java言語におけるインターフェイス定義を例にして説明すると分かりやすいだろう。まず以下のリストを見ていただきたい。

public interface Estimate {
  public int getPrice(String Code, int Value);
}
リスト1 Java言語のインターフェイス定義

 これは商品コードCodeと商品の個数Valueを渡すと、価格を返すメソッドを定義しているインターフェイスをJava言語でコーディングしたものだ。このEstimateというインターフェイスに、getPrice というメソッドが定義されている。getPriceメソッドは、StringクラスのCodeという第1パラメータとint型のValueという第2パラメータを持つ。またgetPriceメソッドは、処理結果をint型で返すことが分かるだろう。

 このコードによる処理をWSDLで定義するとしたらどうなるだろうか。Javaのインターフェイスに相当する記述を行うのは、WSDLのwsdl:portType要素だ。またインターフェイス内で定義されているメソッドに相当する記述を行うのは、WSDLのwsdl:operation要素だ。すると、メソッドのパラメータの集合に相当するのはwsdl:message要素となる。wsdl:types要素の働きは、パラメータのためにユーザーが型を定義することに似ているだろう。Java言語との対比は、もう一度後述する。

WSDL文書における主要な要素の関係

 図1には、表1の要素がどのようにWSDL文書を構成しているのかを示すイメージである。


図1 WSDL文書を構成する主要な要素

図中で箱が3重に表現されている要素は、複数回出現可能なことを表している。wsdl:message要素、wsdl:portType要素、wsdl:operation要素、wsdl:binding要素とwsdl:port要素がそれに該当する

 WSDLの主要な要素がどのような包含関係にあるのか、イメージをつかんでいただきたい。

実際のWSDL文書を見てみる

  ここまでのところで、WSDL文書の大まかな構造や使用される主な要素の説明をしてきた。では、最後にWSDL文書のソースコードを見ることにしよう。

 以下のリストに示すJavaのクラスが、Webサービスとして公開されているとして、そのインターフェイスがどのように記述されるかを示そう。

public class Estimate {
  public int getPrice(String Code, int Value) {
    ……ロジックの記述……
  }
}
リスト2 サンプルに使うJavaのクラス

 このサンプルは、文字列型の商品コード(Code)と整数型の商品の個数(Value)を渡すと価格を返すメソッドをWebサービスとして公開するというものだ。このWebサービスのサービスを要求するためには、HTTPプロトコル上で以下のリストに示すSOAPメッセージを送る。ここでは、商品コードが「code-001」の商品を30個購入する場合の価格を要求している。

POST /axis/services/Estimate HTTP/1.0
Content-Type: text/xml; charset=utf-8
Accept: application/soap+xml, application/dime, multipart/related, text/*
User-Agent: Axis/1.1beta
Host: localhost
Cache-Control: no-cache
Pragma: no-cache
SOAPAction: ""
Content-Length: nnnn

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <soapenv:Body>
    <ns1:getPrice
soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:ns1="http://unitec-denki.utj.co.jp/schema/2003/">
      <Code xsi:type="xsd:string">code-001</Code>
      <Value xsi:type="xsd:int">30</Value>
    </ns1:getPrice>
  </soapenv:Body>
</soapenv:Envelope>
リスト3 サンプルWebサービスへのSOAP要求メッセージ

 上記の要求に対するSOAP応答メッセージは以下のリストに示すとおりになる。価格として「1575(円)」が返されている。

HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Date: Wed, 12 Mar 2003 15:06:08 GMT
Server: Apache Coyote/1.0
Connection: close

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <soapenv:Body>
    <ns1:getPriceResponse
soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:ns1="http://unitec-denki.utj.co.jp/schema/2003/">
      <getPriceReturn xsi:type="xsd:int">1575</getPriceReturn>
    </ns1:getPriceResponse>
  </soapenv:Body>
</soapenv:Envelope>
リスト4 サンプルWebサービスへのSOAP応答メッセージ

 これらのSOAPメッセージをやりとりするWebサービスのインターフェイスは、以下のリストに示したWSDL文書(ただしwsdl:types要素は省略されている)によって記述することができる。今回の解説記事を思い出しながらWSDL文書を概観して、WSDL文書の大まかな構造のイメージをつかんでいただきたい。

<?xml version="1.0" encoding="shift_jis"?>
<wsdl:definitions
targetNamespace="http://unitec-denki.utj.co.jp/schema/2003/"
xmlns:impl="http://unitec-denki.utj.co.jp/schema/2003/"
xmlns:intf="http://unitec-denki.utj.co.jp/schema/2003/"
xmlns:apachesoap="http://xml.apache.org/xml-soap"
xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns="http://schemas.xmlsoap.org/wsdl/">

<wsdl:message name="getPriceRequest">
<wsdl:part name="Code" type="xsd:string"/>
  <wsdl:part name="Value" type="xsd:int"/>
</wsdl:message>

<wsdl:message name="getPriceResponse">
  <wsdl:part name="getPriceReturn" type="xsd:int"/>
</wsdl:message>

<wsdl:portType name="Estimate">
  <wsdl:operation name="getPrice" parameterOrder="Code Value">
    <wsdl:input name="getPriceRequest" message="impl:getPriceRequest"/>
    <wsdl:output name="getPriceResponse"
message="impl:getPriceResponse"/>
  </wsdl:operation>
</wsdl:portType>

<wsdl:binding name="EstimateSoapBinding" type="impl:Estimate">
  <wsdlsoap:binding style="rpc"
transport="http://schemas.xmlsoap.org/soap/http"/>
  <wsdl:operation name="getPrice">
    <wsdlsoap:operation soapAction=""/>
    <wsdl:input name="getPriceRequest">
      <wsdlsoap:body use="encoded"
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="http://unitec-denki.utj.co.jp/schema/2003/"/>
    </wsdl:input>
    <wsdl:output name="getPriceResponse">
      <wsdlsoap:body use="encoded"
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="http://unitec-denki.utj.co.jp/schema/2003/"/>
    </wsdl:output>
  </wsdl:operation>
</wsdl:binding>

<wsdl:service name="EstimateService">
  <wsdl:port name="Estimate" binding="impl:EstimateSoapBinding">
    <wsdlsoap:address
location="http://localhost:8080/axis/services/Estimate"/>
  </wsdl:port>
</wsdl:service>

</wsdl:definitions>
リスト5 サンプルWebサービスのインターフェイスを記述したWSDL文書

連載 Webサービスのキホン(5)
WSDL文書が持つ二層構造の前段部

Webサービスの基軸を構成するのは、SOAP、WSDL、UDDIの3つのテクノロジだ。この連載では、Webサービスの基本的な知識を身に付けるために、この3つのテクノロジの背景と仕様、機能などを分かりやすく解説していく。(編集局)


株式会社日本ユニテック
吉田 稔、青木 秀起
2003/4/24


WSDL文書の構造をおさらい

 前回「WSDL:Webサービスのインターフェイス情報」では、Webサービスのインターフェイス情報を記述するWSDL文書の基本的な構造を紹介した。今回は、WSDL文書の中に含まれる抽象的な定義と具体的な定義との関係や、WSDLの要素間の関係に注目しつつ、WSDL文書を構成するそれぞれの要素を解説していく。

 まずは、前回紹介したWSDL文書の構造をもう一度思い出していただこう。


図1 WSDL文書を構成する主要な要素

図中で箱が3重に表現されている要素は、複数回出現可能なことを表している。wsdl:message要素、wsdl:portType要素、wsdl:operation要素、wsdl:binding要素、wsdl:service要素とwsdl:port要素がそれに該当する

 WSDLの最上位要素はwsdl:definitions要素であるが、その下位要素としてwsdl:types要素、wsdl:message要素、wsdl:portType要素(以上、抽象的な定義)、wsdl:binding要素、wsdl:service要素(以上、具体的な定義)が記述される。今回は抽象的な定義に該当する要素を中心に解説する。

最上位要素(wsdl:definitions要素)

 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>
リスト1 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”を使うことにする。

Webサービス・インターフェイスの抽象的な定義

 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)
リスト2 Webサービスとして公開するgetPriceメソッド

 このメソッドを利用するために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 getPriceメソッドの要求メッセージのWSDL記述

 リスト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 wsdl:types要素の書き方
この例では、wsdl:types要素の中で定義された型「EstimateType」を、wsdl:part要素の中で使用している。型の名前空間接頭辞は「xsd1」

 リスト4を見ると、getPriceRequestというメッセージを構成するgetPriceParmというパートがwsdl:types要素で定義された型(xsd1:EstimaeType)を使用していることが分かるだろう。

 ここまで説明してきたwsdl:types要素、wsdl:message要素、wsdl:part要素の関係を整理すると図2のようになる。


図2 wsdl:types要素、wsdl:message要素、wsdl:part要素の関係
 

メッセージを抽象的に定義する理由

 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のようになる。


図3 wsdl:portType要素の構成

wsdl:message要素の中で定義されたwsdl:part要素は、wsdl:operation要素の中のwsdl:input要素、wsdl:output要素、wsdl:fault要素の定義に利用される

子要素の組み合わせによってさまざまなパターンの操作を表現する

 wsdl:operation要素において、wsdl:input要素やwsdl:output要素の出現の仕方によって、さまざまなパターンの操作を表現できる。

 例えば各支店から本部サーバに月間報告書を送りつけるだけで、何の応答も求めない場合のように、クライアントからWebサービスにメッセージを一方的に送信する「一方向操作」を考えてみよう(図4)。


図4 一方向操作

クライアントからWebサービスに、メッセージを一方的に送信することを想定する

 この場合、クライアントからの入力メッセージを受信するだけの操作となるので、wsdl:operation要素の子としてwsdl:input要素が1個だけ出現するリスト5のような記述になる。

<wsdl:operation name="MonthlyReport">
  <wsdl:input message="impl:MonthlyReport"/>
</wsdl:operation>
リスト5 wsdl:operation要素での一方向操作の記述

 wsdl:input要素のmessage属性には、特定のメッセージ名(前述したwsdl:message要素のname属性値のこと)を指定してメッセージのフォーマットを参照する。リスト5では、「impl:MonthlyReport」というメッセージ名を持つ抽象的なメッセージフォーマットを指定している。

 次に、商品コードと商品数を取引先サーバに渡すと見積価格が返される場合のように、Webサービスがクライアントからのメッセージを受信し、対応する応答メッセージを送信する「要求/応答操作」を考えてみよう(図5)。


図5 要求/応答操作

Webサービスがクライアントからのメッセージを受信し、対応する応答メッセージを送信することを想定する

 応答/要求操作のための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 要求/応答操作の記述

 リスト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では、具体的なプロトコルを別の場所で定義し、それを抽象的な定義にバインドするという仕組みをとっている。

連載 Webサービスのキホン(6)
WSDL文書によるインターフェイス定義の仕上げ

Webサービスの基軸を構成するのは、SOAP、WSDL、UDDIの3つのテクノロジだ。この連載では、Webサービスの基本的な知識を身に付けるために、この3つのテクノロジの背景と仕様、機能などを分かりやすく解説していく。(編集局)


株式会社日本ユニテック
吉田 稔、青木 秀起
2003/5/14


Webサービスインターフェイスの具体的な定義

 WSDL文書によるWebサービスのインターフェイスを定義する方法の解説も3回目となる(前々回「WSDL:Webサービスのインターフェイス情報」、前回「WSDL文書が持つ二層構造の前段部」)。これまで、WSDL文書はWebサービスのインターフェイスを抽象的に定義したうえで、それを具体的なインターフェイスとして定義し直すという構造を持つことを解説してきた。前回はその抽象的な部分を説明したので、今回は後半の具体的な定義を解説する。

 前回、前々回にも登場したWSDL文書の構造図をあらためて示す。今回はこの中で、具体的な定義を行っているwsdl:binding要素と、wsdl:service要素について説明することになる。

図1 WSDL文書を構成する主要な要素
図中で箱が3重に表現されている要素は、複数回出現可能なことを表している。wsdl:message要素、wsdl:portType要素、wsdl:operation要素、wsdl:binding要素、wsdl:service要素とwsdl:port要素がそれに該当する

1)プロトコルにバインドする(wsdl:binding要素)

 抽象的な操作や抽象的なメッセージを、具体的なプロトコルや具体的なメッセージにバインドするために使用する要素がwsdl:binding要素だ。WSDLでは、抽象的なポート定義であるポートタイプに、具体的なプロトコルをバインドした定義のことをバインディング(binding)と呼ぶ

 以下のリスト1で、wsdl:portType要素で定義された抽象的な操作が、どのように具体的に定義し直されるかに注目していただきたい。

リスト1 wsdl:biding要素の例
抽象的な定義に対応する具体的な定義を行うのが、wsdl:binding要素だ

 リスト1にあるとおり、wsdl:binding要素にはname属性とtype属性がある。次で説明するport要素で参照できるよう、name属性にバインディング名を指定しておく。またwsdl:type属性は、どのポートタイプをバインドするかを指定する。リスト1では「impl:Estimate」というポートタイプ名を指定して、参照するportType要素を指定している。

 ここまでで説明したwsdl:binding要素、wsdl:portType要素、およびそれらの子孫要素の関係を整理すると図2のようになる。

図2 wsdl:binding要素とwsdl:portType要素の関係
wsdl:portType要素によって抽象的に定義された内容が、wsdl:binding要素によって具体的な操作へと再定義される

(2)具体的なポートを定義する(wsdl:port要素)

 ネットワーク上に存在するWebサービスにアクセスするには、そのネットワークアドレスを知る必要がある。wsdl:port要素は、wsdl:binding要素によって具体的なプロトコルやフォーマットに関連付けられたバインディングに、具体的なネットワークアドレスを関連付ける要素だ。

<wsdl:binding name="EstimateSoapBinding" type="impl:Estimate">
  ……
</wsdl:binding>

<wsdl:service name="EstimateService">
  <wsdl:documentation>これは見積サービスです。</wsdl:documentation>
  <wsdl:port binding="impl:EstimateSoapBinding" ←バインディングの参照
     name="Estimate"> ←ポート名
    <wsdlsoap:address
      location="http://localhost:8080/axis/services/Estimate"/>
             ↑ポートのネットワークアドレスの指定
</wsdl:port>
</wsdl:service>
リスト2 wsdl:port要素の書き方

 wsdl:port要素には、binding属性とname属性という2つの属性がある。binding属性には、wsdl:binding要素で定義されたバインディングのうち使用するバインディングの名前を指定し、name属性にはポート名を指定する。リスト2では、binding属性に「impl:EstimateSoapBinding」というバインディング名を指定してバインディングを参照している。

(3)サービスを定義する(wsdl:service要素)

 wsdl:port要素をグループ化して1つのサービスを定義するのがwsdl:service要素だ。以下のリスト3を見ていただきたい。

<wsdl:service name="Estimate"> ←サービス名「Estimate」を定義
  <wsdl:port ……>
    ……
  </wsdl:port>
</wsdl:service>
リスト3 wsdl:service要素の書き方
wsdl:service要素の中でサービス名「Estimate」を定義している

 wsdl:service要素のname属性にはサービス名を指定する。リスト3では「Estimate」というサービス名を指定している。wsdl:service要素は、子要素としてwsdl:port要素を0個以上持つ。つまり、同一サービス内に複数のポートを定義できる。具体的な例として、同じ内容のサービスを、HTTP/SOAPやHTTP GET/POSTやSMTP/SOAPなど、複数のプロトコルを使ってアクセスできるようにする場合が考えられる。また、ネットワークアドレスの異なる複数のサーバに負荷分散させる場合にも複数のポートをサーバの数だけ定義することになる。

 ここまでで説明したwsdl:port要素、wsdl:service要素、wsdl:binding要素の関係を整理すると図3のようになる。

図3 wsdl:service要素、wsdl:port要素、wsdl:binding要素の関係 


図4 抽象的な定義と具体的な定義の分離 

 この連載の第4回「WSDL:Webサービスのインターフェイス情報」で、WSDLは抽象的な定義と具体的な定義の2段構えでインターフェイスを記述すると述べた。図4で示すとおり、WSDLは再利用できる抽象的な定義と再利用できない具体的な定義とを分離して定義し、それらをバインドさせるという仕組みになっていることが分るだろう。

WSDL拡張性要素を使ってプロトコル依存の定義を行う

 第4回でも述べたが、抽象的な定義と具体的なプロトコルに依存する定義の橋渡しをするため、WSDLでは、WSDLの中核となる要素に加えて特定のプロトコルに依存する拡張性要素を使用する。WSDL仕様では、以下の3つのプロトコルについて拡張性要素を定めている。

SOAP 1.1
HTTP 1.1 GET/POST
MIME

 例として、SOAP 1.1にバインドするための拡張性要素(以下、SOAP拡張性要素と呼ぶことにする)を取り上げてみよう。抽象的に定義した操作をSOAP 1.1プロトコルにバインドする際、SOAP拡張性要素を使用して定義する主な情報は以下のとおりだ。

(1) SOAP 1.1プロトコルへのバインドの宣言(wsdlsoap:binding要素)
  wsdlsoap:binding要素は、SOAPプロトコルにバインドする。wsdlsoap:binding要素は、操作が「RPC指向」なのか「文書指向」なのかを指定する。「RPC指向の操作」とは、入力メッセージがパラメータで構成され、出力メッセージがその戻り値となるRPCを実現する操作のことだ。「文書指向の操作」とは、例えばXML形式で記述された発注書のようなXML文書をそのままやり取りする 操作のことだ。さらにwsdlsoap:binding要素ではSOAPメッセージがどんなプロトコルで送受信されるのかも指定する。
(2) 操作全体の定義(wsdlsoap:operation要素)
  soap:operation要素は、操作全体に関する情報を定義する。HTTPヘッダに含めるSOAPActionの値はこの要素で指定する。
(3) SOAPメッセージ本体の内容指定(wsdlsoap:body要素)
 

wsdlsoap:body要素は、SOAPメッセージ本体であるsoapenv:body要素内をどのように組み立てるか、を指定する。組み立た方は2種類ある。wsd:part要素で指定されたスキーマ定義のまま組み立てる「リテラル形式」と、SOAPエンコーディング規則のような特定のエンコーディングルールにしたがって組み立てる「エンコー ド形式」の2種類だ。

(4) 通信ポートのアドレス指定(wsdlsoap:address要素)
  wsdlsoap:adress要素は、wsdl:port要素の子として出現して具体的なネットワークアドレスを指定する。

 これら以外にも、エラー情報の内容やSOAPヘッダの内容を指定するSOAP拡張性要素が用意されている。SOAP拡張性要素がwsdl:binding要素で出現しているところを以下のリスト4に示し、簡単に解説する。

<wsdl:binding
  name="EstimateSoapBinding" type="impl:Estimate">
  <wsdlsoap:binding style="rpc"  ←SOAP 1.1にバインドする宣言
    transport="http://schemas.xmlsoap.org/soap/http"/>
  <wsdl:operation name="GetPrice">
    <wsdlsoap:operation soapAction=""/>   ←操作全体の定義
    <wsdl:input>
      <wsdlsoap:body namespace=    ←SOAPメッセージ本体の内容指定
         "http://unitec-denki.utj.co.jp/schema/2003/"
         use="encoded" encodingStyle=
         "http://schemas.xmlsoap.org/soap/encoding/" />
    </wsdl:input>
    <wsdl:output>
      <wsdlsoap:body namespace=    ←SOAPメッセージ本体の内容指定
         "http://unitec-denki.utj.co.jp/schema/2003/"
         use="encode" encodingStyle=
         "http://schemas.xmlsoap.org/soap/encoding/" />
    </wsdl:output>
  </wsdl:operation>
</wsdl:binding>
リスト4 wsdl:binding要素の子孫として出現するSOAP拡張性要素の例

 リスト4では、wsdlsoap:binding要素のstyle属性に「rpc」が指定されているので、操作方式は「RPC指向」になる。またtransport属性に「http://schemas.xmlsoap.org/soap/http」というURI値が指定されており、プロトコルとしてHTTPを使用することを意味している。
またwsdlsoap:body要素のuse属性に「encoded」が指定され、encodingStyle属性にSOAPエンコーディング規則を意味するURI値「http://schemas.xmlsoap.org/soap/encoding/」が指定されている。これは、この入出力メッセージはいずれもSOAPエンコーディング規則に従ってエンコードされることを意味している。

WSDLのその他の要素・属性

 この記事で説明した要素以外にもWSDLには、ほかのWSDL文書の定義を取り込むwsdl:import要素や、注釈を記述するwsdl:documentaion要素など、実用上重要な要素がいくつかある。それらについては、参考書籍などを読んで勉強されることをお勧めする。

WSDLの今後と課題

 現在一般に実装されているWSDLバージョン1.1の仕様にはあいまいな記述が存在しており、仕様の解釈の違いが原因で、異なる開発ツールで開発したWebサービスが相互に接続できないという事態が生じてしまった。例えば、wsdl:types要素、wsdl:message要素、wsdl:portType要素の出現順序が明確でなかったり、SOAPをXML Schemaで表現する方法があいまい、などの点がある。

 W3CはWSDLの改訂作業を進めており、これまでのあいまいさを排する努力がなされている。ただし、進められているWSDL 1.2は本稿執筆段階ではドラフトレベルにすぎず、まだ確定していない。W3CはWSDL 1.2で以下の点を改善すると表明している。

 WSDLだけの話ではないが、WSDLやSOAPやUDDIといったWebサービスのコア技術の仕様が確定するまでの間、相互運用性の問題が生じないようにするために主要ベンダも積極的に努力している。2002年2月、BEAシステムズ、IBM、インテル、およびマイクロソフトは、Webサービスの相互運用性を促進させるため業界団体WS-I(Web Services Interoperability Organization)を設立させた。発足当初は参加していなかった大手ベンダのサン・マイクロシステムズも加盟し、主要ベンダの力が結集されつつある。

 WS-Iは、Webサービス仕様の利用方法の規約策定やテストツールの開発によって、相互運用性を確保する活動を進めている。WS-Iが2003年2月に公開した「Basic Profile Version 1.0」(本稿執筆時点でワーキンググループ承認ドラフト)は、SOAP、WSDL、UDDIの仕様の解釈を明確に規定している(参考記事「WS-I:Webサービス互換性の実現に向けて」)。

 W3CもWS-Iも相互運用性確保を目指しており、これらの仕様が確定すれば、Webサービスの実用化に向けてまた一歩大きく前進することだろう。

 WSDLによる記述があれば、開発ツールやシステムがそれを読み込んでWebサービスのインターフェイスを実装するコードを自動的に生成できる。Webサービスを構築する技術者は生成されたWSDL文書をカスタマイズすればよく、効率的な開発が可能になる。またWebサービスを利用するユーザにとってもアクセスのための負担が減り、WSDLは重要な技術といえる。

 WSDLはインターネットに公開されたアプリケーションを動的に発見・統合するという将来的なWebサービスの理想の実現に欠かすことができない技術だ。WSDLの基本的知識は、Webサービスの開発を行なう技術者の常識になってゆくことだろう。


連載 Webサービスのキホン(最終回)
Webサービスを発見する仕組み〜UDDI

Webサービスの基軸を構成するのは、SOAP、WSDL、UDDIの3つのテクノロジだ。この連載では、Webサービスの基本的な知識を身に付けるために、この3つのテクノロジの背景と仕様、機能などを分かりやすく解説していく。(編集局)


株式会社日本ユニテック
吉田 稔、青木 秀起
2003/6/12


Webサービスを登録し発見する

 ネットワーク上に数多く存在するWebサービスを効果的に利用するためには、Webサービスを登録・公開し、検索する仕組みが必要になる。この、標準的手法を提供する仕様が、今回のテーマであるUDDI(Universal Description, Discovery and Integration)だ。

 UDDIは、Yahoo!やGoogleなどの検索サイトと似ている。検索サイトは入力されたキーワードから目的のWebサイトを検索するようになっている。一方、UDDIには企業名、サービス内容、識別コード、分類などがあらかじめ登録されており、これらのデータによって精度の高い検索を行える。また、前述の検索サイトがHTMLによる結果を返してくるのに対し、UDDIはコンピュータが処理しやすいXMLをベースに入出力を実現しているため、コンピュータによる問い合わせ、結果分析を自動化することが容易だ。

データベースとなるUDDIレジストリ

 UDDIには、Webサービス提供者の企業名や連絡先、その企業が提供しているサービスの内容などさまざまな情報を登録しておく必要がある。その情報を登録する場所を「UDDIレジストリ」という。

図1 UDDIレジストリ

 図1のように、Webサービス提供者は事前にWebサービスについての情報をUDDIレジストリに登録しておく。Webサービス利用者はUDDIレジストリを検索し、目的のWebサービスを発見する。そして、その参照先に接続し、サービスについて記述されたWSDL文書を取得し、サービスのやりとりが始まる。UDDIレジストリとやりとりするためのAPIも用意されており、それらは「UDDIプログラマAPI」と呼ばれる。

 通常の企業間取引では、まず条件に合う企業を探し、その企業との間で何度もやりとりを重ねた上でやっとサービスが始まる。しかしUDDIでは、WSDL文書の取得によって技術的な情報も取得でき、検索からサービスの開始まで自動的に処理されるというわけだ。

 UDDIは、バージョン1.0が2000年9月に公開され、標準化団体UDDI.orgによって検討が進められてきた。バージョン3.0が2002年7月にOASISに提出され、UDDI.orgもOASISの傘下に入って現在も開発が進められている。本稿は、現時点で最新のUDDIバージョン3.0に基づいて解説することにする。

UDDIレジストリがないとWebサービスを使えない?

 本連載第1回「Webサービスの主役、SOAP誕生の背景」で、Webサービスを「インターネットの標準技術を使ってネットワーク上に分散したアプリケーションを連携させる技術」と定義した。この意味でのWebサービスを使って、社内や取引実績のある企業など、接続相手があらかじめ特定されているアプリケーション同士を連携させることも少なくない。この場合、接続先に対してあらためてWebサービスを公開したり発見してもらったりする必要はなく、接続先に直接接続すればよい。つまり、社内や取引先など、すでに密接なつながりのある組織とWebサービスを使って接続するのであれば、UDDIレジストリを使わずに、SOAPとWSDLを静的に連携させればWebサービスを利用できる。

 一方、社内や業界団体内などでも、あらかじめ決められたメンバーだけが利用できるようにWebサービスを公開したい場合がある。こうした目的で、アクセス権のあるユーザーだけが登録・検索を行えるUDDIレジストリを「プライベートUDDI」という。プライベートUDDIは、登録されるWebサービスをその運営者が審査し、その質や信用度を保持できるというメリットがある。

 利用者が限定されているプライベートUDDIに対し、Webサービスの登録や検索が誰にでも許可されているUDDIレジストリを「パブリックUDDI」(あるいは「UDDIビジネスレジストリ(UBR)」)という。例えば、マイクロソフトが運営するMicrosoft UDDI Business Registry、IBMのUDDI Business Registry、SAPのSAP UDDI Business Registry、そして日本のNTTコミュニケーションズによるUDDIビジネスレジトリなどが公開されている。パブリックUDDIはだれでもアクセスできるため、セキュリティを確保し、登録されるWebサービスを評価する仕組みが必要になる。

 パブリックUDDIには評価やセキュリティの課題があるため、近年はプライベートUDDIの重要性が高まっている。UDDIバージョン3.0では、「ルートレジストリ」という概念が新たに取り入れられ、パブリックUDDIを「ルートレジストリ」ととらえて、プライベートUDDIと連携するシナリオが紹介されている。パブリックUDDIを「ルート」、プライベートUDDIを「アフィリエイト(affiliate)」とする階層構造を形成し、レジストリ間でデータを共有できるような仕組みを提供している。

UDDIのデータ構造

 ここからは、UDDIレジストリにはどのようなデータが登録されているのか、それをどのように操作するのか、について紹介していく。

 UDDIレジストリに登録されているデータは、大きく分けて5つの要素を持つ。

図2 UDDIレジストリのデータ構造
要素名 解 説
businessEntity 企業名、連絡先、業種分類コードなどのビジネス情報
businessService その企業が提供しているサービスの名前、内容、分類などのサービス情報
bindingTemplate Webサービスへのアクセスポイントやパラメータなどの技術情報
tModel Webサービスの仕様やWSDL文書のURLなどのサービス仕様
publisherAssertion 企業間の関係
表1 UDDIレジストリで使われる要素

 UDDIレジストリには、基本的には情報は企業単位に登録され、その企業についての情報を記述するbusinessEntity要素が最上位要素になる。

 businessEntity要素は、Webサービスについての情報を記述するbusinessService要素を子要素に、Webサービスの技術的な情報を記述するbindingTemplate要素を孫要素に持つ。

 businessEntity要素とbusinessService要素は、お互いを一意に特定するためのbusinessKey属性により結び付けられており、businessService要素とbindingTemplate要素はお互いを一意に特定するためのserviceKey属性により結び付けられている。また、1つの企業が複数のサービスを提供していることもあるため、1つのbusinessEntity要素が複数のbusinessService要素を持つことも可能だ。

 これら親子関係にある3つの要素とは独立した構造として、tModel要素とpublisherAssertion要素が存在する。tModel要素は“technical Model”の略で、“技術情報”という意味だ。businessEntity以下の要素とは独立した構造になっており、bindingTemplate要素から参照されるようになっている。

 tModelには、サービスのタイプ、つまりプロトコルやメッセージ配信方法などが記述される。ユーザーとサービス提供者の「サービスのタイプ」が同じであればやりとりすることができるというわけだ。ただし、tModelでサービスのタイプが詳細に記述されるわけではなく、その仕様書のURLが記述されているにすぎない。UDDIでは、多種多様なサービス・タイプを記述する文書を参照するという方式を取っている。

 publisherAssertion要素は、fromKey要素やtoKey要素によって企業間の関係を表現する。この情報によりユーザーは目的の企業だけでなくその関連企業を見つけることができる。例えば旅行会社を検索するときには、JTBとJTB関連の信頼できる関連企業を見つけられる。UDDIは企業の信頼性を保証する仕組みは持たないが、信頼できる企業の関連企業の中から目的の企業を選ぶ、といったことができる。また関連企業間でサービスを共有することで、単独で登録するよりも高いヒットを獲得することができるという、サービス提供者側のメリットも表現できる。

 また、businessEntity要素、businessService要素、tModel要素の下位のkeyedReference要素には、製品分類や産業分類などの分類情報や識別情報が格納されており、分類や識別コードによる検索が可能だ。このように、UDDIはXML形式で構造化されて登録されており、企業名、サービス内容、分類情報、識別コードなど複数の検索項目を指定して精度の高い検索を行える。

UDDIに格納されるデータの実際

 簡単にそれぞれの要素のデータを紹介しよう(本稿のリストでは、簡略化のため名前空間を一部省略してある)。

<?xml version="1.0" encoding="utf-8" ?>
<businessEntity businessKey="企業のID">
  <discoverURLs>
    <discoveryURL useType="businessEntity">
      ……企業情報の取得先URL……
    </discoveryURL>
  </discoverURLs>
  <name>企業名</name>
  <description>企業についての説明</description>
  <contact>
    ……連絡先情報……
  </contact>
  <businessServices>
    ……
  </businessServices>
</businessEntity>
リスト1 businessEntityデータ
businessEntity要素には、企業名、企業についての説明、連絡先などのビジネス情報が格納される

 businessEntity要素の子要素に、businessServices要素がある。

<businessServices>
  <businessService serviceKey="サービスのID" businessKey="企業のID">
    <name>サービスの名前</name>
    <description>サービスについての説明</description>
    <bindingTemplates>
      ……
    </bindingTemplates>
  </businessService>
  <businessService serviceKey="サービスのID" businessKey="企業のID">
    ……
  </businessService>
  ……
</businessServices>
リスト2 businessServiceデータ
businessService要素には、サービス名やサービスについての説明などの情報が格納される。1つの企業が複数のサービスを提供する場合もあり、businessServices要素は複数のbusinessService要素を子要素として持つことができる

 businessServices要素の子要素にbindingTemplate要素がある。

<bindingTemplates>
  <bindingTemplate serviceKey="サービスのID" bindingKey="技術情報のID">
    <description>技術情報の説明</description>
    <accsessPoint>サービスに接続するためのアドレス</accsessPoint>
    <tModelInstanceDetails>
      <tModelInstanceInfo tModelKey="tModelのID">
        ……
      </tModelInstanceInfo>
    </tModelInstanceDetails>
  </bindingTemplate>
</bindingTemplates>
リスト3 bindingTemplateデータ
bindingTemplate要素には、サービスに接続するためのアドレス、そのタイプ、説明などの技術情報が格納される。

 bindingTemplateデータの中のtModelInstanceDetails要素の部分に、下記のようなtModel要素への参照情報が組み込まれる。

<tModel tModelKey="tModelのID">
  <name>サービスの名前</name>
  <description>サービスについての説明</description>
  <overviewDoc>
    <description>Webサービスの仕様についての説明</description>
    <overviewURL>Webサービスの仕様のURL</overviewURL>
  </overviewDoc>
  ……
</tModel>
リスト4 tModelデータ
tModel要素には、Webサービスのタイプや仕様のURLなどの技術情報が格納される

 publisherAssertion要素では、fromKey要素とtoKey要素を使って2社の関係を定義する。

<publisherAssertion>
  <fromKey>A社のビジネスキー</fromKey>
  <toKey>B社のビジネスキー</toKey>
  <keyedReference keyName="2社の関係を示すキー名" keyValue="2社の関係"
tModelKey="関連付けシステム"/>
</publisherAssertion>
リスト5 publisherAssertionデータ

UDDIレジストリを利用するためのAPI

 UDDIには、UDDIプログラマAPIと呼ばれるAPIが用意されており、SOAPメッセージの本体 (body)部分に組み込まれてやりとりされる。UDDIプログラマAPIは以下の6つからなる。

発行API
情報の登録や削除、ユーザー認証を行うためのAPI
照会API
情報の検索や詳細情報の取得を行うためのAPI
セキュリティ方針API
認証トークンに関するAPI
管理および所有権移動API
businessEntityまたはtModel構造の管理や所有権を移動するためのAPI
サブスクリプションAPI
UDDIレジストリに加えられた変更を通知するためのAPI
値セットAPI
登録時に呼び出されたkeyedReferenceの値が有効かどうかを検査するためのAPI

 それぞれのAPIセットには以下のAPIが含まれている。

発行API find_binding 登録されたbusinessServiceのバインディングを検索する
find_business ビジネスを検索する
find_relatedBusinesses 特定のビジネスエンティティに関連付けられたbusinessEntityについての情報を検索する
find_service 登録されたビジネスエンティティ内の特定のサービスを検索する
find_tModel tModel 構造を検索する
get_bindingDetail サービス要求をするためのbindingTemplate情報を取得する
get_businessDetail ビジネスや企業のbusinessEntity情報を取得する
get_operationalInfo レジストリ内のエンティティについての操作情報を取得する
get_serviceDetail 登録されたbusinessServiceデータの情報を取得する
get_tModelDetail 登録されたtModelデータの情報を取得する
照会API add_publisherAssertions 関係についてのアサーションを既存のアサーションに追加する
delete_binding 既存のbindingTemplateをレジストリから削除する
delete_business business エンティティ情報をレジストリから削除する
delete_publisherAssertions 特定の発行者のアサーションを削除する
delete_service 既存のbusinessServiceをレジストリから削除する
delete_tModel tModelについての情報を隠ぺいする
get_assertionStatusReport ステータスレポートを取得する
get_publisherAssertions 発行者アサーションのリストを取得する
get_registeredInfo 特定の発行者により登録されているビジネスとtModelの簡略リストを要求する
save_binding 新しいbindingTemplate情報を登録するか、既存のbindingTemplate情報を更新する
save_business 新しいbusinessEntity情報を登録するか、既存のbusinessEntity情報を更新する
save_service businessServiceについての完全な情報を登録または更新する
save_tModel tModelについての情報を登録または更新する
set_publisherAssertions 発行者アサーションの全セットを登録する
セキュリティ方針API discard_authToken 以前に取得した認証トークンが無効であるノードを通知する
get_authToken UDDIノードから認証トークンを取得する
管理および所有権移動API get_transferToken businessEntityまたはtModelエンティティ管理の移動を開始する
transfer_entities 管理を実際に移動するためのAPI
transfer_custody transfer_entitiesの応答として、管理移動操作時にターゲットノードによって呼び出されるAPI
サブスクリプションAPI save_subscription 新しいサブスクリプションを作成するか、既存のサブスクリプションを変更する
delete_subscription 指定されたサブスクリプションを取り消す
get_subscriptions 既存のサブスクリプションのリストを返す
get_subscriptionResults 指定された期間内に特定のサブスクリプションについてのレジストリデータを返す
notify_subscriptionListener サブスクリプション・リスナ・サービスとしてクライアントが実装するAPI
値セットAPI validate_values 値セット検証Webサービスの外部プロバイダが評価するために使用されるAPI
get_allValidValues 有効な値セットを取得するためのAPI
表2 UDDIプログラマAPI

 このように、「UDDIプログラマAPI」を使って、ユーザー認証、登録、検索、情報取得などを行うことができる。例えば、ユーザー認証を行う際にUDDIビジネスレジストリに送信するデータは次のようになる。

<?xml version='1.0' encoding='UTF-8'?>
<soapenv:Envelope ……>
  <soapenv:Body>
    <get_authToken cred="test" generic="2.0" userID="uddi-test@utj.co.jp"
xmlns="urn:uddi-org:api_v2"/>
  </soapenv:Body>
</soapenv:Envelope>
リスト6 ユーザー認証時の送信データ
soapenv:Body要素の内容に、UDDIプログラマAPIが記述されている

 7回にわたって、Webサービスを支える3つの中核技術、SOAP、WSDL、UDDIについて解説してきた。Webサービスは、これら3つの技術を柱に、メッセージ技術やセキュリティ技術などさまざまな技術を包含する複合的な技術だ。まだ策定中の仕様も多いとはいえ、いまやWebサービスは現実のソリューションとして実装されつつある。仕様の標準化や実装動向に注目するとともに、仕様策定の背景や基本的な考えをしっかりと理解しておきたい。