内容


在 WebSphere Process Server 中使用 Failed Event Manager API 管理失败的流程

Comments

简介

WebSphere Process Server(以下简称 Process Server)中的 Recovery 组件提供一种有用的服务,它存储关于失败的流程的信息,帮助管理员在解决问题之后重新启动它们。每当请求处理流程中出现 ServiceRuntimeException 时,Recovery 组件就会起作用。此时,事务回滚到前一个异步点。请求数据被放在一个失败事件中并保存在通用数据库中。除了请求数据之外,失败事件还包含关于创建失败事件的时间和位置的信息。如果失败的流程需要重新启动,这些信息会帮助从原来失败的位置再次开始处理。

可以使用在安装 WebSphere Process Server 时开箱即用的 Failed Event Manager 应用程序查看、修改请求并重新提交失败事件。这个应用程序在应用程序集群上运行,可以通过管理控制台访问它。但是,管理员喜欢编写客户机,让客户机获取特定的失败事件、查看数据、修改数据并重新提交它们。要想编写这些定制的客户机,管理员必须使用 Failed Event Manager API。实际上,管理控制台也使用相同的 API 提供此功能。

在本教程中

本教程讨论如何通过开发定制的 Java™ 应用程序来处理失败事件。本教程使用的 示例应用程序 包含一个仲裁流程和 SCA 组件,演示失败事件的生成和使用 Java Server Pages 处理这些失败事件。

本教程分为以下几节:

先决条件

  • 您需要非常了解 Java 和 Java EE 概念。
  • 您需要有 SCA 编程、仲裁以及 WebSphere Process Server 管理概念和工具方面的实践经验。

系统需求

  • 带 Fixpack 3 和 Unit Test Environment 的 IBM® WebSphere Integration Developer V7.0.0.0

学习时间

3 小时

信用卡网关服务集成应用程序简介

本节介绍本教程中使用的 示例应用程序,解释在处理请求时生成失败事件的条件。

本节包含以下小节:

应用程序的组成

图 1 描述本教程中使用的示例应用程序。可以下载本教程提供的 示例项目交换文件

图 1. 信用卡支付服务集成点
信用卡支付服务集成点
信用卡支付服务集成点

示例应用程序由两个模块组成,CreditCardPaymentProvider 和 CreditPaymentGW。CreditCardPaymentProvider 包含两个 SCA 组件,NormalPaymentProvider 和 PremiumPaymentProvider。NormalPaymentProvider 模拟针对 non-premium 客户的信用卡支付服务,而 PremiumPaymentProvider 模拟针对 premium 客户的信用卡支付服务。为了简单,这些提供者仅仅记录表明支付成功的消息。但是,如果客户名是 "INVALID",PremiumPaymentProvider 组件会抛出一个 ServiceRuntimeException。通过一个 web 服务导出 NormalPaymentProvider,通过 SCA 导出 PremiumPaymentProvider。

这个示例代表某一服务由许多服务提供者提供的典型场景。在运行时,根据请求数据决定选择哪个服务提供者。为了对这种场景进行建模,需要一个使用仲裁流程的服务集成点 (SIP)。CreditCardPaymentGW 是这个信用卡支付服务的服务集成点。它包含一个仲裁组件 CreditCardPaymentGateway,这个组件通过单一接口访问 PaymentData(请求数据)。仲裁流程使用消息筛选检查 PaymentData,决定要通过相应的调出节点调用哪个信用卡支付服务提供者。PaymentData 包含 CustomerType 属性,这个属性的值是 premium 或 non-premium。如果值是 premium,就通过 SCA 导入和相应的 SCA 导出调用 PremiumPaymentProvider。如果值是 non-premium,就通过 webservice 导入和相应的 webservice 导出调用 NormalPaymentProvider。

请下载示例应用程序的 项目交换文件 并检查应用程序中的各个组件。

应用程序中的异步处理

