Wednesday, October 5, 2011

Saving MTOM Attachments using VFS Transport


Message Transmission Optimizing Technique (MTOM) is used to send binary data with SOAP envelopes. These SOAP messages can be send with transports such as HTTP and SMTP which supports Mime boundaries. VFS transport comes with the WSO2 ESB can be used to save the entire soap envelop to the file system. But in some cases there can be situations only the binary attachments have to save. Therefore this document describes how to save such an attachment using a custom message formatter.
Date: Sun, 19th Jun, 2011
Level: Intermediate
Reads: 54 Comments: 0 | Login or register to post comments
Amila Suriarachchi

WSO2 Inc.
amila's picture

Introduction

Message Transmission Optimizing Technique (MTOM) is used to send binary data with SOAP envelopes. These SOAP messages can be send with transports such as HTTP and SMTP which supports Mime boundaries. VFS transport comes with the WSO2 ESB can be used to save the entire soap envelop to the file system. But in some cases there can be situations only the binary attachments have to save. Therefore the rest this document describes how to save such an attachment using a custom message formatter. Complete sample code can be found here.

Proxy service configuration

<proxy xmlns="http://ws.apache.org/ns/synapse" name="MTOMService" transports="https http mailto" startOnLoad="true" trace="disable">
    <target>
        <inSequence>
            <property name="messageType" value="binary/attachment" scope="axis2"/>
            <property name="attachmentPath" value="//{http://org.wso2/sample/mtom}:getBinaryData/{http://org.wso2/sample/mtom}:dataHandler" scope="axis2"/>
            <property name="OUT_ONLY" value="true"/>
            <send>
                <endpoint name="endpoint_urn_uuid_121B17550AB61F010217054386886961-1720160132">
                    <address uri="vfs:file:///home/amila/temp/vfs/woden-impl-dom-1.0-SNAPSHOT.jar"/>
                </endpoint>
            </send>
            <property name="FORCE_SC_ACCEPTED" value="true" scope="axis2"/>
        </inSequence>
        <outSequence>
            <send/>
        </outSequence>
    </target>
    <publishWSDL>
        <wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
		xmlns:ns1="http://org.apache.axis2/xsd" xmlns:ns="http://org.wso2/sample/mtom" 
		xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" 
		xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" 
		xmlns:xs="http://www.w3.org/2001/XMLSchema" 
		xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
		xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" 
		xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" 
		targetNamespace="http://org.wso2/sample/mtom">
            <wsdl:documentation>MTOMService</wsdl:documentation>
            <wsdl:types>
                <xs:schema attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://org.wso2/sample/mtom">
                    <xs:element name="getBinaryData">
                        <xs:complexType>
                            <xs:sequence>
                                <xs:element minOccurs="0" name="dataHandler" nillable="true" type="xs:base64Binary"/>
                            </xs:sequence>
                        </xs:complexType>
                    </xs:element>
                </xs:schema>
            </wsdl:types>
            <wsdl:message name="getBinaryDataRequest">
                <wsdl:part name="parameters" element="ns:getBinaryData"/>
            </wsdl:message>
            <wsdl:portType name="MTOMServicePortType">
                <wsdl:operation name="getBinaryData">
                    <wsdl:input message="ns:getBinaryDataRequest" wsaw:Action="urn:getBinaryData"/>
                </wsdl:operation>
            </wsdl:portType>
            <wsdl:binding name="MTOMServiceSoap11Binding" type="ns:MTOMServicePortType">
                <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
                <wsdl:operation name="getBinaryData">
                    <soap:operation soapAction="urn:getBinaryData" style="document"/>
                    <wsdl:input>
                        <soap:body use="literal"/>
                    </wsdl:input>
                </wsdl:operation>
            </wsdl:binding>
            <wsdl:binding name="MTOMServiceSoap12Binding" type="ns:MTOMServicePortType">
                <soap12:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
                <wsdl:operation name="getBinaryData">
                    <soap12:operation soapAction="urn:getBinaryData" style="document"/>
                    <wsdl:input>
                        <soap12:body use="literal"/>
                    </wsdl:input>
                </wsdl:operation>
            </wsdl:binding>
            <wsdl:binding name="MTOMServiceHttpBinding" type="ns:MTOMServicePortType">
                <http:binding verb="POST"/>
                <wsdl:operation name="getBinaryData">
                    <http:operation location="getBinaryData"/>
                    <wsdl:input>
                        <mime:content type="text/xml" part="parameters"/>
                    </wsdl:input>
                </wsdl:operation>
            </wsdl:binding>
            <wsdl:service name="MTOMService">
                <wsdl:port name="MTOMServiceHttpSoap11Endpoint" binding="ns:MTOMServiceSoap11Binding">
                    <soap:address location="http://10.100.1.107:8080/axis2/services/MTOMService.MTOMServiceHttpSoap11Endpoint/"/>
                </wsdl:port>
                <wsdl:port name="MTOMServiceMailtoSoap11Endpoint" binding="ns:MTOMServiceSoap11Binding">
                    <soap:address location="mailto:synapse.demo.1@gmail.com"/>
                </wsdl:port>
                <wsdl:port name="MTOMServiceMailtoSoap12Endpoint" binding="ns:MTOMServiceSoap12Binding">
                    <soap12:address location="mailto:synapse.demo.1@gmail.com"/>
                </wsdl:port>
                <wsdl:port name="MTOMServiceHttpSoap12Endpoint" binding="ns:MTOMServiceSoap12Binding">
                    <soap12:address location="http://10.100.1.107:8080/axis2/services/MTOMService.MTOMServiceHttpSoap12Endpoint/"/>
                </wsdl:port>
                <wsdl:port name="MTOMServiceHttpEndpoint" binding="ns:MTOMServiceHttpBinding">
                    <http:address location="http://10.100.1.107:8080/axis2/services/MTOMService.MTOMServiceHttpEndpoint/"/>
                </wsdl:port>
            </wsdl:service>
        </wsdl:definitions>
    </publishWSDL>
    <parameter name="mail.pop3.host">pop.gmail.com</parameter>
    <parameter name="transport.PollInterval">5</parameter>
    <parameter name="mail.pop3.password">mailpassword</parameter>
    <parameter name="mail.pop3.socketFactory.port">995</parameter>
    <parameter name="mail.pop3.user">synapse.demo.1</parameter>
    <parameter name="mail.pop3.socketFactory.fallback">false</parameter>
    <parameter name="mail.pop3.port">995</parameter>
    <parameter name="transport.mail.Address">synapse.demo.1@gmail.com</parameter>
    <parameter name="mail.pop3.socketFactory.class">javax.net.ssl.SSLSocketFactory</parameter>
    <parameter name="transport.mail.Protocol">pop3</parameter>
