Wednesday, November 2, 2011

Correlate one event into multiple instances of the same composite in SOA 11g

Inspired by the blog written by Lucas on this topic , I thought of extending it by providing an implementation for the same ( with a slightly different approach).

The Use-Case is as follows :


We have a SOA Composite Application for the Order process. Whenever a customer places an order, a new instance of this process kicks off. At any one time we will potentially have multiple instances for orders from the same customer. These instances are uniquely identified by the order id.

It happens that the F&A department – because of for example financial difficulties with a certain customer or government regulations regarding certain countries – publishes an event: “Terminate Customer”. This event should result in having all running Orders for that Customer being terminated


In Simple terms, we need to fire an event that abruptly terminates all the running instances of the Order Process for a specific customer.


The Approach :

Customer's can place an Order using the operation 'process' of Order Process bpel component exposed as SOAP service to the external users. The OrderProcess component has another operation 'terminate' that is used to terminate the order instance whose OrderId (the correlation key) matches with the orderId passed in the payload.while terminating the order we match it with the same correlation key(defined earlier in the process) to terminate the right instance of the process.

When the Organisation processing the orders intends to terminate a specific customer's(identified by customerId) running orders , it calls another bpel process with the customerId as its payload. This BPEL process in turn calls a java api ( using Spring Component) passing the customerId as input. The api responses back to the call returning it with the list of all OrderId's for that CustomerId. For each of these OrderId's an AbortEvent is published .

The Abort Event is subscribed by another BPEL process which in turn calls the 'terminate' operation of the OrderProcess bpel compoenent passing the OrderId as input.
Back inside the OrderProcess a mid process receive which is waiting on the terminate operation wakes up and terminates the instance(based on the OrderId correlation key) after doing neccessary processing.

Depicted below the process:

a) The Composite



b) OrderProcess BPEL Component:




c) BPEL Component that calls the Java Api ( using Spring Context Component) to get the Order Id's for which Abort Order Event is published.





d) Spring Component :

The following entry in the SpringContext.xml file is added :

Contents of "MyInterface.java":

public interface MyInterface
{
List getInstancesWithCompositeSensorFilter(String customer);
}

Contents of "MyInterface.class":

package com.sridhar;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;


import javax.naming.Context;



import oracle.soa.management.facade.bpel.BPELInstance;
import oracle.soa.management.facade.ComponentInstance;
import oracle.soa.management.facade.Composite;
import oracle.soa.management.facade.CompositeInstance;
import oracle.soa.management.facade.Locator;
import oracle.soa.management.facade.LocatorFactory;
import oracle.soa.management.facade.Sensor;



import oracle.soa.management.util.ComponentInstanceFilter;
import oracle.soa.management.util.CompositeInstanceFilter;
import oracle.soa.management.util.Operator;
import oracle.soa.management.util.SensorFilter;


public class MyClass {
public MyClass() {
super();
}

public List getInstancesWithCompositeSensorFilter(String customer){
System.out.println("Customer Name ---------> "+customer);

List instaceList = new ArrayList();
Locator locator = null;
Hashtable jndiProps = new Hashtable();
jndiProps.put(Context.PROVIDER_URL, "t3://localhost:8001/soa-infra");
jndiProps.put(Context.INITIAL_CONTEXT_FACTORY,
"weblogic.jndi.WLInitialContextFactory");
jndiProps.put(Context.SECURITY_PRINCIPAL, "weblogic");
jndiProps.put(Context.SECURITY_CREDENTIALS, "weblogic123");
jndiProps.put("dedicated.connection", "true");

try
{
locator = LocatorFactory.createLocator(jndiProps);

Composite composite =
(Composite)locator.lookupComposite("default/SpringProjectTrials!1.0");


CompositeInstanceFilter compInstFilter = new CompositeInstanceFilter();
List sFilterList = new ArrayList ();
SensorFilter sFilter =
new SensorFilter("customerIdCompositeSensor" /* sensorname */,
Sensor.SensorDataType.STRING /* type */,
Operator.EQUALS /* operator for comparison */,
customer);
sFilterList.add(sFilter);

compInstFilter.setSensorFilter(sFilterList);
compInstFilter.setCompositeDN(composite.getDN());

List compInstances =
composite.getInstances(compInstFilter);

if (compInstances != null && compInstances.size()>0) {


for (CompositeInstance instance : compInstances)
{
// setup a component filter
ComponentInstanceFilter cInstanceFilter = new ComponentInstanceFilter ();
// get child component instances ..
List childComponentInstances = instance.getChildComponentInstances(cInstanceFilter);
// for each child component instance (e.g. a bpel process)

for (ComponentInstance cInstance : childComponentInstances)
{
BPELInstance bpelInstance = (BPELInstance)cInstance;
//The OrderId which is corelation key for the Order Process BPEL is set as Index 1
instaceList.add(bpelInstance.getIndex(1));
}
}
}//if
else{
System.out.println("====InstaceList -->No Elements Found ===");
}
return instaceList;
}//try
catch (Exception e) {
return instaceList;
}
}
}

e) The BPEL Process Subcribed(Listening) to AbortOrder Event , that inturn calls 'terminate' operation on the Order Process.



That's it about implementing this.


Some Clarifications/ Open Questions :

1) You would have got a doubt about why I created another bpel process ( listed in section 'e' above , that subcribes to the Abort event and routing the same to the terminate operation of the order process , instead of having the mid process recieve activity in the Order Process to directly listen to the Abort Event.

Reason : Having a mid process recieve activity of an event type and setting the correlation set same as defined in the initial receive gives me an error. SCA-50012. I didn't get the straightforward description of the error in the logs , but by trial and error i understood that doing so is probably not supported in this release.

2) In the Spring Context file while retrieving the OrderId's, I am not sure of how to retrieve the field's value of OrderId in the input paylod. ( In 10 g I know we have getField() method on IInstanceHandle interface where the same is not supported in 11g). As a workaround I have created Index( here First Index) on the that field in the Order BPEL process and used the following api to retrieve the Index value thus getting the required OrderId's.

BPELInstance bpelInstance = (BPELInstance)cInstance;
instaceList.add(bpelInstance.getIndex(1));

There should be a better way to do this by directly getting the feild's value. I appreciate input from the blog readers on how to do this as I spent enough time to find how this can be done but unsuccessful.

Comments are welcome...

2 comments:

  1. Hi Sridhar,

    I think you created a very elegant implementation. This is more or less what I had in mind when I scribbled http://technology.amis.nl/blog/13681/soa-suite-11g-event-delivery-network-how-to-correlate-one-event-into-multiple-instances-of-the-same-composite-bpel-or-bpm. Thanks for writing this article.

    However, I think that instead of having the mid-process receive, you could use an asynchronous event handler on the scope. This hanlder receives the event and acts on it. I had that easily working in the past - and it seems more elegant than your current implementation and offers better functionality because the Order Process can be terminated at any given moment - as it probably desirable.

    I am not sure about the Java API for the sensor retrieval either - I hope to look into that one of these days.

    kind regards,

    Lucas

    ReplyDelete
  2. Lucas, Thanks for the comment.
    I agree with you in using a asynchronous fault handler inside a scope rather than mid process receive.Nice idea.

    ReplyDelete