参考前一节中的 图 1。CreditCardPaymentGateway 仲裁流程中的两个调出节点异步地调用相应的伙伴。也就是说,对于两种客户类型(premium 和 non-premium),异步地调用相应的导入(SCA 导入和 webservice 导入)。因此,流程中有一个异步点。请检查项目交换文件中的调出节点属性。图 2 显示两个调出节点的属性。Invocation Style 设置为 Async

图 2. premium 和 non-premium 服务伙伴的调出节点属性
premium 和 non-premium 服务伙伴的调出节点属性
premium 和 non-premium 服务伙伴的调出节点属性

对于 non-premium 客户,如果目标模块 CreditCardPaymentProvider 停止运行,请求会失败并生成失败事件。在 webservice 导入之前引入异步点,见 图 1 中的 "Failed events-1"。这是因为 webservice 客户机与 webservice 提供者之间的交互总是异步的。但是,如果 CreditCardPaymentProvider 停止运行,premium 客户的请求不会失败,不会生成失败事件。相反,请求会累积在 CreditCardPaymentProvider 的模块队列(sca/CreditCardPaymentProvider)中。

注意:有时候,客户报告在 SCA_SYSTEM 队列中累积了许多消息。但是,没有生成的失败事件。这表明目标服务不可用或停止了,而且消费者没有取走消息。在管理控制台中检查目标服务是否停止了。当目标服务启动时,会自动地处理消息。

还要注意,生成失败事件之后,会从内部的 SIB 队列中删除相应的消息并把失败事件存储在通用数据库表中。当重新提交失败事件时,在相应的队列中重新引入消息以供处理。图 1 说明了这个过程。

为了为 premium 客户请求生成失败事件,在 PremiumPaymentProvider 组件中引入了一个检查。如果客户名是 "INVALID",就会抛出一个服务运行时异常。这会生成 图 1 中 "Failed events-2" 点上所示的失败事件。

用应用程序测试失败事件场景

本节包含以下小节:

部署应用程序

部署并运行这个应用程序的步骤如下:

  1. 从 “下载” 小节下载项目交换文件 FEM.zip
  2. 把这个 zip 文件导入 WebSphere Integration Developer。
  3. 启动 Unit Test Environment 并部署两个模块 CreditCardPaymentGWCreditCardPaymentprovider
  4. CreditCardPaymentGW 还包含一个名为 "TestWeb" 的 web 应用程序。这个 web 应用程序有几个 JSP,它们说明如何通过程序处理失败事件。本教程后面的小节会研究这些 JSP。
  5. 部署模块之后,右键单击 TestMediation.jsp 并选择 Run on Server
  6. 一个包含表单的浏览器窗口打开。提供图 3 所示的值并单击 Submit 按钮。
    图 3. non-premium 测试用例
    non-premium 测试用例
    non-premium 测试用例
  7. 对于成功的处理,在 SystemOut.log 文件中会看到以下消息:"Normal Payment DONE!! for customer=Phani"。这表明成功地处理了 non-premium 客户请求。
  8. 返回到表单。提供图 4 所示的值并单击 Submit 按钮。
    图 4. premium 测试用例
    premium 测试用例
    premium 测试用例
  9. 对于成功的处理,在 SystemOut.log 文件中会看到以下消息:"Premium Payment DONE!! for customer=Ramesh"。这表明成功地处理了 premium 客户请求。

为 non-premium 请求生成失败事件

  1. 在服务器上运行 TestMediation.jsp
  2. 停止 CreditCardPaymentProvider 模块。
  3. 提供图 3 所示的值并单击 Submit 按钮。浏览器会抛出运行时异常,SystemOut.log 记录以下消息:
    Recovery I
    com.ibm.wbiserver.manualrecovery.ejb.RecoveryMessageDrivenBean 
    onMessage() CWRCV0001I: A message was received from the 
    exception queue "WBI.FailedEvent.qnode.server1".
    
    Recovery I 
    com.ibm.wbiserver.manualrecovery.ejb.RecoveryMessageDrivenBean 
    updateEventStatus() CWRCV0002I: A failed event with the message 
    ID "4B1DF8A73B5EFEB3_28500065" was saved successfully.
  4. 上面的消息表明请求已经失败,已经生成了一个失败事件。
  5. 打开管理控制台,检查生成的失败事件,见图 5。
    图 5. 当目标服务不可用时生成的失败事件
    当目标服务不可用时生成的失败事件
    当目标服务不可用时生成的失败事件
  6. 生成更多事件,后面将重新提交它们。

