Handling SOAP attachments with Axis2/C

Axis2/C supports MTOM (SOAP Message Transmission Optimization Mechanism) and SwA (SOAP with attachments) that allows you to easily implement clients and services to handle attachments either way. In addition, you can convert binary content to base64 and vice-versa while sending and receiving attachments. This tutorial discusses the API and configuration options for handling SOAP Attachments within Axis2/C.

Date: Wed, 21st May, 2008
Level: Intermediate
Reads: 6500 Comments: 1 | Login or register to post comments
Nandika Jayawardana
Technical Lead
WSO2 Inc
nandika's picture

Applies To

Apache Axis2/C 1.4.0 and above
Environment Linux , Windows

 

Table of Contents

  1. Axis2/C Configurations for Attachments
  2. axiom_data_handler and axiom_text API
  3. Summary
  4. References and Useful Links

 

Axis2/C Configurations for Attachments

Let's start with axis2.xml configurations related to attachments.

<parameter name="enableMTOM" locked= false" >true</parameter>

<parameter name="MTOMMaxChunkBuffers" locked="false" >1000</parameter>

<parameter name="MTOMChunkBufferSize" locked="false" >10</parameter> 

  • When enableMTOM parameter is set to true, it enables MTOM support globally for both clients and services using this axis2.xml and Axis2/C repository. 
  • Using “MTOMChunkBufferSize” parameter, you can define the size of the buffer (in MB) used in reading binary attachments when incoming http stream is a chunked stream 
  • Using “MTOMMaxChunkBuffers” parameter, you can configure the number of buffers that that the system can handle when processing attachments while the http stream is a chunked stream.

The last two parameters are useful because Axis2/C reads the entire attachment at once. When the incoming stream is chunked, you can optimize Axis2/C memory usage.

You can programmatically enable MTOM support using the axis2_options setting.

  
axis2_options_set_enable_mtom(options, env, AXIS2_TRUE);
    

In a service, you can enable MTOM by setting it in the message context.

   
axis2_msg_ctx_set_doing_mtom (msg_ctx, env, AXIS2_TRUE);
    

When enableMTOM option is set to true, attachment is sent as it is, out of the SOAP body,  using MIME headers. By setting enableMTOM option to false, you can send the binary content as base64 encoded text with the SOAP message. Sending binary content as it is called optimized format since it consumes less bandwidth compared to sending base64 encoded format which is called non-optimized.

    --MIMEBoundaryde3daf2c-4363-49d2-954a-4a3273258152
content-transfer-encoding: binary
content-id: <0.b29115af-e909-42c8-bbee-73d0b92c79fd@apache.org>content-type: application/xop+xml;charset=UTF-8;type="text/xml";
   <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
      <soapenv:Header/>
      <soapenv:Body>
         <ns1:mtomSample xmlns:ns1="http://ws.apache.org/axis2/c/samples/mtom">
            <ns1:fileName>test.jpg</ns1:fileName>
            <ns1:image>
               <xop:Include href="cid:1.fea3613d-9541-4749-a558-b984a55a92f6@apache.org" xmlns:xop="http://www.w3.org/2004/08/xop/include"/>
            </ns1:image>
         </ns1:mtomSample>
      </soapenv:Body>
   </soapenv:Envelope>--MIMEBoundaryde3daf2c-4363-49d2-954a-4a3273258152content-transfer-encoding: binarycontent-id: 
   <1.fea3613d-9541-4749-a558-b984a55a92f6@apache.org>content-type: image/jpeg 
   Binary content goes here
    

