Monday, 5 September 2011

Webservice (SOAP) with complex data type and KSOAP Client


When i was new to Android and mobile plateform. My need was to call a webservice deployed in Jboss which accepts a complex input and responds back with a complex output. I searched tro the internet was not able to find a complete guide for the same. I would like to contribute this small tutorial,

Calling a Webservice using Android

Introduction
This tutorial talks about how to consume a webservice (in this case a service deployed into Jboss) from Android 2.2. For this purpose the following tools and SDK’s are used,
1. Android SDK 2.2
2. JDK 1.6
3. Eclipse 3.5 with Android Plug-in
4. Jboss 5.0
5. KSOAP2 2.4
Here kSOAP is a SOAP web service client library for constrained Java environments such as Applets or J2ME applications (CLDC / CDC / MIDP) 

Organization of the Tutorial 

Part1: About the Webservice (Only the WSDL part)
Part2: Calling the webservice with complex request object
Part3: Receiving a complex response object and parsing the same

Part1: About the Webservice (Only the WSDL part)
This Sample webservice has one operation with a complex input parameter and a complex output parameter. The following is the WSDL,


CODE: SELECT ALL
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
xmlns:tns="http://www.example.org/AndroidSampleWebservice/" 
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
name="AndroidSampleWebservice" 
targetNamespace="http://www.example.org/AndroidSampleWebservice/">
  <wsdl:types>
    <xsd:schema targetNamespace="http://www.example.org/AndroidSampleWebservice/">


      <xsd:element name="complexInData" type="tns:inData"></xsd:element>
      <xsd:element name="ComplexOutData" type="tns:outData"></xsd:element>

      <xsd:complexType name="inData">
         <xsd:sequence>
            <xsd:element name="firstName" type="xsd:string"></xsd:element>
            <xsd:element name="lastName" type="xsd:string"></xsd:element>
         </xsd:sequence>
      </xsd:complexType>
    
      <xsd:complexType name="outData">
         <xsd:sequence>
            <xsd:element name="generalData" type="tns:gData"></xsd:element>
            <xsd:element name="addressData" type="tns:aData"></xsd:element>
            <xsd:element name="accountData" type="tns:acData"></xsd:element>
         </xsd:sequence>
      </xsd:complexType>
    
      <xsd:complexType name="gData">
         <xsd:sequence>
            <xsd:element name="firstName" type="xsd:string"></xsd:element>
            <xsd:element name="lastName" type="xsd:string"></xsd:element>
            <xsd:element name="age" type="xsd:string"></xsd:element>
            <xsd:element name="sex" type="xsd:string"></xsd:element>
         </xsd:sequence>
      </xsd:complexType>
    
      <xsd:complexType name="aData">
         <xsd:sequence>
            <xsd:element name="street1" type="xsd:string"></xsd:element>
            <xsd:element name="street2" type="xsd:string"></xsd:element>
            <xsd:element name="postBox" type="xsd:string"></xsd:element>
            <xsd:element name="state" type="xsd:string"></xsd:element>
            <xsd:element name="country" type="xsd:string"></xsd:element>
         </xsd:sequence>
      </xsd:complexType>
    
      <xsd:complexType name="acData">
         <xsd:sequence>
            <xsd:element name="accountno" type="xsd:string"></xsd:element>
            <xsd:element name="bank" type="xsd:string"></xsd:element>
            <xsd:element name="accounttype" type="xsd:string"></xsd:element>
            <xsd:element name="balance" type="xsd:string"></xsd:element>
         </xsd:sequence>
      </xsd:complexType>
    </xsd:schema>
  </wsdl:types>
  <wsdl:message name="getComplexDataRequest">
    <wsdl:part element="tns:complexInData" name="parameters"/>
  </wsdl:message>
  <wsdl:message name="getComplexDataResponse">
    <wsdl:part element="tns:ComplexOutData" name="parameters"/>
  </wsdl:message>
  <wsdl:portType name="AndroidSampleWebservice">
    <wsdl:operation name="getComplexData">
      <wsdl:input message="tns:getComplexDataRequest"/>
      <wsdl:output message="tns:getComplexDataResponse"/>
    </wsdl:operation>
  </wsdl:portType>
  <wsdl:binding name="AndroidSampleWebserviceSOAP" type="tns:AndroidSampleWebservice">
    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
    <wsdl:operation name="getComplexData">
      <soap:operation soapAction="http://www.example.org/AndroidSampleWebservice/getComplexData"/>
      <wsdl:input>
        <soap:body use="literal"/>
      </wsdl:input>
      <wsdl:output>
        <soap:body use="literal"/>
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:service name="AndroidSampleWebservice">
    <wsdl:port binding="tns:AndroidSampleWebserviceSOAP" name="AndroidSampleWebserviceSOAP">
      <soap:address location="http://<your ip and port>/"/>
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>