为 premium 请求生成失败事件

  1. 重新启动 CreditCardPaymentProvider 模块。
  2. 在服务器上运行 TestMediation.jsp
  3. 提供图 6 所示的值并单击 Submit 按钮。
    图 6. 当 Customer Name 是 INVALID 时生成的失败事件(premium 客户)
    当 Customer Name 是 INVALID 时生成的失败事件(premium 客户)
    当 Customer Name 是 INVALID 时生成的失败事件(premium 客户)
  4. 浏览器窗口抛出运行时异常并生成一个失败事件。这是因为提供的 Customer Name 是 "INVALID"。生成更多事件,后面将重新提交它们。
  5. 管理控制台显示已经生成了相当多的失败事件,见图 7。
    图 7. 系统中生成的失败事件
    系统中生成的失败事件
    系统中生成的失败事件

通过程序管理失败事件

在本节中,我们要通过程序处理前几小节中生成的失败事件。关于 Failed Event Manager API 的详细信息,参见信息中心主题 Interface FailedEventManager

清单 1 所示的代码查找 Failed Event Manager MBean 并获取失败事件。

清单 1. 显示所有失败事件的代码片段
Properties connectProps = new Properties();

connectProps.setProperty(AdminClient.CONNECTOR_TYPE, AdminClient.CONNECTOR_TYPE_SOAP);

connectProps.setProperty(AdminClient.CONNECTOR_HOST, "localhost");
connectProps.setProperty(AdminClient.CONNECTOR_PORT, "8888");
connectProps.setProperty(AdminClient.CONNECTOR_SECURITY_ENABLED, "true");
connectProps.setProperty(AdminClient.USERNAME, "admin");
connectProps.setProperty(AdminClient.PASSWORD, "admin");
connectProps.setProperty(AdminClient.CACHE_DISABLED, "false");

