レベル: 中級 Nicholas Chase (nicholas@nicholaschase.com), President, Chase and Chase, Inc.
2003年 7月 10日 2008年 1月 22日 更新 このヒントでは、自ら開発者でもある著者の Nicholas Chase が、SOAPメッセージを作成して送信するプロセスを SOAP with Attachments API for Java (SAAJ) を使って単純化する方法を紹介します。
Web サービスの基礎は、標準化された形式でメッセージを送受信することにより、すべてのシステムがそれらのメッセージを理解できるようにするところにあります。典型的なメッセージ形式は、Simple Object Access Protocol (SOAP) です。SOAPメッセージは、手動で生成して送信することもできます。しかし、(Java API for XML Messaging (JAXM) から派生した) SOAP with Attachments API for Java (SAAJ) を使用すると、接続の作成や、実際のメッセージの作成と送信など、必要なステップの多くを自動化することが可能です。このヒントは、同期 SOAP メッセージの作成と送信の作業を記録したものです。
このプロセスには、次の5つのステップが関係しています。
- SOAP 接続の作成
- SOAP メッセージの作成
- メッセージへの情報の取り込み
- メッセージの送信
- 応答の取得
SAAJ は、Java Web Services Developer Pack 1.2 (「参考文献」を参照) に含まれています。このパッケージには Tomcat Web サーバーのコピーとサンプル・アプリケーションが含まれており、皆さん独自のサービスをホストすることが可能です。
インストールと設定
Java Web Services Developer Pack 1.2 のセットアップは、パッケージに組み込まれている Tomcat Web サーバーからメッセージを送信するようにする場合は、簡単です。この記事で行うように、スタンドアロン・アプリケーションからメッセージを送信するには、以下のステップに従います。
- JWSDP 1.2 を http://java.sun.com/webservices/downloads/webservicespack.html からダウンロードします。
- JWSDP 1.2 を指示にしたがってインストールします。
- Java 1.4 を使用している場合、JWSDP 1.2 に組み込まれている XML 関連のクラスを上書きする必要があります。以下のディレクトリーを作成してください。
<JAVA_HOME>/jre/lib/endorsed
そして、このディレクトリーへ以下のディレクトリーからファイルをコピーします。
<JWSDP_HOME>/jaxp/lib/endorsed
(ここで JAVA_HOME と JWSDP_HOME は、それぞれ Java のインストール・ディレクトリーと JWSDP のインストール・ディレクトリーを表します。)
- 以下のファイルをクラスパスに追加します。
- <JWSDP_HOME>/saaj/lib/saaj-api.jar
- <JWSDP_HOME>/saaj/lib/saaj-impl.jar
- <JWSDP_HOME>/jwsdp-shared/lib/commons-logging.jar
- <JWSDP_HOME>/jwsdp-shared/lib/mail.jar
- <JWSDP_HOME>/jwsdp-shared/lib/activation.jar
- <JWSDP_HOME>/jaxp/lib/endorsed/dom.jar
- <JWSDP_HOME>/jaxp/lib/endorsed/xercesImpl.jar
- <JWSDP_HOME>/jaxp/lib/endorsed/sax.jar
- <JWSDP_HOME>/jaxp/lib/endorsed/xalan.jar
これで、システム上のどこからでもスタンドアロンのプログラムを使ってメッセージを送信することができるはずです。
SOAPメッセージの構造
まずは、メッセージそのものの構造を示すところから始めます。基本的な SOAP メッセージは、ヘッダーと本体という 2 つの主要な部分からなるエンベロープで構成されます。これらの部分をどのように使用するかを決めるのはアプリケーションですが、メッセージ全体の構造は、ある特定の XML 構造に従っていなければなりません。たとえば、次の通りです。
リスト 1. サンプルSOAPメッセージ
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/1999/XMLSchema">
<SOAP-ENV:Header />
<SOAP-ENV:Body>
<ns1:getPrice xmlns:ns1="urn:xmethods-BNPriceCheck"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<isbn xsi:type="xsd:string">0672324229</isbn>
</ns1:getPrice>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
|
このメッセージでは、ヘッダーは空で、本体にペイロードもしくは配信するメッセージが含まれています。メッセージの内容は、本の価格を要求するものです。
メッセージの構造に注目してください。Envelope 要素に Header および Body 要素が含まれており、これら 3 つの要素は http://schemas.xmlsoap.org/soap/envelope/ 名前空間に属しています。このアプリケーションは SOAPConnection を使用してメッセージを送信します。
接続とメッセージの作成
最初のステップは、全体を取りまとめるクラスと接続を作成することです。
リスト 2. 接続の作成
import javax.xml.soap.SOAPConnectionFactory;
import javax.xml.soap.SOAPConnection;
public class SOAPTip {
public static void main(String args[]) {
try {
//First create the connection
SOAPConnectionFactory soapConnFactory =
SOAPConnectionFactory.newInstance();
SOAPConnection connection =
soapConnFactory.createConnection();
//Close the connection
connection.close();
} catch(Exception e) {
System.out.println(e.getMessage());
}
}
} |
このアプリケーションでは、(SAAJ パッケージに含まれている) SOAPConnection を使って直接送信することもできますし、JAXM パッケージに含まれるメッセージング・プロバイダーを使って間接的に送信することもできます。この例では、アプリケーションは、ファクトリーを使って SOAPConnection を作成します。
さらに、ファクトリーは、メッセージそのものも作成します。
リスト 3. メッセージ・オブジェクトの作成
import javax.xml.soap.SOAPConnectionFactory;
import javax.xml.soap.SOAPConnection;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPPart;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPBody;
public class SOAPTip {
public static void main(String args[]) {
try {
//First create the connection
SOAPConnectionFactory soapConnFactory =
SOAPConnectionFactory.newInstance();
SOAPConnection connection =
soapConnFactory.createConnection();
//Next, create the actual message
MessageFactory messageFactory = MessageFactory.newInstance();
SOAPMessage message = messageFactory.createMessage();
//Create objects for the message parts
SOAPPart soapPart = message.getSOAPPart();
SOAPEnvelope envelope = soapPart.getEnvelope();
SOAPBody body = envelope.getBody();
//Close the connection
connection.close();
} catch(Exception e) {
System.out.println(e.getMessage());
}
}
} |
 |
