レベル: 中級 Andre Tost, Senior Technical Staff Member, FIT Team, IBM China Development Lab
2004年 8月 17日 この記事は、カスタム・シリアライゼーションへの<xsd:any/>エレメントの使用法について説明するこのシリーズの前回の記事「カスタム・シリアライゼーションに<xsd:any/>エレメントを使用する」の続編です。この過去の記事はjavax.xml.soap.SOAPElementの読み取りと処理に焦点を合わせましたが、今回はその作成法について説明をします。
「カスタム・シリアライゼーションに<xsd:any/>エレメントを使用する」(参考文献を参照)の記事では、どのようにカスタム・シリアライゼーションに<xsd:any/>エレメントを使用し、このエレメントをjavax.xml.soap.SOAPElementタイプであるJavaオブジェクトにマッピングするかを説明しました。現存のWebサービスから戻された応答を手動で構文解析するためにこのインターフェースをクライアントがどのように使用するかを、このコードの用例は示しました。この記事はサーバー側に注目します。どのようにSAAJ(SOAP API for Attachments in Java)を使用してWebサービスの実装クラス内に応答メッセージを作成できるかを説明します。
<xsd:any/> エレメントのおさらい
<xsd:any/>エレメントがどのようにしてSOAPメッセージのカスタム作成と構文解析を支援するかをもう一度簡単に考察いたしましょう。このエレメントはXML Schema内の任意のXMLコンテントを表記します。言いかえれば、スキーマのワイルドカード(wildcard)と解釈する事ができます。JAX-RPCエンジンはランタイム時にその内容を知ることができませんので、それを中核のSAAJインターフェースのひとつであるjavax.xml.soap.SOAPElementのインスタンスとしてサーバーまたはクライアントに受け渡します。SAAJを介して処理したいと望むスキーマ内のエレメントの代わりに<xsd:any/>を採用することにより、この振る舞いを利用する事ができます。一般的に、正しいJAX-RPC helperコードを生成するためだけにこの置換を実行し、WSDLファイル内にスキーマの元バージョンを結局は保管することになるのでしょう。
JAX-RPC バージョン1.1は、対応するエンジンがタイプ・マッピング(type-mapping)を実行するかどうかをユーザーに選択させるツールを提供する事を義務づけます。これは<xsd:any/>使用時と類似する効果を生みます。つまり、エンジンはXMLコンテントをJavaコンテント(そしてその逆方向)にマッピングしようとせず、javax.xml.soap.SOAPElement インターフェースを利用しようとするのです。
下記にある、どのようにしてSAAJでSOAP応答メッセージを構築するかを説明する記述は、両方のケースに等しく当てはまります。
サービス・エンドポイント・インターフェース
JAX-RPCでは、サービス・エンドポイント・インターフェース(SEI)がそれぞれのWebサービスを表記します。それは基本的にはWSDLportType をJavaタイプにマッピングし、ローカルのJavaプロキシー・クラスを介してのWebサービスの消費そしてSEIの実装によるWebサービスの供給を可能にします。
SEI内のjavax.xml.soap.SOAPElementのいかなるオカレンス(occurrence)をも介するWebサービスにSAAJが使われているかを簡単に検出できます。もしもSEIのメソッドが入力パラメーターとしてこのインターフェースを使用すれば、着信中のSOAP要求メッセージがJavaタイプにマッピングされていないことを意味します。もしもメソッドがjavax.xml.soap.SOAPElementのインスタンスを戻すなら、応答メッセージがマッピングされていないことを意味します。
この例では、2種類の異なるSEIを生成できます。1つはサーバー側にてjavax.xml.soap.SOAPElementを使い、そしてもう1つはクライアント側にてJavaタイプのマッピングを使います。ここではサーバー側のみに注目しますが、この記事の最初または最後にあるCodeアイコンをクリックすれば、クライアント側をも含む両方のSEIの用例をダウンロードできます。
これは、SEI(サービス・エンドポイント・インターフェース)を生成するうえで使用するWSDLファイルからの抜粋です。
リスト1.<xsd:any/> エレメントを使用するWSDLの抜粋
<wsdl:types>
<schema elementFormDefault="qualified"
targetNamespace="http://any.webservices.ibm.com"
xmlns="http://www.w3.org/2001/XMLSchema">
<element name="getOrder">
<complexType>
<sequence>
<element name="searchCriteria" nillable="true" type="xsd:string"/>
</sequence>
</complexType>
</element>
<element name="getOrderResponse">
<complexType>
<sequence>
<xsd:any maxOccurs="unbounded"/>
</sequence>
</complexType>
</element>
</schema>
</wsdl:types>
|
応答メッセージが1つだけエレメント(つまり<xsd:any/>)を含むことに注目してください。そして、これがSEIです。
リスト2. サービス・エンドポイント・インターフェース(SEI)
package com.ibm.webservices.any;
import javax.xml.soap.SOAPElement;
public interface OrderManager extends java.rmi.Remote {
public SOAPElement[] getOrder(java.lang.String searchCriteria) throws
java.rmi.RemoteException;
}
|
ここでおわかりのとおり、javax.xml.soap.SOAPElementインスタンスの配列として応答を返信します。
応答メッセージ
より詳細にわたりサービス実装に注目する前に、その実装にて作成できるSOAPメッセージの構造を説明します。上記のWSDL抜粋からメッセージの構造を派生させる手段がないことをすでにご存知のことと思います。クライアント・コードを生成する前にWSDLファイルに追加するオリジナルのXML Schemaに、この定義は含まれます。
リスト3. 完全なXML Schema
<wsdl:types>
<schema elementFormDefault="qualified" targetNamespace=
"http://any.webservices.ibm.com" xmlns="http://www.w3.org/2001/XMLSchema">
<element name="getOrder">
<complexType>
<sequence>
<element name="searchCriteria" nillable="true" type="xsd:string"/>
</sequence>
</complexType>
</element>
<complexType name="Order">
<sequence>
<element name="createDate" nillable="true" type="xsd:dateTime"/>
<element name="customer" nillable="true" type="xsd:string"/>
<element maxOccurs="unbounded" name="lineItems" nillable="true" type=
"impl:LineItem"/>
</sequence>
</complexType>
<complexType name="LineItem">
<sequence>
<element name="itemDesc" nillable="true" type="xsd:string"/>
<element name="itemNumber" nillable="true" type="xsd:string"/>
</sequence>
</complexType>
<element name="getOrderResponse">
<complexType>
<sequence>
<element name="getOrderReturn" nillable="true" type="impl:Order"/>
</sequence>
</complexType>
</element>
</schema>
</wsdl:types>
|
そして、これが上記のスキーマに続くSOAP応答メッセージの例です。
リスト4. SOAPメッセージのサンプル
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<getOrderResponse xmlns="http://any.webservices.ibm.com">
<getOrderReturn>
<createDate>2004-07-03T22:57:45.359Z</createDate>
<customer>Bill Smith</customer>
<lineItems>
<itemDesc>This is a line item</itemDesc>
<itemNumber>12345</itemNumber>
</lineItems>
</getOrderReturn>
</getOrderResponse>
</soapenv:Body>
</soapenv:Envelope>
|
これがサービス実装クラスにて作成するメッセージです。
Webサービス実装
一般的に、WSDLファイルからSEIを生成するJAX-RPCツールは実際のサービス実装のためにスケルトンを生成したりもします。この実装では、SEIが定義するjavax.xml.soap.SOAPElement インスタンスを作成する必要があります。これにより、SAAJプログラミングの領域にさらに深くはまり込むことになります。
ここでは適切な応答エレメントを構築するのに必要なステップに詳しく案内します。javax.xml.soap.SOAPFactoryタイプのファクトリーを介してjavax.xml.soap.SOAPElementのインスタンスが作成されますので、まずファクトリーを作成すべきです。そのうえ、SAAJではjavax.xml.soap.Nameインターフェースに表記される名前を、それぞれのエレメントは必要とします。Nameインスタンスはファクトリーを介して作成されませんが、その代わりにそのためにjavax.xml.soap.SOAPEnvelopeのインスタンスを必要とします。ここでもこのインスタンスはファクトリー(つまり、javax.xml.soap.MessageFactoryタイプのうちの1つ)を介して作成されます。ここで問われるであろう質問に答えれば、実際のエレメントの作成を開始する前に3つの別々のファクトリーを本当に必要とするのです。
リスト5. SAAJファクトリー・インスタンスを作成
...
MessageFactory messageFactory = MessageFactory.newInstance();
SOAPMessage message = messageFactory.createMessage();
SOAPEnvelope envelope = message.getSOAPPart().getEnvelope();
SOAPFactory factory = SOAPFactory.newInstance();
...
|
ここで応答メッセージのコンテントを作成する準備ができました。このコードの大半が同じパターンの繰り返しで、これよりも効率的なコードを容易に書き出せます。それから、この例ではどのようなエラー・ハンドリングをも挿入しませんが、どの製造レベルのコードでもそれを除外することは適切だとは言えません。
リスト6. SAAJエレメントを作成
...
// use the factory to create a new element
// the Name is created via the SOAPEnvelope, and we must define a
// namespace for each element in the body (to be WS-I compliant)
SOAPElement getOrderReturn = factory.createElement(envelope.createName(
"getOrderReturn", "","http://any.webservices.ibm.com"));
// now start adding children and build the full response message
// hardcode the text content for the sakes of simplicity
SOAPElement createDate = getOrderReturn.addChildElement(envelope.createName(
"createDate", "", "http://any.webservices.ibm.com"));
createDate.addTextNode("2004-07-03T22:57:45.359Z");
SOAPElement customer =
getOrderReturn.addChildElement(envelope.createName(
"customer", "", "http://any.webservices.ibm.com"));
customer.addTextNode("Bill Smith");
SOAPElement lineItems =
getOrderReturn.addChildElement(envelope.createName(
"lineItems", "", "http://any.webservices.ibm.com"));
SOAPElement itemDesc =
lineItems.addChildElement(envelope.createName(
"itemDesc", "", "http://any.webservices.ibm.com"));
itemDesc.addTextNode("This is a line item");
SOAPElement itemNumber =
lineItems.addChildElement(envelope.createName(
"itemNumber", "", "http://any.webservices.ibm.com"));
itemNumber.addTextNode("12345");
...
|
最後に、javax.xml.soap.SOAPElementの配列にgetOrderReturnと言う名のエレメントを追加します。(<xsd:any/>エレメントのmaxOccurs属性の値を”unbounded”に定義したことを忘れないでください。)
リスト7. 戻り配列を構築
...
SOAPElement[] elements = new SOAPElement[1];
elements[0] = getOrderReturn;
return elements;
...
|
現存のDOM文書から応答を作成
多くの場合、エレメント1つごとに応答メッセージを構築せず、現存するバックエンドから回収された可能性のある既存のXML文書を活用するサービス実装を作成したいはずです。この場合、ただ単に新規のXMLメッセージに構築しなおすためだけに、現存するXMLを構文解析したいとは思わないでことしょう。
SAAJ 1.2実装はaddDocument()と呼ばれるjavax.xml.soap.SOAPBodyインターフェースの新規メソッドを紹介しました。そのうえ、(J2EEバージョン1.4にも含まれる)SAAJ 1.2に関して言えば、いくつかのSAAJインターフェースはそれぞれのDOMインターフェースから継承されます。例えば、javax.xml.soap.SOAPElementはorg.w3c.dom.Elementインターフェースを拡張し、メッセージを構築するために通常のDOMプログラミング使用を許可します。まだSAAJ 1.2のサポートは広く普及していませんので、上記の環境ではSAAJ 1.1インターフェースを使いました。
まとめ
SAAJ仕様は、SOAPメッセージをゼロから構築するのを支援するインターフェースとクラスを定義します。どの(JAX-RPCにて定義される)XMLタイプからJavaタイプ(そしてJavaタイプからXMLタイプ)へのタイプ・マッピングの使用をもこれで回避できます。その典型的な使用例は、カスタム・シリアライゼーションが必要とされる場合(例えば、必須のメッセージのXML SchemaがJAX-RPCエンジンにより完全にサポートされていない状態)、またはタイプ・マッピングが時代遅れか少なくとも冗長になるほどに現存するコードがすでにXML成果物を取り扱っている場合です。
この記事は、エレメント方式で応答メッセージを構築するサーバー側サービス実装の例を示しました。現存のXMLパートの取り扱いをより簡単にするための追加的なメソッドを、SAAJ 1.2は導入します。
参考文献
著者について  | 
|  | Andre Tostは、ミネソタ州ロチェスターにあるIBM Software Groupで、ソフトウェア・エンジニア顧問として勤務しています。ドイツのBerufsakademie Stuttgartで電気エンジニアリングの学位を取得し、過去6年間に渡り、さまざまなオブジェクト指向の開発プロジェクトに携わってきました。現在は、IBM WebSphere Solution Components開発のTechnical Architectureグループで働いています。 |
記事の評価
|