【发布时间】:2015-11-02 20:07:06
【问题描述】:
INFO - 示例代码
我已经为您设置了示例代码 (SSCCE) 以帮助您跟踪问题:
https://github.com/ljader/test-cxf-base64-marshall
问题
我正在与第 3 方 JAX-WS 服务集成,因此我无法更改 WSDL。
第 3 方网络服务期望 Base64 编码字节对它们执行一些操作 - 他们期望客户端在 SOAP 消息中发送整个字节。 他们不想改用 MTOM / XOP,所以我坚持目前的要求。
我决定使用 CXF 轻松设置示例客户端,它适用于小文件。
但是当我尝试发送 BIG 数据(即 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 的全部数据正在尝试读取 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 二进制文件之前被读取到内存中。
所以我想在 chunks 中从客户端向服务器发送(“流”)数据(因为 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
问题
为什么 CXF/JAXB 将整个 InputStream 加载到内存中 - DataHandler 的目的不是阻止此类实现?
您知道有什么方法可以将 JAXB 行为更改为以不同方式编组 InputStream 吗?
你知道不同的编组器,它们可以处理这样的大数据编组吗?
作为最后的手段,也许您有一些材料的链接,如何创建将数据直接流式传输到服务器的自定义编组器?
【问题讨论】:
标签: jaxb cxf jax-ws marshalling jaxb2