バージョンの違い 使用している SAAJ のバージョンが異なる場合 (Axis 1.2 beta SAAJ など)、 addChildElement の代わりに addBodyElement を使う必要があるかもしれません。 |
|
まず、MessageFactory を使ってメッセージそのものを作成します。このメッセージには、中身が空の基本的な構成要素 (envelope やheader など) が既に含まれています。SOAPPart にはenvelope が含まれ、envelope には本体が含まれています。SOAPBody など、必要なオブジェクトへの参照を作成します。
次に、SOAPBody に情報を取り込みます。
リスト 4. 本体への情報の取り込み
...
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPElement;
public class SOAPTip {
public static void main(String args[]) {
try {
...
//Create objects for the message parts
SOAPPart soapPart = message.getSOAPPart();
SOAPEnvelope envelope = soapPart.getEnvelope();
SOAPBody body = envelope.getBody();
//Populate the body
//Create the main element and namespace
SOAPElement bodyElement =
body.addChildElement(envelope.createName("getPrice" ,
"ns1",
"urn:xmethods-BNPriceCheck"));
//Add content
bodyElement.addChildElement("isbn").addTextNode("0672324229");
//Save the message
message.saveChanges();
//Check the input
System.out.println("\nREQUEST:\n");
message.writeTo(System.out);
System.out.println();
//Close the connection
connection.close();
} catch(Exception e) {
System.out.println(e.getMessage());
}
}
} |
SOAP メッセージの本体には、通常の XML 要素と同様に getPrice などの子要素を追加することができます。したがって、通常の DOM 要素の場合のように、isbn 要素とそのテキスト・ノードを追加することができます。
SAAJ の場合、外部ファイルを使用してメッセージの SOAPPart を直接作成することも可能です。例えば、prepped.msg ファイルには最初のリストに示した XML 構造が含まれており、文書を手動で作成する代わりにこのファイルを呼び出すことができます。
リスト 5. 外部ファイルからメッセージを作成する
...
import javax.xml.soap.SOAPElement;
import java.io.FileInputStream;
import javax.xml.transform.stream.StreamSource;
public class SOAPTip {
public static void main(String args[]) {
...
//Create objects for the message parts
SOAPPart soapPart = message.getSOAPPart();
SOAPEnvelope envelope = soapPart.getEnvelope();
SOAPBody body = envelope.getBody();
//Populate the Message
StreamSource preppedMsgSrc = new StreamSource(
new FileInputStream("prepped.msg"));
soapPart.setContent(preppedMsgSrc);
//Save the message
message.saveChanges();
...
}
} |
StreamSource クラスは通常 XSL 変換の際に使われますが、ここでは FileInputStream を取得するためだけに使用しています。こうして、送信可能な SOAP メッセージの用意ができました。
メッセージの送信
同期メッセージの場合、SOAP メッセージの送信とそれに対する応答の受信は、1つのステップで実行されます。
リスト 6. メッセージの送信
...
public class SOAPTip {
public static void main(String args[]) {
...
//Check the input
System.out.println("\nREQUEST:\n");
message.writeTo(System.out);
System.out.println();
//Send the message and get a reply
//Set the destination
String destination =
"http://services.xmethods.net:80/soap/servlet/rpcrouter";
//Send the message
SOAPMessage reply = connection.call(message, destination);
//Close the connection
connection.close();
...
}
} |
 |
