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が発生したということ。
さぁ、どうしようかな。。。