Axis2 MTOM file download web service
Published by Thilina Gunarathne on at 4/29/2008 09:13:00 AMSometime back I thought of writing a tutorial on deploying and accessing a web service which provides binary data with it's response using MTOM, similar to my tutorial on SwA. But I was never able to find time to write a good tutorial. On the other hand the number of queries I receive asking for a sample which can download some binary data from a web service using MTOM is increasing over the time. In here I'm going to provide a quick & dirty a very simple sample program to achieve it, hoping to improve it to a full blown tutorial some day.
The easiest way to deploy such a service in Axis2 is to deploy it as a Plain Old Java Object (POJO) service. In order to make your service return some binary data, you just need to have a method similar to the following getFile() method in your pojo class.
import javax.activation.DataHandler;
import javax.activation.FileDataSource;
public class MTOMFileDownloader {
public DataHandler getFile(String fileName) {
FileDataSource dataSource = new FileDataSource(fileName);
DataHandler fileDataHandler = new DataHandler(dataSource);
return fileDataHandler;
}
}
Now we need to write the services.xml file.
<service name="MTOMFileDownloader">
<messagereceivers>
<messageReceiver
mep="http://www.w3.org/2004/08/wsdl/in-out"
class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/>
</messagereceivers>
<parameter name="ServiceClass">
MTOMFileDownloader
</parameter>
<parameter name="enableMTOM">true</parameter>
</service>
Notice the "enableMTOM" property. Now compile the above class. Package the class files and the services.xml as a .aar file. Simplest method to do it is to create a temp directory. Then create the MTOMFileDownloader.java in that directory with the above given code. Create a new directory name META-INF inside the temp directory and copy the above given services.xml file to that. Then execute the following commands in the temp directory. You'll need to add the activation jar to classpath.
>javac -classpath ~/activation-1.1.jar MTOMFileDownloader.java
>jar -cvf MTOMFileDownloader.aar *
Now copy the MTOMFileDownloader.arr file to the services directory of your axis2 server. Now start the server. Access the generated wsdl file by going to the following location in your browser (port and host may be different based on your server).
http://localhost:8080/axis2/services/MTOMFileDownloader?wsdl
You should see a child element like following in the schema part of your wsdl.
<xs:element minoccurs="0" name="return" nillable="true" type="xs:base64Binary">
Now you can go ahead use Axis2 wsdl2java tool to generate a client stub for the service.
Note that POJO is just one of the many ways you can write a service in Axis2. You can also use a contract first (WSDL) approach or raw xml approach to write the above given service.
Enjoy!!!
Thank you Thilina!
Been looking for a similar tutorial across the internet without any luck. Good to know that someone is helping curious minds!
Thank you once again.
Riju
Marc Lefebvre reported (http://markmail.org/message/uhlztrduzx6v57py) that the operation tag is needed as follows in the service.xml when using the above sample in Axis2 1.4.
<service name="MTOMFileService">
<parameter name="ServiceClass">com.akimeka.attachments.MTOMFileService</parameter>
<operation name="getFile">
<messageReceiver
mep=http://www.w3.org/2004/08/wsdl/in-out
class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/>
</operation>
<parameter name="enableMTOM">true</parameter>
</service>
Thanks a lot Marc.
Hi Thilina,
I am having a web service in which I need to send multiple number of attachments 0 to say 10 attachments. On the client I tried adding the datahandlers for the files on the MessageContext and then executing the operation client. but how do I retrieve the same data handlers on the service side? I am trying another approach as you explained in this article with file names in array, but how do I set those filenames (array)in the client side and then make sure it is accessed on the service side in a similar way as you have explained in this article - Your help is much appreciated.
Thanks,
Asmita
Hi, Very helpful article to get me started. I just started Axis-2, I have read several of your blogs. very helpful. Thank you!
Question thou, how do you consume it? say I use wsdl2java to generate stubs and I understand the base64binary is the return type. I looked at the base64binary API. I can get dataHandler object from base64Binary. Then, I see getOutputStream(), getContent(), etc. from DataHandler API. What do I need to do to see the actual data?
second question is: what if I don't have binary data, just have some ASCII data. In this case, is MTOM/Base64Bianry gonna help the performance? comparing to I transmit the data without using attachment.
Third question: it seems that base64binary is also supported by .net, do you see any inter-operability issues there?
Thank you so much.
-Rick
My last post didn't show up... try again...
Hi Thilina, Very helpful article to get me started. I just started Axis-2, I have read several of your blogs. very helpful.
Question thou, how do you consume it? say I use wsdl2java to generate stubs and I understand the base64binary is the return type. I looked at the base64binary API. I can get dataHandler object from Base64Binary. Then I can see getOutputStream(), getContent(), etc. from DataHandler API. What do I need to do to see the actual data?
second question is: what if I don't have binary data, just have some ascii data. in this case, is MTOM/Base64Bianry gonna help the performance? comparing to I transmit the data without using attachment.
Third question: it seems that base64binary is also supported by .net, did you see any inter-operability issues there?
Thank you so much.
Hi Ricky,
I have comment moderation turned on to stop the spam comments on the blog. Sorry about the inconvenience.
Regarding the usage question, you can use the getInpuputStream() to get access to the data using an inputstream. You can read the bytes using the various read() methods in inputstream.
If the ascii data is big, then there might be performance improvements with regards to the XML parser. With respect to the message size, putting inside the SOAP will give you a slight advantage (no mime wrappers).
base64Binary is either base64 encoded binary inside the SOAP body or plain binary attachments. Base64 encoding is a well established standard & the plain binary is just bytes, hence no need of any format. AFAIK there aren't any major interoperability issues with .net.
thanks..
Thank you for the quick response. I got my client work with getInputStream(), read() and then out put to a file.
I am more interested in the second comments that you made. Are you saying it better NOT to use attachment when transmit large ascii data?
This is the use case: I need to return an XML string to client side. This xml string represents the structure of an object or a collection of such objects. Depends on the situation, the collection could be small or big. if big, we are talking about several thousands of such objects. In Axis-1, the entire XML string is on my payload and encoded as well (RPC/encoded). Therefore, the performance could be bad. When I started learning Axis2, with other benefit I get from Axis-2, I was thinking to use MTOM/SOAP Attachments, The reason is that I want to leverage the streaming support that MTOM provides (did I understand this correctly about MTOM)?
Back to your comments, what do you mean by "With respect to the message size, putting inside the SOAP will give you a slight advantage (no mime wrappers)."? especially the "putting inside the SOAP" part, how do I achieve that? Do you need to use Axis2 API? In Axis1, I didn't need to get Axis1 API involved (for example, the method has the signature: public String[] getBooks(){...}, where the return String is an XML presentation of Book objects. Alternatively, the signature of above method: public Book[] getBooks(){...}). What do you recommand in this use case to get the max performance.
Thank you,
-Ricky
Hello Thilina:
I posted a response couple of days ago and didn't see it appear in your comments. Maybe your filter somehow thought it was a spam. I am trying again since I really need your advise on this one...If you see duplicates, please remove...
=====
Thank you for the quick response. I got my client work with getInputStream(), read() and then out put to a file.
I am more interested in the second comments that you made. Are you saying it better NOT to use attachment when transmit large ascii data?
This is the use case: I need to return an XML string to client side. This xml string represents the structure of an object or a collection of such objects. Depends on the situation, the collection could be small or big. if big, we are talking about several thousands of such objects. In Axis-1, the entire XML string is on my payload and encoded as well (RPC/encoded). Therefore, the performance could be bad. When I started learning Axis2, with other benefit I get from Axis-2, I was thinking to use MTOM/SOAP Attachments, The reason is that I want to leverage the streaming support that MTOM provides (did I understand this correctly about MTOM)?
Back to your comments, what do you mean by "With respect to the message size, putting inside the SOAP will give you a slight advantage (no mime wrappers)."? especially the "putting inside the SOAP" part, how do I achieve that? Do you need to use Axis2 API? In Axis1, I didn't need to get Axis1 API involved (for example, the method has the signature: public String[] getBooks(){...}, where the return String is an XML presentation of Book objects. Alternatively, the signature of above method: public Book[] getBooks(){...}). What do you recommand in this use case to get the max performance.
Thank you so much.
-Ricky
This depends on your usecase. If your client is immediately using the returned XML, then it is better to specify the XML in the schema and send it inside the SOAP body. In that way Axis2 will do all the XML parsing and validation for you..
But on the other hand if you are treating your XML string as just another file and do not use it immediately, then MTOM might be good. May be you can zip and attach the file to achieve better efficiencies.
Looking at your usecase I feel it is the earlier case and I recommend sending it inside the soap body as part of soap message.
> for example, the method has the > signature: public String[] > getBooks(){...}
Have you used WSDL2Java code generation with Axis1 or did you follow the POJO method.. Both can be done using Axis2 too...
Hi Thilina:
Thank you for your suggestion. And, yes, the client will use the data immediately. From what you suggested, seems like that I just write this service as usual (I am using code first approach to write services) and return a collection of xml string (public String[] getBooks()) OR an array of 'book' objects (public Book[] getBooks())and no need to do anything special. That way, as usual, all data will be inside soap body as part soap message. Did I understand right? Please confirm.
Yes, on the client side, I have been using wsdl2java to generate client stubs to consume the services (i.e. based on http://myService?wsdl (auto generated wsdl)). if what I mentioned above is what you suggested, does that mean client will begin processing data AFTER all data arrived at client side? will that affect performance when payload is large? please advise.
I think that I may have some mis-understanding in some basics.
Thank you.
-Ricky
> return a collection of xml string >(public String[] getBooks()) OR an >array of 'book' objects (public >Book[] getBooks())and no need to >do anything special.
Assuming that the xml string you r mentioning is the XML corresponding to a book, I would suggest you to use the second option. Using that will generate schema for the book and and your service and client will treat them as XML not just as strings.
Axis2 is written using AXIOM to support the streaming parsing of XML. Pure Axis2 definitely can start processing your message as soon as it starts receiving it.. But when you are using data binding (wsdl2java), it'll depend on the whether the data binding framework you are using supports streaming parsing. I don't have much knowledge about the data binding frameworks used in Axis2 to give you a clear answer. But my guess is atleast ADB supports stream parsing..
Hi,
during example implementation i am getting error.
com.ctc.wstx.exc.WstxEOFException: Unexpected EOF in prolog
at [row,col {unknown-source}]: [1,0]
on setting attribute enableMTOM to True.
without doing it i am not getting attachment.
i am using axis2 1.4, jdk1.4 and jboss 4.0.2.
Thanks in advance.
Hello Thilina,
i have a q question about byte[] and DataHandler.
I´ve got a Webservice, which returns me an Array of Objects. These Objects includes an Attachment, which im getting from a datastore as a byte[].
My Problem is, if i send the Attachment as a byte[], im getting Java Out of Memory eceptions with large Attachments.
Now i want to convert this byte[] into Datahandler, but dont know, how to handle this.
Here is the Construktor of my Object, i want to send as an Array.
My parameter "attachment" coming as a byte[], and my Propertie dhAttachment is a Datahandler, which i want to includ the byte[]
public WebItemServiceBean(Long webItemId, Date objcreated,
Date objmodified, String title, String name, String shortdescription,
String content, String author, Date published, Date validFrom,
Date validTo, String attachmentname, byte[] attachment) {
super();
this.webItemId = webItemId.longValue();
this.objcreated = objcreated;
this.objmodified = objmodified;
this.title = title;
this.name = name;
this.shortdescription = shortdescription;
this.content = content;
this.author = author;
this.published = published;
this.validFrom = validFrom;
this.validTo = validTo;
this.attachmentname = attachmentname;
try {
this.dhAttachment.getOutputStream().write(attachment);
} catch (IOException e) { e.printStackTrace();
}
}
Hi Fatih,
It is better to avoid byte[] for large attachments. byte[] needs to load all the attachment content to the memory, giving the OOM.
You can use DataHandler argument directly in the web service implementation. Use ByteArrayDataSource to create DataHandlers out of byte[].
If you are receiving very large attachments, please switch on attachment file caching in the client side.
http://wso2.org/library/264
Hallo Thilina,
thx for the quick answer. ByteArrayDataSource was my solution!
Now i want to secure my Webservice with Rampart 1.4 .
Which way would u prefer?
UserName Token , Signature or Encryption.
The way to securing the web service depend on the level of security you need.
UserName Token - Authenticating users
Signature - Ensure authenticity of the message
Encryption - Securing the confidentiality of the message