Tuesday, 16 March 2010

Android as SOA player


There is no argue with the fact that one of the most important parts of SOA is to provide business processes combined with human interaction, meaning that an interruption occurs until a person interacts with the system by performing an action on a waiting task to make the process flow. Therefore, it is crucial to provide a medium for human interaction to accelerate the work behind the business process. No doubt that the mobile world is growning very fast providing connection options to Internet supported by GSM operators. The pattern of combining these two trends into IT structure has been used in most SOA implementations.


Whatever the target businesses of an application, it gets more attention by the users once it is supported by the mobile platform. This is why I tried to develop an Android application as a client of composite application developed in Oracle SOA Suite environment. The composite application is TranslationService mentioned in my last blog post. Briefly, it implements client of Google's RESTful service that translates any statement from a source language to a target language. Mediator component in SOA composite application's and its implementation detail is given in my last post, but Java code of Mediator's callout class is given in lowermost part. The Android application's screen design is given in Figure 1, including all of the necessary fields required by the translator client. Source text, language and target language values are prompted first, and then the Translate button is used to perform translation service call at Oracle SOA Suite side. The result is displayed at a particular field and the Reset button rearranges the screen to make it ready for further translations.



Figure 1
Figure 1



Available mobile platforms have several disadvantages that should be taken into account carefully. Extra server or product installation requirement or application signing procedure complexity are examples of these obstacles. I think Android, as a Java developer friendly environment, is pretty enjoyable and a simple mobile application development platform. First of all, it is based on Java and XML technologies similar to trendy and effective platforms. It supports resource bundle usage by also supporting internationalization. Screen development consists of just designning an XML resource with binding a Java bean. Such resources created during demo application are given below.


XML SOURCE CODE ::
<code lang="xml">
<?xml version="1.0" encoding="utf-8"?>
<AbsoluteLayout
android:id="@+id/varTitle1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
xmlns:android="http://schemas.android.com/apk/res/android"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/welcomeToSample1"
/>


<TextView
android:id="@+id/varInputText_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/inputText"
android:layout_x="40px"
android:layout_y="32px"
>
</TextView>
<EditText
android:id="@+id/varInputText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""
android:layout_x="200px"
android:layout_y="32px"
>
</EditText>


<TextView
android:id="@+id/varSLang_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/sourceLang"
android:layout_x="40px"
android:layout_y="82px"
>
</TextView>
<EditText
android:id="@+id/varSLang"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""
android:layout_x="200px"
android:layout_y="82px"
>
</EditText>


<TextView
android:id="@+id/varTLang_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/targetLang"
android:layout_x="40px"
android:layout_y="132px"
>
</TextView>
<EditText
android:id="@+id/varTLang"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""
android:layout_x="200px"
android:layout_y="132px"
>
</EditText>


<Button
android:id="@+id/btnTranslate"
android:layout_width="87px"
android:layout_height="wrap_content"
android:text="@string/button1"
android:layout_x="40px"
android:layout_y="282px"
>
</Button>

<TextView
android:id="@+id/varResult_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/resultTrans"
android:layout_x="40px"
android:layout_y="332px"
>
</TextView>
<TextView
android:id="@+id/varResult"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""
android:layout_x="200px"
android:layout_y="332px"
>
</TextView>
<Button
android:id="@+id/btnReset"
android:layout_width="87px"
android:layout_height="wrap_content"
android:text="@string/button2"
android:layout_x="40px"
android:layout_y="382px"
>
</Button>
</AbsoluteLayout>



JAVA BEAN SOURCE CODE ::

package com.example.android.helloactivity;

import org.ksoap2.SoapEnvelope;
import org.ksoap2.serialization.PropertyInfo;
import org.ksoap2.serialization.SoapObject;
import org.ksoap2.serialization.SoapSerializationEnvelope;
import org.ksoap2.transport.AndroidHttpTransport;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class HelloActivity extends Activity {
private EditText inputText;
private EditText sourceLang;
private EditText targetLang;
private TextView resultText;
private Button translateButton;
private Button resetButton;

private static final String SOAP_ACTION = "execute";
private static final String METHOD_NAME = "translationSource";
private static final String NAMESPACE = "http://xmlns.oracle.com/singleString";
private static final String WSURL = "http://192.168.2.4:8001/soa-infra/services/default/TranslationServiceProject/TranslatorMediator_ep?WSDL";

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

private void initControls() {
inputText = (EditText) findViewById(R.id.varInputText);
sourceLang = (EditText) findViewById(R.id.varSLang);
targetLang = (EditText) findViewById(R.id.varTLang);
resultText = (TextView) findViewById(R.id.varResult);
translateButton = (Button) findViewById(R.id.btnTranslate);
translateButton.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
translate();
}
});
resetButton = (Button) findViewById(R.id.btnReset);
resetButton.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
reset();
}
});
}

