Tuesday 20 August 2013

ErrorHandling in SOA 11g

  In SOA 11g we can go for common error handling approach to capture fault at component and composite level.In this test cases I’ve one HelloWorld bpel process ,just prints hello.We cave another parent process,CallHelloWorldComposite that calls Hello process and here we’ll implement the fault policy.
To implement the Error Handler we need fault-policies.xml and fault-bindings.xml file.You can access those files locally from your project folder as well as from MDS or some other places.
image
Here in this case policies files are there in local project folder and in this case you need to just create those file and no entry in composite.xml is required.
But to access fault policy file from other places you need to add below properties in composite.xml,In this case I added policy at composite level.
image
You can give full path of either directory structure or MDS path in those values.
Now we need to design fault-policies.xml file to capture all kind of faults and actions need to be taken if error occurred.You can add your custom java code to perform any additional task like send email notification or enqueing data into custom error queue.Here is my fault-policies.xml file,
*******************************************************************************
<?xml version="1.0" encoding="UTF-8" ?>
<faultPolicies xmlns="http://schemas.oracle.com/bpel/faultpolicy">
  <faultPolicy version="2.0.1" id="commonErrorHandler"
               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>
      <faultName xmlns:bpelx="http://schemas.oracle.com/bpel/extension"
                 name="bpelx:remoteFault">
   
        <condition>
          <action ref="ora-retry"/>
        </condition>
      
     
      </faultName>
      <faultName xmlns:medns="http://schemas.oracle.com/mediator/faults"
                 name="medns:mediatorFault">
        <condition>
          <action ref="ora-cust"/>
        </condition>
      </faultName>
      <faultName xmlns:bpelx="http://schemas.oracle.com/bpel/extension"
                 name="bpelx:bindingFault">
        <condition>
          <action ref="ora-errorQ"/>
        </condition>
      </faultName>
      <faultName xmlns:bpelx="http://schemas.oracle.com/bpel/extension"
                 name="bpelx:runtimeFault">
        <condition>
          <action ref="ora-errorQ"/>
        </condition>
      </faultName>
    </Conditions>
    <Actions>
      <Action id="ora-terminate">
        <abort/>
      </Action>
      <Action id="ora-rethrow-fault">
        <rethrowFault/>
      </Action>
      <Action id="ora-replay-scope">
        <replayScope/>
      </Action>
      <Action id="ora-human-intervention">
        <humanIntervention/>
      </Action>
      <Action id="ora-retry">
        <retry>
          <retryCount>3</retryCount>
          <retryInterval>2</retryInterval>
          <exponentialBackoff/>
          <retryFailureAction ref="send-notification"/>
          <retrySuccessAction ref="ora-errorQ"/>
        </retry>
      </Action>
      <Action id="ora-cust">
        <javaAction className="com.shrik.TestJavaAction"
                    defaultAction="ora-terminate"
                    propertySet="send-notification-properties">
          <returnValue value="REPLAY" ref="ora-terminate"/>
          <returnValue value="RETRHOW" ref="ora-rethrow-fault"/>
          <returnValue value="ABORT" ref="ora-terminate"/>
          <returnValue value="RETRY" ref="ora-retry"/>
          <returnValue value="MANUAL" ref="ora-human-intervention"/>
        </javaAction>
      </Action>
      <Action id="ora-errorQ">
        <javaAction className="com.shrik.ErrorHospitalQueue"
                    defaultAction="ora-terminate"
                    propertySet="enqueue-properties">
          <returnValue value="REPLAY" ref="ora-terminate"/>
          <returnValue value="RETRHOW" ref="ora-rethrow-fault"/>
          <returnValue value="ABORT" ref="ora-terminate"/>
          <returnValue value="RETRY" ref="ora-retry"/>
          <returnValue value="MANUAL" ref="ora-human-intervention"/>
        </javaAction>
      </Action>
      <Action id="send-notification">
        <javaAction className="com.shrik.ErrorHospitalNotification"
                    defaultAction="ora-human-intervention"
                    propertySet="send-notification-properties">
          <returnValue value="MANUAL" ref="ora-human-intervention"/>
        </javaAction>
      </Action>
    </Actions>
    <!-- Property sets used by custom Java actions -->
    <Properties>
      <propertySet name="send-notification-properties">
        <property name="from"><Give from address></property>
        <property name="to"><Give To Address></property>
        <property name="subject">Test Mail</property>
        <property name="text">Environment: TEST</property>
        <property name="host"><smtp host server></property>
        <property name="port"><smtp port></property>
        <property name="username"><user name></property>
        <property name="password"><password></property>
      </propertySet>
      <propertySet name="enqueue-properties">
        <property name="aq.queueconnectionfactory">aqjms/XAQueueConnectionFactory</property>
          <property name="aq.queue">jms/errorQ</property>
      </propertySet>
    </Properties>
  </faultPolicy>
