目次


Webサービス・プログラミングのヒントと秘訣: 添付ファイル無しでバイナリー・データを送信

Comments

SOAP with Attachments (Sw/A)以外にも、バイナリー・データを交換する方法はいくつかあります。まず複雑な方法に簡単に触れ、それから最も簡素な方法を詳細に渡り考察します。

DIMEのソリューション

もしもそのWebサービス実装がDIMEをサポートするのでしたら、Sw/Aのプロトコルの代わりにDIMEのプロトコルに対処するバインディングを持つラッパーのWSDLを書き込むことができます。しかしながら、このアプローチに対しては2つの大きな懸念を抱くことになります。

  • W3Cの中にあるDIME note は2002年末に期限が切れましたので、DIMEと呼ばれる公式の仕様が存在するわけではありません。公式の仕様が無い限り、Microsoft以外の者がこのプロトコルをサポートし続けることは考えられません。
  • DIMEには、公式のWSDLバインディングがありません。公式のWSDLバインディングが無ければ、与えられたバインディングが全てのベンダーにサポートされるとは考えられません。このため、DIMEバインディングに相互運用性はないのです。

MTOMのソリューション

SOAPのMessage Transmission Optimization Mechanism (MTOM)は、全てのWebサービス・ベンダーにサポートされる可能性の高いバイナリー・データの添付のプロトコルのひとつです。しかし、この記事が作成されている時点ではMTOMの仕様は完成されておりませんので(参考文献を参照)、相互運用可能なMTOMの実装が市場に登場するのにはあと1、2年間ほどの時間を要するでしょう。

添付を使わないソリューション

バイナリー・データの交換で最も簡単なのは、バイト配列(XMLでは、xsd:hexBinary またはxsd:base64Binary のタイプ)を使用する方法です。つまり、添付ファイルを添付ファイルではなくするのです。(SOAPメッセージのパートとは別の)添付ファイルにあるパートのメッセージに現れる raw data の代わりに、raw data はSOAPメッセージそのものに現れます。このソリューションの弱点は、SOAPメッセージの中に raw data がありますので(例えばルーターのような中継機器でのように)たとえ全く使われることがなくても、どのようなXMLパーサーも raw data をスキャンしなくてはなりません。それは特に効率的な選択肢であるわけではなく、ただ単純なだけです。

詳細に渡る例

リスト1に示されるインターフェースを含むレガシーのWebサービスがあるとします。

リスト1. 添付ファイル付きのレガシーAPI
package com.ibm.developerWorks.attachment;
public interface ImageBag extends java.rmi.Remote {
    public void sendImage(java.awt.Image i);
    public java.awt.Image getImage();
}

インターフェースはjava.awt.Imagesを含みます。それがjpeg画像であると想定しましょう。JAX-RPCによれば、これらの画像はimage/jpegのタイプの添付ファイルにマッピングされます。しかし、この添付ファイルをここで使いたくはありません。次にすべきは、元のアプリケーションに呼び出しを委任するラッパーのアプリケーションの作成です。ラッパーのAPI(リスト2を参照)は、画像の代わりにバイト配列を含みます。ラッパーのWSDLはxsd:hexBinaryデータ・タイプを含みます。(この記事の最初と最後にあるcodeアイコンをクリックしてzipファイルをダウンロードすることで得られるEARファイルを取り出すことにより、元のサービスそしてラッパーのサービスの両方のWSDLファイルを見つけることができます。)

リスト2. ラッパーAPI
package com.ibm.developerWorks.attachment;
public interface ImageBagWithNoAttachments extends java.rmi.Remote {
    public void sendImage(byte[] i);
    public byte[] getImage();
}

ここまでのところは順調だと言えますが、ここからが大変な部分(実際のラッパー実装)なのです。実際のサービスにメソッド呼び出しを委任する前に、ラッパー実装はbyte[]java.awt.Image の間で変換をしなくてはなりません。ラッパーAPIの完全な実装は、リスト3にて示されています。これは可能性としてあり得るJ2SE 1.4のソリューションのひとつです。これは結構複雑で、byte[]java.awt.Imageの間でどのように変換を実行するかを詳細に渡り説明するのがこの記事の趣旨ではないのですが、ひとつの情報として取りあえずここにて紹介しておきます。

