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.

10 comments:

  1. Thanks for the article series on faults! Very useful! Just a question, is it possible to reference the javaAction classname from the MDS repository?

    ReplyDelete
  2. Hi Daniela,

    Thanks.

    Do you mean that the actual classname to use ("nl.vennster.MyFaultPolicyJavaAction" in my example) is not hardcoded in the fault policy, but dynamically determined at runtime? Or do you mean something else?

    In the first case, this is not possible. What is your use case for setting the java action dynamically? You could eg also have several Java action handlers, and use the conditions in the fault policy to trigger the correct handler.

    Cheers, Ronald

    ReplyDelete
  3. Thank you for answering so quickly! Sorry, I didn't explain myself correctly. I'm still new at this stuff. What I meant was, I saw in the Oracle documentation that you can reference a fault policy that is in the MDS so that way multiple composites can share the same policies (mentioned here http://docs.oracle.com/cd/E23943_01/dev.1111/e10224/bp_faults.htm on Example 12-3). I was wondering if it was possible for the policies to also re-use the same Java Action class and reference it from the MDS (so something like <javaAction className="oramds:nl.vennster.MyFaultPolicyJavaAction" maybe), or if I was going to have to create the same java action class in each composite.

    Thanks again for your help, Ronald!

    ReplyDelete
  4. Hi Daniela,

    You only need to develop and deploy the Java Action once, and then you can reuse it from multiple Fault Policies. However, deployment of the Java Actions is not done through MDS, but by creating a JAR file and placing it on the runtime. I've described these steps here: http://blog.vennster.nl/2010/09/fault-handling-in-oracle-soa-suite-11g.html

    Good luck!

    Ronald

    ReplyDelete
  5. Cool business, it works now! Thanks!! :)
    Cheers!
    -Daniela

    ReplyDelete
  6. Great, good to hear :) Let me know if you have other questions.

    Cheers, Ronald

    ReplyDelete
  7. Can DVM's be used for fault handling?

    ReplyDelete
  8. Hi Priya,

    Do you mean from Catch and Catch All activities, or from custom Java actions?

    You can use DVMs in Catch and Catch All scopes/sequences.

    Cheers, Ronald

    ReplyDelete
  9. How to check if fault policy is applied or not ? can we capture any logs for this. I have implemented above steps but not sure if the policy is applied.

    ReplyDelete
  10. I had implemented above steps but not sure my fault policies are in effect. How can I make sure if fault policies are applied. FYI ,My condition is on "remote fault" it should retry thrice. But in the instnace flow trace I could c only one invoke activity.

    ReplyDelete