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.
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.
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,
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,
- Create a jar file containing all your custom java code.
- Place the jar file in <OracleMiddlewareHome>/Oracle_SOA1/soa/modules/oracle.soa.ext_11.1.1 directory.
- Make sure ANT/bin is set in your classpath.
- and just run ant from that directory and eventually it will generate oracle.soa.ext.jar file.
- 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
<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><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.</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,
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,
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,
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,
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
Register your view to am.Now create a popup and bind the popup id to your command button popup behaviour.
Now create a pie chart based on SOAInstancesV as below in your popup,
Now just run the page to check the added functionality,Run the query and click on command button on toolbar.
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,
Now in the popup drag drop that datasource in a panel collection as below,
In the menu I added two options,Retry and Abort and here is the action listener binding
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,
Now create a bar chart in popup window as below,
Now
run the page and click on RetryInstances in left navigator,check the
Retry and Abort functionality and verify the same from em console.
Now click on SOAErrorByDay and a bar chart would popup as below,
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.