BPEL的规范中提供了如何捕捉和处理异常,详细说明请参考《BPEL 如何处理异常?》。
但SOA应用中不光是BPEL组件,还有其它组件,比如Mediator。而Mediator并没有提供有关异常的规范,那么该如何处理Mediator中的异常呢?
为此,Oracle SOA Suite 提供了一个基于策略的异常处理机制,这些策略可以绑定到整个SOA 应用或者应用中的一个组件,比如Mediator。
在这个实验中,我们将首先处理基于策略的异常,然后再处理BPEL的异常。
1. 基于策略的异常
为了模拟remote fault,在EM中,我们把getStatusByCC服务Shutdown,输入大订单测试。
(1)remote fault异常
(2)增加策略文件:fault-policies.xml。与POProcessing中的composite.xml文件在同一目录。
<!-- Step #1: Add your fault handler for remote fault here: -->
<faultName xmlns:bpelx="http://schemas.oracle.com/bpel/extension"
name="bpelx:remoteFault">
<condition>
<action ref="ora-human-intervention"/>
</condition>
</faultName>
(3)增加策略文件:fault-bindings.xml。与POProcessing中的composite.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="POProcessingFaults"/>
</faultPolicyBindings>
(4)重新发布POProcessing,重新测试大订单。发现该异常是可以人工恢复重试的。
(5)重启getStatusByCC服务,点击Recover重试。
2. 处理BPEL异常
BPEL的规范中提供了如何捕捉和处理异常,因此发生在BPEL中的异常最好使用规范中的处理办法。
目前,validateForCC的实现:如果传入一个不存在的信用卡号,validateForCC只是简单的返回一个空响应。这个空响应不太直观,因为返回空响应可能还有其它原因。
现在,修改validateForCC的实现如下:如果传入一个不存在的信用卡号,将会抛出一个UnkonwCC的异常。在BPEL中可以捕捉到这个异常,然后进行处理。这样的修改在设计上更加合理。
2.1 实验准备:创建VALIDATECC存储过程
该存储过程用来验证信用卡是否存在,如果不存在抛出应用异常:-20001, 'UNKNOWN CREDIT CARD'。
sqlplus soademo/soademo @create_validate_cc.sql
create or replace FUNCTION VALIDATECC(cc_number IN VARCHAR2)
RETURN VARCHAR2 AS
l_status CREDITCARDINFO.STATUS%TYPE;
BEGIN
select status
into l_status
from creditcardinfo
where ccnumber = cc_number;
RETURN l_status;
EXCEPTION
WHEN NO_DATA_FOUND THEN
raise_application_error(-20001, 'UNKNOWN CREDIT CARD');
END VALIDATECC;
/
2.2 修改ValidateForCC,增加validateCC Reference
Meditor过滤条件如下:以2开头的信用卡号调用validateCC,返回'code=20001'的异常;
不以2开头的信用卡号调用getCreditValidation。
拖放Database Adapter,并设置如下:
2.3 修改ApproveLargeOrder BPEL
把invokeCCStatusService放入一个Scope中,并为这个Scope添加一个Catch分支,捕捉bindingFault异常。
这里有一个BUG:默认生成的.bpel文件中定义的变量是<variable messageType="bpelx:bindingFault" name="FaultVar"/>
但实际上,没有地方定义类型bpelx:bindingFault,应该改成如下的样子:
<variable messageType="bpelx:RuntimeFaultMessage" name="FaultVar"/>
否则,编译时会报错:WSDL messageType "{http://schemas.oracle.com/bpel/extension}bindingFault" of variable "" is not defined in any of the WSDL files。
2.4 修改fault-policies.xml。
由于上一个实验中设置了基于策略的异常处理,而基于策略的异常处理优先于BPEL的异常处理,所以以下的设置是让基于策略的异常处理啥都不做,直接抛出异常,由BPEL异常处理来接管。
<!-- Step #2: Add your fault handler for binding fault here: -->
<faultName xmlns:bpelx="http://schemas.oracle.com/bpel/extension" name="bpelx:bindingFault">
<condition>
<!-- Let the component handle this specific binding fault -->
<test>$fault.code="20001"</test>
<action ref="ora-rethrow-fault"/>
</condition>
</faultName>
2.5 测试大订单,订单号以2开头
你会发现ApproveLargeOrder中,异常被异常策略框架抛出,然后由BPEL异常框架捕捉到并处理。
3. 使用Java Fault Handler捕捉和处理异常
3.1 MyFaultHandler.java
package soatraining.faulthandling;
import java.io.File;
import java.io.PrintStream;
import java.util.*;
import oracle.integration.platform.faultpolicy.IFaultRecoveryContext;
import oracle.integration.platform.faultpolicy.IFaultRecoveryJavaClass;
public class MyFaultHandler
implements IFaultRecoveryJavaClass
{
public MyFaultHandler()
{
}
public void handleRetrySuccess(IFaultRecoveryContext ifc)
{
print("RertySuccess");
}
public String handleFault(IFaultRecoveryContext ifc)
{
print("Handle Fault");
print("Properties");
print("=================================================================");
Map props = ifc.getProperties();
java.util.Map.Entry entry;
for(Iterator iterator = props.entrySet().iterator(); iterator.hasNext(); print((new StringBuilder()).append((String)entry.getKey()).append(" = ").append(((ArrayList)entry.getValue()).get(0)).toString()))
entry = (java.util.Map.Entry)iterator.next();
String logFileName = (String)((ArrayList)props.get("logFileName")).get(0);
String logFileDir = (String)((ArrayList)props.get("logFileDir")).get(0);
PrintStream ps = null;
try
{
ps = new PrintStream((new StringBuilder()).append(logFileDir).append(File.separator).append(logFileName).toString());
}
catch(Exception e)
{
print(e.getMessage());
}
log(ps, "Fault Details");
log(ps, "===============================================================");
log(ps, (new StringBuilder()).append("Fault Type ................ ").append(ifc.getType()).toString());
log(ps, (new StringBuilder()).append("Poilcy ID ................. ").append(ifc.getPolicyId()).toString());
log(ps, (new StringBuilder()).append("Faulted Partner Link ...... ").append(ifc.getReferenceName()).toString());
log(ps, (new StringBuilder()).append("Port Type ................. ").append(ifc.getPortType()).toString());
return "OK";
}
private void log(PrintStream ps, String s)
{
try
{
ps.println(s);
}
catch(Exception e)
{
print(e.getMessage());
}
}
private void print(String s)
{
System.out.println((new StringBuilder()).append("MyFaultHanlder: ").append(s).toString());
}
}
3.2 修改fault-policies.xml。
<!-- Step #2: Add your fault handler for binding fault here: -->
<faultName xmlns:bpelx="http://schemas.oracle.com/bpel/extension" name="bpelx:bindingFault">
<condition>
<!-- Let the component handle this specific binding fault -->
<test>$fault.code="20001"</test>
<action ref="my-java-handler"/>
</condition>
</faultName>
3.3 测试
查看my-java-handler的定义,会发现它只是记录一些Log,然后重新抛出异常。
因此,BPEL的异常处理机制最终来接管该异常。
4. 使用Java Fault Handler捕捉和处理Mediator异常。
Mediator异常处理只能使用基于策略的方式。
Mediator异常可能来自Adapter异常,可能来自Transformation异常。
我们将使用Java Fault Handler捕捉和处理Mediator的所有异常。
为了模拟Adapter异常,我们将把temp目录改成只读,这样对其进行写操作时将抛出异常。
注意,在Windows平台上,改成只读方式不起作用。即使把该目录换名也不行,File Adapter会自动创建该目录。
4.1 修改fault-policies.xml
<!-- Step #3: Add your fault handler for mediator faults here: -->
<faultName xmlns:medns="http://schemas.oracle.com/mediator/faults" name="medns:mediatorFault">
<condition>
<action ref="my-mediator-fault-handler"/>
</condition>
</faultName>
<!-- Step #4: Add the Action definition for handling mediator fauls using custom java here:-->
<Action id="my-mediator-fault-handler">
<javaAction className="soatraining.faulthandling.MyFaultHandler"
defaultAction="ora-terminate" propertySet="myMediatorProps">
<returnValue value="OK" ref="ora-human-intervention"/>
</javaAction>
</Action>
<!-- Step #5: Add new property set for MyFaultHandler for logging Mediator faults here:-->
<propertySet name="myMediatorProps">
<property name="logFileName">mediator-faults.log</property>
<property name="logFileDir">c:\po\log</property>
</propertySet>
4.2 修改routePO mediator
之所以要把“quantity < 1000”的路由从Sequential改成Parallel,是因为Sequential表明Mediator的调用者和Mediator使用的是同一个Thread和Transaction Context,如果出现Mediator异常,异常将直接返回给调用者,而无法被Java Fault Handler捕捉到。
使用Parallel表明进入Mediator后,将启动一个新Thread和Transaction Context,如果出现Mediator异常,可以被Java Fault Handler捕捉到。
4.3 测试
EM 中应该提示等待人工恢复,因为策略上设置的是“ora-human-intervention”。
因为我是Win7平台,所以没有测试,以后在Linux上再验证吧。
Project 下载:LabD POProcessing.7z
参考文献:
1. http://soaprofessional.blogspot.com/2011/05/bpel-not-able-to-find-bindingfault.html。
没有评论:
发表评论