Friday, September 30, 2011

Restandra


JavaScript Testing

For my honours project, I have been working a on RESTful HTTP database connector for the distributed database Cassandra (RESTful + Cassandra = RESTandra). Thankfully the programming side of it is almost done, which leaves the problem of testing.

RESTandra is essentially a RESTful Cassandra API (integrated into the 0.8-dev build of Cassandra), the interface is merely a URL, this makes things like user testing unnecessary, as the components of the URL are specified by the hierarchy of Cassandra & the principles of REpresentational State Transfer.

This leaves actually testing the performance of RESTandra, which causes quite a big problem, as Cassandra is designed to handle very large amounts of data. To tackle this I developed a distributed method of testing in JavaScript. The test 'page' here: http://restandra.dyndns.org:18220/ attempts to simulate the traffic of a real application through a series of AJAX requests to a RESTandra/Cassandra cluster. While the test is running, it times how long the test application takes to preform the set read/write/edit/delete operations and then submits the results to a server outside of the cluster, for processing.

The 'test' page is delivered though HTTP from RESTandra. This is certainly not the best way to serve web pages, however, due to JavaScript's same origin policy, it's necessary (you cant have both RESTandra and a HTTP server running on the same port).

Thanks for your interest in my project and if you can spare the processor cycles, please run the test for as long as you can.


Test Page souce (RESTandra is only set up to deliver 1 page, therefore all the scripting is in the head of the page):


Thanks, Jonny


Android Web Service Client


ConstantDescription
NAMESPACENamespace is the targetNamespace in the WSDL.
URLThe WSDL URL. Its value is the location attribute of the soap:address element for a port element in a WSDL. Unless the web service is also hosted on the Android device, the hostname should not be specified as localhost, because the application runs on the Android device while the web service is hosted on the localhost server. Specify hostname as the IP address of the server hosting the web service.
METHOD_NAMEThe name of the web service operation, which may be obtained form the WSDL.
SOAP_ACTIONNAMESPACE+METHOD_NAME specified as a String literal.

The constant values are as follows; the IP address for the URL constant would vary:
private static final String NAMESPACE = "http://hello_webservice/";
private static String URL="http://192.168.1.68:7001/HelloWebService/HelloWSService?WSDL"; 
private static final String METHOD_NAME = "hello";
private static final String SOAP_ACTION =  "http://hello_webservice/hello";

All Activities must implement the onCreate method for activity initialization. Define the UI using the setContentView method and the layout resource.
setContentView(R.layout.main);

Create an Android widget TextView object using the findViewById method on the TextView element defined in the main.xml.
TextView lblResult = (TextView) findViewById(R.id.result);

Create a org.ksoap2.serialization.SoapObject object to build a SOAP request. Specify the namespace of the SOAP object and method name to be invoked in the SoapObject constructor.
SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);

Create a org.ksoap2.serialization.PropertyInfo object to contain property information to be sent with the SOAP method call. Each property requires a new PropertyInfo object. The hello method takes only 1 argument for a name. Set the property name as "arg0", and specify the type of the property as STRING_CLASS. Add the PropertyInfo object to the SoapObject using the addProperty method.
PropertyInfo propInfo=new PropertyInfo();
propInfo.name="arg0";
propInfo.type=PropertyInfo.STRING_CLASS;
request.addProperty(propInfo, "John Smith");  

Next create a SOAP envelop. Use the SoapSerializationEnvelope class, which extends the SoapEnvelop class, with support for SOAP Serialization format, which represents the structure of a SOAP serialized message. The main advantage of SOAP serialization is portability.
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11); 

The constant SoapEnvelope.VER11 indicates SOAP Version 1.1. Assign the SoapObject request object to the envelop as the outbound message for the SOAP method call.
envelope.setOutputSoapObject(request);

Create a org.ksoap2.transport.HttpTransportSE object that represents a J2SE based HttpTransport layer.HttpTransportSE extends the org.ksoap2.transport.Transport class, which encapsulates the serialization and deserialization of SOAP messages.
HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);

Make the soap call using the SOAP_ACTION and the soap envelop.
androidHttpTransport.call(SOAP_ACTION, envelope);

Get the web service response using the getResponse method of the SoapSerializationEnvelope object and cast the response object to SoapPrimitive, class used to encapsulate primitive types.
SoapPrimitive  resultsRequestSOAP = (SoapPrimitive) envelope.getResponse();

Set the String message in the SOAP response in the TextView UI component.
lblResult.setText(resultsRequestSOAP.toString());

The Activity class is listed below in Listing 4.

Listing 4. Activity Class AndroidWSClient.java
package android.webservice.client;


import org.ksoap2.SoapEnvelope;
import org.ksoap2.serialization.SoapObject;
import org.ksoap2.serialization.SoapPrimitive;
import org.ksoap2.serialization.SoapSerializationEnvelope;
import org.ksoap2.transport.HttpTransportSE;
import org.ksoap2.serialization.PropertyInfo;


import android.widget.TextView;
import android.app.Activity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;


public class AndroidWSClient extends Activity {

 
 private static final String NAMESPACE = "http://hello_webservice/";
 private static String URL = "http://192.168.1.68:7001/HelloWebService/
	HelloWSService?WSDL"; 
 private static final String METHOD_NAME = "hello";
 private static final String SOAP_ACTION =  "http://hello_webservice/hello";
 
