the oracle soa suite 11g http binding or another way to call restful services from soa composite...

27
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.

Upload: irshad-a-buchh

Post on 27-Jul-2015

568 views

Category:

Documents


6 download

TRANSCRIPT

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 componentWe 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 Service1. 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 XSDGo 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 Service13. Deploy the Composite application to the SOA Suite14. Test the Translation Service

The response returned:

The Message Flow Trace: