Sunday, August 1, 2010

Fault handling in Oracle SOA Suite 11g - Part II

This previous blog explained why it is a good idea to address -and handle- business faults separately from technical errors. It also introduced a mechanism used in real life Oracle SOA Suite 11g projects to deal with technical errors in a generic way without having to add this functionality to all our SCA composites again and again. Now it is time to dive into the technical implementation of that mechanism and some nitty gritty details.

First things first: How do we get a hold of these technical errors and how can we determine what to do with them?

Oracle SOA Suite 11g offers a unified fault handling framework for SCA composites and their references, service adapters and components such as BPEL and Mediator components. The framework provides hooks you can use to configure fault handling and possibly call out to your own fault handling code. The unified framework is an improvement compared to the SOA Suite 10g stack that consisted of less integrated components (ESB, BPEL) that had their own fault handling mechanisms. The framework is heavily based on BPEL PM’s 10g fault handling framework.

In SOA Suite 11g you configure the fault handling framework on the level of SCA composites using two files: fault-policies.xml and fault-bindings.xml. By default these files need to be in the same directory as the composite.xml file.

Note that you can place these files somewhere else and have multiple SCA composites point to the same fault handling configuration. MDS is a nice candidate since it is a repository for shared artefacts such as reusable XSD’s, DVM’s, and so on. To do this you need to set the “oracle.composite.faultPolicyFile” and “oracle.composite.faultBindingFile” properties in the composite.xml files and point them to fault binding and policy files in the central MDS location. Whether you use this feature mostly depends on how unique your fault handling per SCA composite will be. For now, we will continue with the basic scenario in which we define fault policies per SCA composite.

First of all we will configure the fault-bindings.xml file. This file defines what elements are bound to what fault policy. Elements can be components, references, service adapters or an entire composite. The actual fault policy that is referred to will be defined later on in the fault-policies.xml file. Since business faults can be dealt with using BPEL activities such as Throw and Catch activities we want to have all remaining faults (all unexpected faults) in the entire composite to be handled the same way.

Let’s say we have a simple SCA composite with an inbound file adapter called “MyInboundFileService” and some other components such as a BPEL and Mediator components. Our fault-bindings.xml file could look like the following:


<?xml version="1.0" encoding="UTF-8"?>
<faultPolicyBindings version="2.0.1"
                     xmlns="http://schemas.oracle.com/bpel/faultpolicy">
    <composite faultPolicy="MyCompositeFaultPolicy"/>
</faultPolicyBindings>


In this example we bind fault handling for the entire composite to the -yet to be defined- policy “MyCompositeFaultPolicy”. Instead of the “composite” element you can use the “component” or “reference” elements to apply fault handling on a more granular level.

Next we need to define the fault-policies.xml file. This file defines the actual policies and the conditions when these policies should be executed.

Following the example we will define a single policy, namely “MyCompositeFaultPolicy”:



As you can see from the example we first define the criteria when the policy should be executed. In this case we want it to be executed in case of any technical error. More specifically in case the error is of type “mediatorFault”, “bindingFault” or “runtimeFault”. Note that we can define more intelligent conditions that can be content-based (e.g. based on process instance variables).


<?xml version="1.0"?>
<faultPolicies xmlns="http://schemas.oracle.com/bpel/faultpolicy">
  <faultPolicy version="2.0.1" id="Subsidie_FaultPolicy">
    <Conditions>
      <faultName xmlns:medns="http://schemas.oracle.com/mediator/faults"
 name="medns:mediatorFault">
        <condition>
          <action ref="MyFaultPolicyJavaAction"/>
        </condition>
      </faultName>
      <faultName xmlns:bpelx="http://schemas.oracle.com/bpel/extension"
 name="bpelx:bindingFault">
        <condition>
          <action ref="BPELJavaAction"/>
        </condition>
      </faultName>
      <faultName xmlns:bpelx="http://schemas.oracle.com/bpel/extension"
 name="bpelx:runtimeFault">
        <condition>
          <action ref="MyFaultPolicyJavaAction"/>
        </condition>
      </faultName>
    </Conditions>
    <Actions>
      <Action id="ora-terminate">
        <abort/>
      </Action>     
      <Action id="MyFaultPolicyJavaAction">
        <javaAction className="nl.vennster.MyFaultPolicyJavaAction"
                    defaultAction="ora-terminate">
          <returnValue value="ora-terminate" ref="ora-terminate"/>
        </javaAction>
      </Action>
    </Actions>
  </faultPolicy>