</faultPolicies>
****************************************************************************************
and here is my fault-bindings.xml file,
<?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="commonErrorHandler"/>
</faultPolicyBindings>

****************************************************************************************
If HelloWorld endpoint is unreachable then  bpelx:remoteFault will happen and as per fault policy it will execure ora-retry action,will try to retry the endpoint 3 times in 2,4,8 seconds intervals and upon subsequent failure it will invoke com.shrik.ErrorHospitalNotification class with send-notification-properties to notify faults to users.
To write your custom java code,create a new project like,
image
in this project we need to import some jars from <OracleMiddlewareHome>/Oracle_SOA1/soa/modules/oracle.soa.bpel_11.1.1 ,oracle.soa.fabric_11.1.1 directory.
For my case here is the code excerpt ,
For sending email notification from fault policy(ErrorHospitalNotification.java),
****************************************************************************************
package com.shrik;
import com.collaxa.cube.engine.fp.BPELFaultRecoveryContextImpl;

import java.util.ArrayList;
import java.util.Map;
import java.util.Properties;
import javax.mail.Authenticator;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import oracle.integration.platform.faultpolicy.IFaultRecoveryContext;
import oracle.integration.platform.faultpolicy.IFaultRecoveryJavaClass;

public class ErrorHospitalNotification implements IFaultRecoveryJavaClass {
    private String from;
    private String to;
    private String subject;
    private String text;
    private String host;
    private String port;
    private String username;
    private String password;
    private Properties props;
    public ErrorHospitalNotification() {
        super();
    }
    private void sendMail() {
        props = new Properties();
        props.put("mail.smtp.host", getHost());
        props.put("mail.smtp.port", getPort());
        props.put("mail.transport.protocol", "smtp");
        props.setProperty("mail.smtps.quitwait", "false");
        props.put("mail.smtp.auth", "true");
        Authenticator auth = new javax.mail.Authenticator() {
            protected PasswordAuthentication getPasswordAuthentication() {
                return new PasswordAuthentication(getUsername(),
                                                  getPassword());
            }
        };
        Session mailSession = Session.getDefaultInstance(props, auth);
        Message simpleMessage = new MimeMessage(mailSession);
        try {
            InternetAddress fromAddress = new InternetAddress(from);
            simpleMessage.setFrom(fromAddress);
            String toAddresses[] = to.split(";");
            if (toAddresses != null && toAddresses.length > 0) {
                InternetAddress toInternetAddresses[] =
                    new InternetAddress[toAddresses.length];
                for (int i = 0; i < toAddresses.length; i++)
                    toInternetAddresses[i] =
                            new InternetAddress(toAddresses[i]);
                simpleMessage.setRecipients(javax.mail.Message.RecipientType.TO,
                                            toInternetAddresses);
            }
            simpleMessage.setSubject(subject);
            simpleMessage.setText(text);
            Transport.send(simpleMessage);
        } catch (AddressException e) {
            System.out.println("Error formatting Internet Email Address: " +
                               e.getMessage().toString());
            e.printStackTrace();
        } catch (MessagingException e) {
            System.out.println("Error sending email: " +
                               e.getMessage().toString());
            e.printStackTrace();
        }
    }
    private String getParameterValue(ArrayList parameterList) {
        String value = null;
        if (parameterList != null && parameterList.size() > 0)
            value = (String)parameterList.get(0);
        return value;
    }
    public void handleRetrySuccess(IFaultRecoveryContext iFaultRecoveryContext) {
        BPELFaultRecoveryContextImpl bpelCtx =
            (BPELFaultRecoveryContextImpl)iFaultRecoveryContext;
        Map properties = iFaultRecoveryContext.getProperties();
        if (properties != null && properties.size() == 8) {
            setFrom(getParameterValue((ArrayList)properties.get("from")));
            setTo(getParameterValue((ArrayList)properties.get("to")));
            setSubject(getParameterValue((ArrayList)properties.get("subject")) +
                       " " + "Retry Success");
            setText("The exception that occurred when processing " +
                    bpelCtx.getTitle() + " was successfully retried.\n" +
                    "This message was automatically generated, please do not reply to it.");
            setHost(getParameterValue((ArrayList)properties.get("host")));
            setPort(getParameterValue((ArrayList)properties.get("port")));
            setUsername(getParameterValue((ArrayList)properties.get("username")));
            setPassword(getParameterValue((ArrayList)properties.get("password")));
            sendMail();
        }
    }