SOAPAction ヘッダー
アクセスしている Web サービスによっては、サービスを特定するために SOAPAction ヘッダーを送信するよう要求するエラーが返ってくるかもしれません。これに対処するには、以下のように、メッセージに MIMEHeaders を設定します。
MimeHeaders headers = message.getMimeHeaders();
headers.addHeader("SOAPAction", "urn:requiredSOAPAction");
|
|
|
実際のメッセージは、call() メソッドを使って送信されます。このメソッドは、引数としてメッセージそのものと宛先を取り、SOAPMessage の型を持つ応答を返します。JAXM の以前のバージョンでは、宛先は Endpoint オブジェクトもしくは URLEndpoint でなければなりませんでしたが、現在のバージョンではオブジェクトでありさえすれば問題ありません。この例では、XMethods にホストされている「本の価格確認」用の Web サービスを使用します。この Web サービスは、本の ISBN を含む要求が送られてくると、その本の価格を返します。
call() メソッドは、返されるSOAPMessage を受け取るまでブロックされています。
応答
返される SOAPMessage (reply) は、送信したメッセージと同じ形式の SOAP メッセージです。そのため、通常の XML メッセージと同じように操作することができます。SOAP では、XSLT を使って応答を直接変換することができます。
リスト 7. 応答を読み出す
...
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamResult;
public class SOAPTip {
public static void main(String args[]) {
try {
...
//Send the message
SOAPMessage reply = connection.call(message, destination);
//Check the output
System.out.println("\nRESPONSE:\n");
//Create the transformer
TransformerFactory transformerFactory =
TransformerFactory.newInstance();
Transformer transformer =
transformerFactory.newTransformer();
//Extract the content of the reply
Source sourceContent = reply.getSOAPPart().getContent();
//Set the output for the transformation
StreamResult result = new StreamResult(System.out);
transformer.transform(sourceContent, result);
System.out.println();
//Close the connection
connection.close();
...
}
} |
Transformer オブジェクトは、通常の XSLT アプリケーションの場合と同じようにして作成します。この例では、内容を出力したいだけなので、スタイル・シートは使いません。ここで出力する内容は、(添付ファイルを含むことのできる SOAP メッセージそのものではなく) メッセージの SOAP 部分全体です。必要なら、処理の前にエンベロープと本体を抽出することもできます。この例では、結果の出力先は単純に System.out にしていますが、変換で通常利用できる任意のものを選択することができます。そして、通常どおりに変換します。
図1. SOAP 要求および応答
次なるステップは
この単純なアプリケーションは、受信したメッセージを出力するだけのものですが、XML 文書から非常に容易に情報を抽出することが可能です。また、このヒントではメッセージの送受信を同期させた例を示しましたが、任意にダウンロード可能な JAXM API では、SOAPConnection の代わりに ProviderConnection オブジェクトを使用することにより、非同期配信用のメッセージ・プロバイダーを使用することができます。このメッセージ・プロバイダーは、メッセージが正常に送達されるまで、そのメッセージを保持しています。
JAXM では、特殊な SOAP メッセージ (SOAP-RP または ebXML メッセージなど) を簡単に作成できるようにするプロファイル を使用することができます。
参考文献
著者について  | 
|  | Nicholas Chase は、「Studio B」の Author であり、Lucent Technologies や Sun Microsystems、Oracle、the Tampa Bay Buccaneers などの会社で Web サイト開発に携わってきました。彼は高校の物理の先生であり、低レベル放射性廃棄物施設の管理者であり、オンライン SF 雑誌の編集者であり、マルチメディアのエンジニアであり、Oracle インストラクターです。最近は、アメリカのフロリダ州 Clearwater にある Site Dynamics Interactive Communications に最高技術責任者として勤務していました。また『XML Primer Plus』(Sams 刊) を始めとする 4 冊の Web 開発に関する書籍の著者でもあります。彼は読者からの感想を楽しみにしています。メールアドレスは、nicholas@nicholaschase.com です。 |
記事の評価
|