In this Java web services tutorial, we are going to discuss how MTOM (Message Transmission Optimization Mechanism) can be used to optimize binary data transfer through web services with JAX-WS (Java API for XML-based Web Services). We will go from background of MTOM and its usages to development of a simple web services application that can transfer large binary data (upload and download files) in the optimized way.

 

1. Why MTOM?

By default, binary data is converted to base64Binary or hexBinary XML data type within a SOAP envelope, meaning that the raw bytes are encoded as a String using base64 technique. For example, the following XML snippet is content of such a SOAP message:

<?xml version="1.0" ?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
	<S:Body>
		<ns2:upload xmlns:ns2="http://server.mtom.ws.codejava.net/">
			<arg0>binary.png</arg0>
			<arg1>iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoL...5CYII=</arg1>
		</ns2:upload>
	</S:Body>
</S:Envelope>
Look at the text inside the element <arg1> - it is the encoded form of the binary data in base64 format. Basically, the base64 encoding technique bloats the original data by a factor of 1.33x (with UTF-8 encoding) or 2.66x (with UTF-16), so it becomes very inefficient when dealing with a large amount of data. To overcome this drawback, MTOM is defined as a specification for optimizing the transmission of this kind of data type in SOAP messages, and XOP (XML-binary Optimized Packaging) is the concrete implementation. The following example is content of a HTTP POST request which is generated by MTOM/XOP:

POST /codejava/fileService HTTP/1.1
Accept: text/xml, multipart/related
Content-Type: multipart/related;start="<rootpart*8b39dc38-7f35-437f-920f-b99b6b2c9888@example.jaxws.sun.com>";
	type="application/xop+xml";boundary="uuid:8b39dc38-7f35-437f-920f-b99b6b2c9888";start-info="text/xml"
SOAPAction: "http://server.mtom.ws.codejava.net/FileTransfererImpl/uploadRequest"
User-Agent: JAX-WS RI 2.2.4-b01
Host: localhost:8787
Connection: keep-alive
Content-Length: 2154

--uuid:8b39dc38-7f35-437f-920f-b99b6b2c9888
Content-Id: <rootpart*8b39dc38-7f35-437f-920f-b99b6b2c9888@example.jaxws.sun.com>
Content-Type: application/xop+xml;charset=utf-8;type="text/xml"
Content-Transfer-Encoding: binary

<?xml version="1.0" ?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
	<S:Body>
		<ns2:upload xmlns:ns2="http://server.mtom.ws.codejava.net/">
			<arg0>binary.png</arg0>
			<arg1>
				<xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" 
					href="cid:187eff8e-fc5c-4aa5-8a89-1e09e153ade6@example.jaxws.sun.com">
				</xop:Include>
			</arg1>
		</ns2:upload>
	</S:Body>
</S:Envelope>

--uuid:8b39dc38-7f35-437f-920f-b99b6b2c9888
Content-Id: <187eff8e-fc5c-4aa5-8a89-1e09e153ade6@example.jaxws.sun.com>
Content-Type: application/octet-stream
Content-Transfer-Encoding: binary

[binary octet stream]
Here, the request’s content type becomes multipart/related in which the SOAP message and the binary data are separated as individual parts of a MIME message. The SOAP message itself doesn’t contain the binary data. Instead, it has a reference to the part that contains the actual binary data:

<xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" 
	href="cid:187eff8e-fc5c-4aa5-8a89-1e09e153ade6@example.jaxws.sun.com">
</xop:Include>
And the binary data is attached to the request as a MIME attachment which is outside the SOAP message.

 

2. Enabling MTOM in JAX-WS

In JAX-WS, it’s easy to enable MTOM for a web service endpoint by using either the @MTOM or @BindingType annotations. At the client side, MTOM can be enabled either by passing a new instance of MTOMFeature class when getting a reference to the web service endpoint (port), or by calling the SOAPBinding.setMTOMEnabled(true) method on the binding provider object. Here are the usages and examples in details.

Enabling MTOM for the web service endpoint

The following example illustrates a web service endpoint is annotated with the @MTOM annotation:

import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.xml.ws.soap.MTOM;


@WebService
@MTOM
public class MyWebService {
	
	@WebMethod
	public void upload(byte[] data) {
		// implementation details...	
	}
}


That enables MTOM support for the MyWebService endpoint. The @MTOM annotation has two optional parameters:

  • enabled: specifies whether MTOM feature is enabled (true) or disabled (false).
  • threshold: specifies the size (in bytes) above which the binary data will be sent as attachment. This would be useful to enable MTOM only for data which is larger than a specified amount.
This example shows MTOM is used but disabled:

@WebService
@MTOM(enabled = false)
public class MyWebService {
}
This example shows MTOM is enabled only for binary data which is larger than 10KB (10240 bytes):

