Powered By Blogger

Wednesday, March 28, 2012

Implementing Java Fault Handling for Oracle Composite

Oracle SOA suite comes with  fault handling framework which is a collection of sub modules to automate handling of faults .
For introduction of fault handling in composites, please visit Oracle's FMW Developer's Guide for Oracle SOA Suite 

Two important files in fault framework are
  • fault-policies.xml
  • fault-bindings.xml 
fault-policies.xml: Talks about what kind of faults it can handle and how. Fault policy file defines fault conditions and their corresponding fault recovery actions.
fault-bindings.xml: Talks about binding of fault-policies with  composite and its components or scope of fault-policies.

As part of fault-policies.xml, we can mention various types of recovery actions like retry,re-throw,human-intervention,replay,terminate and many more.Java action is one of them.
Fault-poloicies.xml
<?xml version="1.0" encoding="UTF-8" ?>
<faultPolicies xmlns="http://schemas.oracle.com/bpel/faultpolicy">
  <faultPolicy version="2.0.1"
           id="ProcessingFaults"
               xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
               xmlns:xs="http://www.w3.org/2001/XMLSchema"
               xmlns="http://schemas.oracle.com/bpel/faultpolicy"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <Conditions>
      <!-- remote fault processing starts here: -->
    <faultName xmlns:bpelx="http://schemas.oracle.com/bpel/extension"
               name="bpelx:remoteFault">
         <condition>
                <action ref="ora-retry"/>
        </condition>
    </faultName>
   
    <!-- binding fault processing starts here: -->
     <faultName xmlns:bpelx="http://schemas.oracle.com/bpel/extension"
                name="bpelx:bindingFault">       
               <condition>
                       <action ref="ora-retry"/>
               </condition>
    </faultName>

    </Conditions> 

<!-- Above two conditions indicates perform retry actions for binding and remote faults -->
    <Actions>  
    <!-- RetryInterval is in seconds. This action handles our retry requirement and in case retry also  failed then that chains to custom java action -->
      <Action id="ora-retry">
        <retry>
          <retryCount>3</retryCount>
          <retryInterval>5</retryInterval>
          <exponentialBackoff/>
          <retryFailureAction ref="ora-custom-java"/> 

<!--in case retry also failed then that chains control to java action-->
        </retry>
      </Action>      
     
 <Action id="ora-custom-java">
 <!-- this is user provided custom java class. This action handles notification requirement -->
      <javaAction className="
com.fault.handler.JavaFaultHandler"
             defaultAction="ora-human-intervention" propertySet="notification-properties">
            <returnValue value="RETRHOW"  ref="ora-rethrow-fault"/>

<!-- If java class returns RETRHOW then action "ora-rethrow-fault" is invoked -->
            <returnValue value="MANUAL"   ref="ora-human-intervention"/>

<!-- If java class returns MANUAL then action "ora-human-intervention" is invoked -->
       </javaAction>
     </Action>

        
<!-- This action re throws fault back to BPEL Catch/CatchAll-->
        <Action id="ora-rethrow-fault">
           
<rethrowFault/>
       
</Action >
  <!-- Human Intervention. This action handles human intervention part-->
        <Action id="ora-human-intervention">
           <humanIntervention/>
        </Action>

     </Actions>
   &lt!-- Below properties are read from java class--> 
     <Properties>    
          <propertySet name="notification-properties">          
            <property name="email_to">team1@gmail.com</property>
            <property name="email_cc">team2@gmail.com</property>   
            <property name="commonErrorHandlingProcess">CommonErrorHandlingBPEL</property>               
            <property name="domain">default</property>
     </propertySet>
       
  </Properties>
</faultPolicy> 
</faultPolicies>



fault-bindings.xml
<?xml version="1.0" encoding="UTF-8" ?>
<faultPolicyBindings version="2.0.1"
                     xmlns="http://schemas.oracle.com/bpel/faultpolicy"
                     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <composite faultPolicy="ProcessingFaults"/> 
</faultPolicyBindings>


This bindings file binds fault recovery actions to composite level.i.e all components with in composite.xml
Now let's see how we can write a custom java class plugged into fault handling mechanism.
Co-locate both xml files with composite.xml. 

Please create a java project in Jdev and add following libraries to project class-path.
  1. BPEL Runtime
  2. Oracle.soa.fabric.jar



Custom Java action class
package com.fault.handler;

import com.fault.handler.Exception;
import com.collaxa.cube.engine.fp.BPELFaultRecoveryContextImpl;

