Sending and receiving attachments with JAX WS and MTOM


In this article, we will learn about transmitting binary data like images in JAX-WS
without MTOM and with MTOM.

What is MTOM?

MTOM stands for Message Transmission Optimization Mechanism

It provides an efficient mechanism for transmitting the binary data (like Image,pdf etc) between client and web service
It uses XOP to serialize and deserialize the binary data in a more efficient way.

What is XOP?

XOP stands for XML Binary Optimized Packaging
The reason behind MTOM optimization is XOP

XOP process extracts binary data(like images,pdf etc) from the XML and replaces it with the external references (also called XOP infoset)
XOP creates a package by serializing XOP infoset and the binary data as a MIME attachment.
On the other side, XOP package is deserialized to get the required binary data.

Since the document is sent outside the SOAP envelope as a MIME attachment in the form of binary data, XML Base64 encoding is not required.
In this way XOP packages the binary data in an efficient way.

Let’s create a web service without MTOM for downloading the image


When we don’t use MTOM with JAX-WS then binary data like image or pdf files will be transmitted as Base64 encoded format along with the SOAP message itself.

Create image service interface

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.kb.ws;
 
import java.awt.Image;
 
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.Style;
 
@WebService
@SOAPBinding(style=Style.RPC)
public interface ImageService {
    //Download image from server
        @WebMethod
        Image downloadImage(String imageName);
    
 
}
package com.kb.ws;

import java.awt.Image;

import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.Style;

@WebService
@SOAPBinding(style=Style.RPC)
public interface ImageService {
	//Download image from server
		@WebMethod
		Image downloadImage(String imageName);
	

}


We have just created the web service interface and added a method for downloading the image.

Create image service implementation class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.kb.ws;
 
import java.awt.Image;
import java.io.File;
import java.io.IOException;
 
import javax.imageio.ImageIO;
import javax.jws.WebService;
 
@WebService(endpointInterface="com.kb.ws.ImageService")
public class ImageServiceImpl implements ImageService{
 