connectProps.setProperty("javax.net.ssl.trustStore", "C:/ibm/WID7_WTE/runtimes/bi_v7/
 profiles/qwps/etc/DummyClientTrustFile.jks");

connectProps.setProperty("javax.net.ssl.keyStore", "C:/ibm/WID7_WTE/runtimes/bi_v7/
 profiles/qwps/etc/DummyClientKeyFile.jks");

connectProps.setProperty("javax.net.ssl.trustStorePassword", 
"WebAS");

connectProps.setProperty("javax.net.ssl.keyStorePassword", 
"WebAS");

AdminClient adminClient = null;
try {
 adminClient = AdminClientFactory.createAdminClient(connectProps);
} 
catch (Exception e) {
    out.println("Exception creating admin client: " + e);
}

ObjectName queryName = new ObjectName("WebSphere:*,
type=FailedEventManager");

ObjectName femObject = null;

Set s = adminClient.queryNames(queryName, null);

if(!s.isEmpty()){

 femObject = (ObjectName) s.iterator().next();
  String opName = "getAllFailedEvents";

Object[] params = new Object[] {new Integer(0)};

String[] signature = new String[] {"int"};

  List failedEventList = (List) adminClient.invoke(femObject, 
   opName, params, signature);
 
  Iterator it = failedEventList.iterator();
	   
while(it.hasNext()) {

   FailedEvent failedEvent = (FailedEvent) it.next();

 //use the failedEvent object to retrieve all the property values 
 of the failed event and display

}
}

上面的代码片段首先获取管理客户机对象并查找对象名为 "WebSphere:*,type=FailedEventManager" 的 Failed Event Manager MBean。可以通过传递适当的参数使用管理客户机在 MBean 上调用任何操作。在上面的 JSP 中,通过在 MBean 上调用 "getAllFailedEvents" 操作获取所有失败事件。这个 JSP 在 web 浏览器中显示到目前为止生成的所有失败事件。输出见图 8。为了简洁,只显示窗口中可以容纳的几列。

图 8. ListFailedEvents.jsp 的输出
ListFailedEvents.jsp 的输出
ListFailedEvents.jsp 的输出

看一下这个 JSP 的输出,这就是管理控制台中 Failed Event Manager 应用程序显示的信息。

注意:要想通过程序处理失败事件,需要从管理服务器或管理客户机查找 FailedEventManager MBean。在网络部署环境中,会有多个 FailedEventManager MBean 在集群成员上运行。只需查找这些 MBean 之一并在它上面调用操作。另外,在上面的代码片段中,要相应地修改本地主机的值、SOAP 端口、管理员用户名和密码。如果您已经修改了信任存储和密钥存储密码,也要提供这些值。提供的用户名应该是一位管理员。否则,无法重新提交失败事件。

另一个 JSP 文件只显示几列。这个 JSP 的输出见图 9。同样为了简洁,图 9 中只显示 3 列。

图 9. ListFailedEventsSummary.jsp 的输出
ListFailedEventsSummary.jsp 的输出
ListFailedEventsSummary.jsp 的输出

ListFailedEventsForNormalType.jsp 只显示为 non-premium 客户请求生成的失败事件。清单 2 给出代码片段。

清单 2. 获取在处理 non-premium 客户请求时生成的失败事件的代码片段
//After obtaining the FEM MBean, run the below query.

String opName = "queryFailedEvents";

QueryFilters queryFilters = new QueryFilters();

queryFilters.setFilter(queryFilters.COMPONENT_NAME, 
 "(import)NormalPaymentInterfaceImport");

Object [] event_type = {FailedEvent.TYPE_SCA,FailedEvent.TYPE_JMS};

queryFilters.setFilterArray(queryFilters.EVENT_TYPE, event_type);

Object[] params = new Object[] {queryFilters ,0,0};
String[] signature = new String[] {"com.ibm.wbiserver.manualrecovery.QueryFilters","int",
 "int"};

List failedEventList = (List) adminClient.invoke
   (femObject, opName, params, signature);

在上面的代码片段中,queryFilters 设置为只针对目标组件 "(import)NormalPaymentInterfaceImport"。正如本教程前面提到的,non-premium 客户请求的失败事件是在 webservice 导入之前生成的。因此,失败的 non-premium 请求的目标组件是 webservice 导入。

运行 ListFailedEventsForNormalType.jsp 以显示这些失败事件。输出见图 10。为了简洁,只显示几列。

图 10. 在处理 non-premium 客户请求时生成的失败事件
在处理 non-premium 客户请求时生成的失败事件
在处理 non-premium 客户请求时生成的失败事件

与之相似,ListFailedEventsForPremiumType.jsp 只列出在处理 premium 客户请求时生成的失败事件。代码片段见清单 3。

清单 3. 获取在处理 premium 客户请求时生成的失败事件的代码片段
String opName = "queryFailedEvents";

QueryFilters queryFilters = new QueryFilters();

queryFilters.setFilter(queryFilters.COMPONENT_NAME, 
  "(exportlink)PremiumPaymentProviderExport");

Object [] event_type = {FailedEvent.TYPE_SCA,FailedEvent.TYPE_JMS};
 		
queryFilters.setFilterArray(queryFilters.EVENT_TYPE, event_type);
 
Object[] params = new Object[] {queryFilters ,0,0};

String[] signature = new String[] {"com.ibm.wbiserver.manualrecovery.QueryFilters","int",
 "int"};
 
List failedEventList = (List) adminClient.invoke(femObject, 
opName, params, signature);

在上面的代码片段中,queryFilters 设置为只针对目标组件 "(exportlink)PremiumPaymentProviderExport"。正如本教程前面提到的,在处理 premium 客户请求时,在 CreditCardPaymentProvider 模块中的 SCA 导出之前生成失败事件。

运行 ListFailedEventsForPremiumType.jsp 以显示这些失败事件。输出见图 11。为了简洁,只显示几列。

图 11. 在处理 premium 客户请求时生成的失败事件
在处理 premium 客户请求时生成的失败事件
在处理 premium 客户请求时生成的失败事件

ResubmitNormalFailedEvents.jsp 重新提交在处理 non-premium 客户请求时生成的失败事件。清单 4 给出执行此任务的代码片段。

清单 4. 重新提交在处理 non-premium 客户请求时生成的失败事件的代码片段
String opName = "queryFailedEvents";

QueryFilters queryFilters = new QueryFilters();

queryFilters.setFilter(queryFilters.COMPONENT_NAME, 
 "(import)NormalPaymentInterfaceImport");

Object [] event_type = {FailedEvent.TYPE_SCA,FailedEvent.TYPE_JMS};
 
queryFilters.setFilterArray(queryFilters.EVENT_TYPE, event_type);
 
Object[] params = new Object[] {queryFilters ,0,0};

String[] signature = new String[] {"com.ibm.wbiserver.manualrecovery.QueryFilters","int",
 "int"};
 
List failedEventList = (List) adminClient.invoke(femObject, opName, 
params, signature);

Iterator it = failedEventList.iterator();
	    
List scaEventList = new ArrayList(); 

while(it.hasNext()) {
	       
 FailedEvent failedEvent = (FailedEvent) it.next();
      
 String opName1 = "getEventDetailForSCA";

 Object[] params1 = new Object[] {failedEvent};
      
 String[] signature1 = new String[] 
  {"com.ibm.wbiserver.manualrecovery.FailedEvent"};

 SCAEvent failedEventWithParameters = (SCAEventadminClient.invoke(femObject, opName1, params1, signature1);

    //Business Object parameters
      
 List paramList = failedEventWithParameters.getFailedEventParameters
  (adminClient.getConnectorProperties());

 Iterator it1 = paramList.iterator();

 while(it1.hasNext()) {
  //Each parameter is of FailedEventParameter type.

  FailedEventParameter failedEventParameter = 
    (FailedEventParameter) it1.next();

  System.out.println("parameter name is: " + 
    failedEventParameter.getName());

  System.out.println("parameter type is: " + 
   failedEventParameter.getType());

  System.out.println("parameter value is: " + 
   failedEventParameter.getValue());
     }

  scaEventList.add(failedEventWithParameters);
	       
}

 String opName2 = "resubmitFailedEvents";

 Object[] params2 = new Object[] {scaEventList};

 String[] signature2 = new String[] {"java.util.List"};

 try {
  adminClient.invoke(femObject, opName2, params2, signature2);
  out.println("<h2>Failed Events Submitted Successfully</h><br/>");
     } 
   catch(MBeanException e) {
     e.printStackTrace();
     }

上面的代码片段首先获取在处理 non-premium 客户请求时生成的所有失败事件。对于每个失败事件,获取 SCAEvent 对象并把它放进一个 ArrayList 中。最后,作为参数把这个 ArrayList 提供给 resubmitFailedEvents 操作。运行这个 JSP 之后,会重新提交在处理 non-premium 客户请求时生成的所有失败事件。代码并不 修改业务对象值。

作为 failedEventParameter 获取参数值并把它们记录在 SystemOut.log 文件中。运行上面的 JSP 之后,运行 ListFailedEventsForNormalType.jsp,可以看到系统中不再有与 non-premium 客户请求相关的失败事件了。在 SystemOut.log 中,查看 "Normal Payment DONE!! for customer=xxxx" 消息。

EditAndResubmitPremiumFailedEvents.jsp 重新提交在处理 premium 客户请求时生成的失败事件。在重新提交失败事件之前,把客户名改为 "VALID",让请求的处理能够成功。清单 5 给出代码片段。

清单 5. 重新提交在处理 premium 客户请求时生成的失败事件的代码片段
String opName = "queryFailedEvents";

QueryFilters queryFilters = new QueryFilters();

queryFilters.setFilter(queryFilters.COMPONENT_NAME, 
  "(exportlink)PremiumPaymentProviderExport");

Object [] event_type = {FailedEvent.TYPE_SCA,FailedEvent.TYPE_JMS};

queryFilters.setFilterArray(queryFilters.EVENT_TYPE, event_type);

Object[] params = new Object[] {queryFilters ,0,0};

String[] signature = new String[] 
  {"com.ibm.wbiserver.manualrecovery.QueryFilters","int","int"};

List failedEventList = (List) 
  adminClient.invoke(femObject, opName, params, signature);

Iterator it = failedEventList.iterator();
	    
List scaEventList = new ArrayList(); 
	   
while(it.hasNext()) {

   FailedEvent failedEvent = (FailedEvent) it.next();

   String opName1 = "getEventDetailForSCA";
   
   Object[] params1 = new Object[] {failedEvent};
   String[] signature1 = new String[] 
        {"com.ibm.wbiserver.manualrecovery.FailedEvent"};

   SCAEvent failedEventWithParameters = (SCAEvent)  adminClient.invoke(nodeAgent, opName1, params1, signature1);

   String ceiTrace = failedEventWithParameters.getCEITraceControl();

   Date expirationTime = failedEventWithParameters.getExpirationTime();

   List paramList = failedEventWithParameters.getFailedEventParameters
    (adminClient.getConnectorProperties());

   Iterator it1 = paramList.iterator();
   
   while(it1.hasNext()) {
    FailedEventParameter failedEventParameter = 
      (FailedEventParameter) it1.next();

    System.out.println("parameter name is: " + 
       failedEventParameter.getName());

    System.out.println("parameter type is: " + 
       failedEventParameter.getType());

    System.out.println("parameter value is: " + 
       failedEventParameter.getValue());

    DataObject premiumPaymentMode =   (DataObject)failedEventParameter.getValue();	 premiumPaymentMode.setString("CustomerName", "VALID");	 failedEventParameter.setValue(premiumPaymentMode);

    }
    scaEventList.add(failedEventWithParameters);
	       
}

String opName2 = "resubmitFailedEvents";
Object[] params2 = new Object[] {scaEventList};
String[] signature2 = new String[] {"java.util.List"};

try {
  adminClient.invoke(femObject, opName2, params2, signature2);

  out.println("<h2>Failed Events Submitted Successfully</h><br/>");
} 
   catch(MBeanException e) {
   e.printStackTrace();
}

在上面的代码片段中,在获取失败事件之后,使用以下代码把客户名更新为 "VALID":

DataObject premiumPaymentMode =  
 (DataObject)failedEventParameter.getValue(); premiumPaymentMode.setString("CustomerName", "VALID");
 failedEventParameter.setValue(premiumPaymentMode);

更新参数值之后,重新提交失败事件列表。运行上面的 JSP 之后,运行 ListFailedEventsForPremiumType.jsp,可以看到系统中不再有与 premium 客户相关的失败事件了。在 SystemOut.log 中,查看 "Premium Payment DONE!! for customer=xxxx" 消息。

结束语

本教程讲解了如何通过程序处理失败事件,通过编写定制的应用程序管理失败事件,以及在重新提交失败事件之前查看和修改数据。示例应用程序会显示生成失败事件的时间,演示了如何通过程序使用 Failed Event Manager API 处理这些事件。


下载资源


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=WebSphere
ArticleID=682644
ArticleTitle=在 WebSphere Process Server 中使用 Failed Event Manager API 管理失败的流程
publish-date=06272011