@WebService
@MTOM(threshold = 10240)
public class MyWebService {
}
An alternative way is using the @BindingType annotation with an appropriate value for the SOAP version used. For example:

  • Enabling MTOM with SOAP version 1.1:
    import javax.xml.ws.BindingType;
    import javax.xml.ws.soap.SOAPBinding;
    
    @WebService
    @BindingType(value = SOAPBinding.SOAP11HTTP_MTOM_BINDING)
    public class MyWebService {
    }
  • Enabling MTOM with SOAP version 1.2:
    import javax.xml.ws.BindingType;
    import javax.xml.ws.soap.SOAPBinding;
    
    @WebService
    @BindingType(value = SOAPBinding.SOAP12HTTP_MTOM_BINDING)
    public class MyWebService {
    }
From the above examples, we can see that using the @MTOM annotation is preferred as its succinct and flexibility (enabled/disabled and threshold).

 

Enabling MTOM for the client

The following example shows how to enable MTOM at the client by passing a new instance of the MTOMFeature class when getting a proxy reference the web service endpoint:

import javax.xml.ws.soap.MTOMFeature;

MyWebServiceService service = new MyWebServiceService();
MyWebService port = service.getMyWebServicePort(new MTOMFeature());
Suppose that the MyWebServiceService and MyWebService classes are generated by the wsimport tool. And similar to the @MTOM annotation, we can also specify the enabled and threshold parameters in the MTOMFeature class’ constructor like this:

boolean enabled = true;
int threshold = 10240;
MyWebService port = service.getMyWebServicePort(new MTOMFeature(enabled, threshold));
And here’s an alternative way, calling the SOAPBinding.setMTOMEnabled(true) method:

MyWebServiceService service = new MyWebServiceService();
MyWebService port = service.getMyWebServicePort();

BindingProvider provider = (BindingProvider) port;
SOAPBinding soapBinding = (SOAPBinding) provider.getBinding();
soapBinding.setMTOMEnabled(true);
So far we have understood the advantages of MTOM and how to apply it for a web service end point and client. Now, let’s go through a complete sample application.

 

3. Code Java Web Service Endpoint

The following class is for a web service endpoint implementation that offers two operations for binary data transfer - files upload and download:

package net.codejava.ws.mtom.server;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.soap.MTOM;


/**
 * A web service endpoint implementation that demonstrates the usage of
 * MTOM (Message Transmission Optimization Mechanism).
 * @author www.codejava.net
 *
 */
@WebService
@MTOM(enabled = true, threshold = 10240)
public class FileTransferer {
	@WebMethod
	public void upload(String fileName, byte[] imageBytes) {
		
		String filePath = "e:/Test/Server/Upload/" + fileName;
		
		try {
			FileOutputStream fos = new FileOutputStream(filePath);
			BufferedOutputStream outputStream = new BufferedOutputStream(fos);
			outputStream.write(imageBytes);
			outputStream.close();
			
			System.out.println("Received file: " + filePath);
			
		} catch (IOException ex) {
			System.err.println(ex);
			throw new WebServiceException(ex);
		}
	}
	
	@WebMethod
	public byte[] download(String fileName) {
		String filePath = "e:/Test/Server/Download/" + fileName;
		System.out.println("Sending file: " + filePath);
		
		try {
			File file = new File(filePath);
			FileInputStream fis = new FileInputStream(file);
			BufferedInputStream inputStream = new BufferedInputStream(fis);
			byte[] fileBytes = new byte[(int) file.length()];
			inputStream.read(fileBytes);
			inputStream.close();
			
			return fileBytes;
		} catch (IOException ex) {
			System.err.println(ex);
			throw new WebServiceException(ex);
		}		
	}
}
As we can see, the @MTOM annotation is used to enable binary transfer optimization with the threshold of 10KB.

NOTE: You should prepare a file to be sent to the client and correct the file path according to your environment in the download() method.

 

4. Coding Java Server Program for Web Services

Write a simple server program (console application) that publishes the web services with the following code:

package net.codejava.ws.mtom.server;

import javax.xml.ws.Endpoint;

/**
 * A simple web service server.
 * @author www.codejava.net
 *
 */
public class WebServiceServer {

	public static void main(String[] args) {
		String bindingURI = "http://localhost:9898/codejava/fileService";
		FileTransferer service = new FileTransferer();
		Endpoint.publish(bindingURI, service);
		System.out.println("Server started at: " + bindingURI);
	}
}
Run this program to start the server. We should see the following output in the console:

Server started at: http://localhost:9898/codejava/fileService
 

5. Code Java Client Program for Web Services

Use the wsimport tool to generate client code for the above web service endpoint (see instructions included in the attached project), then write code for the application client program as follows:

package net.codejava.ws.mtom.client;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

import javax.xml.ws.soap.MTOMFeature;