    @Override
    public Image downloadImage(String imageName) {
        File image = new File("D:\\images\\"+imageName);
        try {
            return ImageIO.read(image);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
 
}
package com.kb.ws;

import java.awt.Image;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.jws.WebService;

@WebService(endpointInterface="com.kb.ws.ImageService")
public class ImageServiceImpl implements ImageService{

	@Override
	public Image downloadImage(String imageName) {
		File image = new File("D:\\images\\"+imageName);
		try {
			return ImageIO.read(image);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

}


In this web service implementation class, we have provided the implementation for downloadImage() method which takes imageName as a parameter and returns the image read from the specified path.

Create a publisher class

1
2
3
4
5
6
7
8
9
10
11
12
package com.kb.ws;
 
import javax.xml.ws.Endpoint;
 
public class ImagePublisher {
    public static void main(String[] args) {
        Endpoint endpoint = Endpoint.create(new MtomImageServiceImpl());
        endpoint.publish("http://localhost:8889/ws/image");
        //Endpoint.publish("http://localhost:8889/ws/image", new ImageServiceImpl());
    }
 
}
package com.kb.ws;

import javax.xml.ws.Endpoint;

public class ImagePublisher {
	public static void main(String[] args) {
		Endpoint endpoint = Endpoint.create(new MtomImageServiceImpl());
		endpoint.publish("http://localhost:8889/ws/image");
		//Endpoint.publish("http://localhost:8889/ws/image", new ImageServiceImpl());
	}

}


Run the above class to publish the end point

Create image download client

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.kb.ws.client;
 
import java.awt.Image;
import java.net.URL;
 
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
 
import com.kb.ws.MtomImageService;
 
public class ImageDownloadClient {
    public static void main(String[] args) throws Exception {
        URL url = new URL("http://localhost:8889/ws/image?wsdl");
 
        QName qname = new QName("http://ws.kb.com/", "ImageServiceImplService");
 
        Service service = Service.create(url, qname);
 
        MtomImageService imageService = service.getPort(MtomImageService.class);
        Image image = imageService.downloadImage("logo.jpg");
        
        //Display it using Swing GUI
        JFrame imageFrame = new JFrame();
        imageFrame.setSize(400, 400);
        JLabel imageLabel = new JLabel(new ImageIcon(image));
        imageFrame.add(imageLabel);
        imageFrame.setVisible(true);
    }
}
package com.kb.ws.client;

import java.awt.Image;
import java.net.URL;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;

import com.kb.ws.MtomImageService;

public class ImageDownloadClient {
	public static void main(String[] args) throws Exception {
		URL url = new URL("http://localhost:8889/ws/image?wsdl");

		QName qname = new QName("http://ws.kb.com/", "ImageServiceImplService");

		Service service = Service.create(url, qname);

		MtomImageService imageService = service.getPort(MtomImageService.class);
		Image image = imageService.downloadImage("logo.jpg");
		
		//Display it using Swing GUI
		JFrame imageFrame = new JFrame();
		imageFrame.setSize(400, 400);
        JLabel imageLabel = new JLabel(new ImageIcon(image));
        imageFrame.add(imageLabel);
        imageFrame.setVisible(true);
	}
}


Run the above class and check the output

/mtom_download_image_output

We can see that , image called logo.jpg stored in D://images folder is downloaded and kept in the JFrame.

Let’s check the output through SOAP UI


Request

1
2
3
4
5
6
7
8
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ws="http://ws.kb.com/">
   <soapenv:Header/>
   <soapenv:Body>
      <ws:downloadImage>
         <arg0>logo.jpg</arg0>
      </ws:downloadImage>
   </soapenv:Body>
</soapenv:Envelope>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ws="http://ws.kb.com/">
   <soapenv:Header/>
   <soapenv:Body>
      <ws:downloadImage>
         <arg0>logo.jpg</arg0>
      </ws:downloadImage>
   </soapenv:Body>
</soapenv:Envelope>


Response

1
2
3
4
5
6
7
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
   <S:Body>
      <ns2:downloadImageResponse xmlns:ns2="http://ws.kb.com/">
         <return>iVBORw0KGgoAAAANSUhEUgAABAAAAAQACAIAAADwf7zUAACAAElEQVR42ux9BVhV2fq+93/v7</return>
      </ns2:downloadImageResponse>
   </S:Body>
</S:Envelope>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
   <S:Body>
      <ns2:downloadImageResponse xmlns:ns2="http://ws.kb.com/">
         <return>iVBORw0KGgoAAAANSUhEUgAABAAAAAQACAIAAADwf7zUAACAAElEQVR42ux9BVhV2fq+93/v7</return>
      </ns2:downloadImageResponse>
   </S:Body>
</S:Envelope>


In the response line number 4, we can see that binary data is coming as inline with SOAP body

SOAP UI Output

WithoutMtomSoapUIRequestResponse

I have extracted only one line of encoded output response.
It’s actually thousands of lines coming as inline with SOAP body.
I recommend you to check it through SOAP UI to understand how big the response is.

Let’s see how MTOM makes it more efficient in transmitting binary data

How to enable MTOM on server side

If we want to enable server to send attachment via MTOM, Just annotate web service implementation class with @MTOM annotation.

Create a web service end point interface for uploading and downloading the images

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.kb.ws;
 
import java.awt.Image;
 
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.Style;
 
@WebService
@SOAPBinding(style=Style.RPC)
public interface MtomImageService {
    
    //Upload image to server
    @WebMethod
    String uploadImage(Image imageData);
    
    //Download image from server
    @WebMethod
    Image downloadImage(String imageName);
 
}
package com.kb.ws;

import java.awt.Image;

import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.Style;

@WebService
@SOAPBinding(style=Style.RPC)
public interface MtomImageService {
	
	//Upload image to server
	@WebMethod
	String uploadImage(Image imageData);
	
	//Download image from server
	@WebMethod
	Image downloadImage(String imageName);

}


We have created the interface and added 2 methods one for upload and other one for downloading the image.

Create a web service implementation class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package com.kb.ws;
 
import java.awt.Image;
import java.io.File;
import java.io.IOException;
 
import javax.imageio.ImageIO;
import javax.jws.WebService;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.soap.MTOM;
 
@WebService(endpointInterface="com.kb.ws.MtomImageService")
@MTOM(threshold=10)
public class MtomImageServiceImpl implements MtomImageService{
 
    @Override
    public String uploadImage(Image imageData) {
        if(null != imageData){
            //Write a code to store the image
            return "Image uploaded successfully";
        }
        //If image data is not there, then throw below exception
        throw new WebServiceException("Image Upload Failed!");
    }
 
    @Override
    public Image downloadImage(String imageName) {
        File image = new File("D:\\images\\"+imageName);
        try {
            return ImageIO.read(image);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
 
}
package com.kb.ws;

import java.awt.Image;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.jws.WebService;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.soap.MTOM;

@WebService(endpointInterface="com.kb.ws.MtomImageService")
@MTOM(threshold=10)
public class MtomImageServiceImpl implements MtomImageService{

	@Override
	public String uploadImage(Image imageData) {
		if(null != imageData){
			//Write a code to store the image
			return "Image uploaded successfully";
		}
		//If image data is not there, then throw below exception
		throw new WebServiceException("Image Upload Failed!");
	}

	@Override
	public Image downloadImage(String imageName) {
		File image = new File("D:\\images\\"+imageName);
		try {
			return ImageIO.read(image);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

}


We can see that @MTOM annotation is provided on top of the class to enable MTOM
we have provided the implementations for both download and upload image methods declared in the interface.

Note:

The @MTOM annotation has 2 optional parameters and they are enabled and threshold.

Enabled parameter
The enabled parameter has a boolean value and indicates MTOM is enabled for the JAX-WS endpoint.
By default it’s true.

Threshold parameter
When threshold is given, binary data above this size in bytes will be XOP encoded or sent as an attachment
If an attachment is smaller than the size specified in threshold parameter, the runtime will inline the binary data as base64 binary instead of creating an attachment.
Default value is zero which means MTOM will be applicable for all the attachments.

Cretae the end point publisher class

1
2
3
4
5
6
7
8
9
10
11
12
package com.kb.ws;
 
import javax.xml.ws.Endpoint;
 
public class MtomImagePublisher {
    public static void main(String[] args) {
        Endpoint endpoint = Endpoint.create(new MtomImageServiceImpl());
        endpoint.publish("http://localhost:8888/ws/mtom/image");
        //Endpoint.publish("http://localhost:8888/ws/mtom/image", new MtomImageServiceImpl());
    }
 
}
package com.kb.ws;

import javax.xml.ws.Endpoint;

public class MtomImagePublisher {
	public static void main(String[] args) {
		Endpoint endpoint = Endpoint.create(new MtomImageServiceImpl());
		endpoint.publish("http://localhost:8888/ws/mtom/image");
		//Endpoint.publish("http://localhost:8888/ws/mtom/image", new MtomImageServiceImpl());
	}

}

Run the above class to publish the web service

Create a web service download image client

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.kb.ws.client;
 
import java.awt.Image;
import java.net.URL;
 
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
 
import com.kb.ws.MtomImageService;
 
public class MtomImageDownloadClient {
    public static void main(String[] args) throws Exception {
        URL url = new URL("http://localhost:8888/ws/mtom/image?wsdl");
 
        QName qname = new QName("http://ws.kb.com/", "MtomImageServiceImplService");
 
        Service service = Service.create(url, qname);
 
        MtomImageService imageService = service.getPort(MtomImageService.class);
        Image image = imageService.downloadImage("logo.jpg");
        
        //Display it using Swing GUI
        JFrame imageFrame = new JFrame();
        imageFrame.setSize(400, 400);
        JLabel imageLabel = new JLabel(new ImageIcon(image));
        imageFrame.add(imageLabel);
        imageFrame.setVisible(true);
    }
}
package com.kb.ws.client;

import java.awt.Image;
import java.net.URL;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;

import com.kb.ws.MtomImageService;

public class MtomImageDownloadClient {
	public static void main(String[] args) throws Exception {
		URL url = new URL("http://localhost:8888/ws/mtom/image?wsdl");

		QName qname = new QName("http://ws.kb.com/", "MtomImageServiceImplService");

		Service service = Service.create(url, qname);

		MtomImageService imageService = service.getPort(MtomImageService.class);
		Image image = imageService.downloadImage("logo.jpg");
		
		//Display it using Swing GUI
		JFrame imageFrame = new JFrame();
		imageFrame.setSize(400, 400);
        JLabel imageLabel = new JLabel(new ImageIcon(image));
        imageFrame.add(imageLabel);
        imageFrame.setVisible(true);
	}
}


Check the output by running the above client class

We will be able to see the image in the frame as below

/mtom_download_image_output

How to enable MTOM on client side

If we want to enable client to send an attachment via MTOM, we can do that by setting true to the MTOM enable flag under SOAPBinding class as below

1
2
3
BindingProvider bp = (BindingProvider) imageService;
SOAPBinding binding = (SOAPBinding) bp.getBinding();
binding.setMTOMEnabled(true);
BindingProvider bp = (BindingProvider) imageService;
SOAPBinding binding = (SOAPBinding) bp.getBinding();
binding.setMTOMEnabled(true);


Complete client code to upload image to the server side

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.kb.ws.client;
 
import java.awt.Image;
import java.io.File;
import java.io.IOException;
import java.net.URL;
 
import javax.imageio.ImageIO;
import javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.Service;
import javax.xml.ws.soap.SOAPBinding;
 
import com.kb.ws.MtomImageService;
 
public class MtomImageUploadClient {
    public static void main(String[] args) throws IOException {
 
        URL url = new URL("http://localhost:8888/ws/mtom/image?wsdl");
 
        QName qname = new QName("http://ws.kb.com/", "MtomImageServiceImplService");
 
        Service service = Service.create(url, qname);
 
        MtomImageService imageService = service.getPort(MtomImageService.class);
        Image image = ImageIO.read(new File("D:\\images\\logo.jpg"));
        
        //Enabling MTOM in client code
        BindingProvider bindingProvider = (BindingProvider) imageService;
        SOAPBinding binding = (SOAPBinding) bindingProvider.getBinding();
        binding.setMTOMEnabled(true);
 
    }
}
package com.kb.ws.client;

import java.awt.Image;
import java.io.File;
import java.io.IOException;
import java.net.URL;

import javax.imageio.ImageIO;
import javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.Service;
import javax.xml.ws.soap.SOAPBinding;

import com.kb.ws.MtomImageService;

public class MtomImageUploadClient {
	public static void main(String[] args) throws IOException {

		URL url = new URL("http://localhost:8888/ws/mtom/image?wsdl");

		QName qname = new QName("http://ws.kb.com/", "MtomImageServiceImplService");

		Service service = Service.create(url, qname);

		MtomImageService imageService = service.getPort(MtomImageService.class);
		Image image = ImageIO.read(new File("D:\\images\\logo.jpg"));
		
		//Enabling MTOM in client code
		BindingProvider bindingProvider = (BindingProvider) imageService;
		SOAPBinding binding = (SOAPBinding) bindingProvider.getBinding();
		binding.setMTOMEnabled(true);

	}
}


Let’s check the download image output in SOAP UI


Request

1
2
3
4
5
6
7
8
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ws="http://ws.kb.com/">
   <soapenv:Header/>
   <soapenv:Body>
      <ws:downloadImage>
         <arg0>logo.jpg</arg0>
      </ws:downloadImage>
   </soapenv:Body>
</soapenv:Envelope>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ws="http://ws.kb.com/">
   <soapenv:Header/>
   <soapenv:Body>
      <ws:downloadImage>
         <arg0>logo.jpg</arg0>
      </ws:downloadImage>
   </soapenv:Body>
</soapenv:Envelope>


Response

1
2
3
4
5
6
7
8
9
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
   <S:Body>
      <ns2:downloadImageResponse xmlns:ns2="http://ws.kb.com/">
         <return>
            <xop:Include href="cid:b40740e1-a0b8-44f0-add1-e1a7dc0a5e93@example.jaxws.sun.com" xmlns:xop="http://www.w3.org/2004/08/xop/include"/>
         </return>
      </ns2:downloadImageResponse>
   </S:Body>
</S:Envelope>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
   <S:Body>
      <ns2:downloadImageResponse xmlns:ns2="http://ws.kb.com/">
         <return>
            <xop:Include href="cid:b40740e1-a0b8-44f0-add1-e1a7dc0a5e93@example.jaxws.sun.com" xmlns:xop="http://www.w3.org/2004/08/xop/include"/>
         </return>
      </ns2:downloadImageResponse>
   </S:Body>
</S:Envelope>

SOAP UI Output

WithMtomSoapUIRequestResponse

We can see that xop:Include is pointing to the attachment reference.
So Soap body will not have the binary data of the image, instead it is sent as an attachment

Download this project JaxwsMTOMAttachments.zip

About the Author

Karibasappa G C (KB)
Founder of javainsimpleway.com
I love Java and open source technologies and very much passionate about software development.
I like to share my knowledge with others especially on technology 🙂
I have given all the examples as simple as possible to understand for the beginners.
All the code posted on my blog is developed,compiled and tested in my development environment.
If you find any mistakes or bugs, Please drop an email to kb.knowledge.sharing@gmail.com

Connect with me on Facebook for more updates

Share this article on