JAXBのバグで@XmlMimeType("application/octet-stream")が効かない

Webサービス(MTOM)を作成してごにょごにょやってたんだけど、1つ壁にぶつかった。
今回はそのことについて書きたいと思う。

まずはWebサービス(MTOM)の作成方法について。

◆サービスクラス

package org.ws.sample.stub.service.impl;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

import javax.activation.DataHandler;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.ParameterStyle;
import javax.jws.soap.SOAPBinding.Style;
import javax.jws.soap.SOAPBinding.Use;
import javax.xml.bind.annotation.XmlMimeType;
import javax.xml.ws.BindingType;

import org.apache.commons.io.IOUtils;
import org.sample.soa.stub.service.MTOMStubService;

@WebService(serviceName = "MTOMStubService", portName = "MTOMStubServicePort", targetNamespace = "http://impl.mtom.service.stub/")
@SOAPBinding(style = Style.DOCUMENT, use = Use.LITERAL, parameterStyle = ParameterStyle.BARE)
@BindingType(value = javax.xml.ws.soap.SOAPBinding.SOAP11HTTP_MTOM_BINDING)
public class MTOMStubServiceImpl implements MTOMStubService {

	public void attach(
			@XmlMimeType("application/octet-stream") DataHandler value,
			String fileName) {
		FileOutputStream fos = null;
		try {
			fos = new FileOutputStream(new File("C:/tmp/" + fileName));
			value.writeTo(fos);
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (fos != null) {
				IOUtils.closeQuietly(fos);
			}
		}
	}
}

基本的には
apache CXF+SpringでWebサービス作成時にWSDLを分離しない方法 - ITエンジニアとして生きる
と同じ感じ。
変わった点は

@BindingType(value = javax.xml.ws.soap.SOAPBinding.SOAP11HTTP_MTOM_BINDING)

で明示的にMTOMサービスであることを定義していること。(SOAP1.1のMTOM)
なお今回のサービスクラスは「C:/tmp/」配下に、指定されたファイル名で添付ファイルを保存するサービスとした。


ちなみにWebサービスクライアントはこんな感じ。

package org.ws.sample.stub.client;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Map;

import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.Service;
import javax.xml.ws.soap.SOAPBinding;

import org.ws.sample.stub.service.MTOMStubService;

public class MTOMStubClient {

	public void execute() throws MalformedURLException {

		URL url = new URL("http://localhost:8888/webapp/ws/MTOMService?wsdl");
		QName qname = new QName(
				"http://impl.mtom.service.stub/",
				"MTOMStubService");

		Service service = Service.create(url, qname);
		MTOMStubService stub = service.getPort(MTOMStubService.class);

		SOAPBinding bind = (SOAPBinding) ((BindingProvider) stub).getBinding();
		bind.setMTOMEnabled(true);
		
		URL file = getClass().getResource("/data.jpg");
		DataHandler dh = new DataHandler(new FileDataSource(new File(
				file.getPath())));
		stub.attach(dh, "attach-data.jpg");
	}
}

こちらも基本的には
JAX-WSで簡単Webサービスクライアント - ITエンジニアとして生きる
とほぼ同じ。
変わった点は

		SOAPBinding bind = (SOAPBinding) ((BindingProvider) stub).getBinding();
		bind.setMTOMEnabled(true);

の部分で、ここでMTOMを有効にしている。



で、本題の困ったこと。
困ったのは

	public void attach(
			@XmlMimeType("application/octet-stream") DataHandler value,
			String fileName) {

の部分。
もし「@XmlMimeType("application/octet-stream") 」がなくて

	public void attach(
			DataHandler value,
			String fileName) {

となっているとすると、データが全てJVM上にロードされるためヒープサイズを超えるファイルを送付することが出来ない。
MTOMによる大容量添付ファイルの送受信

「@XmlMimeType("application/octet-stream") 」はストリーミング技術を使用するためのものであり、JVM上にデータをロードしないため、ヒープサイズに関係なく大容量ファイルがやりとり出来る。


そして動作確認。
大容量ファイルを送付してみるとあっさりOutOfMemoryErrorが発生。。。
えっ?なんで???と思ってWSDLを確認してみたら

  <xs:complexType name="attach">
    <xs:sequence>
      <xs:element minOccurs="0" name="arg0" type="xs:base64Binary"/>
      <xs:element minOccurs="0" name="arg1" type="xs:string"/>
    </xs:sequence>
  </xs:complexType>

となっていた。
あれ??application/octet-streamは???

想定だと

  <xs:complexType name="attach">
    <xs:sequence>
      <xs:element xmlns:ns1="http://www.w3.org/2005/05/xmlmime" 
          ns1:expectedContentTypes="application/octet-stream" 
          minOccurs="0" name="arg0" type="xs:base64Binary"/>
      <xs:element minOccurs="0" name="arg1" type="xs:string"/>
    </xs:sequence>
  </xs:complexType>

っとなるハズなんだが。。。。


いろいろ調べてみてたら
Apache CXF -- MTOM Attachments with JAXB
の中に

Note the use of 'application/octet-stream'. According to the standard, 
you should be able to use any MIME type you like, in order to specify 
the actual content of the attachment. However, due to a defect in the 
JAX-B reference implementation, this won't work.

という記述を発見。
要約すると「JAXBの欠陥で'application/octet-stream'は動作しない」ということ。

なんと・・・・・・

つまり@XmlMimeType("application/octet-stream")が効いておらず、データがJVM上にロードされていたということ。
そしてOutOfMemoryErrorが発生したということ。


さぁ、どうしようかな。。。