This mechanism can for example be used in exception handling implementations to retire a composite in case of errors.
For example, messages have to be processed in a specific order by a message processor. To offer the messages in for example FIFO order, a multi consumer queue is used. The message processor offers the messages to the database for storage. What if the database is not available for a longer period? For example, a tablespace is full. It is no use trying to continue processing messages. Also retry mechanisms will continue to raise errors and can even make the problem worse by filling the logs with error messages (potentially filling the disk the application server is running on if log rotation is not correctly configured). Also since it is a multi consumer queue, disabling the queue itself will effect other consumers which might not have this problem (for example use a different database). Temporarily disabling the message processor by retiring it might be a solution (of course in combination with informing operations to fix the problem).
Implementation
When using the BPEL Java embedding activity in Oracle SOA Suite, one must be aware of the context under which the code is executed.The embedded code runs as a subclass of
SOA Suite 10g
com.collaxa.cube.engine.ext.BPELXExecLet
(see http://technology.amis.nl/2007/10/05/embedding-java-in-bpel-process/)
SOA Suite 11g (BPEL 2.0)
com.collaxa.cube.engine.ext.bpel.v1.nodes.BPELXExecLet
(see http://technology.amis.nl/2011/01/04/embedded-java-in-a-11g-bpel-process/)
In this blog post I will focus on the 11g implementation. If you want to use the Java API to gain more information and control of a composite, the following class is key in 11g: oracle.soa.management.facade.Composite. See http://docs.oracle.com/cd/E17904_01/apirefs.1111/e10659/oracle/soa/management/facade/Composite.html
How do you get from your BPELXExecLet class to your Composite class? This is relatively easy when you have the composite distinguished name (CompositeDN). The composite DN consists of composite name, revision and partition name. See http://docs.oracle.com/cd/E21764_01/apirefs.1111/e10659/oracle/soa/management/CompositeDN.html.
Obtaining the CompositeDN
XPath?
Oracle extension Xpath functions are available to obtain pieces of information which can help with the construction of the CompositeDN. The results of these Xpath functions can be fed to a Java embedded activity (through a BPEL variable and the getVariableData method) which in turn can use the Locator (http://docs.oracle.com/cd/E14571_01/apirefs.1111/e10659/oracle/soa/management/facade/Locator.html) class lookupComposite method to get to the Composite class.
The partition however can not be easily be obtained. Thus this method cannot be used to obtain all the information needed in order to construct the CompositeDN. (of course we can hardcode it but that would reduce the reuse potential).
From available properties within the Java embedding activity?
From Java it might be possible to programmatically access the fields by using a similar method as described on: http://technology.amis.nl/2007/10/05/embedding-java-in-bpel-process/. Here the method getFromEnvironment was used to obtain the InstanceId. This method is not available in com.collaxa.cube.engine.ext.bpel.v1.nodes.BPELXExecLet however there are: getInstanceProperties and getInstanceHeaders and getMetadata. Metadata was null so not much information there.
For obtaining information about what was available in getInstanceProperties, I used the following code:
java.util.Map<?,?> map = this.getInstanceProperties();
java.util.Iterator myIt = map.keySet().iterator();
String myKey = "";
Object key = null;
while (myIt.hasNext()) {
key = myIt.next();
myKey = (String)key;
addAuditTrailEntry("Key: "+myKey+" Value: "+map.get(key));
}
This yielded:
No CompositeDN yet... I tried getInstanceHeaders next. These were oracle.xml.parser.v2.XMLDocument instances. To convert them to strings I used the following:
http://docs.oracle.com/cd/B28359_01/appdev.111/b28391/oracle/xml/parser/v2/XMLDocument.html and http://stackoverflow.com/questions/216894/get-an-outputstream-into-a-string
This resulted in the following Java embedded code
java.util.List<?> list = this.getInstanceHeaders();
java.util.Iterator myIt = list.iterator();
oracle.xml.parser.v2.XMLDocument myKey = null;
Object key = null;
java.io.ByteArrayOutputStream output = null;
java.lang.StringBuilder string = null;
String result = null;
try {
while (myIt.hasNext()) {
key = myIt.next();
myKey = (oracle.xml.parser.v2.XMLDocument)key;
output=new java.io.ByteArrayOutputStream();
myKey.print(output);
result = output.toString("Windows-1252");
addAuditTrailEntry("Result: "+result);
}
} catch (Exception e) {
addAuditTrailEntry("Error: "+e.getMessage());
}
This however did not yield much better results:
Even though we have some information available through the BPELXExecLet class which can be useful, the partition or CompositeDN of the running process were not directly present.
Via the InstanceId?
The BPELXExecLet provides a method getInstanceId. Upon further examination, this appeared to be the component InstanceId while I was interested in the Composite InstanceId. Then I remembered the getInstanceProperties method which yielded a field called tracking.compositeInstanceId which I could use to obtain the composite instance id.
This InstanceId can be used to create a oracle.soa.management.util.CompositeInstanceFilter instance. This filter can be used with the oracle.soa.management.facade.Locator class to get to the CompositeInstance. The CompositeInstance class has a method to obtain the CompositeDN which in turn can be used with the Locator to obtain the Composite class.
The Java embedding code for retiring a composite can look something like:
try {
java.util.Map<?,?> map = this.getInstanceProperties();
String instanceId = (String)map.get("tracking.compositeInstanceId");
String ecid = (String)map.get("tracking.ecid");
oracle.soa.management.facade.Locator myLoc = oracle.soa.management.facade.LocatorFactory.createLocator();
oracle.soa.management.util.CompositeInstanceFilter filter = new oracle.soa.management.util.CompositeInstanceFilter();
filter.setId(instanceId);
filter.setECID(ecid);
java.util.List<oracle.soa.management.facade.CompositeInstance> myInstance = myLoc.getCompositeInstances(filter);
oracle.soa.management.facade.Composite myComposite = myLoc.lookupComposite(myInstance.get(0).getCompositeDN());
myComposite.retire();
} catch (Exception e) {
addAuditTrailEntry("Java embedding Retire Exception: "+e.getMessage());
}
Once we have the Composite class, retiring a process becomes an easy one-liner. I also decided to try a different path. Using the InstanceId of the component would allow me to get to the Composite. I have not compared both methods in performance.
try {
String instanceId = String.valueOf(this.getInstanceId());
addAuditTrailEntry("InstanceId: "+instanceId);
oracle.soa.management.util.ComponentInstanceFilter filter = new oracle.soa.management.util.ComponentInstanceFilter();
filter.setId(instanceId);
oracle.soa.management.facade.Locator myLoc = oracle.soa.management.facade.LocatorFactory.createLocator();
java.util.List<oracle.soa.management.facade.ComponentInstance> myInstance = myLoc.getComponentInstances(filter);
addAuditTrailEntry("Instances found: "+String.valueOf(myInstance.size()));
oracle.soa.management.facade.Composite myComposite = myLoc.lookupComposite(myInstance.get(0).getCompositeDN());
myComposite.retire();
} catch (Exception e) {
addAuditTrailEntry("Java embedding Exception: "+e.getMessage());
}
Both methods did not work at first because the composite information was not committed to the dehydration store yet. Using the dehydrate activity before calling the embedded Java code would fix this (at the cost of some performance though).
For more ideas on what you can do when you have the Composite class, look at http://docs.oracle.com/cd/E17904_01/apirefs.1111/e10659/oracle/soa/management/facade/Composite.html
Conclusion
The Java embedding activity in BPEL can be used to access pieces of information which are not readily available. This information can then be used to access API's which allow more control of a composite from within the composite itself. In this blog post I have provided an example which can be used to obtain the Composite class in a Java embedding activity. This class provided access to several handy methods such as the retire method. The class can also be used to obtain services, properties, wires, imports, and other interesting classes. This provides options for more complex logic which can be steered from within the composite.
It is a shame I did not find a more direct way (in the time I spend on this...) to obtain an instance of the Composite class. By using the methods described in this post, a dehydrate needs to be executed before executing the Java embedding code. This comes at the cost of some performance. I did manage though to make the code independent of BPEL variables. This allows for relatively easy implementation (copy/paste).
No comments:
Post a Comment