</proxy>
This proxy service named as MTOMService is exposed using HTTPS, HTTP and MAIL to receive messages both with HTTP and SMTP. The proxy service end point saves the attachment as /home/amila/temp/vfs/woden-impl-dom-1.0-SNAPSHOT.jar to file system. The normal message formatter for this request saves the SOAP request as received. However in this sample, we set a custom message formatter with an arbitrary content type name called 'binary/attachment'. The attachment path is a special format which specify an Xpath with in-line namespaces. Message Formatter uses this value to obtain the binary part. Then it contains the in-line wsdl and mail transport properties to receive the messages.

Message Formatter

public class AttachmentFormatter implements MessageFormatter{

    public byte[] getBytes(MessageContext messageContext, OMOutputFormat omOutputFormat) throws AxisFault {
        return new byte[0];
    }

    public void writeTo(MessageContext messageContext, OMOutputFormat omOutputFormat, OutputStream outputStream, boolean b) throws AxisFault {

        try {
            String attachmentPath = (String) messageContext.getProperty("attachmentPath");

            SOAPEnvelope soapEnvelope = messageContext.getEnvelope();
            OMElement body = soapEnvelope.getBody().getFirstElement();

            AXIOMXPath xpath = getAXIOMXPath(attachmentPath);
            xpath.addNamespaces(body);
            OMElement selectedNode = (OMElement) xpath.selectSingleNode(body);

            DataHandler dataHandler = getDataHandler(selectedNode);
            dataHandler.writeTo(outputStream);

        } catch (IOException e) {
            e.printStackTrace();
        } catch (JaxenException e) {
            e.printStackTrace();
        }

    }

