JAX-WS Attachment with MTOM

Creating sample JAX-WS service using Message Transmission Optimization Mechanism (MTOM) to send attachment. 

In SOAP world, request and response are transmitted via XML format only. When sending/receiving binary data/ file (byte[]), it will be converted to XML base 64 which will increase request / response size by 33%. To avoid this, file can be send via MTOM or XML binary Optimized packaging (XOP). This will send file as attachment without any conversation and hence size of request / response will be more or less same.

Let us create exam using MTOM. In this post, we are creating Profile Web Service. Profile contains - name, address and display image. There  are 2 operations:-
  1. Create Profile
  2. Get Profile 
Profile Service:- 
package com.profile.service;
import java.awt.Image;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class Profile {
private String name;
private String address;
private Image displayPhoto;
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name
* the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the address
*/
public String getAddress() {
return address;
}
/**
* @param address
* the address to set
*/
public void setAddress(String address) {
this.address = address;
}
/**
* @return the displayPhoto
*/
public Image getDisplayPhoto() {
return displayPhoto;
}
/**
* @param displayPhoto
* the displayPhoto to set
*/
public void setDisplayPhoto(Image displayPhoto) {
this.displayPhoto = displayPhoto;
}
}
view raw Profile.java hosted with ❤ by GitHub

package com.profile.service;
import java.util.HashMap;
import java.util.Map;
/**
* In memory Profile database
*
*
*/
public class ProfileDB {
private static final Map<String, Profile> profiles = new HashMap<>();
public static void addProfile(final Profile profile) {
profiles.put(profile.getName(), profile);
}
public static Profile getProfile(final String name) {
return profiles.get(name);
}
}
view raw ProfileDB.java hosted with ❤ by GitHub

package com.profile.service;
import javax.jws.WebService;
/**
* Service Endpoint Interface
*
*/
@WebService(name = "ProfileService")
public interface ProfileService {
public String createProfile(Profile profile);
public Profile getProfile(String name);
}

package com.profile.service;
import javax.jws.WebService;
import javax.xml.ws.soap.MTOM;
/**
*
* Service Implementation bean
*/
@MTOM
@WebService(endpointInterface = "com.profile.service.ProfileService")
public class ProfileServiceImpl implements ProfileService {
@Override
public String createProfile(Profile profile) {
ProfileDB.addProfile(profile);
return profile.getName()+ " Created.";
}
@Override
public Profile getProfile(String name) {
return ProfileDB.getProfile(name);
}
}

package com.profile.service;
import javax.xml.ws.Endpoint;
/**
* Profile Service Publisher
*
*/
public class ProfileServicePublisher {
public static void main(String[] args) {
Endpoint.publish("http://localhost:8080/ws/pofile", new ProfileServiceImpl());
System.out.println("Profile Service running at http://localhost:8080/ws/pofile");
}
}

Profile Client:- 
Create new java project, create client using wsimport and add generated files in project. Below is Profile client example:
package com.profile.service;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.Service;
import javax.xml.ws.soap.SOAPBinding;
public class ProfileClient {
private static final ObjectFactory FACTORY = new ObjectFactory();
private static final String TESTFILE = "/var/tmp/test.png";
public static void main(String[] args) throws IOException {
// Create Proxy
URL url = new URL("http://localhost:8080/ws/pofile?wsdl");
QName qname = new QName("http://service.profile.com/", "ProfileServiceImplService");
Service service = Service.create(url, qname);
ProfileService profileService = service.getPort(ProfileService.class);
// Enabling MTOM at client end
BindingProvider bp = (BindingProvider) profileService;
SOAPBinding binding = (SOAPBinding) bp.getBinding();
binding.setMTOMEnabled(true);
// Create Profile
System.out.println("Creating Profile");
String profileName = profileService.createProfile(getProfile());
System.out.println("Response from Create profile " + profileName);
// Get Profile
System.out.println("\n\nCalling Get Profile");
Profile response = profileService.getProfile("Test User");
System.out.println(response.getName() + "\n"+response.getAddress() );
}
private static Profile getProfile() throws IOException {
Profile profile = FACTORY.createProfile();
profile.setName("Test User");
profile.setAddress("Address Lines....");
profile.setDisplayPhoto( Files.readAllBytes( Paths.get(TESTFILE) ) );
return profile;
}
}

