My experience with web services with Spring, Axis, XFire
First thing to note is that, this being my first contact with web services (SOAP and WSDL), it took me quite a lot of time (almost five working days) to learn about the technologies involved and try to implement and publish a web service in my application.
Of these four and a half working days (36 hours), about 14 cumulated hours were spent stepping through the library code, trying to determine the causes for various errors (especially for XFire, which returned all sorts of cryptic and unhelping error messages).
1) First try: Apache Axis
The first day was dedicated to learning about Axis and trying what it can do.
The second day was dedicated to writing and testing the necessary configurations to publish my specific service, which contained many service methods and serializable classes.
My opinion after this short experience is this: the setting up of the service with Axis seems to be too complicated for my simple needs.
After writing the service interface, you have to run two tools: Java2WSDL and WSDL2Java. The first one will generate the WSDL for the web service. The second one will generate auxilliary classes: remote interface, client stub, service locator helper – all of them hard-coded for my service.
Of course, to integrate them into a build process you have to learn to use the ant tasks for Java2WSDL and WSDL2Java, and after the code generation, compile the generated files.
And then you have to take care of the deployment of the service by using the generated deployment.xml, and I didn’t quite understand how to automate the deployment (the documentation was not very clear about this).
All I needed was some kind of generic Java class to expose web services by proxying SOAP calls to a Spring managed bean that implemented my interface. Something like:
I didn’t want no WSDL or XMLSchema or namespaces or some other messy XML stuff.
And for the client part, something like this was desirable:
Which, fortunately, I actually found the next day in the form of XFire.
2) Second try: XFire
The third day I decided to switch to XFire, because of its simpler usage (no more WSDL, no more deployment.xml, no more two-steps build with Java2WSDL and WSDL2Java…)
So I tried to learn about services.xml and xfire-servlet.xml and stuff.
The first thing that sticks out is the “not-so-complete” documentation and with the way that XFire just doesn’t like to show useful error messages. Most of the errors are either silently ignored, or replaced by some generic message like “Invalid SOAP request”.
One big problem that I had was that at first I tried to access the XFire service with the Axis client that I already have. Big mistake! After half a day of stepping through the XFire code, I discovered that the problem was that my client was sending a request as RPC/encoded, which XFire does not support. And instead of telling me that, XFire was simply returning a fault with the internal exception message, which was: “ Index: 1, Size: 1 “.
Imagine my frustration! I didn’t have any clue as to what this can mean. Could it have something to do with the SOAP elements? Could it be some internal tables used? Could it mean the index of the method parameters? And, most annoying: is this an ERROR MESSAGE? What is the error? “Index: 1, Size: 1” – I don’t see any error in here!
Of course searching the web didn’t help me in this case, so I started debugging the XFire service handlers, and learning the XFire insides along the way.
After about an hour I discovered that this was no more than an IndexArrayOutOfBoundsException! It seems I haven’t met one very often in my days, otherwise I could have been able to recognise the error message. But XFire didn’t return in the fault message the name of the exception, it returned just the error message. And no error logging. And of course, do not even dare to ask a stacktrace.
Anyway, eventually I discovered that the exception was caused because the handler wasn’t able to recognise the format of the SOAP request, which contained multiref elements. After googling some more and not finding any reference to multiref and XFire, I eventually found that the multirefs were there because of the RPC/encoded format – which, no need to mention, I had very little knowledge about. Remember, this was my first contact with SOAP, and I wasn’t in the mood to learn all the “subtleties” of the protocol. I just wanted to expose my service with the least effort.
The fourth day I learned how to expose services with XFire by using JSR 181 Annotations. Because of the incomplete documentation and examples, this caused a problem that took me 3 hours to debug and still couldn’t solve until next day: the service class wasn’t accesible through XFire, which just kept returning empty pages (another annoying behaviour: no error message, no 404, no service fault, no “Invalid SOAP request”, no nothing. Just a blank page with the footer “Generated by XFire”)
I couldn’t tell if it was a problem with SimpleUrlMapper, or with Jsr181HandlerMapping, or if I should use DispatcherServlet instead of XFireSpringServlet…
The documentation contained very confusing examples, in which a SimpleUrlMapper had a mapping from “/” to handlerMapping, and handlerMapping was a Jsr181HandlerMapping. My confusion was: what does Jsr181HandlerMapping do? It’s another URL mapper? And if it is, when are the Spring beans initialized?
The next morning I finally discovered the cause for the last problem encountered.
After yet another session of analysing the XFire source code, it was clear for me that Jsr181HandlerMapping was not an URL mapper. Actually, all it did was to call XFire to register my classes that had @WebService annotations. But then again, why didn’t it work?
To configure this part, I followed an example that I found on some blog, because the XFire documentation lacked this part. I actually followed exact steps showed in the example, along with the example classes and settings. And what frustrated me more was that his conclusion was “So we see that setting up web services by using XFire and JSR 181 annotations is a no brainer.” If it is a no brainer, why it doesn’t work ?! :p
But apparently the author of the example forgot to mention that in order for this to work, you have one more thing to configure: to add your bean in applicationContext.xml
In his example this step was missing, so of course the service was not registered, because (as I found out after analysing the XFire source code) Jsr181HandlerMapping checked all the already configured beans to see if they had @WebService annotation.
So in conclusion:
- XFire is indeed very easy to configure (if you follow a complete example, like this one that I found later), but lacks many “advanced” SOAP features.
- Axis is more advanced, more configurable and with more advanced SOAP and WSDL features, but a “little” bit more difficult to configure.