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
- 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
- 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
- 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
- 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
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
- <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
- <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
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
- 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
- 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
- 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
- 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
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
- 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
- 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
- <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
- <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
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
Hi,
I am using same MTOM as you have suggested here, but for me it’s not working and giving the SOAP response same as without MTOM.
package com.kb.ws;
import java.awt.Image;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.xml.ws.soap.MTOM;
@MTOM
@WebService(endpointInterface = “com.kb.ws.ImageService”)
public class ImageServiceImpl implements ImageService {
@Override
@WebMethod
public Image downloadImage(String imageName) {
File image = new File(“C:\\SOAPUI(don’t delete)\\”
+ imageName);
try {
Image img = ImageIO.read(image);
return img;
} catch (IOException ie) {
ie.printStackTrace();
}
return null;
}
}
Can you please suggest me the solution.
Thanks,
VicXj