Powered By Blogger

Wednesday, March 14, 2012

Invoking SOA11g Composite Using Direct Binding Invocation API

This article talks about how to invoke a Oracle SOA 11g composite from java program using Java direct binding API.

By default when composite is created it is exposed as SOAP WS but we also have an option of exposing it as direct binding to clients.
Well, the first question that comes to our mind is why should we use 'Direct Binding'  instead default SOAP WS.
Answer is performance gain.This binding is especially useful when applied to commonly re-used  or utility like services, for example CommonExceptionHandler and CommonLoggerService.This is because these services are invoked from many other business services and if we expose this service as WS then there are chances of performance degrade.To overcome this we may choose direct binding.
Below are some characteristics of direct binding.
  • The direct binding enables Java clients to directly invoke composite services, bypassing the intermediate conversion to XML required with the web service binding
  •  Direct binding uses T3 protocol and uses RMI over EJB to communicate with target service.
Now let's see how can we achieve this.
Assume that we have created a process named-CommonStatusLogger which will be invoked from every other business service and this service logs execution status of business service

When it is exposed as direct binding, following code is generated in composite.xml

  <service name="Service" ui:wsdlLocation="CommonStatusLogger.wsdl">
    callbackInterface="http://xmlns.oracle.com/CommonStatusLogger#wsdl.interface(CSLCallback)"/>
    <binding.direct/>
  </service>

Please note  <binding.direct/>.

Assume  that we also created BookBusTicket composite Service and invoked CommonStatusLogger service, then below code is generated in BookBusTicket service 's composite.xml

. <reference name="CommonStatusLogger"
   ui:wsdlLocation="http://host:port/soa-infra/directWsdl/default/CommonStatusLogger/Service?resource=%2FCommonStatusLogger.wsdl">
   <interface.wsdl interface="http://xmlns.oracle.com/CommonStatusLogger#wsdl.interface(CSL)"
   callbackInterface="http://xmlns.oracle.com/CommonStatusLogger#wsdl.interface(CSLCallback)"/>
    <binding.direct address="soadirect:/default/CommonStatusLogger/Service"
     connection-factory="oracle.soa.api.JNDIDirectConnectionFactory"
      useLocalJNDIProvider="true">
      <property name="java.naming.provider.url">t3://host:port</property>

    </binding.direct>
  </reference>

Please concentrate on bold font data.
binding.direct address is actual address where it is accessible  using direct binding API
connection-factory  is connection factory java class using which java clients can create connections.

Below is the java client which invokes CommonStatusLogger service using Java API.

package com.beachbody.bpel.fault.handler;
import com.beachbody.bpel.fault.handler.Exception;//This class is available in implementing-java-fault-handling thread

import java.io.StringReader;
import java.util.Map;
import java.util.HashMap;
import oracle.soa.api.PayloadFactory;
import oracle.soa.api.XMLMessageFactory;
import oracle.soa.api.invocation.DirectConnection;
import oracle.soa.api.message.Message;
import oracle.soa.api.message.Payload;
import org.w3c.dom.Element;
import org.w3c.dom.Document;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import oracle.soa.api.JNDIDirectConnectionFactory;
import oracle.soa.api.invocation.DirectConnectionFactory;
import org.xml.sax.InputSource;


public class CommonStatusLoggerDirectBinding  {

    /**
     * @param errorData
     * @return
     */
    public String invokeCommonErrorStatusDirect(Exception errorData){     
        try {             
          
            /* The JNDIDirectConnectionFactory can be used to establish SOA instance
             connections for exchanging messages over the direct binding.*/
            DirectConnectionFactory dcFactory = JNDIDirectConnectionFactory.newInstance();
            // Connections are created based on the configuration, which is a map of standard
             naming properties, which will be used for the underlying connection lookup.
            Map properties = new HashMap();        
            String soaDirectURL = "soadirect:/"+errorData.getDomainId()+"/"+errorData.getCommonEHProcessName()+"/Service";
            DirectConnection conn =
                dcFactory.createConnection(soaDirectURL, properties);            

                    
            DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
            docBuilderFactory.setNamespaceAware(true);          
            DocumentBuilder builder = docBuilderFactory.newDocumentBuilder();
            Document doc =  builder.parse(new InputSource(new StringReader(errorData.toString())));
            Element  root = doc.getDocumentElement();          

            Map partData = new HashMap();
            // have to use payload see BPELProcess1RequestMessage
            partData.put("payload", root);

            Payload payload = PayloadFactory.createXMLPayload(partData);

            //Messages are created using the MessageFactory
            Message request = XMLMessageFactory.getInstance().createMessage();
            request.setPayload(payload);
          
            // for a request-reply service we need to use conn.request else use conn.post
             need to provide operation name so we need to use process
              Message response = conn.request("initiate", request);
            conn.post("initiate", request);      
        
            return "S";
      
        } catch (Exception e) {
            e.printStackTrace();
            return "E";
        }
   
    }
    public CommonStatusLoggerDirectBinding  () {
        super();   

    }
     public static void main(String[] args) {     

   CommonStatusLoggerDirectBinding  startRMIProcess = new CommonStatusLoggerDirectBinding();
    }

}      


There is also another way of doing i.e using LocatorFactory,  if we go by this approach then following chunk can be used for connection purpose and remaining part of code is same

Hashtable jndiProps = new Hashtable();
Locator locator = LocatorFactory.createLocator(jndiProps);
CompositeDN compositedn = new CompositeDN(domainName, compositename, version);
String serviceName = "Service";
return locator.createDirectConnection(compositedn, serviceName);

Please remember here Service is direct binding partnerlink name in exposed service lane.

Please note that errorData contains data to be passed to CommonStatusLogger and coming from other class otherwise you can pass static data in input XML

Note:Above two approaches works only if java client and called services are co located on same server.If Java client is remote client then add below properties to Hashtable object

jndiProps.put(Context.PROVIDER_URL, "t3://" + hostname + ':' + portname +
 "/soa-infra");
jndiProps.put(Context.INITIAL_CONTEXT_FACTORY,
 "weblogic.jndi.WLInitialContextFactory");
jndiProps.put(Context.SECURITY_PRINCIPAL,"weblogic"); 
jndiProps.put(Context.SECURITY_CREDENTIALS,"welcome1");
jndiProps.put("dedicated.connection","true");

If you want to make this class available on WLS server classpath then,we need to jar this class and copy to domain/lib folder and restart server.
To successfully  compile this program we need to add following jars/libraries to Jdev libraries and classpath
  • BPEL Runtime
  • Oracle.soa.fabric.jar

In next article, we will discuss how to integrate this class into java-fault policies 

3 comments:

  1. It was very interesting for me to read that blog. Thanks the author for it. I like such topics and everything that is connected to them. I would like to read more soon.
    1992 Mazda 929 AC Compressor

    ReplyDelete
    Replies
    1. Thanks for the comments and stay tune for my next blog on java fault handling

      Delete
  2. Praveen, this is helpful.. thanks..!!

    ReplyDelete