目次


Webサービス・プログラミングのヒントと秘訣: JAX-RPCでSOAP添付

Comments

SOAPメッセージ・プロトコルを使用すれば、SOAPメッセージをとおしてMIME添付を送信できます。WSDLは添付に関する記述を提供します。JAX-RPCは添付に関するWSDL記述のマッピングをJava成果物へ提供します。この記事では、SOAPメッセージ内の添付データを送信するために、どのようにしてJAX-RPCマッピングを活用するかを説明します。

WSDL

残念ながら、添付に関するWSDL記述はそれほどに簡単ではありません。リスト1に示されるWSDL(特に太字で表記されたbinding内のエレメント)に注目してください。

リスト1. 添付と関連するWSDL
<?xml version="1.0" encoding="utf-8"?>
<definitions
    xmlns="http://schemas.xmlsoap.org/wsdl/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
    xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
    targetNamespace="urn:attachment.tip"
    xmlns:tns="urn:attachment.tip">
  <message name="empty"/>
  <message name="imageMsg">
    <part name="image" type="xsd:hexBinary"/>
  </message>
  <message name="octetMsg">
    <part name="octet" type="xsd:hexBinary"/>
  </message>
  <portType name="AttachmentTip">
    <operation name="sendImage">
      <input message="tns:imageMsg"/>
      <output message="tns:empty"/>
    </operation>
    <operation name="sendOctet">
      <input message="tns:octetMsg"/>
      <output message="tns:empty"/>
    </operation>
  </portType>
  <binding name="AttachmentBinding" type="tns:AttachmentTip">
    <soap:binding style="rpc"
                  transport="http://schemas.xmlsoap.org/soap/http"/>
    <operation name="sendImage">
      <soap:operation soapAction=""/>
      <input>
        <mime:multipartRelated>
          <mime:part>
            <soap:body use="literal"/>
          </mime:part>
          <mime:part>
            <mime:content part="image" type="image/jpeg"/>
          </mime:part>
        </mime:multipartRelated>
      </input>
      <output>
        <soap:body use="literal"/>
      </output>
    </operation>
    <operation name="sendOctet">
      <soap:operation soapAction=""/>
      <input>
        <mime:multipartRelated>
          <mime:part>
            <soap:body use="literal"/>
          </mime:part>
          <mime:part>
            <mime:content part="octet" type="application/octet-stream"/>
          </mime:part>
        </mime:multipartRelated>
      </input>
      <output>
        <soap:body use="literal"/>
      </output>
    </operation>
  </binding>
  <service name="AttachmentService">
    <port name="AttachmentTip" binding="tns:AttachmentBinding">
      <soap:address
          location="http://localhost:9080/attachment/services/AttachmentTip"/>
    </port>
  </service>
</definitions>

まず、始めの部分からbindingの部分にたどり着くまで、MIME関連の情報が無いことに注目してください。ひとまずbindingのことを忘れ、WSDLの「インターフェース」(portType と message)に注目すれば、2つのoperation(sendImagesendOctet)がそこにあるのがわかります。それぞれが(byte []にマッピングする)hexBinary入力を持ちます。この情報からだけでも、portTypeがJava言語のService Endpoint Interface (SEI)にマッピングすることが比較的容易に予測できます。そのSEIには2つのメソッド(それぞれに1つのbyte[]パラメーター)があります。これは妥当な予測であり、(添付データが)MIMEタイプでなければそれは正しいです。しかし、(添付データが)MIMEタイプの場合、実際のパラメーター・タイプを認識するためにbindingを調べる必要があります。

bindingの中を見れば、これらのoperationの入力のコンテント・タイプは実を言えばhexBinary では無く、MIME(image/jpeg そしてapplication/octet-stream)であるのがわかります。JAX-RPCは、それぞれがjava.awt.Imagejavax.activation.DataHandler にマッピングするように定義します。

サービス・エンドポイント・インターフェース

リスト1のWSDLから生成されたJava言語SEIを、リスト2に表記します。

リスト2. AttachmentTip SEI
package tip.attachment;
import java.awt.Image;
import java.rmi.Remote;
import java.rmi.RemoteException;
import javax.activation.DataHandler;
public interface AttachmentTip extends Remote {
    public void sendImage(Image image) throws RemoteException;
    public void sendOctet(DataHandler octet) throws RemoteException;
}

WSDL内(bindingによればimage/jpegのMIMEコンテントを含む)のsendImageのoperationは、java.awt.Imageパラメーターの付属するJavaプログラム・メソッドになります。これはきれいに整理されています。MIMEタイプからのJavaタイプへの整然としたマッピングを、表1に表記します。