1. A SOAP message with a binary attachment

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
   <soapenv:Header/>
   <soapenv:Body>
      <ns1:mtomSample xmlns:ns1="http://ws.apache.org/axis2/c/samples/mtom">
         <ns1:fileName>test.jpg</ns1:fileName>
         <ns1:image>/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAAgADwDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD2PRvC+m6LI10iPdalKMT6hdHzLiX6seg/2Vwo7CsnU/id4W0jxZB4cvNQCXknDyAZihY9Fdv4Sf074qt8VPG7eC/CpazIOr3zeRZJjJDd3x/sg/mVrldG+BWmT+CJoddaR/EV8PPlvS5ZreU5IUc/MBn5v7xzz0wAex1zfjfxnZeBdDj1W/tbm4he4WDbbhSwJViCckcfLj8RXH/CHxRqBa/8D+ICf7Y0QlEZj/rYQQo+uMjB7qy12Hj3wqnjLwbf6NuCTSKHt5D0WVTlc+x6H2JoA3LYW0+2/hVS1xEn70DlkGSoz6DcfzNWK8X+HXxRtdEsU8I+NC2lanpmLZJJ1wjovCgkcAgYGehGDmuk8V/GXwp4f0yV7HUbfVb8r+5t7V96lu25xwB6859qANrTPHmnar461Lwnb29z9ssIzJLMQvlHG0EAg5zlwOnY1t6XrFvqs2pRQLIrafdm0l3gAFwiPkc9MOP1rz34OeE9S0611HxR4gRhrOtyeayuuHjjJLcj+EsTnHYBenSun8Ff8hHxh/2Hn/8ASeCgDz3Ulbxh+0naafMBLY6BbiYx5+UMFDbvrveMH/dFehXPhG8n+Itn4oXX7uO1t7cwtpoH7t8hhnOcYyQcYJyo56Yx/B3gLUtA+IPibxJqF1azJqkj/Z1jLF0QybgGyAOgUcZ6V6FQB4l8TAvhD4v+FPF6MEhum+zXePRcKzH1+ST/AMcr1nxHr1n4Y8PXus3zYgtYy5APLnoqj3JIA+tcp8VvAd9480WwtdOuLWC4tbnzS9wWA27SCBtB5zj8qn+JvhDVfGng2HRtNuraCf7RHJM1wzBWVVbj5QTncVPTtQB5rofgTWfjHdHxZ4uvpbLTpMpZW1soDmME9CRhV9yCW5PTGb+t/AMaPCuq+C9XvV1WzImiiumRt7DnCsFGD9QQehx1r2jS7CLStJs9PgXbDawJCg9lUAfyq3QBwvwt8eN448PSG9jEWr2DiG9jAxk84cDtnB47EGr/AIK/5CPjD/sPP/6TwVk+GvAWoaB8Udf8SLc239l6ojEW6M28SFlYswxjqH7/AMVdR4f0abSbrXJZZI3Goak15GEz8qmKNMH3yh/MUAf/2Q==</ns1:image>
      </ns1:mtomSample>
   </soapenv:Body></soapenv:Envelope>

2. A SOAP message with base64 encoded text in its payload

One of the issues in using enableMTOM parameter to true in axis2.xml is that it causes MIME headers to be sent with the SOAP message, whether or not you are sending attachments. This may not be the desired behavior for your other clients and services. Therefore, it is always better to set enableMTOM programatically instead of using the axis2.xml parameter.

 

axiom_data_handler and axiom_text API

Data Handler is the structure that represents the attachments and it provides functions for sending and receiving attachments in axiom. It is attached to a text node in axiom tree.

You can create a data handler by providing the path of the attachment file and the MIME type. Then, it should be attached to an axiom text element.

 image_om_ele = axiom_element_create(env, mtom_om_node, "image", ns1, &image_om_node);
 data_handler = axiom_data_handler_create(env, "axis2.jpg", "image/jpeg");
 data_text    = axiom_text_create_with_data_handler(env, image_om_node, data_handler, &text_om_node);
        

Of course you can create the data handler without specifying the file name and MIME type and then later set them in. This is specially useful when you already have your attachment contents in memory.

 
 data_handler = axiom_data_handler_create(env, NULL, NULL);
 axiom_data_handler_set_binary_data(data_handler, env, buffer, buffer_len);
 axiom_data_handler_set_content_type(data_handler, env,"image/jpeg");
 axiom_text_create_with_data_handler(env, mtom_om_node, data_handler, &text_node);
    

You can set whether the attachment should be sent in optimized or non optimized format on the axiom text,  which contains the data handler. However, if you have enableMTOM option set to false, the attachment will be sent in base64 format, irrespective of this option.

 data_text = axiom_text_create_with_data_handler(env, image_om_node, data_handler, &data_om_node);
 axiom_text_set_optimize(data_text, env, AXIS2_TRUE);
       

When receiving attachments, you can either save the attachment to a file or you can obtain it as a buffer with the buffer length.

axiom_data_handler_set_file_name(data_handler, env, text_str);
axiom_data_handler_write_to(data_handler, env);
buffer = axiom_data_handler_get_input_stream(data_handler, env);
buffer_len = axiom_data_handler_get_input_stream_len(data_handler, env);
mime_type =  axiom_data_handler_get_content_type(data_handler, env);