From the webservice the following are clear,
Service Name: AndroidSampleWebservice
Operation: getComplexData
Input: getComplexDataRequest (A complex request)
Output: getComplexDataResponse (A complex response)

Part2: Calling the webservice with complex request object

The structure of the input is:

CODE: SELECT ALL
<xsd:element name="complexInData" type="tns:inData"></xsd:element>
<xsd:complexType name="inData">
         <xsd:sequence>
            <xsd:element name="firstName" type="xsd:string"></xsd:element>
            <xsd:element name="lastName" type="xsd:string"></xsd:element>
         </xsd:sequence>
      </xsd:complexType>


Step1: 
The first step would be constructing the SoapObject. The following code demonstrates the construction,


CODE: SELECT ALL
private static final String NAMESPACE = "http://www.example.org/AndroidSampleWebservice/";
private static final String METHOD_NAME = "complexInData"; 
SoapObject loSoapObject = new SoapObject(NAMESPACE, METHOD_NAME);
//At this point our loSoapObject here refers to the inData complex type.
//After this we have to set the elements firstName and the lastName into the loSoapObject
loSoapObject.addProperty("firstName","Dwarka");
loSoapObject.addProperty("lastName","Damodaran");


Step2:
Now we have got the SoapObject after this point we would be creating a Soap envelop, putting the envelop into a HTTP transport and calling the required method in the service. In most of the cases the step 2 would remain the same.
The following is the code for it,


CODE: SELECT ALL
private static final String SOAP_ACTION = "http://www.example.org/AndroidSampleWebservice/getComplexData";
private static final String URL = "http://172.25.108.49:8080/AndroidWebservice/AndroidSampleWebservice?wsdl";
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.setOutputSoapObject(loSoapObject);
AndroidHttpTransport httpTransport = new AndroidHttpTransport(URL);
httpTransport.debug = true;
httpTransport.call(SOAP_ACTION, envelope);


With this we have called the webservice. 

Part3: Receiving a complex response object and parsing the same
After Part2 the webservice implementation would do the required work and respond back with the data. Now this data is available in the envelop from the previous part. To extract the body out of the envelop the following has to be done,

SoapObject loresponse = (SoapObject) envelope.bodyIn;

Here since out response type is a complex type we are type casting it into a Soapobject. Now this object would represent the xml tag “outData” in the WSDL. From this loResponseObject we can then extract the aData or acData or the gData tags and in the similar way the inner tags. For doing the following see the following code,


CODE: SELECT ALL
SoapObject logObject = (SoapObject)loresponse.getProperty("generalData");
SoapObject loaObject = (SoapObject)loresponse.getProperty("addressData");
SoapObject loacObject = (SoapObject)loresponse.getProperty("accountData");
Log.d("WS", "logObject: "+logObject);
Log.d("WS", "loaObject: "+loaObject);
Log.d("WS", "loacObject: "+loacObject);            
Log.d("WS", "firstName: "+logObject.getProperty("firstName"));
Log.d("WS", "lastName: "+logObject.getProperty("lastName"));
Log.d("WS", "age: "+logObject.getProperty("age"));
Log.d("WS", "sex: "+logObject.getProperty("sex"));


The following is the complete code for the example:


CODE: SELECT ALL
package com.android.example;

import java.io.IOException;

import org.ksoap2.SoapEnvelope;
import org.ksoap2.serialization.SoapObject;
import org.ksoap2.serialization.SoapSerializationEnvelope;
import org.ksoap2.transport.AndroidHttpTransport;
import org.xmlpull.v1.XmlPullParserException;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;

public class WebserviceConsumer extends Activity {
   /** Called when the activity is first created. */

   private static final String NAMESPACE = "http://www.example.org/AndroidSampleWebservice/";
   private static final String METHOD_NAME = "complexInData";
   private static final String SOAP_ACTION = "http://www.example.org/AndroidSampleWebservice/getComplexData";
   private static final String URL = "http://172.25.108.49:8080/AndroidWebservice/AndroidSampleWebservice?wsdl";

