Web Services Attachment with MTOM

Intro

I was preparing for a blog about binary attachment using web services. Just this week, I had to write a client code to help one of my co-workers test uploading binary data via Document Content Web Services for his newly installed I/PM framework. What a coincidence!

This post shows how you can upload and download attachment via web services using the SOAP Message Transmission Optimization Mechanism/XML-binary Optimized Packaging (MTOM/XOP) specification. MTOM/XOP defines a method for optimizing the transmission of XML data of type xs:base64Binary or xs:hexBinary in SOAP messages.

Project Setup and Code

Let’s create a project for the web services in JDeveloper first. We will name it FaMtomAttachment. Click on File menu –> select New –> under General select Projects –> select Generic Project –> click OK button.
Create Project

Let’s add the interface and implementation for the web services in its appropriate packages. Click on File menu –> select New –> under General select Java –> select Java Interface –> click OK button. Repeat same steps but select Java Class this time. MTOM support is standard in JAX-WS. Enabling server to send attachment via MTOM is fairly straight forward, use JWS annotation of javax.xml.ws.soap.MTOM in implementation class.

@MTOM
@WebService(endpointInterface = "com.fusionapplied.ws.intf.IFaWsAttachmentService")
public class FaWsAttachmentServiceImpl implements IFaWsAttachmentService {
    @Override
    public String attachFile(DataHandler dataHandler, String fileName) {
	...
    }

    @Override
    @XmlMimeType("application/octet-stream")
    public DataHandler downloadFile(String fileName) {
    	...
    }
}

Interface

Now that we have defined 2 methods in the interface, attachFile() and downloadFile(), here is how the implementation look like.
File upload code:

   @Override
    public String attachFile(DataHandler dataHandler, String fileName) {
        System.out.println("File Name::" + fileName);
        StringBuffer sb = new StringBuffer();
        String fileNameWithPath = "";
        try {
            File f = new File(fileName);
            fileNameWithPath = PATH+f.getName();
            System.out.println("File Name with Path::" + fileNameWithPath);
            BufferedOutputStream bout =
                new BufferedOutputStream(new FileOutputStream(fileNameWithPath));
            BufferedInputStream bin = new BufferedInputStream(dataHandler.getInputStream());

            byte[] buffer = new byte[512];
            while (true) {
                int bytesRead = bin.read(buffer);
                if (bytesRead == -1)
                    break;
                bout.write(buffer, 0, bytesRead);
            }
            bin.close();
            bout.close();
        } catch(FileNotFoundException fnfe){
            System.out.println("FaWsAttachmentServiceImpl::FileNotFoundException: "+fnfe);
            sb.append(fnfe.toString());
        } catch (IOException e) {
            System.out.println("FaWsAttachmentServiceImpl::IOException : "+e);
            sb.append(e.toString());
        }finally{
            if(sb.length() > 0){
                return sb.toString();
            }else{
                return sb.append("File Uploaded Succesfully")
                        .append(" on ")
                        .append(new Date())
                        .append(". File Name including its path is: ")
                        .append(fileNameWithPath).toString();
            }
        }
    }

File download code is rather simple:

    @Override
    @XmlMimeType("application/octet-stream")
    public DataHandler downloadFile(String fileName) {
        return new DataHandler(new FileDataSource(PATH+fileName));
    }

Deployment

Once we are done deploying the services, we will test those from JDeveloper proxy project and the soapUI tool. Use JDeveloper to create WAR and EAR profiles and use Deployment Wizard to deploy the application. To create WAR file; right click on the FaMtomAttachment project –> select Project Properties –> select Deployment –> click New button –> select WAR File as Archive Type –> type FaMtomAttachment as Name –> click OK button. For the sake of simplicity Specify Java EE Web Context Root as faws. Click OK twice to exit the dialog and this will create FaMtomAttachment.war deployment profile.

To create EAR file; click Application menu –> select Application Properties –> select Deployment –> click New button –> select EAR File as Archive Type –> type FaMtomAttachment as Name –> click OK button. Select Application Assembly –> select FaMtomAttachment project –> click OK twice and this will create FaMtomAttachment.ear deployment profile.