 private TextView lblResult;
 

 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);

  lblResult = (TextView) findViewById(R.id.result);
 
  SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME); 

  
  PropertyInfo propInfo=new PropertyInfo();
  propInfo.name="arg0";
  propInfo.type=PropertyInfo.STRING_CLASS;
  
  request.addProperty(propInfo, "John Smith");  

  SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11); 

  envelope.setOutputSoapObject(request);
 HttpTransportSE androidHttpTransport = new HttpTransportSE(URL);

  try {
   androidHttpTransport.call(SOAP_ACTION, envelope);
  
    SoapPrimitive  resultsRequestSOAP = (SoapPrimitive) envelope.getResponse();
  

   lblResult.setText(resultsRequestSOAP.toString());
   
 
  } catch (Exception e) {
	  
   
  }

 }
}  


Android Web Service Client


Using AndroidSOAP is a very easy way to call a SOAP service, because it is based on JAX-WS interfaces. You can use all interfaces that the 'wsimport' generates from the WSDL. Short example:
 /**
  * Creates a request, it is a JAX-WS generated class
  */
 ListSkinPacksRequest request = new ListSkinPacksRequest();
 /**
  * Put it into a 'request' map
  */ 
 Map<String, Object> parameters = new HashMap<String, Object>();
 parameters.put("request", request);

 /**
  * Creates an envelope with namespace and creates a body with operation name 'listSkinPacks'
  */
 Envelope envelope = new SimpleEnvelope("http://skinpack.pop.javaforum.hu/");
 envelope.setHeader(new SimpleHeader());
 envelope.setBody(new SimpleBody("listSkinPacks", parameters));

 /**
  * Creates a transport (HTTP or HTTPS) with an optional username/password
  */
 Transport transport = new HttpTransport("http://services.power.of.planets.hu/PoP-SkinPack-remote/listSkinPacks",
                                         "androidsoap.demo@javaforum.hu", "demopassword");
 /**
  * Call a service, the result arrives into the JAX-WS class
  */
 ListSkinPacksResponse response = transport.call(envelope, "return", ListSkinPacksResponse.class);

 /**
  * You can use the result
  */
 for (SkinPackMetadata metadata : response.getReturn().getSkinPacksList())
 {
   String fileName = metadata.getFileName();
   String name = metadata.getName();
 }
Can you see? The lists are lists, the classes are classes, the values are in the properties, just like in the JAX-WX client... 

Where?

Android Web service client


kSOAP2 – Based Web Service Client

If you are trying to get web services working on your Android device you have a few options. First, there is kSOAP. What is it? Well, it is a Java-based library for building SOAP clients. It was designed for Java clients with limited resources (Applets, Android Devices, and embedded solutions). But why do we need kSOAP, you ask. After all, there are rich libraries and tools in the existing JDKs for creating clients. True that. But SOAP communications require overhead that may be too much for mobile devices. (That is the thinking. I do not buy it. My XOOM has a ton of RAM and dual core NVidia chips. And the phones that are coming out are getting to be pretty well equipped with computing power.) But I do not recommend using kSOAP unless being mired in APIs is your thing. The whole point of web services was to add a layer of abstraction over the remote procedure call. In a typical kSOAP project there are numerous (relative) calls to low level APIs. For example, a simple call to the aforementioned HelloWorld looks like the following:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package com.bif.client;
 
import java.io.IOException;
import org.ksoap2.SoapEnvelope;
import org.ksoap2.SoapFault;
import org.ksoap2.serialization.SoapObject;
import org.ksoap2.serialization.SoapSerializationEnvelope;
import org.ksoap2.transport.HttpTransportSE;
import org.xmlpull.v1.XmlPullParserException;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
 
public class KSOAP2Client extends Activity {
    private static final String stringMethodName = "getGreeting";
    private static final String stringNamespace = "http://bif.com/";
    private static final String stringURL = "http://192.168.1.100:9876/BIFWebServices";
    TextView textView = null;
 
    /**
     * Called when the activity is first created.
     */
    public void onCreate(Bundle bundleSavedInstanceState) {
        super.onCreate(bundleSavedInstanceState);
        this.setContentView(R.layout.main);
        this.textView = (TextView)findViewById(R.id.textView);
 
        this.textView.setText("Testing");
 
        AsyncTaskGreetingServiceCaller asyncTaskGreetingServiceCaller = new AsyncTaskGreetingServiceCaller();
        asyncTaskGreetingServiceCaller.execute();
    }
 
    private class AsyncTaskGreetingServiceCaller extends AsyncTask<Void, Void, Void> {
        protected Void doInBackground(Void...voids) {
            SoapObject soapObject = new SoapObject(KSOAP2Client.stringNamespace, KSOAP2Client.stringMethodName);
            SoapSerializationEnvelope soapSerializationEnvelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
            soapSerializationEnvelope.setOutputSoapObject(soapObject);
 
            try {
                HttpTransportSE httpTransportSE = new HttpTransportSE(KSOAP2Client.stringURL);
                httpTransportSE.debug = true;
                httpTransportSE.call(KSOAP2Client.stringNamespace + KSOAP2Client.stringMethodName, soapSerializationEnvelope);
                Object objectResult = (Object)soapSerializationEnvelope.getResponse();
                textView.setText(objectResult.toString());
                return null;
            } catch (Exception exception) {
                exception.printStackTrace();
            }
 
            return null;
        }
    }
}
When this code runs on a tablet you will get a screen similar to the following