Monday, August 4, 2014

Calling external REST services supporting JSON messaging standard in Oracle SOA 11g

SOA Suite 11g doesn't provide an out of box way to call JSON based REST services. In this blog we shall the alternative options to achieve the same.

HTTP binding in SOA Suite 11g  has ability to make calls to endpoints based on HTTP verbs(GET, POST only) in request-response (or) one way modes as depicted below.
  



However Oracle's Documentation states that -"An outbound HTTP binding reference supports only XML as a response from an external HTTP endpoint. The response should contain the correct XML part name according to outbound expectations."

In other words Http binding supports only XML as output for either GET/POST. For input it supports only url-encoded and xml. Though the url-encoded input has to be expressed/defined in an xsd format as shown below.



 Another major hindrance is that - Majority of the REST services created in IT world use JSON rather than XML as their messaging standard dues to its light weight and simplicity ( the same reason to use REST as against SOAP). HTTP adapter/binding has no support for calling REST endpoints which use JSON as a message standard as against XML. 
Does this leave us no way to call REST services using JSON ? Well not really. We always have Java to fall back on to do something that can't be done ( or provided) in SOA Suite.

The below Java code can be used to call REST service using JSON. The input of the methods is an xml format represented in a string ( to make it easier to interact with bpel). The following are the key points of this code

a. Takes input in XML string format.
b. Converts the XML string to JSON object.
c. Creates a HTTPClient and makes a call to post/get method using the REST URI providing the required basic http authentication credentials.
d. Extra code to bypass the SSL layer ( only to be used in dev mode). In production one should import the right certificates to authenticate themselves.

RestService.java

package oracle.soa.rest;

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

import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;

import net.sf.json.JSON;
import net.sf.json.xml.XMLSerializer;

import org.apache.http.auth.AuthScope;
import org.apache.http.auth.Credentials;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.methods.HttpGet;

public class RestService
{
    public HttpResponse callPOSTService(String xmlStr,String url) throws ClientProtocolException, IOException
    {
          // Convert the input XML to the JSON String
   XMLSerializer xmlSerializer = new XMLSerializer();  
   JSON json = xmlSerializer.read(xmlStr);  
   System.out.println(json);
   // Call POST of REST Service with JSON input

                        DefaultHttpClient client = new DefaultHttpClient();
                        client = WebClientDevWrapper.wrapClient(client);

                        Credentials creds = new UsernamePasswordCredentials("username", "password");
                        client.getCredentialsProvider().setCredentials( new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT), creds);

                        HttpPost post = new HttpPost(url);
   StringEntity se = new StringEntity( json.toString());
   post.setEntity(se);
                        
   HttpResponse response = client.execute(post);
   return response;
 }
    public HttpResponse callGETService(String url) throws ClientProtocolException, IOException
    {   
                        // Call GET of REST Service 
                        DefaultHttpClient client = new DefaultHttpClient();
                        client = WebClientDevWrapper.wrapClient(client);

                        Credentials creds = new UsernamePasswordCredentials("username", "password");
                        client.getCredentialsProvider().setCredentials( new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT), creds);

                        HttpGet get = new HttpGet(url);
                        HttpResponse response = client.execute(get);
                
                        return response;
        }

    public static void main(String[] args) throws ClientProtocolException, IOException
    {
        RestService rs = new RestService();
 /*       String inputString="<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + 
        "<root>\n" + 
        "   <description>Product New one</description>\n" + 
        "   <sku>146-4100-901</sku>\n" + 
        "</root>";
        
        // Create the product with SKU mentioned above.

        HttpResponse response = rs.callPOSTService(inputString,"https://abc.com/api/v1/products");
        // Get the URL to update the product - Eg: "https://abc.com/api/v1/products/18"
        Header header = response.getFirstHeader("Location");
        String update_url=header.toString().substring(10);
        System.out.println(update_url);*/

       /* // Update the product
        String updatedinputString="<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + 
        "<root>\n" + 
        "   <description>Update Product New one</description>\n" + 
        "   <family>Items</family>\n" + 
        "</root>";
        System.out.println(rs.callIPOSTService(updatedinputString,"https://abc.com/api/v1/products/22"));
        */
        // Get Product
        HttpResponse response = rs.callGETService("https://abc.com/api/v1/products/22");
        String xmlResponse = "";
        BufferedReader rd = new BufferedReader (new InputStreamReader(response.getEntity().getContent()));
        String line = "";
        while ((line = rd.readLine()) != null) {
          xmlResponse+=line;
        }
        System.out.println(xmlResponse);
    }

}

WebClientDevWrapper.java - Used to bypass the SSL

package oracle.soa.rest;

import java.io.IOException;

import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.impl.client.DefaultHttpClient;

/*
*/
public class WebClientDevWrapper {

    public static DefaultHttpClient wrapClient(DefaultHttpClient base) {
        try {
            SSLContext ctx = SSLContext.getInstance("TLS");
            X509TrustManager tm = new X509TrustManager() {

                public void checkClientTrusted(X509Certificate[] xcs,
                                               String string) throws CertificateException {
                }

                public void checkServerTrusted(X509Certificate[] xcs,
                                               String string) throws CertificateException {
                }

                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }
            };
            X509HostnameVerifier verifier = new X509HostnameVerifier() {

                @Override
                public void verify(String string,
                                   SSLSocket ssls) throws IOException {
                }

                @Override
                public void verify(String string,
                                   X509Certificate xc) throws SSLException {
                }

                @Override
                public void verify(String string, String[] strings,
                                   String[] strings1) throws SSLException {
                }

                @Override
                public boolean verify(String string, SSLSession ssls) {
                    return true;
                }
            };
            ctx.init(null, new TrustManager[] { tm }, null);
            SSLSocketFactory ssf = new SSLSocketFactory(ctx);
            ssf.setHostnameVerifier(verifier);
            ClientConnectionManager ccm = base.getConnectionManager();
            SchemeRegistry sr = ccm.getSchemeRegistry();
            sr.register(new Scheme("https", ssf, 443));
            return new DefaultHttpClient(ccm, base.getParams());
        } catch (Exception ex) {
            ex.printStackTrace();
            return null;
        }
    }
}

The following are the libraries added to the class path:



I googled and found all of these in the net and added to the class path.

The main() method provides a way to
1. perform GET to retrieve the records using the URL
2. perform POST to create.
3. perform POST to update.

The Java code can be called via a BPEL/Mediator component using Java Embedding ( or more cleaner way by using a Spring component). The code in main method could replace the most of the code in the Java embedding. Most importantly we need to create an XML schema containing the request/response in accordance to the JSON request/response schema of the REST URI.
Eg : In JSON

{
    "sku":"130-01-099",
    "description":"Test"
}

equivalent XML for the same

<?xml version="1.0" encoding="UTF-8" ?>
<root>
   <sku>130-01-099</sku>
   <description>Test</description>
</root>

You could use the following tool for this conversion - http://www.utilities-online.info/xmltojson/#.U9_NevldWAg
You can create an XSD based on the above XML and accordingly use it in the BPEL process to call the Java embedding code/Spring Context component that calls REST service using JSON messaging standard.

Conclusion:  

 While Oracle SOA Suite 12c provides an out of box solution for interacting with REST services with JSON data standard using HTTPAdapter, 11g still need to seek the help of Java code to achieve the same. The way of  creating a Java class that takes XML as input ,converting it to an equivalent JSON and  then calling REST service using JSON ,provides us an interoperability between SOA suite components those primarily still rely on XML messaging with REST services relying on JSON.