Create Profile Request/Response:-
---[HTTP request]---
Accept: text/xml, multipart/related
Connection: keep-alive
Host: localhost:8080
User-agent: JAX-WS RI 2.2.9-b130926.1035 svn-revision#5f6196f2b90e9460065a4c2f4e30e065b245e51e
Content-type: multipart/related;start="<rootpart*4db29ca4-a6eb-4596-a5d4-ae0fda21c201@example.jaxws.sun.com>";type="application/xop+xml";boundary="uuid:4db29ca4-a6eb-4596-a5d4-ae0fda21c201";start-info="text/xml"
Soapaction: "http://service.profile.com/ProfileService/createProfileRequest"
Content-length: 12103
--uuid:4db29ca4-a6eb-4596-a5d4-ae0fda21c201
Content-Id: <rootpart*4db29ca4-a6eb-4596-a5d4-ae0fda21c201@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:createProfile xmlns:ns2="http://service.profile.com/">
<arg0>
<address>Address Lines....</address>
<displayPhoto>
<xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="cid:a167bf8a-4a9f-4fc7-9f45-bb7c5bf93ed3@example.jaxws.sun.com"></xop:Include>
</displayPhoto>
<name>Test User</name>
</arg0>
</ns2:createProfile>
</S:Body>
</S:Envelope>
--uuid:4db29ca4-a6eb-4596-a5d4-ae0fda21c201
Content-Id: <a167bf8a-4a9f-4fc7-9f45-bb7c5bf93ed3@example.jaxws.sun.com>
Content-Type: application/octet-stream
Content-Transfer-Encoding: binary
<Binary Content of PNG File>
--------------------

---[HTTP response 200]---
Date: Sat, 23 Sep 2017 09:45:01 GMT
Transfer-encoding: chunked
Content-type: multipart/related;start="<rootpart*663ae683-7cff-4a47-9fe3-ddbcb4bbb96b@example.jaxws.sun.com>";type="application/xop+xml";boundary="uuid:663ae683-7cff-4a47-9fe3-ddbcb4bbb96b";start-info="text/xml"
--uuid:663ae683-7cff-4a47-9fe3-ddbcb4bbb96b
Content-Id: <rootpart*663ae683-7cff-4a47-9fe3-ddbcb4bbb96b@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:createProfileResponse xmlns:ns2="http://service.profile.com/">
<return>Test User Created.</return>
</ns2:createProfileResponse>
</S:Body>
</S:Envelope>
--uuid:663ae683-7cff-4a47-9fe3-ddbcb4bbb96b----------------------

Get Profile Request/Response:-
---[HTTP request]---
Accept: text/xml, multipart/related
Connection: keep-alive
Host: localhost:8080
User-agent: JAX-WS RI 2.2.9-b130926.1035 svn-revision#5f6196f2b90e9460065a4c2f4e30e065b245e51e
Content-type: multipart/related;start="<rootpart*527f0b86-5670-4318-b7b1-3dc2996d39fb@example.jaxws.sun.com>";type="application/xop+xml";boundary="uuid:527f0b86-5670-4318-b7b1-3dc2996d39fb";start-info="text/xml"
Soapaction: "http://service.profile.com/ProfileService/getProfileRequest"
Content-length: 488
--uuid:527f0b86-5670-4318-b7b1-3dc2996d39fb
Content-Id: <rootpart*527f0b86-5670-4318-b7b1-3dc2996d39fb@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:getProfile xmlns:ns2="http://service.profile.com/">
<arg0>Test User</arg0>
</ns2:getProfile>
</S:Body>
</S:Envelope>
--uuid:527f0b86-5670-4318-b7b1-3dc2996d39fb----------------------

---[HTTP response 200]---
Date: Sat, 23 Sep 2017 09:45:01 GMT
Transfer-encoding: chunked
Content-type: multipart/related;start="<rootpart*e760379b-7bc8-4673-bea9-3bb94e2423db@example.jaxws.sun.com>";type="application/xop+xml";boundary="uuid:e760379b-7bc8-4673-bea9-3bb94e2423db";start-info="text/xml"
--uuid:e760379b-7bc8-4673-bea9-3bb94e2423db
Content-Id: <rootpart*e760379b-7bc8-4673-bea9-3bb94e2423db@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:getProfileResponse xmlns:ns2="http://service.profile.com/">
<return>
<address>Address Lines....</address>
<displayPhoto>
<xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="cid:f9e7325e-98e6-440c-9d33-abe2f2ea7e90@example.jaxws.sun.com"></xop:Include>
</displayPhoto>
<name>Test User</name>
</return>
</ns2:getProfileResponse>
</S:Body>
</S:Envelope>
--uuid:e760379b-7bc8-4673-bea9-3bb94e2423db
Content-Id: <f9e7325e-98e6-440c-9d33-abe2f2ea7e90@example.jaxws.sun.com>
Content-Type: image/png
Content-Transfer-Encoding: binary
<Binary Content Of PNG>
--------------------
Cheers..!!!

Comments

Popular posts from this blog

Create BeanShell Script to Make Database call

JAX-WS - SOAP Handlers

WSDL Elements