INFO - Przykładowy kodDlaczego CXF/JAXB przeczytać cały InputStream do pamięci przed etapowy do wiadomości SOAP
Wcześniej skonfigurować przykładowy kod (SSCCE), aby pomóc śledzić problem:
https://github.com/ljader/test-cxf-base64-marshall
problem
mam integracji z 3rd party usługi JAX-WS, więc ja nie mogę Chang e WSDL.
Usługa internetowa innego producenta oczekuje, że Base64 zakodowane bajty, aby wykonać na nich pewną operację - oczekują, że klient wyśle całe bajty w komunikacie SOAP. One nie chcą zmienić na MTOM/XOP, więc utknąłem z bieżącymi wymaganiami.
Postanowiłem użyć CXF do łatwego ustawienia przykładowego klienta i działało dobrze dla małych plików.
Ale gdy próbuję wysłać Big Data, czyli 200MB, CXF/JAXB zgłasza wyjątek:
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)
moich ustaleń
mam błąd gąsienicowe, że w oparciu o typ xsd "base64Binary", to
com.sun.xml.bind.v2.model.impl.RuntimeBuiltinLeafInfoImpl
zdecyduje, że
com.sun.xml.bind.v2.runtime.unmarshaller.Base64Data
powinien obsługiwać rozrządowych danych z
javax.activation.DataHandler
Podczas porządkowania, CAŁKOWITYCH danych z podstawowych InputS tream próbuje zostać odczytanym 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, co powoduje OOME wyjątek.
Problem
CXF wykorzystuje JAXB podczas zestawiania Java obiektów w wiadomości SOAP - podczas zestawiania InputStream, strumień wejściowy CAŁY jest odczytywany w pamięci przed beeing przekształcony Base64 binarny.
Więc chcę wysłać („strumień”) danych od klienta do serwera w kawałkami (od OutputSteam w naziemnego jest owinięty bezpośredni HttpURLConnection), więc mój klient mógłby może obsługiwać dowolną ilość wysyłania danych.
Szczególnie, gdy wiele wątków używa mojego klienta, przesyłanie strumieniowe jest bardzo pożądane przez IMHO.
Nie mam dobrej wiedzy JAX-WS/CXF/JAXB, stąd pytanie.
Jedyne materiały, które znalazłem i może być przydatne to:
Can JAXB parse large XML files in chunks
http://rezarahim.blogspot.com/2010/05/chunking-out-big-xml-with-stax-and-jaxb.html
pytania
Dlaczego CXF/JAXB ładuje cały InputStream do pamięci - nie jest celą DataHandler, aby zapobiec takiej implementacji n?
Czy znasz jakiś sposób, aby zmienić zachowanie JAXB, aby w różny sposób skonfigurować InputStream?
Czy znasz różnych marszałkowców, którzy mogą obsłużyć tak duże gromadzenie danych?
W ostateczności możesz mieć linki do niektórych materiałów, jak stworzyć niestandardowego koordynatora, który będzie przesyłał dane bezpośrednio na serwer?
** 1. ** Czy widziałeś mój przykładowy projekt (https://github.com/ljader/test-cxf-base64-marshall), do którego odwołuję się na początku mojego pytania? ** 2. ** W tym przykładzie robię już to, co sugerujesz, ale OOME nadal występuje - to dlatego, że implementacja JAXB ignoruje użycie DataHandler i nadal buforuje wszystko do pamięci (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) ** 3. ** Czy masz jakieś inne pomysły, jak to naprawić? – ljader