Articles
第1回:WSDL in depth
- INDEX -
J2EEシステムを構築する上で使うことのできるテクノロジは、次々に新しいものが登場してきます。ですが、こういった新しいテクノロジを実際のシステムでうまく使いこなしていくのは、なかなか大変なのではないでしょうか?本連載では、J2EEシステムを構築する上で今後注目すべきトピック、書籍や記事では意外に載っていないポイントなどを取り上げていきます。本連載が、皆さんが新しいテクノロジを使いこなす上でのヒントになれば幸いです。
さて、今回からしばらくは、WebサービスやBPELなど、ビジネス・プロセスを実装するためのテクノロジを取り上げていこうと思います。最初に取り上げるのは、後ほど取り上げるBPELでも重要な役割を担っている「WSDL」です。
「WebサービスはSOAP、WSDL、UDDIという3つの基本技術からなっており…」といった、Webサービス概要の説明は、皆さんはすでに何度となく見聞きしていることと思います。その詳細をここで繰り返すことはやめて、そのポイントだけを簡単に見ていくことにしましょう。
「Webサービス」とは「Web」を使った「サービス」です…といっても何のことだかよく分かりませんね。Webを使うといっても、人間がWebブラウザを使ってWebサーバからHTMLを取得するWWW、という意味ではありません。Webサービスでは、WWWで使われているHTTPプロトコルを使い、HTMLの代わりにXML形式のメッセージ形式でクライアントとサーバが通信をすることをイメージしてください。クライアントは、人間が操作するWebブラウザではなく、通常何らかのプログラムになります。 (細かいことをいえば、WebサービスのプロトコルはHTTPに限定されているのではありません。SMTPやメッセージ・キューイングなど他のプロトコル上で実装することも可能です。また、上記のような同期型(リクエスト/レスポンス)だけではなく、一方向の非同期型のスタイルなども可能です。ですので、上記の説明はあくまで第一義的な説明に過ぎません。) 「Webサービス」の「サービス」の意味は、プログラムの機能を提供する単位だと考えればいいでしょう。上記のようにクライアントとサービスは異なる場所に位置する異機種分散環境ですので、サービスの粒度を小さくしていくと、CORBAのような分散オブジェクトに近づいていきます。ですが、「サービス」という用語からも推測できるように、Webサービスの粒度は、オブジェクトのメソッドのレベルよりはむしろ、ある程度粒度の大きな機能にするべきでしょう。 さて、Webサービスを支える3つの要素技術であるSOAP、WSDL、UDDIについても、簡単に見ていきます。 SOAPは、Webサービスのためのメッセージ構造や、(HTTPなどのプロトコルの上位に位置する)通信プロトコルを定義しています。上の図で、Webサービスとそのクライアントとの間でやり取りされているのがSOAPメッセージにあたります。 2003年6月にSOAP 1.2がW3C勧告になっていますが、最も広く使われているのはまだまだSOAP 1.1です。本連載でも、SOAP 1.1を想定しています。 SOAP 1.1までは SOAPは "Simple Object Access Protocol" の略でしたが、SOAP 1.2からは何の略語でもなく単に "SOAP" と呼ばれることになりました。これは、上述した分散オブジェクトに関連した誤用を避けるためかもしれません。最近では、流行りのSOAに掛けて、"Service-Oriented Architecture Protocol" の略語とされている場合もあります。 WSDL(Web Services Description Language)は、Webサービスのインターフェイスを記述するための言語です。WSDLについては、後ほど詳しく見ていきます。WSDL 2.0はW3CのWorking Draftの段階であり、最も広く使われているのはWSDL 1.1です。本連載でも、WSDL 1.1をベースに説明していきます。 UDDI(Universal Description, Discovery and Integration)は、Webサービスを公開し発見するための技術です。UDDIレジストリに、Webサービス・プロバイダ(提供者) の企業情報や各Webサービス自体の内容などを登録しておきます。一方、Webサービスを利用するコンシューマ(クライアント)は、UDDIレジストリを検索することによって、未知のWebサービスを動的に利用できるようになります。UDDIレジストリには、閉じた環境で使用するプライベートUDDIと、誰もが利用可能なパブリックUDDIがあります。 パブリックUDDIでは結果として未知の相手と通信することになるため、エンタープライズシステムで使うには、信用調査やセキュリティの面で何かと課題が多いのが現状でしょう。UDDIは、現時点ではそれほど使われているわけではありません。
UDDIを使うにせよ使わないにせよ、クライアントがSOAPを使ってWebサービスを使うためには、SOAPリクエスト/レスポンスに必要なデータ構造、下位プロトコルの指定、(HTTPの場合)エンドポイント(接続先のWebサービス)のURLなど、数多くの情報が必要となります。そういった情報を定義するのがWSDLの役割です。
Webサービスを公開するプロバイダは、何らかの形でWSDLをクライアントが入手できるようにしておく必要があります。エンドポイントURLの末尾に"?WSDL"を追加したURLでWSDLをダウンロードできるWebサービス実装が多いようです。 クライアントは、事前、つまりクライアント・プログラム作成時にWSDLを入手し、特定のWebサービスにアクセスできます。また、UDDI利用時など、接続先のWebサービスが事前に分からない場合は、実行時により動的にWebサービスにアクセスできるようなWebサービス実装もあります。
J2EE 1.3までは、J2EEシステムでWebサービスやそのクライアントを構築するための標準がありませんでした。そのため、Apache SOAPなどのオープンソースWebサービス実装、商用Webサービス実装、J2EEアプリケーション・サーバなどを使う必要がありました。
そこで、J2EE 1.4では新たにJAX-RPC(Java API for XML-Based RPC) 1.1仕様が追加されました。JAX-RPCは、Java/J2EE環境からXMLベースのRPC(Remote Procedure Call)をサポートするための標準仕様です。JAX-RPC自体は任意のXMLベースRPCメカニズムをサポートできるよう設計されており、JAX-RPC 1.1はSOAP 1.1のサポートを必須としています。つまり、JAX-RPCによって、J2EEシステムで(SOAPを使った)Webサービスのための標準APIが提供された、というわけです。 JAX-RPCでは、WSDL/XMLとJavaとの間のマッピング、コアAPI、クライアント側のプログラミング・モデル、サービス・エンドポイント・モデル(Webサービス側のモデル)などが定義されています。 オープンソースWebサービス実装や商用Webサービス実装も、JAX-RPCをサポートしつつあります。たとえば、Apache SOAPの後継であるApache AxisもJAX-RPCをサポートしています。J2EEアプリケーション・サーバも、J2EE 1.4準拠に伴い必然的にJAX-RPCをサポートすることになります。
それでは、今回サンプルとして使うWSDLを見てみることにしましょう。
このWSDLは、以下のツールと実行環境を用いて作成したものです。
今回は、数字の足し算、引き算ができる計算機を表すJavaクラスを作成し、JAX-RPC Web Services for Oracle JDeveloper 10gを用いて、このJavaクラスをJAX-RPC準拠のWebサービスとして公開しました。インストール手順や開発作業の詳細は解説しませんが、興味のある方は是非チャレンジしてみてください。 まず、Webサービスのインタフェースを定義するJavaインタフェース calc.Calcを作成します。JAX-RPCでは、このインタフェースはjava.rmi.Remoteをextendsし、各メソッドのthrows節はjava.rmi.RemoteExceptionを宣言する必要があります。
次に、このJavaインタフェースをimplementsして、Webサービスの実装コードを含むJavaクラス calc.CalcImplを作成します。
JAX-RPC Web Services for Oracle JDeveloper 10gのウィザードを用いて、calc.CalcImplクラスからJAX-RPC Webサービスを作成します。これにより、標準デプロイメント・ディスクリプタ(web.xml、webservices.xml)、OC4J固有デプロイメント・ディスクリプタ(oracle-webservices.xml)、WSDL CalcService.wsdlなどが自動生成されます。 次のWSDLが、生成されたCalcService.wsdlです(インデントなど一部修正を加えています)。
JAX-RPCサービスを含むWARファイル(J2EE Webアプリケーション)をJDeveloperからOC4Jにデプロイします。WebブラウザでエンドポイントURLにアクセスすると、WSDLやサービスのテストページへのリンクを含むWebページにアクセスできます。 JAX-RPC Web Services for Oracle JDeveloper 10gを使えば、Webサービス・クライアントの開発を容易にするスタブ・クラスも自動生成できます。calc.clientパッケージにスタブ・クラス CalcService_Implや関連するクラス群を自動生成した場合、以下のようなシンプルなコードでWebサービスにアクセスできます。
JDeveloperには、SOAPメッセージをキャプチャするためのツール TCPパケット・モニタも統合されており、簡単にSOAPメッセージを確認できます。 Javaアプリケーション calc.client.CalcClientを実行し、TCPパケット・モニタでキャプチャしたSOAPリクエストとSOAPレスポンスは、次の通りです(インデント、HTTPヘッダを一部修正/削除しています)。
WSDLは、Javaと対比しながら考えると非常に分かりやすい作りになっています。
多くのWebサービス実装は、次の機能を持ったユーティリティを提供しています(JAX-RPC Web Services for Oracle JDeveloper 10gも、もちろんすべての機能をサポートしています)。
WSDLは、Javaと対比しながら考えることで、こういったユーティリティでどのようなものが自動生成されるかについても、理解が進むはずです。
サービス実装からWSDLを生成することも、WSDLからサービス実装の雛型を生成することもできるということは、サービス実装を開発するアプローチにも2種類あることが容易に推測できるでしょう。
1つは、まずWSDLを作成し(あるいは既存のWSDLを採用し)、それに対応するサービス実装を作成していくアプローチです。これを「トップダウン・アプローチ」と呼びます。 もう1つは、まずサービス実装を作成し(あるいは既存のコードを使い)、それに対応するWSDLを自動生成するアプローチです。これを「ボトムアップ・アプローチ」と呼びます。今回取り上げるサンプルのWSDLは、このボトムアップ・アプローチで生成していることになりますね。
WSDLは、登場する要素数が多くXML名前空間も多用されており、一見複雑に見えますね。まずは、WSDLの全体的な構造を概観してみましょう。次のWSDLは、上に挙げたWSDLの主要な構造を抜き出したものです。
WSDLに登場する主な要素の概要は、次の通りです。
<operation>要素は<message>要素を参照していますし、他の要素も同様にそれぞれ参照する要素を持っています。WSDLの中で上方に位置する要素を、より下方に位置する要素が利用していることが分かりますね。 また、WSDLの前半には抽象的な定義、後半には具体的な定義がある点にも注意しましょう。データ型(<types>要素)は、当然抽象的なものですね。メッセージ(<message>要素)、オペレーション(<operation>要素)、ポートタイプ(<portType>要素)には、具体的なプロトコルやネットワークアドレスが含まれているわけではないので、これらもまた抽象的です。一方、バインディング(<binding>要素)やポート(<port>要素)には、プロトコルやネットワークアドレスが含まれており、具体的な情報を定義していることになります。抽象的な定義と具体的な定義を明確に分離しておくことによって、抽象的な定義の再利用が容易になります。 次の図は、WSDLの全体的な構造を表したものです。"....."で示されたボックスは、要素が複数回登場し得ることを示しています。赤い線は、要素の依存関係を示しています。線の始点で定義された情報が、線の終点で使用されている、という意味です。次のセクションからWSDLの各要素を見ていきますが、適宜この図を参照し、「木を見て森を見ない」状態に陥らないようにしましょう。
メッセージは、入出力メッセージなどのメッセージの型を定義します。パートは、1つのメッセージに含まれる複数の論理的単位を表します。
ここでは、Calc_addメッセージがint_1、int_2という2つのパートからなっていることを定義しています。また、両パートのデータ型がXML Schemaのint型であることもを定義しています。 次に、<types>要素を見てみましょう。上記のWSDLでは、<types>要素は空要素になっているので、別の<types>要素の例を挙げておきましょう。
<types>要素では、XML Schemaを使って、add型、addResponse型、<addElement>要素、<addResponseElement>要素を定義しています。一方、<message>要素のelement属性では、<types>要素で定義した要素を参照していることが分かりますね。 今回は<message>要素でtype属性の代わりにelement属性が使われていますが、これに関しては次回詳しく取り上げようと思いますので、今のところはあまり気にしないでください。
<operation>要素は、入力メッセージを表す<input>要素、出力メッセージを表す<output>要素と含んでいます。この例には登場していませんが、エラー時の出力を表す<fault>要素も存在します。それぞれmessage属性で、<message>要素を参照していることが分かりますね。
このWSDLともとのJavaインタフェースを比較すれば、次のような対応関係があることが分かるでしょう。
実は、オペレーションは常に<input>要素、<output>要素、(オプションの)<fault>要素を持つわけではありません。WSDLでは、いくつかのMEP(Message Exchange Pattern: メッセージ交換パターン)が定義されており、上記の例はリクエスト/レスポンス・パターンに相当します。これについても、次回さらに詳しく取り上げる予定です。
ここまでで抽象的な定義は終了し、<binding>要素からは具体的な定義に移ります。
<binding>のtype属性は、ポートタイプを参照しています。 次の<soap:binding>は、このバインディングではSOAPを使うことを指定しています。WSDL 1.1仕様では、SOAPバインディングの他に、HTTP GET & POSTバインディング、MIMEバインディングが定義されています。ですが、現実にはSOAPバインディングのみを使うケースがほとんどでしょう。 <soap:binding>要素のtransport属性では、SOAPプロトコルのトランスポート(下位プロトコル)として、HTTPを使うことを指定しています。 <soap:binding>要素のstyle属性は、オペレーションが(パラメータと戻り値を含む)RPC指向か(メッセージにドキュメントが含まれる)ドキュメント指向かを指定しています。これについても、次回詳しく見ていきます。 続いて、ポートタイプのオペレーションと1対1に対応する形で、<operation>要素が登場しています。ポートタイプのオペレーションでは抽象的な情報のみが指定されていましたが、ここでは具体的な情報が定義されます。 <soap:body>のuse属性では、SOAPメッセージのボディ部(本体)をどのように構築するか指定します。SOAP仕様で定義されたSOAPエンコーディングを使用する「エンコード形式」か、スキーマ定義を直接使う「リテラル形式」のいずれかになります。これについても、次回詳しく見ていくことにします。 抽象的な定義であるポートタイプをJavaインタフェースに対比させたので、ポートタイプの具体的な定義情報を含むバインディングは、そのJavaインタフェースをimplementsしたJavaクラスだと考えることができます。
XMLの名前空間にも注意しましょう。<definitions>要素以下ここまで登場した要素は、名前空間URI "http://schemas.xmlsoap.org/wsdl/" の名前空間(サンプルのWSDLではデフォルト名前空間)に属していました。ですが、<soap:binding>や<soap:body>などは、名前空間URI "http://schemas.xmlsoap.org/wsdl/soap/" の名前空間に属しています。これらの要素は、WSDL共通の情報ではなくSOAPバインディングに固有の情報を持っていることため、名前空間も分けられているのです。
サービスは、分かりやすい構成になっています。
<port>要素のbinding属性は、バインディングを参照していますね。今回はSOAPバインディングを使用しているので、<soap:address>要素のlocation属性で、このポートにアクセスするためのエンドポイントURIを指定します。この例では1つのポートだけですが、1つのサービスは複数のポートを持つことができます。これにより、同じ内容(ポートタイプ)だがプロトコルやサーバの異なる代替ポートを複数定義することが可能になります。
以上の説明を踏まえて、再度WSDLの全体像を眺めてみましょう。その意味するところが、たいぶ明確になったのではないでしょうか?
今回は、WSDLの基本的な説明だけで終わってしまいました。次回は、今回の説明を踏まえ、RPCスタイルとドキュメント・スタイル、SOAPエンコーディングとリテラル形式、MEP(メッセージ交換パターン)などについて、より突っ込んだ議論を進めていく予定です。 |