I am building a WSDL-first java web-service using CXF2.7.7 that uses MTOM. My intention is to be able to upload large ( multiple Gigabytes) files through this web-service Below is a fragment of my the WSDL
<!-- WSDL for MTOM service -->
<wsdl:types>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://objectstoreservice.example.com/" attributeFormDefault="unqualified"
elementFormDefault="unqualified" targetNamespace="http://objectstoreservice.example.com/">
<xs:element name="objectReqParam" type="tns:objectReqParam"/>
<xs:element name="objectRespParam" type="tns:objectRespParam"/>
<xs:complexType name="objectReqParam">
<xs:sequence>
<xs:element minOccurs="0" name="objName" type="xs:string"/>
<xs:element minOccurs="0" maxOccurs="1" name="ObjData"
type="xs:base64Binary" xmime:expectedContentTypes="application/octet-stream"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="objectRespParam">
<xs:sequence>
<xs:element minOccurs="0" name="respCode" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
</wsdl:types>
<wsdl:message name="objectUploadRequest">
<wsdl:part name="objectReqParam" element="tns:objectReqParam">
</wsdl:part>
</wsdl:message>
<wsdl:message name="objectUploadResponse">
<wsdl:part name="objectRespParam" element="tns:objectRespParam">
</wsdl:part>
</wsdl:message>
<wsdl:portType name="ObjectStoreService">
<wsdl:operation name="uploadObject">
<wsdl:input name="objectUploadRequest" message="tns:objectUploadRequest"/>
<wsdl:output name="objectUploadResponse" message="tns:objectUploadResponse"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="ObjectStoreServiceServiceSoapBinding" type="tns:ObjectStoreService">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="uploadObject">
<soap:operation soapAction="" style="document"/>
<wsdl:input name="objectUploadRequest">
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output name="objectUploadResponse">
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="ObjectStoreServiceService">
<wsdl:port name="ObjectStoreServicePort"
binding="tns:ObjectStoreServiceServiceSoapBinding">
<soap:address location="http://localhost:9090/ObjectStoreServicePort"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
My intention is to use MTOM ( as it would be evident from the element type="xs:base64Binary" xmime:expectedContentTypes="application/octet-stream) for uploading objects through this service.
I have also configured my jax WS end-point to use MTOM as seen from my Server side Spring configuration file fragment below
<beans xmlns="http://www.springframework.org/schema/beans"
... />
<jaxws:endpoint xmlns:objectstore="http://objectstoreservice.example.com/"
id="ObjectStoreServiceHTTP" address="http://localhost:9090/ObjectStoreServicePort"
serviceName="objectstore:ObjectStoreServiceService"
endpointName="objectstore:ObjectStoreServiceEndpoint"
implementor="com.example.objectstoreservice.server.ObjectStoreServiceImpl">
<jaxws:properties>
<!-- MTOM properties -->
<entry key="mtom-enabled" value="true"/>
<entry key="attachment-directory" value="/tmp/mtomattachments"/>
<entry key="attachment-memory-threshold" value="100000"/>
</jaxws:properties>
</jaxws:endpoint>
</beans>
And similarly I have also configured my jax-ws client to use MTOM as seen from my client side spring configuration file below
<beans xmlns="http://www.springframework.org/schema/beans ..../>
<jaxws:client id="objectStoreService"
serviceName="objectstore:ObjectStoreServiceService"
endpointName="objectstore:ObjectStoreServiceEndpoint"
address="http://localhost:9090/ObjectStoreServicePort"
serviceClass="com.example.objectstoreservice.ObjectStoreService">
<jaxws:properties>
<!-- MTOM properties -->
<entry key="mtom-enabled" value="true"/>
<entry key="attachment-memory-threshold" value="100000"/>
<entry key="attachment-directory" value="/tmp/mtomattachments"/>
<entry key="javax.xml.ws.client.connectionTimeout"
value="10000000" />
<entry key="javax.xml.ws.client.requestTimeout"
value="10000000" />
<jaxws:properties>
</jaxws:client>
And I am also including a relevant fragment of my Java code for client below
/* --- Web-service client
*************************************************************************** */
package com.example.objectstoreservice.client;
import java.util.List;
import java.awt.Image;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.xml.namespace.QName;
import com.example.objectstoreservice.ObjectStoreService;
import com.example.objectstoreservice.ObjectReqParam;
import com.example.objectstoreservice.ObjectRespParam;
import javax.xml.ws.Binding;
import javax.xml.ws.BindingProvider;
public final class ObjectStoreServiceTester {
ObjectStoreService objectStoreService;
Binding binding ;
public ObjectStoreService getObjectStoreService() {
return objectStoreService;
}
// I haven't shown the code that sets the port object - objectStoreService
// Take my worrd for it - I have the right port object
public void setObjectStoreService(ObjectStoreService objectStoreService) {
this.objectStoreService = objectStoreService;
}
public void enableMTOM () {
binding = ((BindingProvider)objectStoreService).getBinding();
((SOAPBinding)binding).setMTOMEnabled(true);
}
public void testObjectStoreService()
{
enableMTOM();
System.out.println("Now uploading a data file to service");
ObjectReqParam objReqParam = new ObjectReqParam() ;
objReqParam.setObjName("File");
String fileName="C:/root/opt/files/ToSend.jpg";
FileDataSource inFileDataSource=new FileDataSource(fileName);
DataHandler dataHandler = new DataHandler(inFileDataSource);
System.out.println("Check content-type from dataHandler : " + dataHandler.getContentType());
System.out.println("Check obj signature from dataSource : " + dataHandler.getDataSource());
System.out.println("Check file-name from dataSource : " + dataHandler.getDataSource().getName());
objReqParam.setObjData(dataHandler);
System.out.println("Now uploading file:" + fileName);
objectStoreService.uploadObject(objReqParam);
System.out.println("Object upload successful");
}
}
When run - this client runs fine for small attachments, but gives following error for large attachments ( based on my JVM heap space allocated)
** An exception occured while executing the Java class. null: InvocationTargetException: Java heap space **
Below is the full stack trace. *Please let me know if you see something amiss or if I should be doing something different *
*- And BTW I have seen some other posts with similar problems and am wondering if anyone has done MTOM for large files.Is there a way I can explicitly force it to stream the file or force it to send in small chunks ?
*
MTOM: An exception occured while executing the Java class. null
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor
.java:217)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor
.java:153)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor
.java:145)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProje
ct(LifecycleModuleBuilder.java:84)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProje
ct(LifecycleModuleBuilder.java:59)
at org.apache.maven.lifecycle.internal.LifecycleStarter.singleThreadedBu
ild(LifecycleStarter.java:183)
at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(Lifecycl
eStarter.java:161)
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:320)
at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:156)
at org.apache.maven.cli.MavenCli.execute(MavenCli.java:537)
at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:196)
at org.apache.maven.cli.MavenCli.main(MavenCli.java:141)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.
java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAcces
sorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Laun
cher.java:290)
at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.jav
a:230)
at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(La
uncher.java:409)
at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:
352)
Caused by: org.apache.maven.plugin.MojoExecutionException: An exception occured
while executing the Java class. null
at org.codehaus.mojo.exec.ExecJavaMojo.execute(ExecJavaMojo.java:346)
at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(Default
BuildPluginManager.java:101)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor
.java:209)
... 19 more
Caused by: java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.
java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAcces
sorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.codehaus.mojo.exec.ExecJavaMojo$1.run(ExecJavaMojo.java:291)
at java.lang.Thread.run(Thread.java:744)
Caused by: java.lang.OutOfMemoryError: Java heap space
at com.sun.xml.bind.v2.util.ByteArrayOutputStreamEx.readFrom(ByteArrayOu
tputStreamEx.java:75)
at com.sun.xml.bind.v2.runtime.unmarshaller.Base64Data.get(Base64Data.ja
va:196)
at com.sun.xml.bind.v2.runtime.unmarshaller.Base64Data.writeTo(Base64Dat
a.java:312)
at com.sun.xml.bind.v2.runtime.output.UTF8XmlOutput.text(UTF8XmlOutput.j
ava:312)
at com.sun.xml.bind.v2.runtime.XMLSerializer.leafElement(XMLSerializer.j
ava:356)
at com.sun.xml.bind.v2.model.impl.RuntimeBuiltinLeafInfoImpl$PcdataImpl.
writeLeafElement(RuntimeBuiltinLeafInfoImpl.java:183)
at com.sun.xml.bind.v2.runtime.MimeTypedTransducer.writeLeafElement(Mime
TypedTransducer.java:96)
at com.sun.xml.bind.v2.runtime.reflect.TransducedAccessor$CompositeTrans
ducedAccessorImpl.writeLeafElement(TransducedAccessor.java:256)
at com.sun.xml.bind.v2.runtime.property.SingleElementLeafProperty.serial
izeBody(SingleElementLeafProperty.java:130)
at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBean
InfoImpl.java:361)
at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsXsiType(XMLSerialize
r.java:696)
at com.sun.xml.bind.v2.runtime.property.SingleElementNodeProperty.serial
izeBody(SingleElementNodeProperty.java:158)
at com.sun.xml.bind.v2.runtime.ElementBeanInfoImpl$1.serializeBody(Eleme
ntBeanInfoImpl.java:161)
at com.sun.xml.bind.v2.runtime.ElementBeanInfoImpl$1.serializeBody(Eleme
ntBeanInfoImpl.java:131)
at com.sun.xml.bind.v2.runtime.ElementBeanInfoImpl.serializeBody(Element
BeanInfoImpl.java:333)
at com.sun.xml.bind.v2.runtime.ElementBeanInfoImpl.serializeRoot(Element
BeanInfoImpl.java:340)
at com.sun.xml.bind.v2.runtime.ElementBeanInfoImpl.serializeRoot(Element
BeanInfoImpl.java:76)
at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsRoot(XMLSerializer.j
ava:494)
at com.sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:
323)
at com.sun.xml.bind.v2.runtime.MarshallerImpl.marshal(MarshallerImpl.jav
a:251)
at javax.xml.bind.helpers.AbstractMarshallerImpl.marshal(AbstractMarshal
lerImpl.java:95)
at org.apache.cxf.jaxb.JAXBEncoderDecoder.writeObject(JAXBEncoderDecoder
.java:612)
at org.apache.cxf.jaxb.JAXBEncoderDecoder.marshall(JAXBEncoderDecoder.ja
va:240)
at org.apache.cxf.jaxb.io.DataWriterImpl.write(DataWriterImpl.java:169)
at org.apache.cxf.interceptor.AbstractOutDatabindingInterceptor.writePar
ts(AbstractOutDatabindingInterceptor.java:114)
at org.apache.cxf.interceptor.BareOutInterceptor.handleMessage(BareOutIn
terceptor.java:68)
at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseIntercept
orChain.java:272)
at org.apache.cxf.endpoint.ClientImpl.doInvoke(ClientImpl.java:565)
at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:474)
at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:377)
at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:330)
at org.apache.cxf.frontend.ClientProxy.invokeSync(ClientProxy.java:96)
And another peculiar thing If I check the SOAP message I still see the attachment going "inline" as base64 encoded and not as a separate part ... atleast the CXF logging shows that to me ...
This problem was result of the spring configuration relating to MTOM NOT getting picked up due to some strange reason - while I have not figured out why - I could alleviate this by programatically adding setting the MTOM related properties to my binding class - and then MTOM worked fine. One caveat though - I did away with my https transport for now ( am using plain http transport )