表1 - JAX-RPC による、MIMEタイプからJavaタイプへのマッピング
MIMEタイプJavaタイプ
image/gif, image/jpegjava.awt.Image
text/plainjava.lang.String
multipart/*javax.mail.internet.MimeMultipart
text/xml, application/xmljavax.xml.transform.Source

SendOctetのoperationでそうしたように、この表内に無いMIMEタイプを定義すれば、JAX-RPC実装はjavax.activation.DataHandler にタイプをマッピングします。DataHandler タイプは Java Activation Framework(JAF - 下記の詳細、そして参考文献の章にあるJAFページへのリンクを参照して下さい。)にて定義されています。

クライアント実装

ここからは、使い慣れたJAX-RPC実装を使用してAttachmentTip WSDLからクライアント側マッピングを生成したと想定します。クライアント実装はこれらのマッピング(特にリスト2で示されるAttachmentTip SEI)に依存します。

添付データのクライアント実装は、先ずService からSEIの実装を入手します。リスト3getTip メソッドを見れば、それがどのように実行されるかがわかります。(ServiceFactoryそしてService についての考察は、この記事では取り上げませんので、参考文献でより詳しい情報を入手して下さい。)

sendImage メソッドを呼び出す

SEI実装を一度入手すれば、添付データ(この例では、任意の名前の付いたファイルの中にあります)付きのメソッドを呼び出せます。sendImage の場合、添付データがjava.awt.Imageであるだけのことです。JAX-RPC実装は、全てのイメージが添付データとして送信されることを知り、SOAPメッセージをそれに応じて構築します。添付が関与していることを、クライアント側のプログラマーは知る必要はありません。SendImageへの呼び出しは、リスト3のコードにて太字で表記されています。

リスト3. AttachmentTip クライアント実装の sendImage 呼び出し
package tip.attachment;
import java.awt.Image;
import java.awt.Toolkit;
import java.net.URL;
import java.rmi.RemoteException;
import javax.xml.namespace.QName;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceFactory;
public class AttachmentTipClient {
    static AttachmentTip getTip() throws Exception {
        QName serviceName = new QName(
                "urn:attachment.tip",
                "AttachmentService");
        URL wsdlLocation = new URL(
                "http://localhost:9080/attachment/services/AttachmentTip?wsdl");
        ServiceFactory factory = ServiceFactory.newInstance();
        Service service = factory.createService(wsdlLocation, serviceName);
        QName portName = new QName("", "AttachmentTip");
        return (AttachmentTip) service.getPort(
                portName,
                AttachmentTip.class);
    }
static void sendImage(AttachmentTip tip, String fileName)
            throws RemoteException {
        Toolkit toolkit = Toolkit.getDefaultToolkit();
        Image image = toolkit.createImage(fileName);
        tip.sendImage(image);
    }
    public static void main(String[] args) {
        try {
            AttachmentTip tip = getTip();
sendImage(tip, args[0]);
        }
        catch (Throwable t) {
            t.printStackTrace();
        }
    }
}

sendOctet メソッドの呼び出し

JAX-RPCにマッピングを定義された数少ないMIMEタイプを使えば、何もかもが簡単です(表1参照)。明確なJAX-RPCマッピングと縁の無いMIMEタイプには、javax.activation.DataHandler オブジェクトを使う手が残されています。このクラスはJava Activation Framework (JAF -参考文献を参照してください)の一部ですのでJAFについて多少なりとも知るべきですが、それほどに難しくはありません。DataHandler は(その代わりとして入力ストリームと出力ストリームを含む)javax.activation.DataSource を含みます。大半のJavaプログラミング・タイプとストリームの間で変換(どちらの方向も可能)が比較的容易にできます。そのうえに、activation framework そのものが少しばかり役立ちます。仮に添付データがこのようにファイル内にあれば、activation framework がjavax.activation.FileDataSource クラスを供給しますので、ストリームの段階をバイパスできます。sendOctet を呼び出しDataHandler にそれを手渡す新規のメソッドをリスト3のコードに追加すれば、リスト4のコードができあがります。

リスト4. 完全な AttachmentTip クライアント実装
package tip.attachment;
import java.awt.Image;
import java.awt.Toolkit;
import java.net.URL;
import java.rmi.RemoteException;
import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.xml.namespace.QName;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceFactory;
public class AttachmentTipClient {
    static AttachmentTip getTip() throws Exception {
        QName serviceName = new QName(
                "urn:attachment.tip",
                "AttachmentService");
        URL wsdlLocation = new URL(
                "http://localhost:9080/attachment/services/AttachmentTip?wsdl");
        ServiceFactory factory = ServiceFactory.newInstance();
        Service service = factory.createService(wsdlLocation, serviceName);
        QName portName = new QName("", "AttachmentTip");
        return (AttachmentTip) service.getPort(
                portName,
                AttachmentTip.class);
    }
    static void sendImage(AttachmentTip tip, String fileName)
            throws RemoteException {
        Toolkit toolkit = Toolkit.getDefaultToolkit();
        Image image = toolkit.createImage(fileName);
        tip.sendImage(image);
    }
static void sendOctet(AttachmentTip tip, String fileName)
            throws RemoteException {
        FileDataSource fds = new FileDataSource(fileName);
        DataHandler dh = new DataHandler(fds);
        tip.sendOctet(dh);
    }
    public static void main(String[] args) {
        try {
            AttachmentTip tip = getTip();
            sendImage(tip, args[0]);
sendOctet(tip, args[0]);
        }
        catch (Throwable t) {
            t.printStackTrace();
        }
    }
}

両方の添付用オペレーションに同一のファイルをデータとして使用したことに注目してください。しかし、sendOctet に関して言えば、ファイルのコンテントはapplication/octet-stream です。それが実はイメージなのかどうかなど、それは気にも留めません。application/octet-stream は、FileDataSource のデフォルト・コンテント・タイプなので、ここで使用するうえで大いに役立ちました。仮にデータをファイルとして持つほどに運がよいわけではなく、application/octet-stream 以外の形でそれを送信しなくてはならなければ、独自のDataSource実装を作成する必要があります。それは単純なインターフェースなので、それほどに難しくはないでしょう(それでも、この課題に自主的に取り組まれることをおすすめします)。

まとめ

ここで説明したとおり、JAX-RPCマッピングを介して添付データを送信するのはかなり簡単なことです。アプリケーション・プログラマーは添付データの存在を全く知る必要がありません。最悪でも、プログラマーはDataHandlerDataSource、そしてストリームに気をつかえばよいだけです。


ダウンロード可能なリソース


関連トピック


コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=SOA and web services, XML
ArticleID=245146
ArticleTitle=Webサービス・プログラミングのヒントと秘訣: JAX-RPCでSOAP添付
publish-date=02272004