    public String handleFault(IFaultRecoveryContext iFaultRecoveryContext) {
        Map properties = iFaultRecoveryContext.getProperties();
        BPELFaultRecoveryContextImpl bpelCtx =
            (BPELFaultRecoveryContextImpl)iFaultRecoveryContext;
        if (properties != null && properties.size() == 8) {
            setFrom(getParameterValue((ArrayList)properties.get("from")));
            setTo(getParameterValue((ArrayList)properties.get("to")));
            setSubject(getParameterValue((ArrayList)properties.get("subject")) +
                       bpelCtx.getTitle());
            setText(getParameterValue((ArrayList)properties.get("text")) +
                    "BPEL Process Instance: " + bpelCtx.getInstanceId() +
                    " needs intervention to recover from a technical exception: " +
                    bpelCtx.getFault().getMessage() +
                    "Check the Activities tab in the BPEL Management Console in order to resolve the error as soon as possible. This message was automatically generated, please do not reply to it.");
            setHost(getParameterValue((ArrayList)properties.get("host")));
            setPort(getParameterValue((ArrayList)properties.get("port")));
            setUsername(getParameterValue((ArrayList)properties.get("username")));
            setPassword(getParameterValue((ArrayList)properties.get("password")));
            bpelCtx.addAuditTrailEntry("Sending Email...");
            sendMail();
        }
        return "MANUAL";
    }

