You are on page 1of 23

The Oracle SOA Suite 11g HttpBinding or another way to call RESTful services

from SOA Composite Applications


I wanted to take a quick look at REST(ful) WebServices and see how those can be integrated into the
SCA based SOA Composite Applications that we create with the Oracle SOA Suite. Currently, it does not
have the HTTP binding that the 10.1.3 release of the SOA Suite used to have. So what are the
alternatives?
In this article, I want to demonstrate a way of calling RESTful (simple http request based) services into a
SOA Composite application. I show one way of doing so using the Google Translation Service, a
RESTful service described at http://code.google.com/apis/ajaxlanguage/documentation/ and to be
accessed at http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&q=hello
%20world&langpair=en%7Cit. This service takes a string to translate and an indication of a source and a
destination language. Though maybe not formally resource oriented enough to be called REST-style (or
RESTful) service by some, it is a service that does not require SOAP or WS* but simply a HTTP Get
request. So at least quite restful.
In this article I will use the work I did and described in the previous article: Leveraging RESTful Services
from Java application using Jersey (Introduction). Using project Jersey's support for clients of RESTful
services and the JSON-SIMPLE library to interpret the response I get from the GoogleTranslation service,
I will hook up with the RESTful Service. Then I will leverage the Mediator Java Callout mechanism to
integrate this REST-service-client into a SOA Composite application. The Mediator I create will be the
fully SOA Suite integrated front-end of this RESTful Service.

With this Mediator in place, I can make calls such as:


There are several ways to go about this DIY HttpBinding. Which one is best and ultimately my favorite is
hard to say at this stage. A few of the ways that I can see right now – and I presume at some point Oracle
will add the HttpBinding again – and perhaps even a RESTful, JSON aware service binding. Then that
would be the favorite you would think.
For now we could use:
 use the Mediator Java Callout mechanism to call a Java client of the RESTful service
 create a Java client of the RESTful service and expose that client as a SOAP WebService that can
be bound to from composite applications
 create a custom JCA connector to take on this task
 create a custom XPath function and use it in the mapping
 use the new (currently in preview mode) Java Spring context (that allows integration of beans in a
Spring Bean context to be invoked); this seems an easier approach than my Mediator Java Callout;
however, at the time of writing (11gPS1) it is not yet a production ready feature
 create an SDO enabled EJB and invoke it using the EJB binding service
 put a translation request to be fulfilled by the RESTful service on a JMS queue; have a queue-
listener pick up the message, make the call and put a correlated response on another queue; have
the composite application wait for the response to arrive (it seems that the work recently published
by Edwin Biemond could help here:JMS Request Reply Interaction Pattern in Soa Suite 11g)
 and many other ways I am sure exist to.
As stated before, I will make use of the Mediator Java Callout mechanism to have the mediator reach out
to the Java class that invokes the RESTful service and so indirectly calls the HTTP based RESTful
translation service.
However, the original approach based on the Jsersey client for consuming RESTful services failed. I kept
on running into a problem with Jersey deployed to SOA Suite (I put the libraries in the SCA-INF/lib
folder in my SOA Composite Application. This fails after deployment when I try to run the application by
invoking its WebService interface, complaining about
com.sun.jersey.core.impl.provider.header.LocaleProvider. There are some resources on the internet that
suggest OSGi clashes). Whatever the cause, this article was not about Jersey but about binding RESTful
http services into SOA Composite applications. So I will go for a more straightforward alternative for
invoking the Google Translation Service.
Steps to implement Http Binding (well, sort of)

1. Create new SOA Composite Application in JDeveloper 11g with SOA extension; pick the Composite
with Mediator component template; call the Mediator Translator and choose the Synchronous service
interface. Add the Oracle XML Parser v2 library to the project.

2. Edit the XSD created for the mediator: singleString.xsd. Add a new element:

<element name="translationSource">
<complexType>
<sequence>
<element name="input" type="string"/>
<element name="sourceLanguage" type="string"/>
<element name="targetLanguage" type="string"/>
</sequence>
</complexType>
</element>

3. Edit the WSDL created for the mediator – change the element defining the requestMessage, replace it
with the translationSource element that was defined in the XSD:

<wsdl:message name="requestMessage">
<wsdl:part name="request" element="inp1:translationSource"/>
</wsdl:message>

I also prefer to have more meaningful names for portType and operation – instead of execute_ptt and
execute – so I have changed them too. However, these changes need to be propagated into the
composite.xml, .componentType and .mplan files.