リスト3. ラッパーAPIの実装
package com.ibm.developerWorks.attachment;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.Iterator;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
public class ImageBagWithNoAttachmentsSoapBindingImpl
        extends Component implements ImageBagWithNoAttachments{
    private ImageBag imageBag = new ImageBagSoapBindingImpl();
    public void sendImage(byte[] i) {
        try {
            ByteArrayInputStream bais = new ByteArrayInputStream(i);
            ImageIO.setUseCache(false);
            imageBag.sendImage(ImageIO.read(bais));
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
    public byte[] getImage() {
        try {
            Image image = imageBag.getImage();
            Iterator iter = ImageIO.getImageWritersByMIMEType("image/jpeg");
            ImageWriter writer = iter.hasNext() ? (ImageWriter) iter.next() : null;
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ImageOutputStream ios = ImageIO.createImageOutputStream(baos);
            writer.setOutput(ios);
            BufferedImage rendImage = null;
            if (image instanceof BufferedImage) {
                rendImage = (BufferedImage) image;
            } else {
                MediaTracker tracker = new MediaTracker(this);
                tracker.addImage(image, 0);
                tracker.waitForAll();
                rendImage = new BufferedImage(image.getWidth(null),
                        image.getHeight(null), 1);
                Graphics g = rendImage.createGraphics();
                g.drawImage(image, 0, 0, null);
            }
            writer.write(new IIOImage(rendImage, null, null));
            writer.dispose();
            return baos.toByteArray();
        }
        catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

クライアント側の状況は、サーバー側の状況に類似します。byte[]java.awt.Imageの間の変換が望まれていることでしょう。そうであるからこそ、クライアント側のコードはサーバー側のコードと類似します。(この記事の最初と最後にあるcodeアイコンをクリックしてzipファイルをダウンロードすれば、実際のクライアント側コードの一部を見ることができます。)

JAX-RPCにサポートされた別の添付のタイプ(attachment types)は全て(通常はストリームを介して)byte[]へ変換できるJavaタイプにマッピングします。それぞれのタイプの実装はこの例とは異なりますが、それでも根本的な発想は一緒です。

サンプル・クライアント

インライン化されたバイナリー・データにバイナリー添付(binary attachment)を変換する主な理由として.NETがSw/Aをサポートしないと言うのがあるため、このサービスにC#クライアントも紹介します。この場合、.NET Framework SDK バージョン 1.1を基にしたクライアントを構築します。

上記のサービス実装にて記述されているとおり、java.awt.Imageオブジェクトをbyte[]に変換しました。この場合、byte[]System.Drawing.Imageの間を行き来します。Javaの場合のように、この変換を実行するには様々な方法があり、これは例のひとつにしか過ぎません。

リスト4にあるC#クライアント・コードは、ファイル・システムから画像をロードして使用可能なsendImage(byte[] i)オペレーションを介してそれをサーバーに送信します。一度送信されれば、クライアントはgetImage()オペレーションを使って同じ画像を回収して別の名前でそのファイル・システムに保存します。

リスト4. C#クライアントの実装
using System;
using System.Drawing;
using System.IO;
using System.Text;
using System.Web.Services;

namespace ImageClient
{
    class ImageClient
    {
        [STAThread]
        static void Main(string[] args)
        {
            if (args.Length < 4) {
	            Console.WriteLine("Insufficient argument list: <host> " +
	            	"<port> <sendImageName> <newImageName>");
            }
            else {	
                string host          = args[0];
                string port          = args[1];
                string sendImageName = args[2];
                string newImageName  = args[3];

ImageBagWithNoAttachmentsService service = 
                	new ImageBagWithNoAttachmentsService();
                service.Url = "http://" + host + ":" + port + 
                	"/AttachmentWar/services/ImageBagWithNoAttachments";

                // Create a cookie container so that the session will be saved 
                // and we can retreive the image.
                service.CookieContainer = new System.Net.CookieContainer();

                ImageClient client = new ImageClient();

                // Turn the image into a byte[] and send it. 
                Console.WriteLine("Sending data to the server.");
                service.sendImage(client.createByteArray(sendImageName));

                Console.WriteLine("");

                // Get the byte[] from the service and turn it into an image.
                Console.WriteLine("Retreiving image data from the server.");
                client.saveAsImage(service.getImage(), newImageName);
            }
        }

		

        public byte[] createByteArray(string imageName) 
        {
            FileInfo fileInfo = new FileInfo(imageName);
            FileStream fileStream = fileInfo.OpenRead();
            byte[] byteArray = new byte[fileInfo.Length];		
            int bytesRead = fileStream.Read(byteArray, 0, byteArray.Length);
            Console.WriteLine("{0} bytes have been read from {1}", 
            	bytesRead.ToString(), imageName);
            return byteArray;
        }


        public void saveAsImage(byte[] bytes, string imageName) 
        {
            MemoryStream memStream = new MemoryStream(bytes);
            System.Drawing.Image image = 
            	System.Drawing.Image.FromStream(memStream);
            image.Save(imageName);
            Console.WriteLine("{0} was created successfully.", imageName);
        }
    }
}

注釈:リスト4にあるImageBagWithNoAttachmentsService オブジェクトは.NETのクライアント・プロキシーを表記します。.NET Framework SDK 1.1と一緒にあるwsdl.exeツールを使って、このオブジェクトは作成されました。このプロキシーを生成する方法に関するより詳しい情報をお求めでしたら、下記の参考文献の章を参照してください。

まとめ

Sw/Aを介する以外にも、バイナリー・データを送信する方法がいくつかあります。この記事では、最も簡単な方法(xsd:hexBinaryを使用してバイナリー・データをインライン化)について詳細に渡って説明しました。xsd:hexBinaryを使ったサービスで、java.awt.Imageを使用してサービスをラッピングしました。


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


関連トピック


コメント

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

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=SOA and web services, XML
ArticleID=245198
ArticleTitle=Webサービス・プログラミングのヒントと秘訣: 添付ファイル無しでバイナリー・データを送信
publish-date=09282004