Tuesday, July 15, 2014

Don't support MessageVariable in fromPart - Oracle SOA 11g

This error will be thrown while invoking the service with Multipart Message type and the receive activity uses the fromPart to retrieve the parts data and assign to a variables created based on message type or invoke activity uses toPart to assign the data to parts based message type variable.




To resolve this whenever retrieving or assigning values to parts in receive/invoke activity through fromPart or toPart use the element based variables instead using the message type based variable.



Multipart Message Support in Oracle BPEL

Thursday, July 10, 2014

Integrating CQ5 with REST services

The below document explains the approach to integrate CQ5 with REST services.



Download Integrating_CQ5_with_RESTServices.pdf

Saturday, July 5, 2014

java.io.IOException: Server returned HTTP response code: 400 - While invoking the Salesforce login url from java to get the access token

I was getting the below exception while invoking the Salesforce login url to get the OAuth access_token from java.

java.io.IOException: Server returned HTTP response code: 400 for URL: https://login.salesforce.com/services/oauth2/token
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1436)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:234)
at RestCaller.execute(RestCaller.java:46)
at RestCaller.main(RestCaller.java:10)

This exception will happen normally when the values of client_id, client_security, username and password are not specified correctly while invoking the login url - https://login.salesforce.com/services/oauth2/token to get the OAuth access token.

"grant_type=password&client_id=13MVG9Y6d_xxxxxxx&client_secret=8039xxxxx&username=albinsharpxxxxx&password=Albinxxxx"

In my case, I have specified all the values properly but still it was not working, it worked after resetting the password and getting the new security token - password(password+security token)

Calling Salesforce REST based webservice from Java

This post will explain the approach to call the Salesfore REST based webservice through java.
I have exposed a Apex class as REST service from salesforce to fetch the account details.

@RestResource(urlMapping='/AccountDetails/*')
global with sharing class AccountDetails {

    @HttpGet
    global static Account doGet() {
        RestRequest req = RestContext.request;
        RestResponse res = RestContext.response;
        String accountId = req.requestURI.substring(req.requestURI.lastIndexOf('/')+1);
        System.debug('Account Id:'+accountId);
        Account result = [SELECT Id, Name, Phone, Website FROM Account WHERE Id = :accountId];
        return result;
    }
}

The communication is of two steps

  •  getting the access_token 
  •  invoking the service endpoint by attaching the access_token to the request.

To obtain an access token, we will send an HTTP POST request to the authentication endpoint exposed by Salesforce - https://login.salesforce.com/services/oauth2/token  with the details client_id, client_secret, username and password.This values can be get from the configured Connected Apps from salesforce.



import java.io.*;
import java.net.*;
import org.w3c.dom.*;

public class RestCaller {
public static void main(String[] args) throws Exception {

String urlParameters = "grant_type=password&client_id=3MVG9Y6d_xxxxxxxx&client_secret=80xxxxxxxx&username=albinsharpxxxxx&password=xxxxxxxxx";

String loginresponse = RestCaller.execute(
"https://login.salesforce.com/services/oauth2/token", "POST",urlParameters, "application/x-www-form-urlencoded", null);
String sessionId=getSessionId(loginresponse);
String serviceResponse =RestCaller.execute("https://ap1.salesforce.com/services/apexrest/AccountDetails","GET","0019000000AZBCNAA5","application/xml",sessionId);
System.out.println("Service Response \n" + serviceResponse);
}

public static String execute(String targetURL, String HttpMethod,
String urlParameters, String contentType, String SessionId) {
URL url;
HttpURLConnection connection = null;
try {
url = new URL(targetURL);
if (HttpMethod == "GET") {
url = new URL(url.toString() + "/" + urlParameters);
}
connection = (HttpURLConnection) url.openConnection();
if (SessionId != null)
connection.setRequestProperty("Authorization", "OAuth "+ SessionId);
connection.setRequestProperty("accept", "application/xml");
if (HttpMethod == "POST") {
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Length","" + Integer.toString(urlParameters.getBytes().length));
connection.setRequestProperty("Content-Type", contentType);
connection.setRequestProperty("Content-Language", "en-US");
connection.setUseCaches(false);
connection.setDoInput(true);
connection.setDoOutput(true);
OutputStream os = connection.getOutputStream();
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, "UTF-8"));
writer.write(urlParameters);
writer.flush();
writer.close();
os.close();
}

InputStream is = connection.getInputStream();
BufferedReader rd = new BufferedReader(new InputStreamReader(is));
String line;
StringBuffer response = new StringBuffer();
while ((line = rd.readLine()) != null) {
response.append(line);
response.append('\r');
}
rd.close();
return response.toString();
} catch (Exception e) {
e.printStackTrace();
return (e.getClass().getName());
}

finally {
if (connection != null) {
connection.disconnect();
}
}

}

static String getSessionId(String loingResponse)
{
   java.io.InputStream sbis = new java.io.StringBufferInputStream(loingResponse.toString());
       javax.xml.parsers.DocumentBuilderFactory b = javax.xml.parsers.DocumentBuilderFactory.newInstance();
       b.setNamespaceAware(false);
       org.w3c.dom.Document doc = null;
       javax.xml.parsers.DocumentBuilder db = null;
       try {
           db = b.newDocumentBuilder();
           doc = db.parse(sbis);
       } catch (Exception e) {
           e.printStackTrace();
       }  
       org.w3c.dom.Element element = doc.getDocumentElement();
       String access_token="";
       NodeList nodeList = element.getElementsByTagName("access_token");
       if(nodeList!=null && nodeList.getLength() > 0){
        Element myElement = (Element)nodeList.item(0);
        access_token= myElement.getFirstChild().getNodeValue();
       }
       return access_token;
}
}