/**
 * A client program that demonstrates how to use MTOM to optimize binary data
 * transfer with JAX-WS web services.
 * @author www.codejava.net
 *
 */
public class WebServiceAppClient {

	public static void main(String[] args) {
		// connects to the web service
		FileTransfererService service = new FileTransfererService();
		FileTransferer port = service.getFileTransfererPort(new MTOMFeature(10240));
		
		String fileName = "binary.png";
		String filePath = "e:/Test/Client/Upload/" + fileName;
		File file = new File(filePath);
		
		// uploads a file
		try {
			FileInputStream fis = new FileInputStream(file);
			BufferedInputStream inputStream = new BufferedInputStream(fis);
			byte[] imageBytes = new byte[(int) file.length()];
			inputStream.read(imageBytes);
			
			port.upload(file.getName(), imageBytes);

			inputStream.close();
			System.out.println("File uploaded: " + filePath);
		} catch (IOException ex) {
			System.err.println(ex);
		}		
		
		// downloads another file
		fileName = "camera.png";
		filePath = "E:/Test/Client/Download/" + fileName;
		byte[] fileBytes = port.download(fileName);
		
		try {
			FileOutputStream fos = new FileOutputStream(filePath);
			BufferedOutputStream outputStream = new BufferedOutputStream(fos);
			outputStream.write(fileBytes);
			outputStream.close();
			
			System.out.println("File downloaded: " + filePath);
		} catch (IOException ex) {
			System.err.println(ex);
		}
	}
}
As we can see, MTOM is enabled by passing an instance of the MTOMFeature class with an integer indicates a threshold value. This program uploads a file by sending a chunk of bytes to the server, and downloads a file by receiving an array of bytes from the server.

NOTE: You should prepare a file to be uploaded to the server and correct the file path according to your environment.

 

6. Test binary data transfer via web services and inspect SOAP Messages

Now, execute the application client program to test out the web service. We should see the following output at the client:

File uploaded: e:/Test/Client/Upload/binary.png
File downloaded: E:/Test/Client/Download/camera.png
And output at the server:

Server started at: http://localhost:9898/codejava/fileService
Received file: e:/Test/Server/Upload/binary.png
Sending file: e:/Test/Server/Download/camera.png
We inspect the SOAP messages in details by using a monitoring tool such as TCP/IP Monitor in Eclipse IDE. Here’s the summary (WSDL request/response is omitted):

  • For upload operation:
    • Client Request:
      POST /codejava/fileService HTTP/1.1
      Accept: text/xml, multipart/related
      Content-Type: multipart/related;start="<rootpart*0d441e76-7247-40de-a6e5-d49c9bf4a172@example.jaxws.sun.com>";
      		type="application/xop+xml";
      		boundary="uuid:0d441e76-7247-40de-a6e5-d49c9bf4a172";start-info="text/xml"
      SOAPAction: "http://server.mtom.ws.codejava.net/FileTransferer/uploadRequest"
      User-Agent: JAX-WS RI 2.2.4-b01
      Host: localhost:9898
      Connection: keep-alive
      Content-Length: 2133
      
      --uuid:0d441e76-7247-40de-a6e5-d49c9bf4a172
      Content-Id: <rootpart*0d441e76-7247-40de-a6e5-d49c9bf4a172@example.jaxws.sun.com>
      Content-Type: application/xop+xml;charset=utf-8;type="text/xml"
      Content-Transfer-Encoding: binary
      
      <?xml version="1.0" ?>
      <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
      	<S:Body>
      		<ns2:upload xmlns:ns2="http://server.mtom.ws.codejava.net/">
      			<arg0>binary.png</arg0>
      			<arg1>
      				<Include xmlns="http://www.w3.org/2004/08/xop/include" 
      					href="cid:876daf6e-de3b-41ff-b8dc-0d85b3a8951e@example.jaxws.sun.com"/>
      			</arg1>
      		</ns2:upload>
      	</S:Body>
      </S:Envelope>
      
      --uuid:0d441e76-7247-40de-a6e5-d49c9bf4a172
      Content-Id: <876daf6e-de3b-41ff-b8dc-0d85b3a8951e@example.jaxws.sun.com>
      Content-Type: application/octet-stream
      Content-Transfer-Encoding: binary
      
      [binary octet stream]
    • Server Response:
      HTTP/1.1 200 OK
      Transfer-encoding: chunked
      Content-type: multipart/related;start="<rootpart*e8611ccc-2442-4d56-8b17-d15eb6138c24@example.jaxws.sun.com>";
      	type="application/xop+xml";
      	boundary="uuid:e8611ccc-2442-4d56-8b17-d15eb6138c24";start-info="text/xml"
      Date: Fri, 29 Nov 2013 09:57:13 GMT
      
      --uuid:e8611ccc-2442-4d56-8b17-d15eb6138c24
      Content-Id: <rootpart*e8611ccc-2442-4d56-8b17-d15eb6138c24@example.jaxws.sun.com>
      Content-Type: application/xop+xml;charset=utf-8;type="text/xml"
      Content-Transfer-Encoding: binary
      
      <?xml version="1.0" ?>
      <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
      	<S:Body>
      		<ns2:uploadResponse xmlns:ns2="http://server.mtom.ws.codejava.net/"></ns2:uploadResponse>
      	</S:Body>
      </S:Envelope>
      
      --uuid:e8611ccc-2442-4d56-8b17-d15eb6138c24--
  • For download operation:
    • Client Request:
      POST /codejava/fileService HTTP/1.1
      Accept: text/xml, multipart/related
      Content-Type: multipart/related;start="<rootpart*95d74f1f-5c21-474b-9481-400785c18ae5@example.jaxws.sun.com>";
      	type="application/xop+xml";
      	boundary="uuid:95d74f1f-5c21-474b-9481-400785c18ae5";start-info="text/xml"
      SOAPAction: "http://server.mtom.ws.codejava.net/FileTransferer/downloadRequest"
      User-Agent: JAX-WS RI 2.2.4-b01
      Host: localhost:9898
      Connection: keep-alive
      Content-Length: 493
      
      --uuid:95d74f1f-5c21-474b-9481-400785c18ae5
      Content-Id: <rootpart*95d74f1f-5c21-474b-9481-400785c18ae5@example.jaxws.sun.com>
      Content-Type: application/xop+xml;charset=utf-8;type="text/xml"
      Content-Transfer-Encoding: binary
      
      <?xml version="1.0" ?>
      <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
      	<S:Body>
      		<ns2:download xmlns:ns2="http://server.mtom.ws.codejava.net/">
      			<arg0>camera.png</arg0>
      		</ns2:download>
      	</S:Body>
      </S:Envelope>
      
      --uuid:95d74f1f-5c21-474b-9481-400785c18ae5--
    • Server Response:
      HTTP/1.1 200 OK
      Transfer-encoding: chunked
      Content-type: multipart/related;start="<rootpart*b581cf1e-507a-4b6a-b1c6-0bf16e1278c6@example.jaxws.sun.com>";
      	type="application/xop+xml";
      	boundary="uuid:b581cf1e-507a-4b6a-b1c6-0bf16e1278c6";start-info="text/xml"
      Date: Fri, 29 Nov 2013 10:30:12 GMT
      
      --uuid:b581cf1e-507a-4b6a-b1c6-0bf16e1278c6
      Content-Id: <rootpart*b581cf1e-507a-4b6a-b1c6-0bf16e1278c6@example.jaxws.sun.com>
      Content-Type: application/xop+xml;charset=utf-8;type="text/xml"
      Content-Transfer-Encoding: binary
      
      <?xml version="1.0" ?>
      <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
      	<S:Body>
      		<ns2:downloadResponse xmlns:ns2="http://server.mtom.ws.codejava.net/">
      			<return>
      				<Include xmlns="http://www.w3.org/2004/08/xop/include" 
      					href="cid:6a5fab52-948d-44aa-b89e-072e05af6a60@example.jaxws.sun.com"/>
      			</return>
      		</ns2:downloadResponse>
      	</S:Body>
      </S:Envelope>
      
      
      --uuid:b581cf1e-507a-4b6a-b1c6-0bf16e1278c6
      Content-Id: <6a5fab52-948d-44aa-b89e-072e05af6a60@example.jaxws.sun.com>
      Content-Type: application/octet-stream
      Content-Transfer-Encoding: binary
      
      [binary octet stream]
So far we have finished discussing what MTOM is and how it is applied in JAX-WS in terms of optimization of binary data transfer via web services.

 

References:

 

Other Java Web Services Tutorial:


About the Author:

is certified Java programmer (SCJP and SCWCD). He started programming with Java in the time of Java 1.4 and has been falling in love with Java since then. Make friend with him on Facebook and watch his Java videos you YouTube.



Attachments:
Download this file (JAX-WS-MTOM-Example.zip)JAX-WS-MTOM-Example.zip[Source codes and compile instructions]20 kB

Add comment

   


Comments 

#6avn2020-02-10 16:27
what is the maximum file size limit has to be ?
Quote
#5Giovanni2019-03-31 04:26
Very well explained and very useful. Thanks.
Quote
#4Tommy Manik2016-10-10 23:11
Nice article. Very well explained. Please upload the WSDL file for reference. Thank you
Quote
#3Vinila2016-03-05 11:45
Nice article. Very well explained. Please upload the WSDL file for reference. Thank you
Quote
#2Frank2016-01-06 14:12
Well done, clear and thorough sample code. Thanks.
Quote