4. From the composite editor, double click the mediator component in order to start editing. Click the
green plus sign, to create a new static routing rule. Click on the Echo button (instead of Service and
Event) to create a roundtrip mediator that takes on the request and returns it back to the requestor, after
performing transformations and java callouts.

5. Edit the mapping/transformation for this routing rule.

The underlying XSLT source for this mapping is:

<xsl:template match="/">
<inp1:singleString>
<inp1:input>
<xsl:value-of
select="concat(/inp1:translationSource/inp1:input,',',/inp1:translationSource/inp1:sourceLanguage,',',/inp1
:translationSource/inp1:targetLanguage)"/>
</inp1:input>
</inp1:singleString>
</xsl:template>

this mapping is used to have the translation request available in the response message for the Java Callout
to act on.
 
6. Specify the Java Callout for the Mediator. Set it to
nl.amis.soasuite11g.mediator.MediatorCalloutToHttpRestProcessor – the name of a class we are about to
create.

7. Create new class MediatorCalloutToHttpRestProcessor that extends AbstractJavaCalloutImpl:

package nl.amis.soasuite11g.mediator;

public class MediatorCalloutToHttpRestProcessor extends AbstractJavaCalloutImpl{


@Override
public boolean postRouting(CalloutMediatorMessage calloutMediatorMessage,
CalloutMediatorMessage calloutMediatorMessage2,
Throwable throwable) throws MediatorCalloutException {
System.out.println("postRouting");
boolean returnValue =
super.postRouting(calloutMediatorMessage, calloutMediatorMessage2,
throwable);
String sPayload = "null";
for (Iterator msgIt = calloutMediatorMessage2.getPayload().entrySet().iterator();msgIt.hasNext(); ) {
Map.Entry msgEntry = (Map.Entry)msgIt.next();
Object msgKey = msgEntry.getKey();
if (msgKey.equals("reply")) {
try {
Object msgValue = msgEntry.getValue();
sPayload = XmlUtils.convertDomNodeToString((Node)msgValue);
XMLDocument changedoc;
changedoc = XmlUtils.getXmlDocument(sPayload);
XMLNode inputS =
(XMLNode)changedoc.selectSingleNode("//inp1:input", new LocalNamespaceResolver());
String[] params = inputS.getTextContent().split(",");
XMLDocument doc = prepareReturnMessage(params[0], params[1], params[2]);

String mykey = "reply";


calloutMediatorMessage2.addPayload(mykey, doc.getDocumentElement());
} catch (Exception f) {
System.out.println(f);
}

}
} //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");
//http://xmlns.oracle.com/singleString
// singleString/input
XMLElement rootElement = (XMLElement)
(doc.createElementNS("http://xmlns.oracle.com/singleString","singleString"));
doc.appendChild(rootElement);
XMLElement inputElement =
(XMLElement)(doc.createElement("input"));
rootElement.appendChild(inputElement);
String translation = NoJerseyTranslationService.translate(input, sourceLanguage, targetLanguage);
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";
}
}
}

(see the previous blog article on SOA Suite 11g Mediator Java Callout for details on this class)

8. This class uses the NoJerseyTranslationService, another class that needs to be created. However, before
we can do so, we need to download the json-simple library from http://code.google.com/p/json-simple/
and add the jar file json_simple-1.1.jar to the SCA-INF/lib directory in our project.

9. Create class NoJerseyTranslationService – see for details about its implementation the blog article
Leveraging RESTful Services from Java application using Jersey (Introduction).

package nl.amis.soasuite11g.mediator;

public class NoJerseyTranslationService {


private static String googleTranslationService =
"http://ajax.googleapis.com/ajax/services/language/translate";

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);
//set up out communications stuff
connection = null;

//Set up the initial connection


connection = (HttpURLConnection)serverAddress.openConnection();
connection.setRequestMethod("GET");

connection.setDoOutput(true);
connection.setReadTimeout(10000);

connection.connect();

//get the output stream writer and write the output to the server
//not needed in this example
//wr = new OutputStreamWriter(connection.getOutputStream());
//wr.write("");
//wr.flush();

//read the result from the server


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
{
//close the connection, set all objects to null
connection.disconnect();
rd = null;
sb = null;
wr = null;
connection = null;
}
return null;
}
}
10. Deploy the composite application to the SOA Suite. Test the Web Service exposed by the Mediator –
to have for example the string "Hello World" translated from english to french.