private void translate() {
resultText.setText("error occured");

SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);

PropertyInfo pi1 = new PropertyInfo();
pi1.setNamespace(NAMESPACE);
pi1.setName("input");
pi1.setType(String.class);
pi1.setValue(inputText.getText().toString());
request.addProperty(pi1);

PropertyInfo pi2 = new PropertyInfo();
pi2.setNamespace(NAMESPACE);
pi2.setName("sourceLanguage");
pi2.setType(String.class);
pi2.setValue(sourceLang.getText().toString());
request.addProperty(pi2);

PropertyInfo pi3 = new PropertyInfo();
pi3.setNamespace(NAMESPACE);
pi3.setName("targetLanguage");
pi3.setType(String.class);
pi3.setValue(targetLang.getText().toString());
request.addProperty(pi3);

SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(
SoapEnvelope.VER11);
envelope.setOutputSoapObject(request);
AndroidHttpTransport androidHttpTransport = new AndroidHttpTransport(
WSURL);
try {
androidHttpTransport.call(SOAP_ACTION, envelope);

// Get the SAOP Envelope back and the extract the body
SoapObject resultsRequestSOAP = (SoapObject) envelope.bodyIn;
if(resultsRequestSOAP != null && resultsRequestSOAP.getProperty("output") != null)
resultText.setText(resultsRequestSOAP.getProperty("output").toString());


} catch (Exception E) {
System.out.print("ERROR:" + E.getClass().getName() + ": "
+ E.getMessage());
}
}

private void reset() {
initControls();
}
}



When analyzing the source code, you will see usage of kSOAP framework that helps on calling a web service via Java mobile platform. At that point, usage of such a memory consuming framework in mobile appliation can be discussed. But, I only aimed to create a client of a SOA composite application, thats scenario and details are mentioned before. While researching the topic, I came across with kSOAP and it seemed worth to try. The main problem with the framework was namespace conflict, although the target namespace value was given to the root node, it was not assigned to the given parameters. This caused namespace conflict at the service side. As given at the source code, I needed to assign the same namespace value to each parameter manually. Finally, I am able to say that the combination of these technologies worked well. Figure 2 shows the result of Android application that with respect of given input values, the translation result was displayed at Result field. As analyzing effects at Oracle SOA Suite 11gR1 product side, Figure 3 shows the log output of composite application provided by the callout class.


Figure 2
Figure 2


Figure 3
Figure 3




MEDIATOR'S JAVA CALLOUT SOURCE CODE ::

package com.eteration.sample;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;

import java.util.Iterator;

import java.util.Map;

import javax.xml.parsers.ParserConfigurationException;

import oracle.tip.mediator.common.api.AbstractJavaCalloutImpl;
import oracle.tip.mediator.common.api.CalloutMediatorMessage;
import oracle.tip.mediator.common.api.MediatorCalloutException;
import oracle.tip.mediator.utils.XmlUtils;

import org.w3c.dom.Node;

import oracle.xml.jaxp.JXDocumentBuilder;
import oracle.xml.jaxp.JXDocumentBuilderFactory;
import oracle.xml.parser.v2.NSResolver;
import oracle.xml.parser.v2.XMLDocument;
import oracle.xml.parser.v2.XMLElement;
import oracle.xml.parser.v2.XMLNode;

import oracle.xml.parser.v2.XMLText;

import org.json.simple.JSONObject;
import org.json.simple.JSONValue;

