この章の内容は、次のとおりです。
Webサービス・アークテクチャの目標は、異機種のビジネス・アプリケーションが円滑に連携することです。アーキテクチャは疎結合されXML標準に基づいています。Webサービスは、サービス規約としてWeb Service Description Language(WSDL)ファイルを定義することで、その背後にあるオペレーティング・システムや開発技術にかかわらず相互に連携するよう設計されています。ただし、サービス規約が複雑なため、WSDLやSOAPなどの標準の解釈が曖昧になっています。また、ベンダー固有の機能強化や拡張が全体的な相互運用性の妨げとなっています。
ビジネス・アプリケーションは、お互いのサービスを起動する必要があります。これらのサービスが異なるテクノロジで実装されていることもあります。Webサービスの複雑さが増すと、相互運用がうまくいかないケースも増える傾向にあります。一般に公開されているWebサービスを運用しているとしたら、様々なツールキットを使用している世界中のクライアントがそのWebサービスを正常に使用できることを希望するでしょう。同じように、ビジネス・アプリケーションにも、既存のレガシー・システム上に構築されていて、インタフェースの設計があまり一般的でない他ベンダーのWebサービスとの統合または連携が必要な場合もあります。
相互運用性の問題は、プロトコル・スタックのどのレイヤーからも生じる可能性があります。トランスポート・レイヤーでは、メッセージ交換に関わるどちらの側でも、特定の物理的な転送メカニズムについて合意している必要があります。たとえば、Java以外のプラットフォームを使用している相手にJMSトランスポートの使用を期待することはできません。基本的なHTTPプロトコルを使用した方が、相互運用性が高まる理由はここにあります。メッセージ・レイヤーでは、任意のデータ・エンコーディングの使用がSOAPで事実上許可されているため相互運用は難しくなります。たとえば、Javaプラットフォームの標準のArrayList
は、.NETプラットフォームのSystem.collections.ArrayList
には自動的には変換されません。また、相互運用性の問題は、基本的なWSDLやSOAPのレベルでも発生します。高度なWebサービスの開発者は、セキュリティ、信頼性、トランザクション・サービスなどのQuality of Service(QOS)機能の実装を開始すると、その他にも様々な難題に直面するでしょう。
相互運用性の実現は難しい問題です。ただし、適切なガイドラインがあれば、Oracle Webサービスはその他のJ2EEベンダーのプラットフォームや、Microsoft .NETなどのJava以外のプラットフォームとシームレスに連携できます。
Webサービスのコミュニティで相互運用性の重要度が増すにつれ、その目標を達成するために多くの組織が設立されました。
SOAPBuildersコミュニティ
SOAPBuildersは、SOAP実装間のテストを行う相互運用性のテストベッド確立に向けて活動する開発者の、緩やかに組織化されたフォーラムです。フォーラムの参加者によって共同で定義された基準となる一連のテストを実装することで、相互運用性が証明されます。
SOAPBuildersコミュニティによって開発されたテストは、全般的にベンダーの慣例に基づいています。ただし、慣例は時とともに変わります。Webサービス・ベンダー、Webサービス開発者およびWebサービスの利用者には、正式にまとめられた、不要なものの混じっていないはっきりとしたルールが必要です。
WS-Interoperability
Web Services Interoperability(WS-I)は、Webサービス間の相互運用可能なメッセージ交換のための一般的なプロトコルの作成、普及、サポートを行う業界のオープンな組織です。WS-Iプロファイルは、標準の適用方法に関するガイドラインおよび推奨事項です。これらのプロファイルは、基礎となる仕様に契約を追加して曖昧さをなくすことを目的としています。
WS-Iの成果物は、プロファイル、一般的なプラクティスまたはベスト・プラクティス、シナリオ、テスト・ソフトウェアおよびテスト資料です。
Webサービスは、WS-I Basic Profileに準拠するように設計する必要があります。WS-Iに準拠したサービスは明瞭な規約に同意しており、相互運用性を実現する可能性が高くなります。
たとえば、WS-I Basic Profileに準拠しているWebサービスでは、次の機能を使用する必要があります。
トランスポート・バインディングとしてHTTPまたはHTTPSを使用します。HTTP 1.0よりもHTTP 1.1が優先されます。
literalスタイルのエンコーディングを使用します。SOAPエンコーディングは使用しないでください。
より厳密な障害メッセージ構文を使用します。MESSAGE
にsoap:Fault
要素が含まれている場合、その要素の子は非修飾である必要があります。
XMLバージョン1.0を使用します。
サービスでは、規則ArrayOf
XXXを使用したArrayラッパー要素の宣言はできません。
OracleはWS-Iのメンバーで、顧客が相互運用性を実現する手助けとなるよう取り組んでいます。Oracle Application Server Web Servicesプラットフォームは非常に柔軟性が高く、相互運用性のあるWebサービスの作成を支援します。
WebServicesAssemblerツールおよびOracle JDeveloper 10gでは、ボトムアップ方式でdocument-literalスタイルのWebサービスを作成すると、WS-Iに準拠したサービスが生成されます。Oracle実装では、その他のSOAPスタックとのメッセージ交換の成功率を高めるため、SOAPメッセージの受信時には厳密に処理せずに対応するようになっています。
Oracle JDeveloper 10gは、WSDLファイルの統合テストおよびWS-I Basic Profile準拠のWebサービスの実行をサポートしています。監視およびロギング用の拡張HTTPアナライザが用意されており、相互運用性の問題をより詳細に診断する組込みの分析およびレポート・ツールも提供されています。
一般的なガイドラインの1つ目は、可能な場合にはWS-Iに準拠したWebサービスを作成することです。
ただし、WS-Iプロファイルですべての相互運用性の問題が解決するわけではありません。WS-Iプロファイルが存在する以前から、多くのWebサービスが実装されていました。また、Webサービスとして有効化しているレガシー・システムにより、設計が制限される場合もあります。
そのため、可能な場合には、開発プロセスの初期段階からWebサービス設計の秘訣を取り入れることです。次の項で、それらのガイドラインを説明します。
Webサービスをトップダウン方式で設計する
WSDLからのトップダウン方式を採用することにより、プラットフォームや言語に固有の特徴に制限されないサービス規約でWebサービスを設計できます。WSDLが既存のレガシーAPIに影響される可能性が低くなります。
最初はXSDを使用してデータ型を設計する
可能な場合には、XSDスキーマ・エディタを使用してスキーマ型を持つデータ型を設計します。.NETのDataSet
データ型やJavaコレクションなど、プラットフォーム固有のデータ型の使用は避けます。
データ型はシンプルにする
xsd:choice
などの不必要に複雑なスキーマ・データ型の使用は避けます。最高の相互運用性はシンプルなデータ型により実現され、パフォーマンスの向上という利点ももたらします。
NULL値に注意する
NULL値を使用するケースを決定します。たとえば、配列型にNULL値を許可しますか。NULL文字列または空の文字列を使用しますか。プラットフォーム全体にNULL値を送信すると、受信者側で例外の原因となりますか。
可能な場合は、NULL値を送信しないようにします。アプリケーションでNULL値を使用する必要がある場合は、NULL値が許可されていることが明確にわかるようにスキーマ型を設計します。
WSDLの検証にコンプライアンス・テスト・ツールを使用する
WebサービスがWS-Iに準拠するよう設計されている場合は、WS-Iモニタリング・ツールを使用してメッセージを記録し、分析ツールを使用して準拠しているかどうかを検証します。
各プラットフォームにネイティブのデータ型の違いを理解する
xsd:unsignedshort
およびxsd:unsignedint
などのスキーマ型には、常にネイティブのデータ型のダイレクト・マッピングがあるわけではありません。たとえば、Javaプラットフォームには等価のunsigned型はありません。xsd:double
、xsd:float
およびxsd:decimal
などのスキーマの数値型は、一度プラットフォームにネイティブのデータ型にマッピングされると精度が変更される可能性があります。また、xsd:string
型には制限があります。文字列には無効なXML文字を含めることはできず、\r
(キャリッジ・リターン)文字は\n
(改行)文字にマッピングされます。
rpc-encodedメッセージ書式は使用しない
rpc-encodedメッセージ書式自体に、他のプラットフォームやクライアントとの相互運用性がないわけではありません。今日稼働しているWebサービスの多くは、rpc-encodedです。rpc-encodedメッセージ書式の使用を避ける理由は、基礎となる仕様の解釈や実装の選択の違いが相互運用性の妨げになるという問題を回避するためです。これらの問題の例には、疎配列、マルチディメンション配列、カスタム障害コードQName、タイプ付きでないペイロードなどの扱いが含まれます。
相互運用性の問題の多くは、WSDL、ワイヤ書式、データ表現の有効性の関係を理解することで認識および解決できます。問題の原因を特定できれば、修正するのは簡単です。
図1-1に、クライアント・スタックとサービス・スタック間の相互作用を示します。WSDLはクライアント・アーティファクトの生成に使用されます。JAX-RPCまたは.NETプラットフォームに実装可能なクライアント・アプリケーションは、XML SOAPリクエスト・メッセージを生成します。アプリケーションによりそのリクエストがシリアライズされ、HTTP経由でサービスに渡されます。JAX-RPCまたは.NETプラットフォームに実装可能なサービスは、リクエストをデシリアライズして処理します。
クライアント・リクエストを処理すると、サービス・アプリケーションによってXML SOAPリクエスト・メッセージが生成され、シリアライズされてHTTP経由でクライアントに渡されます。クライアント・アプリケーションによって、レスポンスがデシリアライズされ、結果が処理されます。
図の番号とアルファベットは、クライアント・サイドから見た、相互運用に失敗する可能性のある箇所を示しています。サーバー・サイドでも失敗する箇所は類似しています。次のリストでこれらの問題のカテゴリを説明します。
クライアント・アーティファクトを生成するユーティリティが、WSDL(規約)の処理に失敗します。WSDLがWS-Iプロファイルに準拠していないため無効であるか、クライントのプラットフォームでサポートされていません。たとえば、.NET 1.1ではrpc-literalスタイルのWebサービスはサポートされていません。
このタイプの失敗の詳細は、「無効または書式が不適切なWSDL」で説明されています。
クライアント・アーティファクトを生成するユーティリティによりWSDLが正常に処理されますが、使用できない、または使用しにくいアーティファクトが生成されます。WSDLにツールが処理できない独自のスキーマ拡張が含まれている場合などがこのケースに該当します。
このタイプの失敗の詳細は、「独自のデータ・バインディング拡張が含まれるWSDL」で説明されています。
実行時、実際にペイロードが処理される前に、クライアント・アプリケーションに例外がスローされます。通常、エラーは、障害コード・セットがSOAP 1.1ではClient
に、SOAP 1.2ではSender
に設定されているSOAP障害に変換されます。サービスのエンドポイントが使用できないか、認証ネゴシエーションに失敗したことが理由です。
このタイプの失敗の例は、「無効なXML文字」で説明されています。
次に示すいずれかの理由のため、サーバー・サイドからSOAP障害がスローされます。
リクエストが正しい操作にルーティングされません。SOAP障害コードは、SOAP 1.1ではClient
、SOAP 1.2ではSender
に設定されます。
このタイプの失敗の例は、「同期外れのSOAPAction値」で説明されています。
リクエストが正常にデシリアライズされません。SOAP障害コードは、SOAP 1.1ではClient
、SOAP 1.2ではSender
に設定されます。
このタイプの失敗の詳細は、「SOAPメッセージのNULL値」および「unsignedスキーマ数値型」で説明されています。
リクエストがアプリケーション・コードで処理されません。スローされる内部例外は、未処理例外か、ビジネス・ロジック・レベルのアプリケーション固有のSOAP障害です。サーバー・アプリケーションで入力パラメータの検証に関連するエラーとみなされないかぎり、SOAP障害コードはServer
またはReceiver
に設定されます。
レスポンスがシリアライズされません。SOAP障害コードは、Server
またはReceiver
に設定されます。
実行時、レスポンスをデシリアライズしてSOAPエンベロープのコンテンツをJavaインスタンスに変換する際に、クライアントによって例外がスローされます。
このタイプの失敗の例は、「精度の欠落」で説明されています。
これ以降の各項では、それぞれの箇所で起こりうる失敗の詳細と例を説明します。
相互運用は、WSDLの処理中に発生するエラーが原因で失敗する場合があります。これは、図1-1の1に当たります。ツールキットが異なると、WSDLの処理方法も異なります。たとえば、書式が不適切なWSDLは、あるツールキットでは問題なく受け入れられ、別のツールキットでは拒否される場合があります。
例
次のコード・サンプルでは、書式が不適切なWSDLがどのようにして相互運用失敗の原因となるかを示します。
例1-1に、書式が不適切なWSDLのフラグメントを示します。フラグメントには、名前が不適切な入力パラメータgetQuote
が含まれます。portType
のoperation
にgetQuote
という名前の入力パラメータが定義されていますが、対応するバインディング操作に入力名または出力名が指定されていないことに注意してください。デフォルトのネーミング・パターン(<
operationName>Request
および<
operationName>Response
)を使用して指定されていないバインディング操作の入力名および出力名を作成すると、getQuote
操作の入力パラメータに一貫性がなくなるためWSDLが無効になります。
例1-1 名前が不適切な入力パラメータが含まれるWSDLフラグメント
<portType name="qotdPortType"> <operation name="getQuote"> <input name="getQuote" message="tns:getQuoteRequest"/> <output name="getQuoteResponse" message="tns:getQuoteResponse"/> </operation> </portType> <binding name="qotdBinding" type="tns:qotdPortType"> <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/> <operation name="getQuote"> <soap:operation soapAction="urn:xmethods-qotd#getQuote"/> <input> <soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:xmethods-qotd"/> </input> <output> <soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="urn:xmethods-qotd"/> </output> </operation> </binding>
このWSDLからクライアント・プロキシ・クラスの生成を試行すると、.NETプラットフォームであるかOracleAS Web Servicesプラットフォームであるかにかかわらず問題が発生します。.NETプラットフォームでクライアント・プロキシC#クラスの生成を試行すると、一致するバインディングがないためにWSDLが拒否されるエラーが発生します。ただし、Oracle Application Server Web Servicesプラットフォームでは、Javaクライアント・プロキシ・クラスを生成すると、WSDLに不適切な入力名または出力名が使用されているという警告が出力されます。
WSDL仕様には、入力名および出力名が指定されていない場合、バインディング操作にportType
操作のデフォルトのネーミング・パターンを使用する必要があるということが明確に指定されていません。WSDLを拒否するかどうかが明確ではありません。OracleAS Web ServicesツールキットではこのようなWSDLを受け入れ、警告を出力します。
この例で示されている起こりうる相互運用の問題は、バインディング操作とportType
の入力にname
属性を指定することで修正できます。
図1-1の2は、ツールキットが指定されたWSDLから使用可能なアーティファクトを生成できないために失敗する箇所を示しています。この問題は、WSDLに独自のデータ・バインディング拡張が含まれる場合に発生します。
例
次のコード・サンプルでは、独自のデータ・バインディング拡張が含まれるWSDLを処理することがどのようにして相互運用失敗の原因となるかを示します。
注意: 次のコード・サンプルでは、相互運用失敗の原因となる独自のデータ・バインディングの例として、ADO.NETSystem.Data.DataSet データ型を使用しています。ただし、XMLは本質的に拡張可能であるため、これ以外の独自のデータ・バインディングを処理する際にも同じ問題が発生する可能性があります。 |
.NETプラットフォーム用のWebサービスを開発していて、System.Data.DataSet
データ型を戻す次のC#メソッドがあるとします。
public System.Data.DataSet ListBooks ( )
例1-2に、メソッドが.NET Webサービスとして公開された場合に、レスポンスの出力要素を表すXMLスキーマ・フラグメントを示します。
例1-2 .NET WebサービスのXMLスキーマ・フラグメント
<s:element name="ListBooksResponse"> <s:complexType> <s:sequence> <s:element minOccurs="0" maxOccurs="1" name="ListBooksResult"> <s:complexType> <s:sequence> <s:element ref="s:schema"/> <s:any/> </s:sequence> </s:complexType> </s:element> </s:sequence> </s:complexType> </s:element>
WebServicesAssemblerツールを使用して、このWSDLに基づくクライアント・プロキシ・コードを生成する必要があるとします。WebServicesAssemblerはWSDLを正常に処理し、次のJavaメソッドを持つクライアント・コードが含まれる一連のプロキシ・クラスを生成します。
public javax.xml.soap.SOAPElement listBooks(ListBooks parameters) throws java.rmi.RemoteException
OracleAS Web Servicesでは、スキーマ定義からそれ以上詳細な情報を推測できないため、レスポンスはSOAPElement
として戻されます。例1-2の.NET WSDLのスキーマには、.NET DataSet
データ型を表すワイヤで受信するペイロードが完全に説明されていません。スキーマには、SOAPメッセージには2つの部分があるということのみが説明されています。1つ目はペイロードのスキーマ定義で、その後にペイロードが続いています。
例1-3に、クライアントが受信するSOAPメッセージを示します。xs:schema
要素の1番目の子(<xs:element name="NewDataSet"...>
)で、メッセージのペイロードが説明されています。2番目の子(<diffgr:diffgram ...>
)にペイロードが含まれています。
例1-3 .NET DataSetデータ型から生成されるSOAPメッセージ
<soap:Body> <ListBooksResponse xmlns="http://francisshanahan.com/"> <ListBooksResult> <xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> <xs:element name="NewDataSet" msdata:IsDataSet="true"> <xs:complexType> <xs:choice maxOccurs="unbounded"> <xs:element name="bible_content"> <xs:complexType> <xs:sequence> <xs:element name="Book" type="xs:string" minOccurs="0"/> <xs:element name="BookTitle" type="xs:string" minOccurs="0"/> </xs:sequence> </xs:complexType> </xs:element> </xs:choice> </xs:complexType> </xs:element> </xs:schema> <diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1"> <NewDataSet xmlns=""> <bible_content diffgr:id="bible_content1" msdata:rowOrder="0"> <Book>01</Book> <BookTitle>The First Book of Moses, called Genesis</BookTitle> </bible_content> <bible_content diffgr:id="bible_content2" msdata:rowOrder="1"> <Book>02</Book> <BookTitle>The Second Book of Moses, Called Exodus</BookTitle> </bible_content> … </NewDataSet> </diffgr:diffgram> </ListBooksResult> </ListBooksResponse> </soap:Body> </soap:Envelope>
WebServicesAssemblerツールでは、ペイロード・スキーマを捕捉するJava型クラスを特定できません。データセット・レコードを適切に処理するJavaクライアントを記述するにはどうしたらよいでしょうか。
次に示す2つの手順に従うことで解決できます。
スキーマを取得します。コードで受信SOAPElement
を解析し、スキーマの子要素を抽出してスキーマ定義を保存する必要があります。
<xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
所有するスキーマに基づいて実際のペイロードを処理します。
JAXBやOracle TopLinkなどのツールを使用してペイロードを処理できます。JAXBにはJava APIおよびコマンドライン・ツールがあり、スキーマから直接Javaの型保証クラスを生成する際に役立ちます。
Oracle TopLinkは、JAXBのサポートを含むリレーショナル永続性フレームワークへの拡張オブジェクトです。ただし、TopLinkでは、視覚的なマッピング・エディタやその他多数の拡張機能も使用できます。
好みのツールを使用し、スキーマに基づいてJava型を生成すると、実際のSOAPペイロードを処理し、スキーマに続く任意の要素を抽出できます。この要素にはタグ名diffgr
があり、実際のペイロードは<diffgr>
要素の1番目の子として開始されます。
<diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1">
クライアント・アーティファクトを正常に生成できる完全に有効なWSDLでも、サーバー・サイドに到達する前にアプリケーションが停止する場合もあります。これは、図1-1の3に当たります。この失敗はサービスが使用できない場合に発生します。その他にも、検出の難しい場合があります。たとえば、アプリケーションによりSOAPメッセージに無効なXMLデータが生成される場合があります。また、アプリケーションからXMLでは無効な文字が渡される場合もあります。これらのような場合には、メッセージがWebサービスに到達する前に、サーバーのトランスポート・レイヤーによりすぐに拒否されます。
アプリケーションで、XML解析エラーの原因になる可能性のある特別な文字やデータをSOAP Body
に含めて送信する必要がある場合は、アプリケーションがBASE64Encoding
などの基本的なエンコーディング・スキーマを使用するように設計されている必要があります。
例
次のコード・サンプルでは、SOAPメッセージで無効なXML文字を渡すことがどのようにして相互運用失敗の原因となるかを示します。
C#クライアント・アプリケーション・コードによりコマンド文字列が送信され、そのコマンドがサービスによりクライアントに対して実行されるとします。また、コマンドの1つにバックスペース文字(\b
)が使用されているとします。
MyCommand request = new MyCommand("\b"); MyCommandResponse response = soapClient.runCommand(request);
クライアント・アプリケーションによりバックスペース文字(\b
)を含むコマンド文字列の送信が試行されると、バックスペース文字はXMLでは無効であるためOracleAS Web Servicesにより拒否されます。これにより、HTTPトランスポート・エラー、および次のようなHTMLの応答が戻されます。
<HTML><HEAD><TITLE>Web Service</TITLE></HEAD><BODY><H1>Bad Request</H1><PRE>Error parsing envelope: (2, 237) Invalid char in text.</PRE></BODY></HTML>
OracleAS Web Servicesでは、WSDL operation
要素のsoapAction
属性の値は、ターゲット名前空間および操作名から構成されるURIです。
http://<
target-namespace>/<
operation-name>
SOAPリクエスト・メッセージのこの操作に対するSOAPAction
HTTPヘッダーの値にも、これと同じ値が使用されている必要があります。使用されていない場合、予期しない動作をする可能性があります。このタイプの失敗は、図1-1の4aに当たります。
多くのSOAPプロセッサでは、ボディ全体をデコードせずにリクエストを適切な操作にルーティングするための目印として、SOAPAction
HTTPヘッダーが使用されています。引用符付き文字列のSOAPAction
値をサポートしている実装もあります(""
は空の文字列)。SOAPAction
がHTTPヘッダーにない場合もあります。相互運用性の問題を回避するためには、常に引用符付き文字列を使用する必要があります。
この問題は、クライアントがサービスによって公開されたWSDLと同期していないことを意味します。この問題を解決するには、クライアントを再生成するとサービスのWSDLと同期します。
例
次のコード・サンプルでは、WSDL operation
要素のsoapAction
属性と、この操作に対するSOAPAction
HTTPヘッダー間の不一致が、どのようにして相互運用失敗の原因となるかを示します。
WSDLに次に示すadd
操作が定義されているとします。
<soapbind:operation soapAction= "http://ws.oracle.com/demo/:add"/>
例1-4に、実行時に送信されるSOAPリクエストを示します。メッセージによってadd
操作にmultiply
SOAPAction
が設定されることに注意してください。
例1-4 SOAPActionの値を説明するSOAPリクエスト・フラグメント
POST http://localhost/khub/MathService.asmx HTTP/1.1 Host: localhost Proxy-Connection: Keep-Alive Connection: TE TE: trailers, deflate, gzip, compress User-Agent: Oracle HTTPClient Version 10h SOAPAction: "http://ws.oracle.com/demo/:multiply" Accept-Encoding: gzip, x-gzip, compress, x-compress Content-type: text/xml; charset="UTF-8" Content-length: 347 <?xml version = '1.0' encoding = 'UTF-8'?> <env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns0="http://ws.oracle.com/demo/"> <env:Body> <ns0:addElement> <ns0:a>3</ns0:a> <ns0:b>2</ns0:b> </ns0:addElement> </env:Body> </env:Envelope>
サーバーでSOAPAction
HTTPヘッダーの値とSOAP Body要素(<ns0:addElement>
)の1番目の子要素に不一致が検出されるため、OracleAS Web Servicesによりエラーがスローされ、処理が停止されます。次のようなエラーが表示されます。
unexpected element name: expected={http://ws.oracle.com/demo/}addElement, actual={http://ws.oracle.com/demo/}multiplyElement
.NET 1.1実装では、SOAPメッセージのディスパッチにデフォルトでSOAPAction
ヘッダーが使用されているため、add
操作ではなくmultiply
操作が起動されてしまいます。障害は生成されません。例1-5に、.NETがmultiply
サービスから戻す不適切なレスポンスを示します。間違ったWebサービスが起動されているので障害が戻される必要があります。SOAPAction
の無効な値が無視され、リクエストのペイロードに基づき、メッセージがadd
操作にルーティングされる可能性もあります。
例1-5 .NETにより戻される不適切なレスポンス
<multiplyResponseElement xmlns="http://ws.oracle.com/demo/"> <MultiplyResult>0</MultiplyResult> </multiplyResponseElement>
Apache Axisプラットフォームは、操作のルーティングでSOAPAction
ヘッダーに依存しません。SOAP Body
ペイロードの最上位要素を使用して一致する操作を検出します。
Webサービスの相互運用で予期しない動作が起こらないようにするには、WSDLのsoapAction
属性の値がどのように設定され、SOAPメッセージのSOAPAction
ヘッダー値への影響を理解することが有効です。次の項では、この属性がどのように設定され、OracleAS Web Servicesおよび.NETプラットフォームでどのように使用されるかを説明します。
Oracle WebServicesAssemblerツールを使用してJavaコードからWebサービスをアセンブルすると、WSDLの各operation
に一意のsoapAction
属性値が生成されます。
ブール型引数emptySoapAction
を使用してこの属性の値を制御できます。引数をtrue
に設定すると、生成されたWSDLの各SOAP binding
操作のsoapAction
属性は空の文字列に設定され、SOAPメッセージのSOAPAction
ヘッダーは引用符(""
)付き文字列に設定されます。この引数をtrue
に設定すると、別のベンダーのツールと相互運用できる可能性が高くなります。
emptySoapAction
引数のデフォルト値はfalse
です。この場合、soapAction
属性の値は、targetNamespace
およびoperation
名が連結されています。この値は、SOAPメッセージのSOAPAction
ヘッダーの値でもあります。
例
次のコード・サンプルでは、emptySoapAction
引数の値がOracleAS Web ServicesのsoapAction
属性値の制御にどのように使用されるかを示します。
getDateTime
サービスを定義する、次のようなTimeService
Javaインタフェースがあるとします。
public interface TimeService extends Remote { public String getDateTime(String name) throws RemoteException; }
このインタフェースは、WebServicesAssemblerのassemble
コマンドへの入力、またはemptySoapAction
引数のないAntタスクとして使用できます。emptySoapAction
引数はコマンドやタスクでは使用されないため、その値はデフォルトでfalse
であると推測されます。結果のWSDLでは、次のsoapAction
値です。
soapAction="http://timeService/getDateTime"
この値は、SOAPメッセージのSOAPAction
ヘッダーの値でもあります。
.NETプラットフォームでは、XML WebサービスのRoutingStyle
をSoapAction
として指定できます。WebサービスがdocumentスタイルであるかRPCスタイルであるかによって、SoapDocumentMethod
またはSoapRpcMethod
属性のAction
パラメータを使用してSoapAction
を設定します。デフォルトでは、Action
パラメータの値を次のURIで定義します。
http://<
web service-namespace>/
MethodName
このURIでは、MethodNameはXMLWebサービス・メソッドの名前です。
注意: SoapAction パラメータを定義してSOAPメッセージをディスパッチするかわりに、RequestElement パラメータを使用してrequest 要素の名前を設定するように.NETプラットフォームを構成できます。これを実行するには、RoutingStyle=SoapServiceRoutingStyle.RequestElement および[SOAPDocumentMethod(Action="")] を設定します。
このトピックの詳細は、このマニュアルでは説明しません。 |
例
次のコード・サンプルでは、RoutingStyle
パラメータを使用して.NET WebサービスのSoapAction
値をどのように制御できるかを示します。
例1-6では、XML WebサービスのRoutingStyle
はSoapServiceRoutingStyle.SoapAction
に設定されています。SoapDocumentMethod
属性はdocumentスタイルとしてのサービスを識別し、Action
パラメータはmyAddService
およびmyMultiplyService
としてのWebサービスのURIを識別します。
例1-6 .NETプラットフォームにおけるサービスのRoutingStyleの値の設定
<%@ WebService Language="C#" Class="MathService" %> using System; using System.Web.Services; using System.Web.Services.Protocols; [SoapDocumentService(SoapBindingUse.Literal, SoapParameterStyle.Wrapped, RoutingStyle=SoapServiceRoutingStyle.SoapAction)] public class MathService : WebService { [SoapDocumentMethod(Action="http://localhost/myAddService")] [WebMethod] public float Add(float a, float b) { return a + b; } [SoapDocumentMethod(Action="http://localhost/myMultiplyService")] [WebMethod] public float multiply(float a, float b) { return a * b; } }
データ型の不一致は、相互運用失敗の最も一般的な原因です。このタイプの失敗は、サーバーまたはクライアントがSOAPメッセージのデシリアライズを試行する際に発生します。これは、図1-1の4bおよび5に当たります。
データ型の不一致が原因の失敗は、プラットフォームの違いを越えてNULL値が送信された場合に発生します。可能な場合には、NULL値を送信しないようにする必要があります。あるWebサービス・アプリケーションから他のアプリケーションにNULL値を送信する必要がある場合は、別のシステムでそのデータ型が何にマッピングされていて、そのデータ型でNULL値を正しく処理できるかどうかを把握する必要があります。
たとえば、xsd:dateTime
スキーマ型は、.NETプラットフォームではSystem.DateTime
にマッピングされています。Javaプラットフォームでは、java.util.Calendar
またはjava.util.Date
にマッピングされています。JavaプログラムでCalendar
またはDate
オブジェクトがNULL値で初期化されている場合は、SOAPメッセージでNULLのxsd:dateTime
が送信されます。.NETプラットフォーム上に作成されたWebサービスでSOAPメッセージを受信すると、System.DateTime
型ではNULL値が許可されていないため、そのメッセージは正しくデシリアライズされません。
例
次のコード・サンプルでは、発信プラットフォームと受信プラットフォーム間でNULL値を送信することがどのようにして相互運用失敗の原因となるかを示します。
nil
属性がtrue
に設定されている場合、XML要素はNULL要素です。WSDLで、対応する要素のnillable
属性をtrue
に設定することで対応する必要があります(nillable
のデフォルト値はfalse
です)。
例1-7のWSDLでは、source
要素でnillable
属性が省略されています。そのため、このWSDLにはデフォルトでnillable=false
属性があるのと同じことになります。
例1-7 source要素でNULL値が許可されていないWSDLフラグメント
<s:element name="Src2html">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="login" type="s:string"/>
<s:element minOccurs="0" maxOccurs="1" name="passe" type="s:string"/>
<s:element minOccurs="0" maxOccurs="1" name="lan" type="s:string"/>
<s:element minOccurs="0" maxOccurs="1" name="source" type="s:string"/>
</s:sequence>
</s:complexType>
</s:element>
クライアント・アプリケーションで、source要素を表すJavaオブジェクトにNULL値が割り当てられているとします。Apache Axisプラットフォームで、このWSDLからクライアント・プロキシ・コードが生成されるとします。例1-8にワイヤ書式の結果のリクエストSOAPメッセージを示します。source
要素のxsi:nil
属性がtrue
に設定されていることに注意してください。
例1-8 xsi:nilがtrueに設定されているリクエスト・メッセージ
<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>
<Src2html xmlns="http://www.dotnetisp.com/webservices/dotnetisp/src2html.asmx">
<login>Gigi</login>
<passe>oracle</passe>
<lan>C#</lan>
<source xsi:nil="true"/>
</Src2html>
</soapenv:Body>
</soapenv:Envelope>
WSDL定義ではsource
要素にnil
値を含めることが許可されていないため、OracleAS Web Servicesプラットフォーム上に作成されたWebサービスは、受信したSOAPメッセージのデシリアライズに失敗します。例1-9に、OracleAS Web Servicesにより前述のリクエストに対して生成される、サンプルのSOAP障害レスポンスを示します。
例1-9 OracleAS Web ServicesからのサンプルのSOAP障害レスポンス
<env:Body> <env:Fault> <faultcode>env:Client</faultcode> <faultstring>caught exception while handling request: unexpected null</faultstring></env:Fault> </env:Body> </env:Envelope>
一方、source
要素にNULLを割り当てる同じクライアント・アプリケーションをOracleAS Web Servicesを使用して開発すると、source
要素はスキーマでオプションとして定義されるため完全に省略されます。例1-10に、OracleAS Web Servicesを使用して開発されたクライアントにより送信される、ワイヤ書式のリクエストSOAPメッセージを示します。
例1-10 オプションの要素が省略されているリクエスト・メッセージ
<env:Envelope xmlns:env=http://schemas.xmlsoap.org/soap/envelope/ xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance xmlns:ns0="http://www.dotnetisp.com/webservices/dotnetisp/src2html.asmx"> <env:Body> <ns0:Src2html> <ns0:login>Gigi</login> <ns0:passe>oracle</passe> <ns0:lan>C#</lan> </ns0:Src2html> </env:Body> </env:Envelope>
例1-10のリクエストには無効なNULL要素が含まれていないため、サーバーはエラーのないレスポンスを戻します。例1-11に、このリクエストに対するサンプルのレスポンスを示します。
例1-11 サンプルのSOAPメッセージ
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance xmlns:ns0="http://www.dotnetisp.com/webservices/dotnetisp/src2html.asmx"> <env:Body> <ns0:Src2htmlResponse/> </env:Body> </env:Envelope>
図1-1の4bや5で発生する別のタイプのデシリアライズの失敗には、unsignedスキーマ型の使用が関係します。.NETプラットフォームでは、unsigned型はネイティブのunsignedデータ型に直接マッピングされます。一方、Javaプラットフォームでは、unsigned型は定義されていません。その結果、WSDLでunsignedスキーマ型を使用すると、予期しない相互運用失敗の原因になります。
可能な場合には、unsignedShort
、unsignedInt
、unsignedDouble
またはunsignedFloat
などのunsigned数値データ型をクライアント・アプリケーションに使用しないようにします。これらのデータ型を使用した場合、各システムにおけるこれらのデータ型の範囲や精度の制限を確認する必要があります。
例
次のコード・サンプルでは、クライアント・アプリケーションにunsignedデータ型を使用することがどのようにして予期しない相互運用失敗の原因となるかを示します。
次に示すC# Webメソッドではunsigned入力引数ui
を取り、コール元に同じ値を戻します。
[WebMethod] Public uint getUint(uint ui) { Return ui }
例1-12に、getUnit
Webメソッドの入力および出力パラメータを含むWSDLフラグメントを示します。これらのパラメータは、xsd:unsignedInt
型に直接マッピングされます。
例1-12 入力および出力パラメータがマッピングされているWSDLフラグメント
... <s:element name="getUint"> <s:complexType> <s:sequence> <s:element minOccurs="1" maxOccurs="1" name="ui" type="s:unsignedInt" /> </s:sequence> </s:complexType> </s:element> <s:element name="getUintResponse"> <s:complexType> <s:sequence> <s:element minOccurs="1" maxOccurs="1" name="getUintResult" type="s:unsignedInt" /> </s:sequence> </s:complexType> </s:element> ...
WebServicesAssemblerツールを使用すると、getUnit
サービスを消費する、JAX-RPCに準拠したクライアント・コードを生成できます。生成されたクライアント・コードは、request
入力型をJavaのネイティブのデータ型long
にマッピングします。
例1-13に示されているコード・フラグメントに類似のJavaクライアント・アプリケーションで、相手側のWebサービスで予期される型を把握せずに、非常に大きなlong
値を渡すとします。
例1-13 非常に大きな値を渡すクライアント・アプリケーション・フラグメント
...
stubs.Service1SoapClient myPort = new stubs.Service1SoapClient();
System.out.println("calling " + myPort.getEndpoint());
long l1 = 9223372036854775807L;
GetUint request = new GetUint(l1);
GetUintResponse response = myPort.getUint(request);
long l2 = response.getGetUintResult();
...
メッセージがケーブルで送信されると、.NETプラットフォームにより値が拒否され、例1-14に示すようなSOAP障害が戻されます。
例1-14 .NETプラットフォームにより送信されるサンプルのSOAP障害
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <soap:Body> <soap:Fault> <faultcode>soap:Client</faultcode> <faultstring>Server was unable to read request. --> There is an error in XML document (2, 261). --> Value was either too large or too small for a UInt32.</faultstring> <detail /> </soap:Fault> </soap:Body> </soap:Envelope>
精度の欠落は、メッセージがデシリアライズされる箇所で発生します。これは、図1-1の4bおよび5に当たります。SOAP障害は必ずスローされるわけではないため、このタイプの失敗は常に明白になるとはかぎりません。
精度の欠落は、XMLデータ型の値を別のプラットフォーム用に変換する際に発生します。たとえば、XML dateTime
単純型の値は、Javaおよび.NETプラットフォームでは異なる場合があります。これは、これらのプラットフォームではこのXMLデータの値を変換する際に、異なる精度を使用しているためです。
例
次のコード・サンプルでは、Javaおよび.NET Webサービス間でxsd:dateTime
XML単純型を送信するとどのようにして精度が欠落するかを示します。精度が欠落すると、相互運用性の問題の発生につながります。
次に示すC# Webメソッドは、DateTime
データ型のSystem MAX_VALUE
を戻します。
[WebMethod] Pubic DateTime getDateTime() { Return DateTime.MaxValue; }
例1-15に、getDateTime
.NET WebサービスにアクセスするJavaクライアント・コード・フラグメントを示します。
GetDateTime request = new GetDateTime(); GetDateTimeResponse response = myPort.getDateTime(request); Calendar cal = response.getGetDateTimeResult(); ...
例1-16に、Javaクライアントがワイヤ書式で受信するSOAPレスポンスを示します。日付に指定された値に注意してください(太字で強調してあります)。
例1-16 日付を戻す.NET WebサービスからのSOAPレスポンス・メッセージ
<?xml version="1.0" encoding="utf-8" ?>
<dateTime xmlns="http://tempuri.org/">9999-12-31T23:59:59.9999999-08:00</dateTime>
Javaクライアントにより、レスポンスがjava.util.Calendar
クラスにデシリアライズされます。例1-17に、Calendar
オブジェクトのコンテンツの出力を示します。dateTime
値が、ワイヤ書式で渡された値よりも少し遅くなっていることに注意してください。これは、2つのネイティブのdateTime
型で異なる精度が使用されているためです。.NETプラットフォームでは、年の値には4桁の数値が、ミリ秒には7桁の数値が使用されています。Javaプラットフォームでは、年の値には5桁の数値が、ミリ秒には3桁の数値が使用されています。さらに、Java Webサービスではミリ秒の3桁のみが維持され、日付が切り上げられています。受信側の新しい値は、10000年1月1日です。
例1-17 Java Calendarオブジェクトの出力
[java] ERA: 1 [java] YEAR: 10000 [java] MONTH: 0 [java] WEEK_OF_YEAR: 1 [java] WEEK_OF_MONTH: 1 [java] DATE: 1 [java] DAY_OF_MONTH: 1 [java] DAY_OF_YEAR: 1 [java] DAY_OF_WEEK: 7 [java] DAY_OF_WEEK_IN_MONTH: 1 [java] AM_PM: 0 [java] HOUR: 0 [java] HOUR_OF_DAY: 0 [java] MINUTE: 0 [java] SECOND: 0 [java] MILLISECOND: 0 [java] ZONE_OFFSET: -8 [java] DST_OFFSET: 0
前述の項では、Webサービスの相互運用性を調べるための明確な手順があることを説明しました。これらの手順は、取得、再実行、分析プロセスとしてまとめることができます。
Webサービス規約の取得: WSDL、XSDファイル、およびワイヤ書式のデータ(SOAPペイロードおよびHTTPヘッダー)を取得します。
メッセージ・ペイロードの再実行: メッセージ・ペイロードを再送信してプロキシとサービス・エンドポイントとの相互作用を再実行します。また、ペイロードを編集して、異なるコンテキストで相互作用を再実行することもできます。
相互作用の分析: サービスのコンシューマとプロバイダとの相互作用の結果を分析します。場合によっては、正常に転送されたメッセージ・ペイロードと比較して、中断した相互作用の内容を理解する必要があります。
Oracleには、各手順を支援するツールが用意されています。どのツールを使用するかは、Webサービスのエンドポイントおよびプロキシを開発している環境によって異なります。これ以降の項では、各手順を支援するツールを説明します。
Webサービス規約には、WSDLファイル、SOAPメッセージ・ペイロードおよびHTTPヘッダーが含まれます。
WSDLおよびXSDファイルを取得するには、次のツールを使用できます。
Oracle JDeveloperウィザードには、プロジェクト内にWSDLファイルのローカル・コピーを作成するオプションが用意されています。図1-2に、Oracle JDeveloperの「WSDLをプロジェクトにコピー」オプションを示します。
WebServicesAssemblerツールには、基底(またはトップレベル)のWSDLファイルと、インポート済/組込み済のすべてのWSDLとスキーマを、指定された出力ディレクトリにコピーするfetchWsdl
コマンドが用意されています。このコマンドは、コマンドラインまたはAntタスクで実行できます。
この引数の詳細は、『Oracle Application Server Web Services開発者ガイド』の「fetchwsdl」を参照してください。
一般的なAntタスクget
を使用してWSDLを戻すことができます。Antタスクを使用する場合は、wsdl:import
またはxsd:import
を使用して参照されたすべてのリモート・リソースのローカル・コピーを作成する必要があります。
SOAPペイロードおよびHTTPヘッダーの取得
SOAPペイロードおよびHTTPヘッダーを取得するには、次のツールを使用できます。
Oracle JDeveloper 10gに同梱されているHTTPアナライザ・ツールには、サービス・コンシューマとサービス・プロバイダ間のHTTPトラフィックを取得する便利な方法があります。この方法は、2つのノード間に簡単にHTTPプロキシを導入できることを前提としています。クライアントの動作を変更するには、クライアントの稼働中に、http.proxyHost
およびhttp.proxyPort
システム・プロパティを使用する必要があります。
HTTPプロキシの導入が現実的でない場合は、受信トラフィックを最終的な宛先にリダイレクトする中間ノードを使用できます。WS-Iモニターが、この方法を実行できるツールの例です。HTTPプロキシとは異なり、中間ノードを使用する場合には、クライアント・サイドのエンドポイントのURLを変更する必要があります。
クライアント・コードの変更や、プロキシを介したHTTPトラフィックのルーティングができない場合には、TCP Snifferを設定します。Etherealが、このコンテキストで使用できるUnixおよびWindows用のネットワーク・プロトコル・アナライザの例です。
ペイロードを取得したら、同じペイロードの再送信、または変更内容の影響を確認するためのペイロードの編集および送信ができる必要があります。
同じペイロードの再送信処理にはJUnitを使用できます。WebServicesAssemblerのgenProxy
コマンドにより生成されたテスト・クラスを利用できます。また、Oracle JDeveloper 10gでもこれらのテスト・クラスを生成できます。図1-3に、Oracle JDeveloperのウィザードに表示される、JUnitクラスを生成するためのオプションを示します。
Oracle JDeveloper 10gに同梱されているHTTPアナライザ・ツールには、同じペイロードまたは編集済のペイロードを再送信する便利な方法があります。図1-4に、Oracle JDeveloperに同梱されているHTTPアナライザ・ツールの「リクエストの再送信」コマンドを示します。
図1-4 JDeveloperに同梱されているHTTPアナライザ・ツールの「リクエストの再送信」コマンド
サービスのコンシューマとプロバイダとの相互作用を分析します。相互作用の分析には、次のツールを使用できます。
Oracle JDeveloper 10gのHTTPアナライザを使用すると、IDEから直接WS-I Analyzerを使用できます。IDEからAnalyzerを使用するには、まずWS-IツールのJavaバージョンをダウンロードする必要があります。WS-I AnalyzerによりXML構成ファイルが生成され、レポート生成の進捗状況が表示されます。図1-5に、統合されたWS-I Analyzerコマンドが選択された状態のOracle JDeveloperのHTTPアナライザを示します。
WebServicesAssemblerツールには、WSDLの全体スキャンを実行し、このバージョンのWebServicesAssemblerでWSDLを処理できるかどうかを確認するanalyze
コマンドが用意されています。genProxy
およびgenInterface
コマンドは同じタスクを実行しますが、analyze
はこれらのコマンドとは異なり、コードを生成しません。
関連資料: 『Oracle Application Server Web Services開発者ガイド』の「analyze」を参照してください。 |
Oracle JDeveloper IDEには、WSDL検証コマンドも統合されています。選択されたリソースのタイプに基づいて、ポップアップ・メニューから使用できます。図1-6に、ファイルを比較するOracle JDeveloperのコマンドを示します。この場合、コマンドでは2つのWSDLファイルが比較されます。
この章で説明されたツールの詳細は、次のWebアドレスを参照してください。
WS-Iテスト・ツールの使用方法:
http://www.oracle.com/technology/products/jdev/howtos/10g/WS_WSI/WSI_HowTo.html
WS-I Basic Profile 1.0に準拠したアナライザのデモ:
WS-Iツールのダウンロード:
http://www.ws-i.org/deliverables/workinggroup.aspx?wg=testingtools