为什么CXF / JAXB在编组到SOAP消息之前将整个InputStream读入内存

时间:2015-08-10 19:25:28

标签: jaxb cxf jax-ws marshalling jaxb2

信息 - 示例代码

我已为您设置示例代码(SSCCE)以帮助您跟踪问题:

https://github.com/ljader/test-cxf-base64-marshall

问题

我正在与第三方JAX-WS服务集成,因此我无法更改 WSDL。

第三方Web服务期望 Base64 编码的字节对它们执行某些操作 - 它们期望客户端在SOAP消息中发送整个字节。 他们不希望更改为MTOM / XOP,因此我一直坚持当前的要求。

我决定使用CXF轻松设置示例客户端,它适用于小文件。

但是当我尝试发送大数据,即200MB时,CXF / JAXB会抛出异常:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at com.sun.xml.bind.v2.util.ByteArrayOutputStreamEx.readFrom(ByteArrayOutputStreamEx.java:75)
at com.sun.xml.bind.v2.runtime.unmarshaller.Base64Data.get(Base64Data.java:196)
at com.sun.xml.bind.v2.runtime.unmarshaller.Base64Data.writeTo(Base64Data.java:312)
at com.sun.xml.bind.v2.runtime.output.UTF8XmlOutput.text(UTF8XmlOutput.java:312)
at com.sun.xml.bind.v2.runtime.XMLSerializer.leafElement(XMLSerializer.java:356)
at com.sun.xml.bind.v2.model.impl.RuntimeBuiltinLeafInfoImpl$PcdataImpl.writeLeafElement(RuntimeBuiltinLeafInfoImpl.java:191)
at com.sun.xml.bind.v2.runtime.MimeTypedTransducer.writeLeafElement(MimeTypedTransducer.java:96)
at com.sun.xml.bind.v2.runtime.reflect.TransducedAccessor$CompositeTransducedAccessorImpl.writeLeafElement(TransducedAccessor.java:254)
at com.sun.xml.bind.v2.runtime.property.SingleElementLeafProperty.serializeBody(SingleElementLeafProperty.java:130)
at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.java:360)
at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsXsiType(XMLSerializer.java:696)
at com.sun.xml.bind.v2.runtime.ElementBeanInfoImpl$1.serializeBody(ElementBeanInfoImpl.java:155)
at com.sun.xml.bind.v2.runtime.ElementBeanInfoImpl$1.serializeBody(ElementBeanInfoImpl.java:130)
at com.sun.xml.bind.v2.runtime.ElementBeanInfoImpl.serializeBody(ElementBeanInfoImpl.java:332)
at com.sun.xml.bind.v2.runtime.ElementBeanInfoImpl.serializeRoot(ElementBeanInfoImpl.java:339)
at com.sun.xml.bind.v2.runtime.ElementBeanInfoImpl.serializeRoot(ElementBeanInfoImpl.java:75)
at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsRoot(XMLSerializer.java:494)
at com.sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:323)
at com.sun.xml.bind.v2.runtime.MarshallerImpl.marshal(MarshallerImpl.java:251)
at javax.xml.bind.helpers.AbstractMarshallerImpl.marshal(AbstractMarshallerImpl.java:95)
at org.apache.cxf.jaxb.JAXBEncoderDecoder.writeObject(JAXBEncoderDecoder.java:617)
at org.apache.cxf.jaxb.JAXBEncoderDecoder.marshall(JAXBEncoderDecoder.java:241)
at org.apache.cxf.jaxb.io.DataWriterImpl.write(DataWriterImpl.java:237)
at org.apache.cxf.interceptor.AbstractOutDatabindingInterceptor.writeParts(AbstractOutDatabindingInterceptor.java:117)
at org.apache.cxf.wsdl.interceptors.BareOutInterceptor.handleMessage(BareOutInterceptor.java:68)
at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308)
at org.apache.cxf.endpoint.ClientImpl.doInvoke(ClientImpl.java:514)
at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:423)
at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:324)
at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:277)
at org.apache.cxf.frontend.ClientProxy.invokeSync(ClientProxy.java:96)
at org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:139)

我的发现

我已经跟踪了基于xsd类型的错误" base64Binary",

  

com.sun.xml.bind.v2.model.impl.RuntimeBuiltinLeafInfoImpl

决定,

  

com.sun.xml.bind.v2.runtime.unmarshaller.Base64Data

应处理来自

的数据编组
  

的javax.activation.DataHandler

在编组期间,来自底层InputStream的WHOLE数据正在尝试读取http://grepcode.com/file/repo1.maven.org/maven2/com.sun.xml.bind/jaxb-impl/2.2.11/com/sun/xml/bind/v2/runtime/unmarshaller/Base64Data.java/#311,这会导致OOME异常。

问题

CXF在将Java对象编组到SOAP消息期间使用JAXB - 在编组InputStream时, WHOLE 输入流在转换为Base64二进制文件之前被读取到内存中。

所以我想在中从客户端向服务器发送(" stream")数据(因为marshaller中的OutputSteam直接包装了HttpURLConnection),所以我的客户端可以处理发送任意数量的数据。

特别是当许多线程使用我的客户端时,流媒体是恕我直言。

我没有很好的JAX-WS / CXF / JAXB知识,因此这个问题。

我发现并且可能有用的唯一材料是:

Can JAXB parse large XML files in chunks

http://rezarahim.blogspot.com/2010/05/chunking-out-big-xml-with-stax-and-jaxb.html

问题

  1. 为什么CXF / JAXB将整个InputStream加载到内存中 - 不是DataHandler purpouse来阻止这样的实现?

  2. 您知道如何将JAXB行为改为不同的输入流吗?

  3. 你知道不同的marshallers,可以处理这样的大数据编组吗?

  4. 作为最后的手段,也许你有一些材料的链接,如何创建自定义编组器,将数据直接流式传输到服务器?

0 个答案:

没有答案