   @Override
   public void onCreate(Bundle savedInstanceState) {
      Log.d("WS", "Inside onCreate");

      super.onCreate(savedInstanceState);
      setContentView(R.layout.main);

      Log
            .d("WS",
                  "--------------------- Webservice Part Begins ---------------------");
      Log.d("WS", "1. SoapObject Construction");

      SoapObject loSoapObject = new SoapObject(NAMESPACE, METHOD_NAME);

      // If this is not a direct type i.e. a primitive or string then you
      // have to construct another SoapObject and put in the place of the
      // obejct
      loSoapObject.addProperty("firstName", "Dwarka");
      loSoapObject.addProperty("lastName", "Damodaran");
      Log.d("WS", "Construction of SOAP Object End !!!");

      SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(
            SoapEnvelope.VER11); // put all required data into a soap
                              // envelope
      Log.d("WS", "2. Envelop Created");
      envelope.setOutputSoapObject(loSoapObject); // prepare request
      Log.d("WS", "3. Request Into Envelop");
      AndroidHttpTransport httpTransport = new AndroidHttpTransport(URL);
      Log.d("WS", "4. Transport Created");
      httpTransport.debug = true; // this is optional, use it if you don't
                           // want to use a packet sniffer to check
                           // what the sent message was
                           // (httpTransport.requestDump)
      Log.d("WS", "5. Transport Level to True");
      try {
         httpTransport.call(SOAP_ACTION, envelope);
         if (envelope != null) 
         {
            SoapObject loresponse = (SoapObject) envelope.bodyIn;
            SoapObject logObject = (SoapObject)loresponse.getProperty("generalData");
            SoapObject loaObject = (SoapObject)loresponse.getProperty("addressData");
            SoapObject loacObject = (SoapObject)loresponse.getProperty("accountData");
            Log.d("WS", "logObject: "+logObject);
            Log.d("WS", "loaObject: "+loaObject);
            Log.d("WS", "loacObject: "+loacObject);
            
            Log.d("WS", "firstName: "+logObject.getProperty("firstName"));
            Log.d("WS", "lastName: "+logObject.getProperty("lastName"));
            Log.d("WS", "age: "+logObject.getProperty("age"));
            Log.d("WS", "sex: "+logObject.getProperty("sex"));
            
         } 
         else 
         {
            Log.d("WS", "Response Envelop Error");
         }

      } catch (IOException e) {
         e.printStackTrace();
      } catch (XmlPullParserException e) {
         e.printStackTrace();
      }
   }
}


If you need to work with the same, the following has to be done,

1. Generate a webservice using the WSDL that i have provided.
2. Deploy the webservice
3. Create a Android project
4. Use the activity code above.

The following are the references for this material,

http://www.javaworld.com/javaworld/jw-08-2002/jw-0823-wireless.html?page=1
http://www.javaworld.com/javaworld/jw-08-2002/jw-0823-wireless.html?page=1
http://developer.android.com/reference/android/app/ListActivity.html
http://supportforums.blackberry.com/t5/Java-Development/Example-of-KSoap2-Wrapper-classes/m-p/473088
http://www.codeproject.com/KB/webservices/CallWebServiceFromkSOAP.aspx
http://stackoverflow.com/questions/1052300/how-to-call-a-net-webservice-from-android-using-ksoap2
http://android.amberfog.com/?p=45
http://www.google.co.in/search?q=ksoap2+example&hl=en&ei=10xlTJKuIY3CcbessboK&start=10&sa=N
http://www.blackberryforums.com/developer-forum/155972-blackberry-ksoap2-tutorial.html
http://devcentral.f5.com/Tutorials/TechTips/tabid/63/articleType/ArticleView/articleId/102/The-Minimum-

Thursday, 25 August 2011

Upload an image to a server in Android

Hi, in today’s tutorial I will show you how to send an image to an server using POST method in ANDROID.

Uploading an image to server is a basic requirement in many of our usual applications.

Sending data to server which is using a PHP Script is already explained in this example .

Now in this example I will show you how to send an image file.

For that first we have to read the file, put it in nameValuePairs and then send using HttpPost.

Now can we quickly go to the code.

This is the main java file UploadImage.java