    private static AXIOMXPath getAXIOMXPath(String attachmentPath) throws AxisFault, JaxenException {
        StringBuffer xpathStringBuffer = new StringBuffer();
        Map xpathMap = new HashMap();
        String namespace = null;
        int i = 0;
        while (attachmentPath.indexOf("{") > -1) {
            xpathStringBuffer.append(attachmentPath.substring(0, attachmentPath.indexOf("{")));
            attachmentPath = attachmentPath.substring(attachmentPath.indexOf("{") + 1);
            if (attachmentPath.indexOf("}") == -1) {
                throw new AxisFault("Invalid attachment path");
            } else {
                namespace = attachmentPath.substring(0, attachmentPath.indexOf("}"));
                if (xpathMap.containsKey(namespace)) {
                    xpathStringBuffer.append(xpathMap.get(namespace));
                } else {
                    xpathMap.put(namespace, "ns" + i);
                    xpathStringBuffer.append("ns" + i);
                    i++;
                }
                attachmentPath = attachmentPath.substring(attachmentPath.indexOf("}") + 1);
            }
        }

        xpathStringBuffer.append(attachmentPath);

        AXIOMXPath axiomxPath = new AXIOMXPath(xpathStringBuffer.toString());
        for (String key : xpathMap.keySet()){
            axiomxPath.addNamespace(xpathMap.get(key), key);
        }
        return axiomxPath;
    }

    public String getContentType(MessageContext messageContext, OMOutputFormat omOutputFormat, String s) {
        return null;
    }

    public URL getTargetAddress(MessageContext messageContext, OMOutputFormat omOutputFormat, URL url) throws AxisFault {
        return null;
    }

    public String formatSOAPAction(MessageContext messageContext, OMOutputFormat omOutputFormat, String s) {
        return null;
    }

     public DataHandler getDataHandler(OMElement element) {
        OMNode node = element.getFirstOMChild();
        if (node instanceof OMText) {
            OMText txt = (OMText)node;
            if (txt.isOptimized()) {
                return (DataHandler)txt.getDataHandler();
            } else {
                return (DataHandler) DataHandlerUtils.getDataHandlerFromText(txt.getText(), null);
            }
        }
        return null;
    }
}

As any other message formatter it implements org.apache.axis2.transport.MessageFormatter interface. For this scenario the most important method is the writeTo method. First it gets the AXIOMXpath from the attachment string which proxy service sets. Then it gets the relavent OMText element from the SOAP envelop using the xpath and use the data handler to write the message to the folder.

Client program

public class MTOMServiceClient {

        public static void main(String[] args) {
        try {
//            MTOMServiceStub stub = new MTOMServiceStub("http://localhost:8280/services/MTOMService");
            ConfigurationContext configurationContext =
                    ConfigurationContextFactory.createConfigurationContextFromFileSystem(null, "conf/axis2_client.xml");
            MTOMServiceStub stub = new MTOMServiceStub(configurationContext, "mailto:synapse.demo.1@gmail.com");

            FileDataSource fileDataSource = new FileDataSource("conf/woden-impl-dom-1.0-SNAPSHOT.jar");
            DataHandler dataHandler = new DataHandler(fileDataSource);

            stub._getServiceClient().getOptions().setProperty(Constants.Configuration.ENABLE_MTOM, Constants.VALUE_TRUE);
            stub.getBinaryData(dataHandler);
        } catch (AxisFault axisFault) {
            axisFault.printStackTrace();
        } catch (java.rmi.RemoteException e) {
            e.printStackTrace();
        }
    }
}
First client side code can be generated using the wsdl2java tool. Then it can be used to send the message either with mail or http transport.

Author

Amila Suriarachchi, Software Architect, WSO2 Inc.
AttachmentSize
sample.zip79.93 KB

No comments:

Post a Comment