import oracle.integration.platform.faultpolicy.IFaultRecoveryContext;
import oracle.integration.platform.faultpolicy.IFaultRecoveryJavaClass;
oracle.integration.platform.faulthandling.recovery.RejectedMsgRecoveryContext;
import java.util.Map;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import java.io.StringWriter;

import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;


public class JavaFaultHandler implements IFaultRecoveryJavaClass {

    public void handleRetrySuccess(IFaultRecoveryContext iFaultRecoveryContext) {
        System.out.println("Retry Success Happend...");
       // handleFault(iFaultRecoveryContext);
    }

    private String nodeToString(Node node) {
         StringWriter sw = new StringWriter();
         try {
             Transformer t = TransformerFactory.newInstance().newTransformer();
             t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
             t.transform(new DOMSource(node), new StreamResult(sw));
             } catch (TransformerException te) {
             System.out.println("nodeToString Transformer Exception");
    }
    return sw.toString();
    }
   //Below method will be invoked by fault handling mechanism when there is a fault. 

public String handleFault(IFaultRecoveryContext iFaultRecoveryContext) {
       
      
        try{
           Exception excObj = new Exception();           
 
         if(iFaultRecoveryContext instanceof BPELFaultRecoveryContextImpl){
      
          // Get BPEL specific context here
             BPELFaultRecoveryContextImpl bpelCtx =
                                         (BPELFaultRecoveryContextImpl)iFaultRecoveryContext;   
             // bit of log statements for debug purpose   
                System.out.println("*** BPEL Context + getComponentInstanceId ***: " +
                bpelCtx.getComponentInstanceId());                    
                System.out.println("*** BPEL Context + getFault ***: " +
                bpelCtx.getFault());              
                System.out.println("***  BPEL Context+ getInstanceId ***: " +
                bpelCtx.getInstanceId());              
                System.out.println("***  BPEL Context + getCompositeTitle ***: " + 
                bpelCtx.getTitle());                             
                System.out.println("***  BPEL Context + FaultedProcess Revision ***:"+
                bpelCtx.getProcessDN().getRevision());              
                                                  
                try{
// This code assumes all BPELs will have a variable called-
faultHandlerVariable which is defined on canonical xsd containing generic error data        
                Element root =(Element) bpelCtx.getVariableData  

  ("faultHandlerVariable", "payload", "/");                  
                Node exception =   root.getFirstChild();
                String errorDataStr   =   nodeToString(
exception);
                
                //Setting business payload  data 
                 excObj.setBusinessPayload(errorDataStr);  
                } catch (Exception e){
                    //Ignore this exception; because setting this business payload data is optional for my requirement
                    ;
                }
                  
                //Setting BPEL specific error data
                excObj.setErrorCode((bpelCtx.getFault().getFaultName().toString().contains("remoteFault"))?"remoteFault":"bindingFault");
                excObj.setErrorMessage(bpelCtx.getFault().getMessage().toString());
                excObj.setErrorInstanceId(bpelCtx.getInstanceId());
                excObj.setErrorProcessId( bpelCtx.getCompositeName());             
                excObj.setErrorProcessVersion(bpelCtx.getProcessDN().getRevision());                                              
                           
                } else if (iFaultRecoveryContext instanceof RejectedMsgRecoveryContext) {
              
                //Get Rejected messages context here
                System.out.println("*** iFaultRecoveryContext is an instanceof RejectedMsgRecoveryContext ***");
                System.out.println("==========================================================");
            
            } else{
             //Get Non-BPEL context here, if error is raised from other than BPEL component
               System.out.println("*** iFaultRecoveryContext is neither instanceof BPELFaultRecoveryContextImpl nor RejectedMsgRecoveryContext***");               

      
              System.out.println("====================================");
            }                      
            
             //Setting Email error data
             excObj.steEmailTo(props.get("email_to").toString().substring(1, props.get("email_to").toString().length()-1));
             excObj.setEmailCC(props.get("email_cc").toString().substring(1, props.get("email_cc").toString().length()-1));          
             excObj.setDomainId(props.get("domain").toString().substring(1, props.get("domain").toString().length()-1));           
          
                   
 //Setting CommonStatusLogger Process invocation context data  
             excObj.setCommonEHProcessName(props.get("commonEH_process").toString().substring(1, props.get("commonEH_process").toString().length()-1));  
                       

//CommonStatusLoggerDirectBinding direct binding Invocation
Please refer to SOA direct binding API This call invokes a composite which sends email.I am passing all error related data and email notification details as an object.
             CommonStatusLoggerDirectBinding ceh =new
CommonStatusLoggerDirectBinding ();
             String status = ceh.invokeCommonErrorStatusDirect(excObj);
             if (status == "S") 
                return "MANUAL";
             else               
                return "RETRHOW";      
            }
            catch(Exception e){
            System.out.println("***JavaFaultHandler Exception******\n"+ e.getMessage());
              e.printStackTrace();
              return "RETRHOW";
    
        }
    }
}