public class TranslatorMediatorCalloutClass extends AbstractJavaCalloutImpl {
private static String googleTranslationService =
"http://ajax.googleapis.com/ajax/services/language/translate";

@Override
public boolean postRouting(CalloutMediatorMessage calloutMediatorMessage,
CalloutMediatorMessage calloutMediatorMessage2,
Throwable throwable) throws MediatorCalloutException {
boolean returnValue =
super.postRouting(calloutMediatorMessage, calloutMediatorMessage2,
throwable);
String sPayload = "null";
for (Iterator msgIt1 =
calloutMediatorMessage.getPayload().entrySet().iterator();
msgIt1.hasNext(); ) {
System.out.println("Google Translator postRouting4");
Map.Entry msgEntry = (Map.Entry)msgIt1.next();
Object msgKey = msgEntry.getKey();
Object msgValue = msgEntry.getValue();
sPayload = XmlUtils.convertDomNodeToString((Node)msgValue);
try {
XMLDocument changedoc = XmlUtils.getXmlDocument(sPayload);
XMLNode inputS1 =
(XMLNode)changedoc.selectSingleNode("//inp1:input",
new LocalNamespaceResolver());
System.out.println("the value of the source text = " +
inputS1.getTextContent());
XMLNode inputS2 =
(XMLNode)changedoc.selectSingleNode("//inp1:sourceLanguage",
new LocalNamespaceResolver());
System.out.println("the value of the sourceLanguage = " +
inputS2.getTextContent());
XMLNode inputS3 =
(XMLNode)changedoc.selectSingleNode("//inp1:targetLanguage",
new LocalNamespaceResolver());
System.out.println("the value of the targetLanguage = " +
inputS3.getTextContent());

XMLDocument doc =
prepareReturnMessage(inputS1.getTextContent(),
inputS2.getTextContent(),
inputS3.getTextContent());
if (doc != null) {
String mykey = "reply";
calloutMediatorMessage2.addPayload(mykey,
doc.getDocumentElement());
}

} catch (Exception e) {
System.out.println(e);
}
} //for
return returnValue;
}

private XMLDocument prepareReturnMessage(String input,
String sourceLanguage,
String targetLanguage) throws ParserConfigurationException {
JXDocumentBuilderFactory factory =
(JXDocumentBuilderFactory)JXDocumentBuilderFactory.newInstance();
JXDocumentBuilder documentBuilder =
(JXDocumentBuilder)factory.newDocumentBuilder();
XMLDocument doc = (XMLDocument)documentBuilder.newDocument();
doc.setVersion("1.0");
doc.setEncoding("UTF-8");
XMLElement rootElement =
(XMLElement)(doc.createElementNS("http://xmlns.oracle.com/singleString",
"translationResult"));
doc.appendChild(rootElement);
XMLElement inputElement = (XMLElement)(doc.createElement("output"));
rootElement.appendChild(inputElement);
String translation = this.translate(input, sourceLanguage, targetLanguage);
System.out.println("translation result = " + translation);

if (translation != null && translation.equalsIgnoreCase(input))
return null;

XMLText translationElement = (XMLText)doc.createTextNode(translation);
inputElement.appendChild(translationElement);
return doc;
}

class LocalNamespaceResolver implements NSResolver {
public String resolveNamespacePrefix(String prefix) {
return "http://xmlns.oracle.com/singleString";
}
}

private static String extractTranslationFromJSON(String response) {
final JSONObject jsonObj = (JSONObject)JSONValue.parse(response);
String translation = null;
if (jsonObj != null && jsonObj.containsKey("responseData")) {
final JSONObject responseData =
(JSONObject)jsonObj.get("responseData");
translation = responseData.get("translatedText").toString();
}
return translation;
}

public static String translate(String sourceString, String sourceLanguage,
String targetLanguage) {
return extractTranslationFromJSON(translateString(sourceString,
sourceLanguage,
targetLanguage));
}

private static String translateString(String sourceString,
String sourceLanguage,
String targetLanguage) {
HttpURLConnection connection = null;
OutputStreamWriter wr = null;
BufferedReader rd = null;
StringBuilder sb = null;
String line = null;

URL serverAddress = null;

try {
serverAddress =
new URL(googleTranslationService + "?v=1.0&&q=" + sourceString.replace(' ','+') + "&&langpair=" + sourceLanguage + "%7C" + targetLanguage);
connection = null;
connection = (HttpURLConnection)serverAddress.openConnection();
connection.setRequestMethod("GET");
connection.setDoOutput(true);
connection.setReadTimeout(10000);
connection.connect();
rd = new BufferedReader(new InputStreamReader(connection.getInputStream()));
sb = new StringBuilder();

while ((line = rd.readLine()) != null) {
sb.append(line + '\n');
}

return (sb.toString());

} catch (MalformedURLException e) {
e.printStackTrace();
} catch (ProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
connection.disconnect();
rd = null;
sb = null;
wr = null;
connection = null;
}
return null;
}
}