Downloading a Binary File from a Web Service using Axis2 and SOAP with Attachments
Submitted on June 5, 2007 - 04:02. Story : Level : Project :
Apache Axis2 is well supported for sending and receiving binary payloads with SOAP using several standard mechanisms such as Base64 encoded binary, SOAP Message Transmission Optimization Mechanism (MTOM) and SOAP with Attachments (SwA). This tutorial by Thilina Gunarathne will walk you through writing a sample application (Web service and a client) that demonstrates how to download a binary file from a server using SOAP with Attachments.
Introduction
The sample application we are going to develop through this tutorial includes writing an Apache Axis2/Java [1] Web service, which will respond to the requests by a SOAP with Attachments (SwA) [2] type message containing a binary file. The sample application also includes writing an Apache Axis2 Web service client, which can receive the above SOAP with Attachments type response. The sample use case which we are going to implement is a Download Statistics Service.
This service provides data relating to monthly download statistics of a set of software products. These statistics includes a graph image of the distribution of downloads over a period of one month together with some other data. Other similar use cases of this kind of a service includes providing graph images of stock market data through a Web service, providing hospital patients' profiles together with images from a central national server, etc.
Background
SOAP with Attachments or SwA is a Note submitted to the W3C by Microsoft and HP Labs. The objective of the SOAP with Attachments note is to describe a standard way to associate one or more binary attachments with a SOAP message in their original formats. SOAP with Attachments utilizes the Multipart/Related MIME [3] packaging to package the SOAP message together with the binary attachments, and utilizes URI based mechanisms for referencing the MIME parts. More information about Apache Axis2/Java SOAP with Attachments support can be found in the Using SOAP with Attachments in Apache Axis2[4] article.
Writing and Deploying the "StatisticsService" Web Service
In this example, we are going to write an XML in/out Web service implementation class with the use of org.apache.axis2.receivers.RawXMLINOutMessageReceiver. The service implementation class returns the download statistics data specific to a software product. The name of the product needs to be sent in the form of a SOAP request by the client. The product name is extracted at the service implementation class.
In this scenario, we need to add a binary image file as an attachment to the response SOAP message. We can achieve this by creating a javax.activation.DataHandler object corresponding to the image file and adding it to the attachment map of the outgoing org.apache.axis2.context.MessageContext.
Open StatisticsService.java to have a look at the complete source listing of the Web service implementation class.
1. Creating the javax.activation.DataHandler Object
In order to create a DataHandler object corresponding to the image file, first we need to create an appropriate javax.activation.DataSource object from the image file. There are several javax.activation.DataSource implementations available in the javabeans activation framework[5] as well as in the Apache Axis2/Java distribution. You can choose an appropriate implementation based on the type of data you are going to handle. You can also write your custom DataSource implementation class based on your need. In this example, we are using the javax.activation.FileDataSource to create the DataSource object corresponding to the image file.
// Create a data source for the image FileDataSource
graphImageDataSource = new FileDataSource(sampleResourcePath +"1.png");
Now we can wrap the above created DataSource object using a javax.activation.DataHandler object.
// Create the dataHandler out of the above created datasource
DataHandler graphImageDataHandler = new DataHandler(graphImageDataSource);
2. Accessing the Axis2 MessageContext Object of the Outgoing(response) Message
Now we need to access the MessageContext object of the outgoing message in order to add the above created DataHandler object to the attachment map. We can use the Axis2 MessageContext.getCurrentMessageContext() method within a service implementation class to access the MessageContext object of the incoming(request) message to that service. Accessing the outgoing MessageContext is not so straight forward. For that we first need to access the incoming MessageContext.
MessageContext inMessageContext = MessageContext.getCurrentMessageContext();
We can obtain the OperationContext for this operation through the above requested MessageContext.
operationContext = inMessageContext.getOperationContext();
Now we can use the OperationContext object to access the response (outgoing) MessageContext object.
MessageContext outMessageContext = operationContext
.getMessageContext(WSDLConstants.MESSAGE_LABEL_OUT_VALUE);
3. Adding the DataHandler Object to the AttachmentMap of the Outgoing MessageContext
Since we have a reference to the outgoing MessageContext, now we can add the above created DataHandler object to the attachments map of that MessageContext. In this example, we expect Axis2/Java to take care of generating a unique Content-ID for our attachment.
String graphImageID = outMessageContext.addAttachment(graphImageDataHandler);
4. Adding a Reference to the Attachment from the SOAP Payload
Let's add a reference to the attachment from the XML payload of the message to complete the implementation of the service class. We need to make sure that this reference conforms to the cid: URL scheme defined in the RFC 2392[6]. In order to do that, we create an OMElement, then add a "href" attribute with the value set to the Content-ID of the attachment. Care needs to be taken to prefix the Content-ID value with a "cid:" when setting the value of the "href" attribute.
OMElement graphElement = factory.createOMElement("graph", omNs, wrapperElement);
String graphCID = "cid:"+graphImageID; graphElement.addAttribute("href",graphCID, omNs); NOTE: Make sure to copy the "StatisticsServiceResources" directory provided with this sample in to your local machine. Then set the value of the "sampleResourcePath" variable of the service implementation class point to the full path of the "StatisticsServiceResources" directory in your local machine file system.
5. Writing the Service Descriptor
In order to deploy our service in Axis2/Java, we need to write an Axis2 service descriptor (services.xml) for our service. We are going to use the RawXMLINOutMessageReceiver as the MessageReceiver for the getStats() method of our service implementation.
<operation name="getStats">
<actionMapping>urn:getStats</actionMapping>
<messageReceiver
class="org.apache.axis2.receivers.RawXMLINOutMessageReceiver"/>
</operation>
We need to add the "enableSwA" parameter to the services.xml with a value of TRUE for the service to be able to send SOAP with Attachment type messages. Alternatively, you can also set this parameter in the axis2 server's axis2.xml. Enabling this parameter in the axis2.xml will enable sending of SOAP with Attachments messages from all the services deployed in your Axis2 server.
<parameter name="enableSwA">true</parameter>
Open services.xml to have a look at the complete source listing of the service descriptor.
6. Deploying the Service
We need to create a service archive (.aar) file to deploy this service into an Axis2 server. You can use the provided Ant[7] build script to create the service archive by using the "generate.service" goal. Make sure to set the AXIS2_HOME environmental variable pointing to the path of your extracted Axis2 binary distribution (e.g., /opt/axis2-1.2) before running the Ant build. You can also create the service archive file manually. The Axis2 Deployment Model[8] article contains a good description about the format of an Axis2 service archive file.
Now copy the created service archive file to the services directory of the repository of your Apache Axis2 server. (In a standard Apache Tomcat based Axis2 server installation the services directory is <CATALINA_HOME>/webapps/axis2/WEB-INF/services). Start the Axis2 server. (In the case of a servlet based installation, start the servlet container.) Visit the services listing (http://localhost:{port_the_server_is_running}/axis2/services/listServices) to check the status of the deployed service.

Writing the Web Service Client
In this example, we are going to write our client application as a the Raw XML (Axiom) client[9] using the OperationClient API. The OperationClient API provides direct access to the incoming response MessageContext object, which is needed to retrieve the attachments from the incoming message.
1. Using the OperationClient API to Invoke the Service
In the client application, we create an OutInOperationClient with the help of a service client API.
ServiceClient sender = new ServiceClient();
//org.apache.axis2.client.Options object can be used to configure the service client
sender.setOptions(options);
OperationClient mepClient = sender.createClient(ServiceClient.ANON_OUT_IN_OP);
We create the request MessageContext object. Then we create the SOAP Envelope for the request message and set it to the request MessageContext. After that, we can add the request MessageContext to the OperationClient object. Now we are ready to execute the OperationClient in order to send the request message.
MessageContext mc = new MessageContext();
SOAPEnvelope env = createEnvelope(projectName);
mc.setEnvelope(env);
mepClient.addMessageContext(mc);
mepClient.execute(true);
2. Retrieving the Attachment Content from the Web Service Response Message
After the execution, we can access the incoming MessageContext from the OperationClient API.
MessageContext response = mepClient.getMessageContext(WSDLConstants.MESSAGE_LABEL_IN_VALUE);
We can access the attachments of the incoming message through the attachment API of the MessageContext by giving the corresponding content-ID values. Once again we need to remember to remove the "cid:" prefix from the value taken out of the "href" attribute.
//retrieving the ID of the attachment
String graphImageID = graphElement.getAttributeValue(new QName("href"));
//remove the "cid:" prefix
graphImageID = graphImageID.substring(4);
//Accessing the attachment from the response message context using the ID DataHandler
dataHandler = response.getAttachment(graphImageID);
3. Manipulating the Received Attachment Data
We can manipulate the real attachment data contained in the javax.activation.DataHandler object using the mechanisms provided by the DataHandler API. We can use the DataHandler.writeTo(java.io.OutputStream) method to write the content of the DataHandler to an outputStream as follows. You can also use the DataHandler.getInputStream() to retrieve a java.io.InputStream of the contained data and DataHandler.getDataSource() and to access the underlying DataSource respectively.
File graphFile = new File("responseGraph.png");
FileOutputStream outputStream = new FileOutputStream(graphFile);
dataHandler.writeTo(outputStream);You can run the client by using the "run.client" target of the provided Ant build by giving a project name as the parameter as follows. Supported values for the project parameter are "axis1" and "axis2". In order to use the Ant build, make sure to set the AXIS2_HOME environment variable to the pathname of the directory in which you installed the JDK version. Also update the targetEPR variable according to your axis2 server deployment.
ant run.client -Dproject axis2
You may also use your favorite IDE to run the client by giving the project name as a parameter.
Open StatisticsServiceClient.java to have a look at the complete source listing of the Web service client implementation.
Resources
References
- Apache Axis2/Java
- SOAP Messages with Attachments
- MIME (see the references section)
- Using SOAP with Attachments in Apache Axis2
- JavaBeans Activation Framework
- Content-ID and Message-ID Uniform Resource Locators
- Apache Ant
- Axis2 Deployment Model
- Writing Web Service Clients Using Axis2's Primary APIs
- SOAP Message Transmission Optimization Mechanism
Author
Thilina Gunarathne, Senior Software Engineer, WSO2 Inc. thilina at wso2 dot com
- Login or register to post comments
- Printer friendly version
- 15161 reads










awesome example
thank you -- its hard to find a cleanly written and documented example on this stuff..
OutOfMemory with Rampart
Hi,
Your tutorial is really helpful. It's work for file size less than 10 mb but for bigger file (74Mb) I have a "OutOfMemory Error : JavaHeap Space" in the "mepClient.execute(true);" on client side.
I set heap space to 728Mb and it works (same exception below). This level of memory is is not usable in a real-life scenario.
I use caching large file on client side.
I have Axis2 1.2 webapp installed on Tomcat/5.5, and i engage Rampart-1.2 module on server side and on client side.
When i disengage rampart both on client and server side, it works for large file.
Is there a reason that rampart don't work for large file ??
Grégoire Rolland
Bug in Rampart
This looks like a bug in Rampart. Please log a JIRA at https://issues.apache.org/jira/browse/RAMPART..
I have no idea why Rampart tries to load the SwA attachments in to memory.
Bug in Rampart
I already submit a bug in rampart (RAMPART-77)
Thank you
Grégoire Rolland
Really good one..
It was really good one and good example. I have deployed the same code in Weblogic using AXIS2 and its working properly.
Can I use Axis available(axis.jar) in Weblogic server and directly deploy this example ?
If we can deploy directly, what are things I need to take care of ?
Thanks,
Chandrashekar.
Only if it is using Axis2
Hi,
I"m not sure whether weblogic has Axis1.x or Axis2.. Chances are high that it's still using Axis1.x..
This sample will not work with Axis1.x.
thanks,
Thilina
it works!
thanks, i have looked for such tipps for few weeks,this article is the best one, it is very useful...
btw, as i deloyed the service by using "ant generate.service", i met the problem: ...example..\..\ AXIS2_HOME\lib can not be found..... actually, i set the AXIS2_HOME in right way....so i tried to use " ant " instead of using " ant generate.service", it worked well and the .aar file was then created...i dont know why... and at the end of the test, the .png file is smoothly downloaded.....
one question about the client,,, , how can i develop a JSP Client rather than Raw XML (Axiom) client,,, ( using Tomcat ,Axis ,saaj.jar, Eclipse with WTP), ..the goal is: download jpg data from web service by using SOAP....
Nice tutorial and worked
Nice tutorial and worked fine.
However I was trying to test SwA service (axis2 swa example) for uploading of a very large file like 500 MB and I get Out of Memory exception on the server side. Upload service works fine for smaller files (order of tens of MB's).
Is there a way to configure axis2 to receive a large file as an attachment.
Thank you
nsm
File Caching
Hi,
You need to have attachment file caching enabled in the server side to receive very large attachments. I was able to upload attachments of size more than 700 MB using Axis2 SwA as well as Axis2 MTOM.. When file caching is enabled, the size limit is the available space in your temp dir (disk)..
Also you would need to increase the socket time out in the client side..
thanks,
Thilina
Following article will help
Following article will help you out..
http://wso2.org/library/264
Cannot find the data handler
Hello.
I tried to run the client, but failed. Could you tell me what is wrong and how I can manage it?
Here's the message.
run.client:
[java] axis2
[java] Project Name : Apache Axis2
[java] Month : 2007 January
[java] Downloads : 34000
[java] urn:uuid:F9449FEDF7653F31DD1201583329575
[java] Exception in thread "main" java.lang.Exception: Cannot find the data handler.
[java] at sample.client.StatisticsServiceClient.processResponse(StatisticsServiceClient.java:120)
[java] at sample.client.StatisticsServiceClient.getMonthlyDownloadStats(StatisticsServiceClient.java:90)
[java] at sample.client.StatisticsServiceClient.main(StatisticsServiceClient.java:46)
[java] Java Result: 1
BUILD SUCCESSFUL
Total time: 3 seconds
Thanks in advance.
Enable SwA in the server side
Hi,
Please enable SwA in the server side.. Make sure MTOM is disabled.. If both are enabled MTOM gets priority.
Refer here for more details..
http://wso2.org/library/264
thanks,
Thilina
Intereoperable with .Net?
Hi Thilina,
Is this example interoperable with .Net webservices. I have deployed this webservice and tried to test this one from .Net C# webservice client, it is giving me some xml exception.
Thanks,
Kiran
Not sure whether .net supports SwA
Hi,
I'm not sure whether .net support SwA... For sure I know that it supports MTOM. cause Axis2 MTOM interoperates with .net MTOM..
Even if it supports SwA, there is no standard way to publish details about SwA attachment in the WSDL.. In that case .net will not be able to identify it and you might need to hand tweak to add the attachements..I don't know whether it's possible to write a web service clients in .net without using contract first approach (using WSDL).
Sorry.. I'm not much familiar with .net...
thanks,
Thilina
MTOM or SwA?
Hi Thilina,
I am currently designing a web service in Axis 2 using the Eclipse IDE and wanted to know whether MTOM or SwA is better when it comes to the server transferring files to the client (for downloads made by the client). Where can I find some advantages and disadvantages of each?
Thanks
Riju
MTOM is the way forward
Hi Riju,
Your decision mainly needs to be based on your service clients. If the clients are still using older web service clients without support for MTOM, then I would say go for SwA.
MTOM is the new specification and it's the way forward. Most of the new web service stacks support MTOM. If you can assume your clients will use newer WS stacks & you want your service to be future proof, then I would say go for MTOM.. MTOM is wire level backward compatible with SwA.. So there is a chance that some older WS clients might still be able to communicate thinking it as SwA..
I cannot recall a specific article which compares the two.. I noted down some advantages of MTOM in the following tutorial.
http://ws.apache.org/axis2/1_3/mtom-guide.html#1
Feel free to ask in here if you need to clarify it more..May be you can tell your use case.
Thanks,
Thilina
needed jars in eclipse
hi there,
i'm just getting familiar with soap and axis and so on.
I also want to send a file via a soap message. I tried this sample and it worked. but only if I ran it from the console ("any run.client").
When I wanted to run the client from within eclipse with importing the build file and then clicking on the the run.client-task.. nothing happened.
Wenn I tried just to run the client-class manually via the "play"-button in eclipse, I had many errors, whick I had to solve with adding many many jars to the classpath. but also then I get an "java.lang.SecurityException"- error. But apart from that. I just want to call the client from within Eclipse...
why doesn't that work (without adding so many jars)??
and why does it work from the console with "ant run.client" without adding any jars or whatever???
I hope somebody can help. Thanks.
my system: win xp with eclipse 3.3