Bean Utility class 

package com.fault.handler;

import java.util.Date;
//This BEAN class represents all the common attributes any fault handler requires.
public class
Exception {
    protected String errorCode;
    protected String errorMessage;
    protected Date errorTime= new Date();
    protected String domainId="default";
    protected String errorProcessId;
    protected String errorProcessVer;
    protected String errorProcessURL;
    protected Long  errorInstanceId;
    protected String fileName;
    protected String payload;
    protected String fileDate;
    protected String sourceSystem;
    protected String reference;
    protected String emailTo="team1@gmail.com";
    protected String emailCC;
    protected String errorPartnerLink;
    protected String subject="Runtime Error: ";
    protected String providerURL;
    protected String initialContext="weblogic.jndi.WLInitialContextFactory";
    protected String securityCredential;
    protected String securityPrinciple;
    protected String commonEHProcessName="CommonErrorHandlerProcess";
    protected String commonEHProcessVer="1.0";
    protected String commonEHURL;
    protected String errorPayload;
    protected String businessPayload;
    public Exception() {
        super();       
       
    }
   //Setter methods  
    public void setErrorCode  (String errorCode ){
        this.errorCode = errorCode;
    }
    public void setErrorMessage  (String errorMessage){
        this.errorMessage = errorMessage;
    }
    public void setErrorTime  (Date errorTime){
        this.errorTime = errorTime;
    }
    public void setDomainId  (String domainId){
        this.domainId = domainId;
    }
    public void setErrorProcessId  (String errorProcessId){
        this.errorProcessId = errorProcessId;
    }
    public void setErrorProcessVersion  (String errorProcessVersion){
        this.errorProcessVer = errorProcessVersion;
    }
    public void setErrorProcessURL  (String errorProcessURL){
        this.errorProcessURL = errorProcessURL;
    }
    public void setErrorInstanceId  (Long errorInstanceId){
        this.errorInstanceId = errorInstanceId;
    }
    public void setFileName  (String fileName){
        this.fileName = fileName;
    }
    public void setPayload  (String payload){
        this.payload = payload;
    }
    public void setFileDate  (String fileDate){
        this.fileDate = fileDate;
    }
    public void setSourceSystem  (String sourceSystem){
        this.sourceSystem = sourceSystem;
    }
    public void setReference  (String reference){
        this.reference = reference;
    }
    public void steEmailTo  (String emailTo){
        this.emailTo = emailTo;
    }
    public void setEmailCC  (String emailCC){
        this.emailCC = emailCC;
    }
    public void setErrorPartnerLink  (String errorPartnerLink){
        this.errorPartnerLink = errorPartnerLink;
    }
    public void setSubject  (String subject){
        this.subject = subject;
    }
    public void setProviderURL  (String providerURL){
      this.providerURL = providerURL;
    } 
    public void setInitalContext  (String initialContext){
       this.initialContext = initialContext;
    }
    public void setSecurityCredential (String securityCredential){
       this.securityCredential = securityCredential;
    }
    public void setSecurityPrinciple (String securityPrinciple){
       this.securityPrinciple = securityPrinciple;
    }
    public void setCommonEHProcessName (String commonEHProcessName){
       this.commonEHProcessName = commonEHProcessName;
    }
    public void setCommonEHProcessVer (String commonEHProcessVer){
       this.commonEHProcessVer = commonEHProcessVer;
    }
    public void setCommonEHURL (String commonEHURL){
       this.commonEHURL = commonEHURL;
    }
   
    public void setBusinessPayload (String businessPayload){
       this.businessPayload = businessPayload;
    }
   
    //Getter methods  
     public String getErrorCode  ( ){
        return  this.errorCode;
     }
     public String getErrorMessage  (){
         return this.errorMessage;
     }
     public Date getErrorTime  (){
         return this.errorTime ;
     }
     public String getDomainId  (){
        return this.domainId ;
     }
     public String getErrorProcessId  (){
        return this.errorProcessId ;
     }
     public String getErrorProcessVersion  (){
        return  this.errorProcessVer ;
     }
     public String getErrorProcessURL  (){
       return  this.errorProcessURL ;
     }
     public Long getErrorInstanceId  (){
       return  this.errorInstanceId ;
     }
     public String getFileName  (){
        return this.fileName ;
     }
     public String getPayload  (){
       return  this.payload ;
     }
     public String getFileDate  (){
       return  this.fileDate ;
     }
     public String getSourceSystem  (){
        return this.sourceSystem ;
     }
     public String getReference  (){
        return this.reference ;
     }
     public String getEmailTo  (){
         return this.emailTo ;
     }
     public String getEmailCC  (){
      return this.emailCC ;
     }
     public String getErrorPartnerLink  (){
      return this.errorPartnerLink ;
     }
    public String getSubject  (){
      return this.subject;
    }
    public String getProviderURL  (){
      return this.providerURL;
    } 
    public String getInitialContext  (){
      return this.initialContext;
    }
    public String getSecurityCredential (){
      return this.securityCredential;
    }
    public String getSecurityPrinciple (){
      return this.securityPrinciple;
    }
    public String getCommonEHProcessName (){
      return this.commonEHProcessName ;
    }
    public String getCommonEHProcessVer (){
      return this.commonEHProcessVer;
    }
    public String getCommonEHURL (){
       return this.commonEHURL;
    }
   
    public String getBusinessPayload (){
       return this.businessPayload;
    }
    public String toString(){      
   
        errorPayload =
        "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" +
        "
<BPELException xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
        "xsi:schemaLocation=\"http://www.example.net/BPELException CommonErrorHandlerProcessInput.xsd\"\n" +
        "                  xmlns=\"http://www.example.net/BPELException\">\n" +
        " 
<ErrorCode>"+getErrorCode()+"</ErrorCode>\n" +
        " 
<ErrorMessage>"+getErrorMessage()+"</ErrorMessage>\n" +
        " 
<ErrorTime>"+getErrorTime()+"</ErrorTime>\n" +
        " 
<DomainId>"+getDomainId()+"</DomainId>\n" +
        " 
<ProcessId>"+getErrorProcessId()+"</ProcessId>\n" +
        " 
<ProcessVersion>"+getErrorProcessVersion()+"</ProcessVersion>\n" +
        " 
<ProcessURL>"+getErrorProcessURL()+"</ProcessURL>\n" +
        " 
<InstanceId>"+getErrorInstanceId()+"</InstanceId>\n" +
        " 
<FileName>"+getFileName()+"</FileName>\n" +
        " 
<payload>"+getBusinessPayload()+"</payload>\n" +
        " 
<FileDate>"+getFileDate()+"</FileDate>\n" +
        " 
<SourceSystem>"+getSourceSystem()+"</SourceSystem>\n" +
        " 
<Reference>"+getReference()+"</Reference>+
        " 
<EmailTo>"+getEmailTo()+"</EmailTo>\n" +
        " 
<EmailCC>"+getEmailCC()+"</EmailCC>\n" +
        "
</BPELException>\n" ;
        return this.errorPayload;
    }
     
}  


Configuring Java class
  • Pack above classes into jar 
  • stop  WLS server
  • Place them in  $ORACLE_HOME/soa/modules/oracle.soa.ext_11.1.1
  • run ant from same location
  • Restart Oracle WebLogic Server
Now code is ready for testing.
In a nutshell what we are trying to implement is, whenever there is either remote or binding fault in HellowWorld1 composite, then email notification should be sent out to configured users and human intervention should be waiting for someone to log in and correct payload and resubmit.    

How to test
Create HelloWorld1 and HelloWorld2 composites.HelloWorld1 invokes HelloWorld2.
Place fault-policies.xml and fault-bindings.xml into HellowWorld1 project and deploy both.Now retire HellowWorld2.Initiate HellowWorld1 

3 comments:

  1. Hi Praveen,

    My requirement is to log if any error occurs and then try for auto retry.

    So I handled the remoteFault by Custom Java Handler and then chained the return value with retry action. but It instead of trying for retry it just rethrow the fault and going to CatchAll defined in my bpel process.

    Any pointers on this behavior ?

    Regards,
    Kiran

    ReplyDelete
    Replies
    1. I think your java fault program is not returning gracefully, but it is throwing some errors in code and that inern causes to rethrow error back to BPEL.

      Delete