Reusing the Translator component


We now have a reusable SOA/SCA component, based on the RESTful translation service – and a way to
create such a component for every RESTful service. That is nice. To celebrate, let's use this component in
the following scenario:
A file with a list of words (in some source language) arrives on the file system. A SOA application picks
up the file and translates every word into an indicated target language. The result of this operation is a
file with all the translation results

How to set this up?


The Mediator Routing rule is configured as:
SOA Suite 11g – Using Spring Component to mimic Http
Binding and integrate RESTful services

In an earlier post, I showed for the Oracle SOA Suite 11g how we can use the Mediator's Java Callout
functionality to integrate RESTful services into our SOA Composite applications, even though we currently
have no Http Binding Service nor WSIF support (SOAP Java Binding) at our disposal in the SOA Suite – link
to article.  In SOA Suite 11g PS1 – released in November 2009 – is the preview (not yet officially supported
and only available for PoC and early trials – of Spring components. This feature provides another way of
integrating Java classes into our SOA Composite applications.
This article demonstrates how we can use the Spring component to bind our SOA Composite Application
to the RESTful Translation service provided by Google.

Note:
In order to use this preview feature in SOA Suite 11g PS1, you must first enable preview mode. Go to
$JDeveloper_HOME/integration/seed/soa/configuration and open soa-config.xml. Search for $
{SPRING_COMPONENT_NAME_L} and remove the xml comments (<!– –>) around it. After you've saved
the file, restart JDeveloper.
Steps for creating the SOA Composite with integrated RESTful
Translation Service
1. Create SOA application – with Mediator (Translator)
2. Create Java Interface ITranslator

package nl.amis.soasuite11g.spring;
public interface ITranslator {

public String translate(String sourceString, String sourceLanguage, String


targetLanguage);
}

3. Create implementation class TranslatorImpl

package nl.amis.soasuite11g.spring;

public class TranslatorImpl implements ITranslator {

public String translate(String sourceString, String sourceLanguage,


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

private static String googleTranslationService =


"http://ajax.googleapis.com/ajax/services/language/translate";

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;
}

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);
//set up out communications stuff
connection = null;

//Set up the initial connection


connection = (HttpURLConnection)serverAddress.openConnection();
connection.setRequestMethod("GET");

connection.setDoOutput(true);
connection.setReadTimeout(10000);

connection.connect();

//read the result from the server


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
{
//close the connection, set all objects to null
connection.disconnect();
rd = null;
sb = null;
wr = null;
connection = null;
}
return null;
}
}

4. Create Spring Context using the Spring 2.5.6 node under Business Tier in the New Gallery:
Call the Spring Content restful-beans.xml. For example. Or something else.

5. Configure bean translator in the restful-beans.xml Spring context, based on the TranslatorImpl class

<bean name="translator"
class="nl.amis.soasuite11g.spring.TranslatorImpl" />

6. Configure an sca-service translationService in the restful-beans.xml Spring context,based on the bean


translator
<sca:service name="translationService" target="translator"
type="nl.amis.soasuite11g.spring.ITranslator"/>

 
The Spring Context is immediately added to the composite.xml

 
 
8. Create an XSD for the elements used for the request into and response from the Translation Service:

<schema attributeFormDefault="unqualified"
elementFormDefault="qualified"
targetNamespace="http://nl.amis/translation"
xmlns="http://www.w3.org/2001/XMLSchema">
<element name="translationResult">
<complexType>
<sequence>
<element name="result" type="string"/>
</sequence>
</complexType>
</element>
<element name="translationSource">
<complexType>
<sequence>
<element name="input" type="string"/>
<element name="sourceLanguage" type="string"/>
<element name="targetLanguage" type="string"/>
</sequence>
</complexType>
</element>
</schema>

9. Create the WSDL for Mediator based on this XSD


Go to the Mediator editor and click on the green plus sign behind the prompt WSDL URL. In the Define
Service dialog, click on the generate WSDL icon to create the WSDL based on element definitions in the
XSD. Select translationSource for the request message and translationResult for the response.
10. Wire Spring Component restful-beans to Mediator TranslationService in the composite editor:
this causes the Spring Component's WSDL to be generated based on the Java Interface definition.

11. Create transformations between Mediator and Spring Component


12. Expose Mediator Service as Composite level Service
13. Deploy the Composite application to the SOA Suite
14. Test the Translation Service
The response returned:
The Message Flow Trace:

You might also like