</faultPolicies>


When the error meets any of these criteria the actions within the “Actions” element will be executed. Instead of configuring default actions such as abort, retry or rethrow we redirect the fault to our own Java class called “MyFaultPolicyJavaAction”. This is allowed as long as such a class implements the “IFaultRecoveryJavaClass” class containing the methods “handleFault” and “handleRetrySuccess”. Since the fault may occur within synchronous processes the fault handling framework needs to know what to do after it delegates the fault to some external piece of code. In order to do so the “handleFault” method needs to return the outcome as String. This outcome should map to a predefined fault action. In our example we abort the process instance after our custom Java class has been executed by returning “ora-terminate” that is mapped to the default abort action. Next to that, Java actions need to define a “defaultAction” attribute in case the outcome cannot be mapped to a predefined fault policy.

For some reason rejected messages need to be defined separately. In other words, such faults remain uncaught when using the above fault handling configuration. An example of a rejected message can be an inbound file that cannot be parsed correctly by a File Adapter. To have rejected messages handled we need to specifically include it using the exact name of the adapter service or reference. In our case the inbound file adapter is named “MyInboundFileService”. Our fault-bindings.xml file now looks like this:


<?xml version="1.0"?>
<faultPolicyBindings version="2.0.1"
                     xmlns="http://schemas.oracle.com/bpel/faultpolicy">
    <composite faultPolicy="MyCompositeFaultPolicy"/>
    <service faultPolicy="RejectedMessages">
        <name>MyInboundFileService</name>
    </service>
</faultPolicyBindings>


Note that you can add more than one adapter name to the “service” element. That way all rejected messages of all adapters can be handled the same way. So for instance you can add “MyOutboundDatabaseService” to the “RejectedMessages” policy too.


<?xml version="1.0" encoding="UTF-8"?>
<faultPolicyBindings version="2.0.1"
                     xmlns="http://schemas.oracle.com/bpel/faultpolicy">
    <composite faultPolicy="MyCompositeFaultPolicy"/>
    <service faultPolicy="RejectedMessages">
        <name>MyInboundFileService</name>
<name>MyOutboundDatabaseService</name>
    </service>
</faultPolicyBindings>


We need to add a fault policy to the fault-policies.xml file so our Java class is executed:


<?xml version="1.0"?>
<faultPolicies xmlns="http://schemas.oracle.com/bpel/faultpolicy">
  <faultPolicy version="2.0.1" id="Subsidie_FaultPolicy">
    <Conditions>
      <faultName xmlns:medns="http://schemas.oracle.com/mediator/faults"
 name="medns:mediatorFault">
        <condition>
          <action ref="MyFaultPolicyJavaAction"/>
        </condition>
      </faultName>
      <faultName xmlns:bpelx="http://schemas.oracle.com/bpel/extension"
 name="bpelx:bindingFault">
        <condition>
          <action ref="BPELJavaAction"/>
        </condition>
      </faultName>
      <faultName xmlns:bpelx="http://schemas.oracle.com/bpel/extension"
 name="bpelx:runtimeFault">
        <condition>
          <action ref="MyFaultPolicyJavaAction"/>
        </condition>
      </faultName>
    </Conditions>
    <Actions>
      <Action id="ora-terminate">
        <abort/>
      </Action>     
      <Action id="MyFaultPolicyJavaAction">
        <javaAction className="nl.vennster.MyFaultPolicyJavaAction"
                    defaultAction="ora-terminate">
          <returnValue value="ora-terminate" ref="ora-terminate"/>
        </javaAction>
      </Action>
    </Actions>
  </faultPolicy>
  <faultPolicy version="2.0.1" id="RejectedMessages">
    <Conditions>
      <faultName xmlns:rjm="http://schemas.oracle.com/sca/rejectedmessages">
        <condition>
          <action ref="MyFaultPolicyJavaAction"/>
        </condition>
      </faultName>
    </Conditions>
    <Actions>
      <Action id="ora-terminate">
        <abort/>
      </Action>
      <Action id="MyFaultPolicyJavaAction">
        <javaAction className="nl.vennster.MyFaultPolicyJavaAction"
                    defaultAction="ora-terminate">
          <returnValue value="ora-terminate" ref="ora-terminate"/>
        </javaAction>
      </Action>
    </Actions>
  </faultPolicy>  
</faultPolicies>

Read more on fault handling in part III and part IV of this blog series.