    public void setFrom(String from) {
        this.from = from;
    }
    public String getFrom() {
        return from;
    }
    public void setTo(String to) {
        this.to = to;
    }
    public String getTo() {
        return to;
    }
    public void setSubject(String subject) {
        this.subject = subject;
    }
    public String getSubject() {
        return subject;
    }
    public void setText(String text) {
        this.text = text;
    }
    public String getText() {
        return text;
    }
    public void setHost(String host) {
        this.host = host;
    }
    public String getHost() {
        return host;
    }
    public void setPort(String port) {
        this.port = port;
    }
    public String getPort() {
        return port;
    }
    public void setProps(Properties props) {
        this.props = props;
    }
    public Properties getProps() {
        return props;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getUsername() {
        return username;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public String getPassword() {
        return password;
    }
}
****************************************************************************************

Now your custom java code should be referenced by weblogic server,For that,
  1. Create a jar file containing all your custom java code.
  2. Place the jar file in <OracleMiddlewareHome>/Oracle_SOA1/soa/modules/oracle.soa.ext_11.1.1 directory.
  3. Make sure ANT/bin is set in your classpath.
  4. and just run ant from that directory and eventually it will generate oracle.soa.ext.jar file.
  5. Start the weblogic server.
Now deploy the HelloWorld and caller process and turn helloworld endpoint off from em.
Now when you run CallHelloWorldComposite then definitely it will throw remote fault and parse your fault policy file.
After retrying it will send notification and go to manual intervention for recovery.
You can extend your Error Handling Framework as per your project need.

****************************************************************************************
<Action id="ora-errorQ">
        <javaAction className="com.shrik.ErrorHospitalQueue"
                    defaultAction="ora-terminate"
                    propertySet="enqueue-properties">
          <returnValue value="REPLAY" ref="ora-terminate"/>
          <returnValue value="RETRHOW" ref="ora-rethrow-fault"/>
          <returnValue value="ABORT" ref="ora-terminate"/>
          <returnValue value="RETRY" ref="ora-retry"/>
          <returnValue value="MANUAL" ref="ora-human-intervention"/>
        </javaAction>
      </Action>
<propertySet name="enqueue-properties">
        <property name="aq.queueconnectionfactory">jms/shrikCF</property>
          <property name="aq.queue">jms/shrikQueue</property>
      </propertySet>
So first of all you need to create a jms queue and connection factory in weblogic server , in my case it is shrikQueue and shrikCF and change the enqueue-properties value accordingly.
Here is the code excerpts that help you to enqueue data into shrikQueue,
package com.shrik;

import com.collaxa.cube.engine.fp.BPELFaultRecoveryContextImpl;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Map;
import java.util.UUID;
import javax.jms.Connection;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.QueueConnectionFactory;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import oracle.integration.platform.faulthandling.recovery.RejectedMsgRecoveryContext;
import oracle.integration.platform.faultpolicy.IFaultRecoveryContext;
import oracle.integration.platform.faultpolicy.IFaultRecoveryJavaClass;
public class ErrorHospitalQueue implements IFaultRecoveryJavaClass {
    private String queueCF;
    private String queueName;
    public ErrorHospitalQueue() {
        super();
    }

    public String handleFault(IFaultRecoveryContext iFaultRecoveryContext) {
        BPELFaultRecoveryContextImpl ctx =
            (BPELFaultRecoveryContextImpl)iFaultRecoveryContext;
        Map properties = iFaultRecoveryContext.getProperties();
        UUID uuid = UUID.randomUUID();
        ctx.addAuditTrailEntry("Enqueueing Data into shrikQueue...");
        ctx.addAuditTrailEntry(createEventPayload(ctx));
        ctx.addAuditTrailEntry((String)getParameterValue((ArrayList)properties.get("aq.queueconnectionfactory")));
        ctx.addAuditTrailEntry((String)getParameterValue((ArrayList)properties.get("aq.queue")));
        try {
            enqueueAqEvent(createEventPayload(ctx), uuid, properties, ctx);
        } catch (JMSException e) {
            e.printStackTrace();
        } catch (NamingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "ora-terminate";
    }
    private String createEventPayload(IFaultRecoveryContext context) {
        String eventPayload =
            "<AdminFault xmlns=\"http://www.shrik.com/\">\n" +
            " <ecid>UNKNOWN_ECID</ecid>\n" +
            "</AdminFault>";
        if (context instanceof RejectedMsgRecoveryContext) {
            RejectedMsgRecoveryContext rejectedMessageContext =
                (RejectedMsgRecoveryContext)context;
            String ecid = null;
            if (rejectedMessageContext.getRejectedMessage() != null &&
                rejectedMessageContext.getRejectedMessage().getEcid() !=
                null) {
                ecid = rejectedMessageContext.getRejectedMessage().getEcid();
            } else if (rejectedMessageContext.getFault() != null &&
                       rejectedMessageContext.getFault().getECID() != null) {
                ecid = rejectedMessageContext.getFault().getECID();
            }
            eventPayload = eventPayload.replace("UNKNOWN_ECID", ecid);
        } else if (context instanceof BPELFaultRecoveryContextImpl) {
            BPELFaultRecoveryContextImpl bpelFaultRecoveryContextImpl =
                (BPELFaultRecoveryContextImpl)context;
            eventPayload =
                    eventPayload.replace("UNKNOWN_ECID", bpelFaultRecoveryContextImpl.getECID());
        }
        return eventPayload;
    }

    public void enqueueAqEvent(String input, UUID uuid, Map props,
                               BPELFaultRecoveryContextImpl ctx) throws JMSException,
                                                                        NamingException,
                                                                        IOException {
        Session session = null;
        MessageProducer publisher = null;
        TextMessage message = null;
        Context context = new InitialContext();
        QueueConnectionFactory connectionFactory =
            (QueueConnectionFactory)context.lookup((String)getParameterValue((ArrayList)props.get("aq.queueconnectionfactory")));
        Connection connection =
            (Connection)connectionFactory.createConnection();
        Queue errQueue =
            (Queue)context.lookup((String)getParameterValue((ArrayList)props.get("aq.queue")));
        session =
                (Session)connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        publisher = session.createProducer(errQueue);
        message = session.createTextMessage(input);
        message.setJMSCorrelationID(uuid.toString());
        connection.start();
        publisher.send(message);
        connection.stop();
        connection.close();

    }
    private String getParameterValue(ArrayList parameterList) {
        String value = null;
        if (parameterList != null && parameterList.size() > 0)
            value = (String)parameterList.get(0);
        return value;
    }
    public void handleRetrySuccess(IFaultRecoveryContext iFaultRecoveryContext) {
        System.out.println("This is for retry success");
        handleFault(iFaultRecoveryContext);
    }
    public void setQueueCF(String queueCF) {
        this.queueCF = queueCF;
    }
    public String getQueueCF() {
        return queueCF;
    }
    public void setQueueName(String queueName) {
        this.queueName = queueName;
    }
    public String getQueueName() {
        return queueName;
    }
}
After updating the jar you will be able to see message with ecid got enqueued on error scenario, here is the sample message structure,
<AdminFault xmlns="http://www.shrik.com/">
<ecid>11d1def534ea1be0:-918321b:12f07773909:-8000-0000000000000c0d</ecid>
</AdminFault>
You can customize the message as per your need

***********************************************************************************

Error Handling in SOA 11g :Introducing Error Report: Part 3

 In previous blog I discussed how to enqueue ECID to your JMS queue from fault policy on any error.So instead of only enqueueing ECID we can also put Fault complete metadata like fault details, composite id etc.You need to modify your custom java code little bit, here is the modified code,
package com.shrik.world.fault;
import com.collaxa.cube.engine.fp.BPELFaultRecoveryContextImpl;
import java.util.Map;
import java.util.UUID;
import oracle.integration.platform.faultpolicy.IFaultRecoveryContext;
import oracle.integration.platform.faultpolicy.IFaultRecoveryJavaClass;
import javax.jms.Connection;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.QueueConnectionFactory;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import oracle.integration.platform.faulthandling.recovery.RejectedMsgRecoveryContext;
public class CustomFaultHandler implements IFaultRecoveryJavaClass {
  
    Map props;
    public CustomFaultHandler() {
        super();
    }
    public void handleRetrySuccess(IFaultRecoveryContext iFaultRecoveryContext) {
        System.out.println("Retry Success");
        handleFault(iFaultRecoveryContext);
    }
    public String handleFault(IFaultRecoveryContext iFaultRecoveryContext) {
        //Print Fault Meta Data to Console
        System.out.println("****************Fault Metadata********************************");
        System.out.println("Fault policy id: " +
                           iFaultRecoveryContext.getPolicyId());
        System.out.println("Fault type: " + iFaultRecoveryContext.getType());
        System.out.println("Partnerlink: " +
                           iFaultRecoveryContext.getReferenceName());
        System.out.println("Port type: " +
                           iFaultRecoveryContext.getPortType());
        System.out.println("**************************************************************");
        //print all properties defined in the fault-policy file
        System.out.println("Properties Set for the Fault");
        props = iFaultRecoveryContext.getProperties();
        for (Object key : props.keySet()) {
            System.out.println("Key : " + key.toString() + " Value : " +
                               props.get(key).toString());
        }
        //Print Fault Details to Console if it exists
        System.out.println("****************Fault Details********************************");
        BPELFaultRecoveryContextImpl bpelCtx =
            (BPELFaultRecoveryContextImpl)iFaultRecoveryContext;
        if (iFaultRecoveryContext instanceof BPELFaultRecoveryContextImpl) {
            System.out.println("Fault: " + bpelCtx.getFault());
            System.out.println("Activity: " + bpelCtx.getActivityName());
            System.out.println("Composite Instance: " +
                               bpelCtx.getCompositeInstanceId());
            System.out.println("Composite Name: " +
                               bpelCtx.getCompositeName());
            System.out.println("***********************************************************");
        }
        //enqueueing Error Details
        System.out.println("Enqueueing Data into ErrorQ.....");
        try {
            enqueueAqEvent(iFaultRecoveryContext);
        } catch (JMSException e) {
            e.printStackTrace();
        } catch (NamingException e) {
            e.printStackTrace();
        }
        return bpelCtx.getFault().getMessage().contains("env:Server") ? "Terminate":"Manual";
       
    }
    private void enqueueAqEvent(IFaultRecoveryContext iFaultRecoveryContext) throws NamingException,
                                                                                    JMSException {
      
        UUID uuid = UUID.randomUUID();
        Session session = null;
        MessageProducer publisher = null;
        TextMessage message = null;
        InitialContext context = new InitialContext();
        QueueConnectionFactory connectionFactory =(QueueConnectionFactory)context.lookup("error.qcf");
        Connection connection =connectionFactory.createConnection();
        Queue errQueue =(Queue)context.lookup("error.q");
        session =connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        publisher = session.createProducer(errQueue);
        message =session.createTextMessage(createEventPayload(iFaultRecoveryContext));
        message.setJMSCorrelationID(uuid.toString());
        connection.start();
        publisher.send(message);
        connection.stop();
        connection.close();
    }
    private String createEventPayload(IFaultRecoveryContext iFaultRecoveryContext) {
        String eventPayload =
            "<SOAFault xmlns=\"http://www.shrik.world.com/\">\n" +
            " <ecid>UNKNOWN_ECID</ecid>\n" +
            " <policyID>"+ iFaultRecoveryContext.getPolicyId() + "</policyID>\n" +
            " <type>"+ iFaultRecoveryContext.getType() + "</type>\n" +
            " <partnerLink>"+ iFaultRecoveryContext.getReferenceName() + "</partnerLink>\n" +
            " <port>"+ iFaultRecoveryContext.getPortType() + "</port>\n" +
            " <faultDetails>UNKNOWN_FAULT_DETAILS</faultDetails>\n" +
            " <activity>UNKNOWN_ACTIVITY</activity>\n" +
            " <compositeID>UNKNOWN_INSTANCE_ID</compositeID>\n" +
            " <compositeName>UNKNOWN_COMPOSITE_NAME</compositeName>\n" +
            " <compositeName>UNKNOWN_COMPONENT_NAME</compositeName>\n" +
            "</SOAFault>";
        if (iFaultRecoveryContext instanceof RejectedMsgRecoveryContext) {
            RejectedMsgRecoveryContext rejectedMessageContext =
                (RejectedMsgRecoveryContext)iFaultRecoveryContext;
            String ecid = null;
            if (rejectedMessageContext.getRejectedMessage() != null &&
                rejectedMessageContext.getRejectedMessage().getEcid() !=
                null) {
                ecid = rejectedMessageContext.getRejectedMessage().getEcid();
            } else if (rejectedMessageContext.getFault() != null &&
                       rejectedMessageContext.getFault().getECID() != null) {
                ecid = rejectedMessageContext.getFault().getECID();
            }
            eventPayload = eventPayload.replace("UNKNOWN_ECID", ecid);
        } else if (iFaultRecoveryContext instanceof
                   BPELFaultRecoveryContextImpl) {
            BPELFaultRecoveryContextImpl bpelFaultRecoveryContextImpl =
                (BPELFaultRecoveryContextImpl)iFaultRecoveryContext;
       
            eventPayload =eventPayload.replace("UNKNOWN_ECID", bpelFaultRecoveryContextImpl.getECID());
            eventPayload =eventPayload.replace("UNKNOWN_FAULT_DETAILS", bpelFaultRecoveryContextImpl.getFault().getMessage());
            eventPayload =eventPayload.replace("UNKNOWN_ACTIVITY", bpelFaultRecoveryContextImpl.getActivityName());
            eventPayload =eventPayload.replace("UNKNOWN_INSTANCE_ID", bpelFaultRecoveryContextImpl.getComponentInstanceId());
            eventPayload =eventPayload.replace("UNKNOWN_COMPOSITE_NAME", bpelFaultRecoveryContextImpl.getCompositeName());
            eventPayload =eventPayload.replace("UNKNOWN_COMPONENT_NAME", bpelFaultRecoveryContextImpl.getComponentName());
        }
        System.out.println(eventPayload);
        return eventPayload;
    }
}
So whenever any error occur at composite the below message will be put into the queue
image
<SOAFault xmlns="http://www.shrik.world.com/">
<ecid>11d1def534ea1be0:1ba57489:13197ef8107:-8000-0000000000000347</ecid>
<policyID>CompositeFaultPolicy</policyID>
<type>bpel</type>
<partnerLink>Service1</partnerLink>
<port>{
http://xmlns.oracle.com/ErrorHandlingApp/HelloWorld/sayHello}sayHello</port>
<faultDetails>faultName: {{
http://schemas.oracle.com/bpel/extension}remoteFault}
messageType: {{http://schemas.oracle.com/bpel/extension}RuntimeFaultMessage}
parts: {{
summary=<summary>Message Router for shrik/HelloWorld!1.0*soa_a94e595b-965e-48d9-8b15-6735c29a2805 is not able to process messages. The composite state is set to "off".  The composite can be turned "on" by using the administrative consoles.</summary>
,detail=<detail>&lt;exception>Message Router for shrik/HelloWorld!1.0*soa_a94e595b-965e-48d9-8b15-6735c29a2805 is not able to process messages. The composite state is set to "off".  The composite can be turned "on" by using the administrative consoles.&lt;/exception>
</detail>
,code=<code>env:Server</code>}
</faultDetails>
<activity>Invoke1</activity>
<compositeID>bpel:260001</compositeID>
<compositeName>CallHelloWorld</compositeName>
<compositeName>BPELProcess1</compositeName>
</SOAFault>

Now we can have a composite say SOAErrorNotificationProcess to dequeue the data from this error queue and send notification to concerned group along with auditing the same.Here is the design of that composite,
image
Its pretty simple and for auditing purpose I created below table in soainfra.database to store the error details,here is the DDL and sample data,
CREATE TABLE "DEV_SOAINFRA"."XX_COMPOSITE_ERRORS"
  (
    "ECID"           VARCHAR2(1000 BYTE) NOT NULL ENABLE,
    "POLICY_ID"      VARCHAR2(100 BYTE),
    "TYPE"           VARCHAR2(20 BYTE),
    "PARTNERLINK"    VARCHAR2(50 BYTE),
    "PORT"           VARCHAR2(500 BYTE),
    "FAULT_SUMMARY"  VARCHAR2(1000 BYTE),
    "FAULT_DETAILS"  VARCHAR2(1000 BYTE),
    "FAULT_CODE"     VARCHAR2(100 BYTE),
    "ACTIVITY"       VARCHAR2(20 BYTE),
    "COMPOSITE_ID"   VARCHAR2(100 BYTE),
    "COMPOSITE_NAME" VARCHAR2(1000 BYTE),
    "ERROR_TIME" DATE,
    CONSTRAINT "XX_COMPOSITE_ERRORS_PK" PRIMARY KEY ("ECID") USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255 COMPUTE STATISTICS STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645 PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT) TABLESPACE "DEV_SOAINFRA" ENABLE
  )
  SEGMENT CREATION IMMEDIATE PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 NOCOMPRESS LOGGING STORAGE
  (
    INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645 PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT
  )
  TABLESPACE "DEV_SOAINFRA" ;

Make the routing parallel in mediator so that u can incorporate your custom fault policy there,
image
I’m not going into more details of this composite.Now just make one process faulted and our custom framework will do the following things sequentially,
1.It parse the custom policy file.2.It retrieves the fault metadata and details from em 3.It enqueue all the details to error queue. 4.SOAErrorNotificationProcess will keep on polling that queue and as soon as a data comes into error queue it retrieves that.5.It audit all the error details into a table as well as send email to a group with all fault details.
Now what's next? you can see all the faulted instances for manual recovery.Now can’t we have a report by which we can see all the error details , all the recoverable instance and retry or abort from there itself instead of going to em.Even that report would be useful for business users to get a bird’s eye view.
I used ADF here for creating the report.To get started create a view(using entity) based on the error table that you created in soainfra like below,
image
Register you vo to am to make it accessible to your report as a datasource.Create a page and drag drop your data source as a ADF Query panel. Here is the UI of mine,
image
In the menu I just incorporated print page and export as a excel functionality,
There is a button Instance Statistics in toolbar which bound to adf popup. Data source for popup is different and sql query based,here is the query
SELECT (CASE WHEN STATE=1 THEN 'OPEN AND RUNNING'
WHEN STATE=2 THEN 'OPEN AND SUSPENDED'
WHEN STATE=3 THEN 'OPEN AND FAULTED'
WHEN STATE=4 THEN 'CLOSED AND PENDING'
WHEN STATE=5 THEN 'CLOSED AND COMPLETED'
WHEN STATE=6 THEN 'CLOSED AND FAUTED'
WHEN STATE=7 THEN 'CLOSED AND CANCELLED'
WHEN STATE=8 THEN 'CLOSED AND ABORTED'
WHEN STATE=9 THEN 'CLOSED AND STALE'
WHEN STATE=10 THEN 'NON-RECOVERABLE'
ELSE STATE || ''
END) AS STATE, COUNT(*) AS NUM_OF_CUBE_INST FROM CUBE_INSTANCE GROUP BY STATE

image
Register your view to am.Now create a popup and bind the popup id to your command button popup behaviour.
image
Now create a pie chart based on SOAInstancesV as below in your popup,
image
Now just run the page to check the added functionality,Run the query and click on command button on toolbar.
image
image
Now we need to build left hand navigation.There are two links with popup behaviour, RetryInstances and SOAErrorByDay.
In RetryInstances it will popup all the recoverable instances from soa mbean that went to manual intervention in em and there would be option for retry or terminate instances without login into em.     The underlying datasource for retryinstances is based on Java bean.First create below classes in your model,
FaultDetails.java it basically contains all the getter and setter method that will be used later as a table.
package com.shrik.world.model.bean;
public class FaultDetails {
    private String compositeDN;
    private String compositeInstanceID;
    private String componentName;
    private String componentInstanceID;
    private String activityName;
    private String faultID;
    private String faultName;
    private boolean recoverableFlag;
    private String faultMessage;
    public FaultDetails() {
        super();
    }
    public FaultDetails(String compositeDN, String compositeInstanceID, String componentName, String componentInstanceID,
                 String activityName, String faultID, String faultName,boolean recoverableFlag,String faultMessage) {
        this.compositeDN=compositeDN;
        this.compositeInstanceID=compositeInstanceID;
        this.componentName=componentName;
        this.componentInstanceID=componentInstanceID;
        this.activityName=activityName;
        this.faultID=faultID;
        this.faultName=faultName;
        this.recoverableFlag=recoverableFlag;
        this.faultMessage=faultMessage;
    }

    public void setCompositeDN(String compositeDN) {
        this.compositeDN = compositeDN;
    }
    public String getCompositeDN() {
        return compositeDN;
    }
    public void setCompositeInstanceID(String compositeInstanceID) {
        this.compositeInstanceID = compositeInstanceID;
    }
    public String getCompositeInstanceID() {
        return compositeInstanceID;
    }
    public void setComponentName(String componentName) {
        this.componentName = componentName;
    }
    public String getComponentName() {
        return componentName;
    }
    public void setComponentInstanceID(String componentInstanceID) {
        this.componentInstanceID = componentInstanceID;
    }
    public String getComponentInstanceID() {
        return componentInstanceID;
    }
    public void setActivityName(String activityName) {
        this.activityName = activityName;
    }
    public String getActivityName() {
        return activityName;
    }
    public void setFaultID(String faultID) {
        this.faultID = faultID;
    }
    public String getFaultID() {
        return faultID;
    }
    public void setFaultName(String faultName) {
        this.faultName = faultName;
    }
    public String getFaultName() {
        return faultName;
    }
  
    public void setFaultMessage(String faultMessage) {
        this.faultMessage = faultMessage;
    }
    public String getFaultMessage() {
        return faultMessage;
    }
    public void setRecoverableFlag(boolean recoverableFlag) {
        this.recoverableFlag = recoverableFlag;
    }
    public boolean isRecoverableFlag() {
        return recoverableFlag;
    }
}
FaultReport.java that populates FaultDetails with all required information,
package com.shrik.world.model.bean;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import javax.naming.Context;
import oracle.soa.management.facade.Fault;
import oracle.soa.management.facade.FaultRecoveryActionTypeConstants;
import oracle.soa.management.facade.Locator;
import oracle.soa.management.facade.LocatorFactory;
import oracle.soa.management.facade.bpel.BPELServiceEngine;
import oracle.soa.management.util.FaultFilter;
public class FaultReport {
    private Locator locator = null;
    private BPELServiceEngine mBPELServiceEngine;
    private List<Fault> faultList;
    private List<FaultDetails> myfaults = new ArrayList();
    public List<FaultDetails> findAllRecoverableFaults() {
        return myfaults;
    }
    public FaultReport() {
        locator = this.getLocator();
        try {
            mBPELServiceEngine = (BPELServiceEngine)locator.getServiceEngine(Locator.SE_BPEL);
            FaultFilter filter = new FaultFilter();
            filter.setFaultName("{http://schemas.oracle.com/bpel/extension}remoteFault");
            filter.setRecoverable(true);
            //Get faults using defined filter
            faultList = mBPELServiceEngine.getFaults(filter);
            for (Fault fault : faultList) {
                myfaults.add(new FaultDetails(
                                                fault.getCompositeDN().getStringDN(),
                                                fault.getCompositeInstanceId(),
                                                fault.getComponentName(),
                                                fault.getComponentInstanceId(),
                                                fault.getLabel(),
                                                fault.getId(),
                                                fault.getName().toString(),
                                                fault.isRecoverable(),
                                                fault.getMessage().toString()));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public Locator getLocator() {
        try {
            return LocatorFactory.createLocator(getJndiProps());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    public Hashtable getJndiProps() {
        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, "welcome1");
        jndiProps.put("dedicated.connection", "true");
        return jndiProps;
    }
   
    public void retryRecoverableInstances(){
        try {
            mBPELServiceEngine.recoverFaults(faultList.toArray(new Fault[faultList.size()]), FaultRecoveryActionTypeConstants.ACTION_RETRY);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
   
    public void terminateRecoverableInstances(){
        try {
            mBPELServiceEngine.recoverFaults(faultList.toArray(new Fault[faultList.size()]), FaultRecoveryActionTypeConstants.ACTION_ABORT);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
Here you can retrieve the server properties from external file as well instead of hardcoding.
Now right click on FaultReport and generate the data source.After sometime you should be able to see the datasource,
image
Now in the popup drag drop that datasource in a panel collection as below,
image
In the menu I added two options,Retry and Abort and here is the action listener binding
image
image
Methods are written as below,
package com.shrik.world.bean;
import  com.shrik.world.model.bean.FaultReport;
import javax.faces.event.ActionEvent;
public class Reconcile {
    public Reconcile() {
    }
    public void RetrySOARecoverable(ActionEvent actionEvent) {
       new FaultReport().retryRecoverableInstances();
      
    }
    public void TerminateSOAInstances(ActionEvent actionEvent) {
        new FaultReport().terminateRecoverableInstances();
    }
   
}
Now to get error details per day just create a SQL query based vo and register to am as below,
image
Now create a bar chart in popup window as below,
image
Now run the page and click on RetryInstances in left navigator,check the Retry and Abort functionality and verify the same from em console.
image
Now click on SOAErrorByDay and a bar chart would popup as below,
image
Now you can wrap up the whole code into a EAR and deploy that to em.You can customize your GUI as per your need.

No comments:

Post a Comment

Note: only a member of this blog may post a comment.