In receiving attachments, knowing the exact location of the binary attachment allows you to easily obtain axiom text and the data handler. However, if the location is unknown, you will have to iterate through the axiom tree, looking for axiom text containing data handlers. 

There is an easier way to access the attachments received. axiom_soap_builder object contains a hash table with content id (cid ) as the key and data_handler as the value. Content id is the value of the href attribute except "cid:". This uniquely identifies attachments received within a SOAP message.

<xop:Include href="cid:1.fea3613d-9541-4749-a558-b984a55a92f6@apache.org" xmlns:xop="http://www.w3.org/2004/08/xop/include"/>

soap_builder = axiom_soap_envelope_get_soap_builder(soap_envelope, env);
mime_body_parts = axiom_soap_builder_get_mime_body_parts(soap_builder, env);

By iterating through the hash table, you are able to obtain all attachments received. This technique can be used in the event of receiving attachments in SwA format as well.

if(mime_body_parts) {
    axutil_hash_index_t *hi = NULL;
    axis2_char_t *cid = NULL;
    axiom_data_handler_t *data_handler = NULL;
    for (hi = axutil_hash_first(mime_body_parts, env);
    	hi; hi = axutil_hash_next(env, hi))    
    {                                                           
    	axutil_hash_this(hi, (const void **)&cid, NULL, (void**)&data_handler); 
        if(cid && data_handler) 
        {
        	char *mime_type = NULL;
                char *data = NULL;
                int data_len = 0;

                axiom_data_handler_read_from (data_handler, env, &data, &data_len);
                mime_type = axiom_data_handler_get_content_type(data_handler, env);
		/** Use the attachment content here */
         }
    }
}

You can send attachments using the SwA format by setting swa option in axiom text containing the data handler. Note the absence of xop:include element.

 data_text = axiom_text_create_with_data_handler(env, image_om_node, data_handler, &data_om_node);
 axiom_text_set_is_swa(data_text, env, AXIS2_TRUE);
       
--MIMEBoundary09b42e84-c3d9-4d9e-b80b-91f834edc878
content-transfer-encoding: binary
content-id: <0.d9b487fa-22a2-4d63-b8c2-9a91ee4b4dc2@apache.org>content-type: application/xop+xml;charset=UTF-8;type="text/xml";
   <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
      <soapenv:Header/>
      <soapenv:Body>
         <ns1:mtomSample xmlns:ns1="http://ws.apache.org/axis2/c/samples/mtom">
            <ns1:fileName>test.jpg</ns1:fileName>
            <ns1:image>1.9f4dc218-ca93-46f7-86b2-129500358e23@apache.org</ns1:image>
         </ns1:mtomSample>
      </soapenv:Body>
   </soapenv:Envelope>--MIMEBoundary09b42e84-c3d9-4d9e-b80b-91f834edc878content-transfer-encoding: binarycontent-id: 
   <1.9f4dc218-ca93-46f7-86b2-129500358e23@apache.org>content-type: image/jpeg
Binary content goes here

Now you know all necessary detials on how to use Axis2/C to send and receive attachments. 

 

Summary

In this article, we explored the configuration parameters and API's of Axis2/C for handling attachments. Demonstrated code samples covered major functionality available for handling attachments in Axis2/C

 

References and Useful Links

  1.  Axis2/C manual
  2.  MTOM Specification
  3.  SwA Specification

 

Author

Nandika Jayawardana is a senior software engineer at wso2. nandika at wso2.com

mhuffman's picture

Base64 and line endings

Using your above technique to extract attachments, I used axutil_base64_decode() to decode the attachments. But I noticed that it was having problems and would only decode the first 50 some characters of the message. After some trial and error, I figured out it was having problems with '\r' and '\n' characters in the "char *data"; after stripping out all whitespace from the character string, I was able to decode the attachment completely... I'm making a few assumptions, (1) the Pear::Soap implementation that I'm using to test my server is inserting the '\r\n' characters into the mime attachment, despite the client & server both run in a UNIX environment (I would imagine this is per the mime spec?); (2) AXIS2 doesn't contain a function that I'm missing to strip out the line-endings from the encoded attachment before passing off to the decoder? Is this a standard way of going about decoding attachments, or is there a more AXIS2 preferred (easier) way? And will this work differently with streaming? Thanks!