O/P
<?xml version="1.0" encoding="UTF-8"?>
<response xsi:type="sObject" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<type>Account</type>
<Id>0019000000AZBCNAA5</Id>
<Name>GenePoint</Name>
<Phone>(650) 867-3450</Phone>
<Website>www.genepoint.com</Website>
</response>

Friday, July 4, 2014

Ignoring the Host Name verification while invoking the HTTPS service through HttpsURLConnection

Sometimes you may receive the Host Name mismatch exception while invoking the HTTPS service from the Java client even though the valid certificate is installed to the key store.

javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No name matching <Host Name> found

The root cause of the exception is the CN name of the certificate is not matching with the host name used to connect the service.

In real scenario while the certificate is signed with third party certificate authority , the CN name will be specified as the host name of the server where the service is hosted or wildcard name will be specified e.g *.example.com to represent all sub domains in a domain. So there will not be any issue while connecting to the service.

This mismatch exception will happen most of the time communicating with self signed certificate, the certificate is signed with CN name that is not matching with the host name.

To resolve the issue, the certificate should be signed with proper CN name or we can create a custom host name verifier to customize the host name verification functionality.

We have to return true from the custom host name verifier for the host name for which the CN name is mismatching.This will connect to the service irrespective of the host name used .

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.HttpURLConnection;

public class HTTPCaller {
static {
   javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier(
   new javax.net.ssl.HostnameVerifier(){
       public boolean verify(String hostname,
               javax.net.ssl.SSLSession sslSession) {
           if (hostname.equals("localhost")) {
               return true;
           }
           return false;
       }
   });
}

    public static String execute() {
        String targetURL="https://localhost/test"
        URL url;
        HttpURLConnection connection = null;
        try {
            url = new URL(targetURL);
            url = new URL(url.toString());              

            connection = (HttpURLConnection)url.openConnection();
            connection.setRequestProperty("accept", "application/xml"); //for GET service to return xml payload

            InputStream is = connection.getInputStream();
            BufferedReader rd = new BufferedReader(new InputStreamReader(is));
            System.out.println("\n Received response" + System.currentTimeMillis());

            String line;
            StringBuffer response = new StringBuffer();
            while ((line = rd.readLine()) != null) {
                response.append(line);
                response.append('\r');
            }
            rd.close();
            return response.toString();
        } catch (Exception e) {
            e.printStackTrace();
            return (e.getClass().getName()+":"+e.getMessage().toString());
        }
        finally {
            if (connection != null) {
                connection.disconnect();
            }
        }
    }

Thursday, July 3, 2014

Suppressing the Selection Failure exception in Assign activity – Oracle SOA

In BPEL Assign activity if the xpath returns empty result then SelectionFailure RuntimeFault will be thrown by the engine.

But some cases we may need to suppress the selectionFailure error while assigning the optional elements.

In my case I have a assign activity that will mapthe elements  input and input1 from inputVariable to result and result1 of outputVariable.


The input2 is optional and somecases input2 element  will not be available in the request payload.

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sup="http://xmlns.oracle.com/Application1/SuppressSelectionFailure/SuppressSelectionFailure">
   <soapenv:Header/>
   <soapenv:Body>
      <sup:process>
         <sup:input>?</sup:input>
      </sup:process>
   </soapenv:Body>
</soapenv:Envelope>


While invoking the service I am getting the selectionFailure exception as shown below.

<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
   <env:Header/>
   <env:Body>
      <env:Fault>
         <faultcode>env:Server</faultcode>
         <faultstring>faultName: {{http://schemas.xmlsoap.org/ws/2003/03/business-process/}selectionFailure}
messageType: {{http://schemas.oracle.com/bpel/extension}RuntimeFaultMessage}</faultstring>
         <faultactor/>
         <detail>
            <exception/>
         </detail>
      </env:Fault>
   </env:Body>
</env:Envelope>

I want to suppress the selectionFailure exception because input2 is a optional element .

The ignoreMissingFromData attribute will help us to suppress the selectionFailure if the target is missing in the copy operation.

<copy bpelx:ignoreMissingFromData="yes|no"/>



Now even though the input1 element is missing in the request the assign activity will not throw the selectionFailure exception instead the empty tag will get assigned to the outputVariable.


The bpelx:insertMissingToData attribute will help us to suppress the selectionFailure if the target is missing in the copy operation.

<copy bpelx:insertMissingToData="yes|no"/>

If the xpath in the copy operations returns empty node for the target then we will be getting exception "The assign activity of the to node query is returning zero node"



Configure the attribute bpelx:insertMissingToData will help us to suppress this error.

 <copy bpelx:insertMissingToData="yes">
        <from variable="inputVariable" part="payload"
              query="/client:process/client:input1"/>
        <to variable="outputVariable" part="payload"
            query="/client:processResponse/client:result1"/>
      </copy>

The runtime will crate the to-spec element if it is missing and assign the source value(used Remove operation to remove the result1 element from outputVariable for testing).



The attributes insertMissingToData and ignoreMissingFromData can be added by righ clicking the copy rules.