Ok, accessing SOAP web services from an Android device shouldn’t be too much trouble I thought, but I was wrong for different reasons. One of them is that SOAP is not particulary kind to your mobile system resources. Google advocates REST and JSON and as much as I prefer REST over SOAP I had no choice and needed to do SOAP web service communication on my mobile device as the backend did only support SOAP and no REST.
The JDK 6 wsimport fiasco
That said, let’s tackle the SOAP problem. First I tried to create myself a neat little type-safe web service client through the JDK 6 wsimport tool. But hey, that’s not working on Android as classes from the javax.* packages were required which are not available on Android. Ok, there are ways to repackage them as described in http://code.google.com/p/dalvik/wiki/JavaxPackages but the web service I liked to use could not be parsed at all. I could have “cut down”/”fixed” the WSDL and try the repacking, but this looked not very clean to me, starting with workarounds right from the start.
The Apache Axis disaster
So I looked closer and saw that the backend was created using AXIS. Ok, I tried AXIS and it generated me a web service client. I was happy… but only for a short while. Then I saw that AXIS not only required javax.* packages as well but also java.* packages. Of the latter I was not sure whether they could be repackaged and work on a Dalvik VM (some classes were dropped because of the limited use or high resource consumption, but the Dalvic VM is not a standard/certified VM so some stuff might not work at all).
The way through with ksoap2-android on a little detour
My first web service call through ksoap2-android
So, what did I had to do? First I had to get a hand on the library. Very kindly, the team provided a JAR with all dependencies for android called ksoap2-android-assembly-2.5.2-jar-with-dependencies.jar (check out the project site above – at the time of this writing 2.5.2 was the latest release, even accessible through Maven). That was a good start. I also found a very nice videocast on the subject at http://vimeo.com/9633556 that I liked. To make this blog more touchable. Let’s assume the web service I want to access is Jira, so here is what needs to be done for a login:
HttpTransportSE transportSE = new HttpTransportSE(
SoapObject request = new SoapObject("http://soap.rpc.jira.atlassian.com", "login");
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
Securing ksoap2-android web service calls with SSL
Nice, ksoap2-android worked… with HTTP and that was fine for the test server but I needed HTTPS for the productive instance. How to pull that trick with ksoap2? I was a bit blind in the beginning as I did my experiments not in the emulator or on the device, but first tried to understand ksoap2 and the web service I was also new to in a J2SE project as ksoap2 (not the android spin-off) works on a plain JDK, too. However, that version is missing the HTTPS support. So eventually I saw that ksoap2-android has a class called HttpsTransportSE. Boy was I happy to see this, but how to use it? The API looked a bit strange and I found no documentation, but the only source of truth is the code anyway and when looking at the sources it was clear that the parameters are all put into an URL object, so calling it is as simple as this:
HttpTransportSE transportSE = transportSE = new KeepAliveHttpsTransportSE(
“my.server.com”, 443, “/rpc/soap/jirasoapservice-v2, 1000);
Trusting the server certificate
That was almost all I needed, but ksoap2-android or rather HttpClient doeesn’t trust my server as there was no valid certification path, i.e. Android had no built-in root certificate trusting in the end the certificate from my server. “Crazy” Bob Lee, formerly with the Google Android team, explains how to trust SSL certificates on Android here http://blog.crazybob.org/2010/02/android-trusting-ssl-certificates.html, but since I planned to use the web service in a safe network I was also fine with trusting all SSL communication in general (of course opening up the gates to man-in-the-middle attacks and similar vulnarabilities) as described e.g. here http://groups.google.com/group/android-developers/browse_thread/thread/1ac2b851e07269ba/c7275f3b28ad8bbc?lnk=gst&q=certificate. Just call FakeX509TrustManager.allowAllSSL() before you do any HTTPS communication/call to ksoap2-android and you are fine.
All in all
I have to admit that I haven’t expected so much hassle with this task. Clearly, I would have loved to do REST, but I had to do SOAP and this is still the situation in which you will find yourself most of the time if you can’t control the backend and have to use what’s available: Not many companies have made the transition already to REST or offer a REST interface of comparable capabilities. Anyhow, it worked and that’s what counts and in the end it worked even without workarounds like repacking non-shipped packages or other hacks and it worked with just a few lines of code – that’s how it should be. The need for a full-blown web service client is anyway not given on mobile device most of the time as you focus your work on these devices on the certain features that make sense on a mobile device. Blindly re-coding web UIs into mobile applications isn’t the type of mobile applications that makes fun or sense.
What you might find useful
- As mentioned above I was new to the web service I wanted to use and ksoap2 as well, so I played with its J2ME/J2SE version, but that one was missing the (KeepAlive)HttpsTransportSE stuff (I used ksoap2-j2se-full-2.1.2.jar). However, if you get the sources for the three classes HttpsTransportSE, KeepAliveHttpsTransportSE, and HttpsServiceConnectionSE from ksoap2-android project and put them into your J2SE play-around project you can make your first steps with a much simpler environment and productivity gain.
- Debugging is another thing. If you like to see what’s going on with the SSL communication, you can activate the verbose SSL debug output with this call System.setProperty(“javax.net.debug”, “ssl”);
- Debugging of ksoap2-android can be done by making the call transportSE.debug = true; prior to performing your SOAP call transportSE.call(…), setting a breakpoint on that line and inspecting the fields transportSE.requestDump and transportSE.responseDump.