Using JDeveloper deploy the FaMtomAttachment application. Click Application menu –> select Deploy –> select FaMtomAttachment application –> run through the deployment wizard and click Finish.

Nice! now you see reference to MTOM policy in the deployed services. Screen shots below.

<wsp:Policy wssutil:Id="Mtom.xml">
	<ns1:OptimizedMimeSerialization/>
</wsp:Policy>

wsdl

Testing Setup

Create a proxy project to test the services now. Click on File menu –> select New –> under General select Projects –> select Generic Project –> click OK button. Name this project FaMtomAttachmentProxy. Click File –> New –> select Web Services under Business Tier –> select Web Service Proxy –> go through proxy creation wizard to populate the supporting types for testing the services. We will add our test code in the generated FaWsAttachmentServiceImplPortClient class.
Create Proxy
Test Client

Testing Output

Below you see that the file system is empty at first, ie /home/oracle/download/. To run the client right click on source editor for FaWsAttachmentServiceImplPortClient.java and select Run. The results below show successful attachment of a binary file utilizing MTOM from exposed operations.
Test client code:

    public static void attachFile(){
        try {
                System.out.println("::Starting Test Client to upload a file...");
                faWsAttachmentServiceImplService = new FaWsAttachmentServiceImplService();
                IFaWsAttachmentService iFaWsAttachmentService = faWsAttachmentServiceImplService.getFaWsAttachmentServiceImplPort(new MTOMFeature());
                String fileNameWithPath="/home/oracle/upload/memsetting";
                long length=new java.io.File(fileNameWithPath).length();
                FileDataSource fileDataSource = new FileDataSource(fileNameWithPath);
                DataHandler dataHandler = new DataHandler(fileDataSource);
                BufferedInputStream bin;
                try {
                    bin = new BufferedInputStream(dataHandler.getInputStream());
                    byte b[]=new byte[(int)length];
                    bin.read(b);
                    String result = iFaWsAttachmentService.attachFile(b,fileDataSource.getName()+".txt");
                    System.out.println("tResult from FaWsAttachmentService:: nt"+result);
                    System.out.println("::End Test Client");
                } catch (IOException e) {
                    e.printStackTrace();
                }
        } catch (Exception ex) {
            ex.printStackTrace();
        }

    }

Output from attachFile():
::Starting Test Client to upload a file...
	Result from FaWsAttachmentService::
	File Uploaded Succesfully on Thu Jan 24 19:33:52 EST 2013. File Name including its path is: /home/oracle/download/memsetting.txt
::End Test Client
Process exited with exit code 0.
    public static void downloadFile(){
      try {
              System.out.println("::Starting Test Client to download a file...");
              faWsAttachmentServiceImplService = new FaWsAttachmentServiceImplService();
              IFaWsAttachmentService iFaWsAttachmentService = faWsAttachmentServiceImplService.getFaWsAttachmentServiceImplPort(new MTOMFeature());
              String fileNameWithPath="/home/oracle/upload/Test-Screenshot.png"; //location of downloaded file
              byte[] downloadedData = iFaWsAttachmentService.downloadFile("Test-Screenshot.png");
              FileOutputStream fos = new FileOutputStream(fileNameWithPath);
              fos.write(downloadedData);
              fos.close();
              System.out.println("tFile downloaded successfully and is saved at: "+fileNameWithPath);
              System.out.println("::End Test Client");
      } catch (IOException e) {
          e.printStackTrace();
      }
    }

Output from downloadFile():
::Starting Test Client to download a file...
	File downloaded successfully and is saved at: /home/oracle/upload/Test-Screenshot.png
::End Test Client
Process exited with exit code 0.

Empty File System
After Upload

Testing Output from soapUI tool

SoapUI Upload
SoapUI Upload Result
SoapUI Download
SoapUI Download Result

And that is how we implement web services with attachment.

Sanjib