1package pack.coderzheaven;
2
3import java.io.ByteArrayOutputStream;
4import java.io.IOException;
5import java.io.InputStream;
6import java.util.ArrayList;
7import org.apache.http.HttpResponse;
8import org.apache.http.NameValuePair;
9import org.apache.http.client.HttpClient;
10import org.apache.http.client.entity.UrlEncodedFormEntity;
11import org.apache.http.client.methods.HttpPost;
12import org.apache.http.impl.client.DefaultHttpClient;
13import org.apache.http.message.BasicNameValuePair;
14import android.app.Activity;
15import android.graphics.Bitmap;
16import android.graphics.BitmapFactory;
17import android.os.Bundle;
18import android.widget.Toast;
19
20public class UploadImage extends Activity {
21 InputStream inputStream;
22 @Override
23 public void onCreate(Bundle icicle) {
24 super.onCreate(icicle);
25 setContentView(R.layout.main);
26
27 Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.icon); ByteArrayOutputStream stream = new ByteArrayOutputStream();
28 bitmap.compress(Bitmap.CompressFormat.PNG, 90, stream); //compress to which format you want.
29 byte [] byte_arr = stream.toByteArray();
30 String image_str = Base64.encodeBytes(byte_arr);
31 ArrayList<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
32
33 nameValuePairs.add(new BasicNameValuePair("image",image_str));
34
35 try{
36 HttpClient httpclient = new DefaultHttpClient();
37 HttpPost httppost = new HttpPost("http://10.0.2.2/Upload_image_ANDROID/upload_image.php");
38 httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
39 HttpResponse response = httpclient.execute(httppost);
40 String the_string_response = convertResponseToString(response);
41 Toast.makeText(UploadImage.this, "Response " + the_string_response, Toast.LENGTH_LONG).show();
42 }catch(Exception e){
43 Toast.makeText(UploadImage.this, "ERROR " + e.getMessage(), Toast.LENGTH_LONG).show();
44 System.out.println("Error in http connection "+e.toString());
45 }
46 }
47
48 public String convertResponseToString(HttpResponse response) throws IllegalStateException, IOException{
49
50 String res = "";
51 StringBuffer buffer = new StringBuffer();
52 inputStream = response.getEntity().getContent();
53 int contentLength = (int) response.getEntity().getContentLength(); //getting content length…..
54 Toast.makeText(UploadImage.this, "contentLength : " + contentLength, Toast.LENGTH_LONG).show();
55 if (contentLength < 0){
56 }
57 else{
58 byte[] data = new byte[512];
59 int len = 0;
60 try
61 {
62 while (-1 != (len = inputStream.read(data)) )
63 {
64 buffer.append(new String(data, 0, len)); //converting to string and appending to stringbuffer…..
65 }
66 }
67 catch (IOException e)
68 {
69 e.printStackTrace();
70 }
71 try
72 {
73 inputStream.close(); // closing the stream…..
74 }
75 catch (IOException e)
76 {
77 e.printStackTrace();
78 }
79 res = buffer.toString(); // converting stringbuffer to string…..
80
81 Toast.makeText(UploadImage.this, "Result : " + res, Toast.LENGTH_LONG).show();
82 //System.out.println("Response => " + EntityUtils.toString(response.getEntity()));
83 }
84 return res;
85 }
86}
Now download a file from here which encodeBytes in Base64 Format. Put this file in the same package of UploadImage.java. See the screenshot.
Upload Image
Upload Image

Now the server part.
Create a folder named Upload_image_ANDROID in your htdocs folder and inside that create a file named upload_image.php and copy this code into it.

1<?php
2 $base=$_REQUEST['image'];
3 $binary=base64_decode($base);
4 header('Content-Type: bitmap; charset=utf-8');
5 $file = fopen('uploaded_image.jpg', 'wb');
6 fwrite($file, $binary);
7 fclose($file);
8 echo 'Image upload complete!!, Please check your php file directory……';
9?>
Now run your program and check the folder in which your php file resides.

Note: Make sure your server is running.


Here I am uploading the icon image itself.


If you want to upload another file in your SDCARD you have to change this line to give the exact path
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.icon);
For example if I have to upload a file residing in my SDCARD I would change the path like this.
Bitmap bitmap = BitmapFactory.decodeFile(“/sdcard/android.jpg”);
This tutorial explains how to create an SDCARD and start the emulator with the SDCARD

and this tutorial explains how to put file inside your emulator SDCARD

Please comment if you didn’t get it right or you like this post.

Refferenced from : http://coderzheaven.com/2011/04/android-upload-an-image-to-a-server/