整合 WebSphere ILOG JRules 与 IBM Content Manager Enterprise Edition

简介:  自动决策在内容管理系统中变得越来越重要。核心应用逻辑中的外化决策逻辑能够根据动态的业务需求管理和快速修改业务规则。IBM WebSphere ILOG JRules 是一个业务规则管理系统(BRMS),它有许多制作、部署和管理业务规则的功能,管理者可以通过它更好、更快地作出决策。IBM WebSphere ILOG JRules 和 IBM Content Manager Enterprise Edition 的整合扩大了内容管理解决方案在一个组织中更有效管理业务决策的范围。

本文首先阐述了整合 IBM Content Manager Enterprise Edition 与 IBM WebSphere ILOG JRules 的方法。然后概述了事件框架,简单介绍了 ILOG JRules 业务规则管理系统,本文使用一个贷款场景来说明如何开发一个定制的事件处理器从而将 ILOG JRules 整合到一个内容管理应用中。

 

引言

自动决策在内容管理系统中变得越来越重要。然而,对于不断增加的用例,在一个应用中嵌入决策规则可能并不能满足一个快速发展的业务环境的复杂需求。因此,拥有一个专门的支持业务规则建模、规则执行和规则管理的业务规则管理系统变得非常重要。

IBM WebSphere ILOG JRules 是一个业务规则管理系统(BRMS),它提供支持业务敏捷性和效率的业务规则创建、部署和管理功能。ILOG JRules 和 IBM Content Manager Enterprise Edition Version 8.4.2(下面称为 Content Manager)的整合是基于一个事件框架的。这个整合扩展了 Content Manager 的功能,使之能在一个组织中实现更高效的业务决策管理。

Content Manager 支持一个事件基础架构,它能够整合外部应用。本文阐述了如何开发一个定制的事件处理器,同时该处理器是通过能与 ILOG JRules 交互的感知内容的业务逻辑驱动的。

本文概述了 Content Manager 事件框架,并介绍了 ILOG JRules 业务规则管理系统。然后使用一个示例的贷款申请场景说明如何开发一个定制的整合 ILOG JRules 和 Content Manager 的事件处理器。

 

认识 Content Manager 事件框架

图 1 说明了一个支持 Content Manager 和外部应用整合的事件框架的体系结构。


图 1. 事件框架体系结构
描述事件框架体系结构组件关系的流程图。下面的内容解释了它的详细信息。

如上图所示,事件框架实现了 3 个主要功能:

  • 事件订阅 允许用户确定一个项目类型和与该项目相关联的事件。在事件订阅过程中,事件订阅数据存储在 Content Manager 库服务器数据库的配置表中。配置数据是通过使用 Content Manager 系统管理客户端配置的。管理员订阅这些事件,然后通过监控这些事件了解一个具体项目类型及其属性。
  • 事件监控 会在事件发生时记录这些事件,生成事件消息,然后消息被发送到一个 Java Message Service (JMS) 消息队列。事件监控专注于生成事件消息。通过使用 Content Manager 的 Java® 和 C++ API 接口,应用就能够与库服务器进行交互,并将生成的数据保存到数据库中。库服务器会在事件发生时根据配置记录这些事件。所记录的事件会被记录到库服务器数据库的一个事件队列表中。一个事件包含事件类型、事件 ID、项目信息和属性数据。事件监视器能根据配置数据从事件队列表取出事件数据,将事件转换成 JMS 消息,然后将 JMS 消息放回到一个 JMS 事件队列中。
  • 事件处理 从 JMS 队列查询事件消息,然后基于业务逻辑处理这些消息。事件处理专注于事件消息使用。事件处理器会从 JMS 队列读取包含事件数据的 JMS 消息。事件处理器能够将应用逻辑整合到 Content Manager 文档属性中。处理整合的事件处理器是由支持 FileNet Business Process Manager 的 Content Manager 提供的,但是如果要整合其他应用,您必须开发定制的事件处理器。

认识 ILOG JRules 业务规则管理系统

自动化业务决策可以用于接受一个贷款申请以支付一个汽车保险赔付。这些决策是一个完整元素的业务操作。IBM WebSphere ILOG JRules Business Rule Management System (BRMS) 能够使用一组面向业务分析人员、解决方案架构师和应用开发人员的工具以灵活和可预见的方式控制自动业务决策。

图 2 说明了 ILOG JRules 的主要组件。


图 2. ILOG JRules 概述
Rule Studio 和 Rule Team Server 相互同步。它们都部署到 Rule Execution Server。下面是它们的详细信息。

如上图所示,ILOG JRules 组件分成了 3 个方面:

  • Rule Studio 是一个支持业务规则应用开发的基于 Eclipse 的集成开发环境。应用开发人员可以利用 Eclipse 集成环境开发 Java 项目和规则项目。当开发一个业务规则项目时,开发人员需要规定初始规则,设计规则模板和通过文件夹管理和组织规则。通过使用 Decision Validation Services,开发人员就能够根据验证和故障修复场景测试这些规则。
  • Rule Team Server 是一个基于 Web 的规则管理和授权环境,它允许业务用户查看、创建和修改规则。业务分析人员可以在应用开发过程中和开发完成后使用 Rule Team Server 编写和维护业务规则。通过 Rule Studio,开发人员将规则项目发布到 Rule Team Server,然后周期性地同步业务用户成果和 Rule Studio 的副本。
  • Rule Execution Server 能够让管理员访问他们需要监控的已部署规则集,管理决策服务和执行审计。Rule Execution Server 是一个规则执行环境(Java SE 和 Java EE),它与规则引擎交互。Rule Execution Server 具备处理管理、性能、安全性和与业务规则执行相关的日志功能。RuleApp 是 Rule Execution Server 规定的格式。它包含了规则集,并且打包在一个 JAR 文件中的,其中包含了执行所需要的所有东西(规则、规则流等)。规则集既可以部署在 Rule Studio 也可以部署在 Rule Team Server 上。

场景介绍

本节将介绍一个示例场景,它将说明在一个业务上下文中如何整合 ILOG JRules 和事件框架。

一个地区银行的按揭部门计划自动化一个贷款申请过程。这个贷款申请过程由三个工作状态组成:SubmitLoan、Approved 和 Rejected。当一个贷款申请提交后,它会到达 SubmitLoan 操作区。如果申请获得批准,那么它就会转到 Approved 操作区。如果被拒绝,它会转到 Rejected 操作区。一个 Rejected 状态的贷款申请可以重新提交到 SubmitLoan 操作区进行重新评估。

ILOG JRules 被整合到 SubmitLoan 操作区以实现自动决策。在贷款申请提交过程中,贷款代理人负责提供诸如贷款号、贷款数额、利率、期限等贷款信息。当该申请到达 SubmitLoan 操作区时,这个贷款信息就被发送到 ILOG JRules 规则引擎进行评估。如果评估通过,这个贷款申请会自动转移到 Approved 操作区。否则,它会被自动转移到 Rejected 操作区。进入 Rejected 操作区的一个贷款申请可以重新发送到 SubmitLoan 操作区。当它的贷款信息更新后,它会被重新评估。当一个贷款申请转到 Approved 或 Rejected 操作区时,规则引擎都会生成和发送电子邮件通知,通知贷款申请相关人关于该笔申请的状态。

上述具有 SubmitLoan、Approved 和 Rejected 操作区的贷款申请流程见图 3 所示。


图 3. 贷款申请过程
SubmitLoan 操作区之后是 Approved 或 Rejected。Rejected 的事务可以被重新提交或直接停止。Approved 事务将停止。

如图 4 所示,我们定义了三个操作列表(SubmittedWL、ApprovedWL 和 RejectedWL),它们将监控各个操作区的工作项。


图 4. 贷款申请过程的操作列表
截图显示的是一个操作列表:SubmittedWL、ApprovedWL 和 RejectedWL

Content Manager 中定义了贷款申请处理的 Loan 项目类型。 如图 5 所示,它由以下属性组成:

  • LoanNumber — 贷款的唯一标识符
  • LastName — 贷款人的姓
  • FirstName — 贷款人的名字
  • CreditScore — 贷款人信用评分
  • YearlyIncome — 贷款人的年收入
  • LoanAmount — 贷款额
  • InterestRate — 贷款利率
  • Duration — 贷款期限
  • Notification — 通知接收人的电子邮件地址


图 5. Loan 项目类型
Loan 项目类型显示了上面列表中的每一个属性。

Library Server Configuration 对话框(图 6)中的 Enable Event Subscription 复选框必须选中,以便激活一个库服务器实例的事件日志功能。在项目类型级别,图 7 显示一个项目类型 Loan 的事件订阅例子。这个项目类型所订阅的事件是 Add Item 和 Update Item 事件。当管理员单击 Event Subscription 页面的 Add 按钮时,页面就会显示一个 Define Event Subscription 对话框。管理员也可以选择 General integration 选项,因为这个场景是与一个通用外部应用有关的。这个 Loan 项目类型的所有文档属性都会被选中,以便在 Add Item 和 Update Item 事件中进行监控。


图 6. Library Server Configuration
选中 Enable Event Subscription 的 Library Server Configuration 对话框。

图 7. 事件订阅
选中 Event Subscription 对话框的 General integration。

本文的重点是整合 ILOG JRules 和 Content Manager,以及一个贷款例子的业务规则。这个贷款例子来自于 IBM WebSphere ILOG JRules 信息中心(见 参考资料 中的信息中心链接)的“Getting started”小节。

图 8 显示了贷款例子所使用的两组业务规则:

  • 合格标准
    • 最小信用评分 — 保证信用评分不小于 200。
    • 最小收入 — 保证满足最小年收入要求。
    • 还款和信用评分对比 — 保证贷款-收入比例和信用评分都是可接受的。
  • 验证
    • 最大贷款数额 — 保证最大贷款数额不超过 $1,000,000。

在图中,ILOG Rule Team Server 的 Explore 选项卡中显示了两组规则。对于本场景,业务规则是通过 Rule Studio 发布到 Rule Team Server 的,并且它们是以一个规则归档文件(jar 格式)部署的。Rule Execution Server 是一个规则运行环境。为了简单起见,我们选择了 Java SE 规则会话类型,并且在一个规则会话中嵌入 Java SE 运行单元。


图 8. ILOG JRules 业务规则
ILOG Rule Team Server 的 Explore 选项卡截图显示了两组业务规则:合格标准和验证。

图 9 说明了一个业务规则是如何表示的。它在 Rule Team Server 的 Compose 选项卡中显示了最小信用评分业务规则的内容。这个规则规定如果借款人的信用评分小于 200,贷款申请就会被拒绝。


图 9. 最小信用评分
ILOG Rule Team Server 的 Compose 选择选项卡显示信用评分业务规则的详细信息。

 

整合体系架构

本例中使用的产品

下面是本文的例子所使用的产品:

  • IBM WebSphere ILOG JRules Version 7.0.2(试用版)
  • IBM Content Manager Enterprise Edition Version 8.4.2
  • IBM WebSphere MQ Version 6.0

本节阐述了创建贷款申请场景所使用的体系架构。这个体系架构整合了 ILOG JRules 和 Content Manager。如图 10 所示,这个体系架构由三个部分组成:

  • 内容 — Content Manager 是内容知识库。它的库服务器组件既支持贷款申请的数据建模,也支持贷款申请过程的过程建模。
  • 事件处理 — 定制的事件处理器实现了核心应用逻辑,并整合了 ILOG JRules 和 Content Manager。它从 Content Manager 接收贷款申请事件,然后将请求提交到 ILOG JRules 进行评估。它会根据评估的结果采取恰当的操作,并发送相应的电子邮件通知。
  • 业务规则 — 业务决策逻辑是与核心应用逻辑分离的。这个业务规则是通过 ILOG JRules 规则引擎运行的。这些业务规则可以由业务分析人员使用 Rule Team Server 进行修改。


图 10. Content Manager / ILOG 整合体系架构
图片显示了整合体系架构的三个部分:内容、事件处理和业务规则。

下面概括了一个贷款申请场景在整合体系架构中是如何流转的。

  • 贷款代理人在 Content Manager 中创建一个项目类型 Loan 的贷款申请文档。
  • 当贷款申请文档创建之后,Content Manager 服务器会自动启动一个贷款申请过程实例。这个贷款申请会进入 SubmitLoan 操作区等待评估。
  • 管理员配置 Loan 项目类型监控文档创建和修改分别对应的一个 Add Item 事件或 Update Item 事件。因此,库服务器会插入一个事件数据到事件队列表中。这个事件数据包括了诸如贷款号、贷款数额和通知等属性。
  • 事件监视器会扫描事件队列表查找提交的记录,然后查询贷款文档的 Add Item 或 Update Item 事件。这个事件监视器能将配置信息和从事件队列表所查询的事件数据转换到一个自解释的通用基础事件(CBE)格式事件消息中。然后它会将这个事件消息发送给 一个 Java Message Service (JMS) 队列。
  • 有一个定制的事件处理器负责监听 JMS 队列,它可以从队列查询文档创建事件和修改事件。这个事件处理器会解析 CBE 格式的事件数据,然后将贷款信息封装到请求中并发送到 ILOG JRules 规则引擎进行评估。
  • 当接收到请求时,ILOG JRules 规则引擎就会执行这些业务规则,然后返回贷款申请的评估结果。
  • 在接收到 ILOG JRules 规则引擎返回的响应后,事件处理器会将贷款申请转发到贷款申请过程中的正确的操作区(Approved 或 Rejected),同时发送一个电子邮件到通知列表中的接收人。

myEventHandler 定制的事件处理器的设计概述

一个定制的事件处理器的基本功能是使用 JMS 队列中的事件消息,然后相应地与外部应用进行交互。对于本文的场景,这个事件处理器必须连接到一个 Content Manager 服务器,查询事件消息中所引用的项目的文档属性。接下来,处理器会创建一个规则会话,然后发送一个请求到 ILOG JRules 规则引擎,以评估贷款申请。然后这个事件处理器会使用 JavaMail API 生成发送到接收人的电子邮件通知。

下载 示例程序,它定义了一个支持本文的场景所描述的定制的事件处理器。这个定制的事件处理器是一个名为 myEventHandler 的 JMS 应用。它使用一个异步消息发送机制。它实现了一个消息监听器,这个监听器可以在新消息到达 JMS 队列时接收这些新消息。

清单 1 显示了 myEventHandler 示例程序的总体结构。下面是关于这个程序功能的总体描述:

  • 这个程序导入下面必要的程序包:
    • 用于访问 JMS 队列 的 JMS 和 Java Naming and Directory Interface (JNDI) 程序包
    • 用于发送电子邮件的 JavaMail 程序包
    • 用于解析事件消息的 SAX API 程序包
    • 用于查询内容数据的 Content Manager API 程序包
    • 用于创建规则会话的 ILOG API 程序包
  • myEventHandler 类实现了两个 JSM 监听器:
    • javax.jms.MessageListener 接口的 onMessage 方法负责监听 JMS 队列。
    • javax.jms.ExceptionListener 接口的 onException 负责处理 JMS 异常。
  • 两个用于解析事件消息的内部类:
    • myContentHandler 类扩展了 SAX API 的 DefaultHandler 类,它负责解析事件消息。
    • myErrorHandler 类实现了 SAX API 的 ErrorHandler 接口。
  • 函数 sendMail 负责创建电子邮件并使用 JavaMail API 将通知发送给接收者。它是在接收到一个事件消息时由函数 onMessage 调用的。
  • 函数 executeILOGRules 将一个请求发送到 ILOG JRules 规则引擎再执行规则。这个规则引擎会评估贷款申请,然后返回响应给调用者。
  • myEventHandler 类初始化时,函数 waitForQUIT 就会被调用。它会提示用户执行 quit 命令。当接收到一个 quit 命令时,myEventHandler 类就会退出。


清单 1. 程序结构

				
// JMS API
import javax.jms.*;
// JNDI API
import javax.naming.*;
// Java IO and utility
import java.io.*;
import java.util.*;
// SAX abd XML parsing
import org.xml.sax.*;
import org.xml.sax.helpers.*;
// JavaMail API
import javax.mail.*;
import javax.mail.internet.*;
// CM API
import com.ibm.mm.sdk.server.*;
import com.ibm.mm.sdk.common.*;
// ILOG API
import ilog.rules.res.session.*;
import ilog.rules.res.model.*;
import ilog.rules.res.session.ruleset.*;

public class myEventHandler extends Thread 
             implements javax.jms.MessageListener, javax.jms.ExceptionListener
{

    static class myContentHandler extends DefaultHandler
    {
        // methods in the myContentHandler class
    }
    
    static class myErrorHandler implements ErrorHandler
    {
        // methods in the myErrorHandler
    }   
            
    public void run() 
    {      
        this.waitForQUIT();       
    }
    
    private void waitForQUIT() throws Throwable
    {
        // prompt for the QUIT command to exit     
    }     
    
    public myEventHandler(String database, String userid, String pw) throws Throwable
    { 
        // set up JMS connection
        // initialize SAX parser  
        // start the thread for waiting the QUIT command to exit   
    }
    
    public static void main(String[] argv)
    {
        // start the myEventHandler instance
        myEventHandler eh = new myEventHandler(argv[0], argv[1], argv[2]);
    }
    
    public void onMessage(javax.jms.Message message) 
    { 
        // listen to the JMS queue
        // retrieve a JMS message 
        // parse the JMS message to obtain the event data
        // process the event data
        // compose an e-mail
        // call the sendMail function to send out an e-mail notification
    }  

    public void onException(JMSException exc)
    {
        // print the error message when a JMS exception occurs 
    }

    public void sendMail(String mailhost, String from, String[] to, 
                        String subject, String text) 
    {
        // send an e-mail notification to the claim reviewers
    }
    
    public IlrSessionResponse executeILOGRules(String firstName,
                String lastName, int loanAmount, int duration, 
                float interestRate, int creditScore, int yearlyIncome)
    {
        // send a request to ILOG JRules rule engine for rule execution
        // an IlrSessionResponse object is returned with the evaluation result
    }
}            

 

文章的下面几个小节更详细地检查了示例的一些主要部分:

  • 连接一个 JMS 队列
  • 查询一个事件消息中的事件数据
  • 解析一个事件消息中的事件数据
  • 从一个 Content Manager 服务器查询事件所引用的内容数据
  • 执行规则
  • 移动过程中的贷款申请
  • 发送电子邮件给通知接收者

连接一个 JMS 队列

清单 2 显示了 myEventHandler 示例程序中与一个 JMS 队列建立连接的一部分代码。

首先,这个程序通过查找上下文对象获得一个指定名称的队列连接工厂。其次,它在队列连接中创建一个队列会话,并通过查找上下文对象获得一个指定名称的队列。然后它会为一个特定的队列创建一个作为消息接收的队列接收器。这个队列接收器会使用一个消息选择器 APPLICATION = 'none' 来监听队列,这表示数据来自于一般整合。JMS 队列中只有那些带有消息属性 APPLICATION 且属性值为 'none' 的消息会被查询到。

当队列接收器创建后,这个程序会在队列接收器中创建消息监听器。而异常监听器则是在队列连接中创建。在开始连接 JMS 之前,这个程序会执行另外两个任务:

  • 初始化用于解析事件消息的 SAX 解析器
  • 连接 Content Manager 以查询内容数据

最后一步是启动 JMS 连接。在 JMS 连接建立后,onMessage 消息监听器和 onException 异常监听器可以接收来自于 JMS 队列的输入消息。


清单 2. 连接到一个 JMS 队列

				
        // obtain the queue connection factory
        factory = (QueueConnectionFactory)ctx.lookup(_qcf_name);
        // create a queue connection
        connection = factory.createQueueConnection();
        System.out.println("Queue connection created");
        // create a queue session
        qSession = connection.createQueueSession(false, 
                                                 javax.jms.Session.AUTO_ACKNOWLEDGE);
        System.out.println("Queue session created");
        // create a queue
        testQueue = (javax.jms.Queue)ctx.lookup(_queue_name);
        System.out.println("Queue = [" + _queue_name + "]");
        // create a consumer listening to the specified queue 
        // for message property APPLICATION = 'none' (general integration)
        qReceiver = (QueueReceiver)qSession.createConsumer(testQueue, 
                                                           "APPLICATION = 'none'");
        // set the message listener
        qReceiver.setMessageListener(this);
        // set the exception listener        
        connection.setExceptionListener(this);

        // initialize the SAX parser
        try {
            _parser = initParser(_dh);          
        } catch (SAXException se) {
            System.out.println("SAXException - " + se.getMessage());
            throw se;
        }
        
        // connect to the Content Manager
        dsICM = new DKDatastoreICM();
        dsICM.connect(database, userid, pw, "");
        System.out.println("datastore connected.");        
                
        // start the JMS connection
        connection.start();       
        System.out.println("Queue connection started");        

 

查询事件数据

在 JMS 连接启动后,onMessage 消息监听器就能够以先进/先出的方式从 JMS 队列查询消息。一个 JMS 消息首先会被强制转换成一个 TextMessage 对象。

清单 3 显示了 myEventHandler 示例程序中用于查询事件数据的一部分程序。这个程序能够从 TextMessage 对象查询到消息字符串以及下面六个消息属性:

  • DATABASE 是生成事件的 Content Manager 服务器名。
  • TYPE 是事件的类型(例如,"item-create" )。
  • LAUNCH 表示这个消息是否请求运行一个工作流。这个属性的值可能是 "true""false" 。对于一般整合,这个值是 "false"
  • ID 是事件的标识符(例如, "A1001001A10B19B12751I284680000000000000000225000" )。
  • APPLICATION 是与一个工作流运行相关的应用。对于与 FileNet Business Process Manager (BPM) 整合的过程,这个值是 "BPM" 。对于一般整合,这个值是 "none"
  • COUNT 表示事件中包含了多少数据元素。如果事件的长度小于或等于 1024,那么事件中只有一个上下文数据元素。否则,如果计数大于 1,那么定制的事件处理器需要将多个上下文数据元素连接在一起。

接下来,这个程序会解析消息字符串,这个字符串是用一种通用基础事件(CBE)格式表示的。下面是用于将 CBD 格式化的消息字符串的信息传递到程序的变量:

  • strGlobalInstanceId 是事件的全局实例 ID,(例如, "A1001001A10B19B12751I284680000000000000000225000" ,就是相同的消息 ID)。
  • strReturnMsg 包含了来自于 Content Manager 内容知识库的事件数据。
  • strReturnTime 是 GMT 表示的事件创建时间(例如, "2010-03-19T01:03:11.256Z" )。
  • strApplication 是生成事件的应用名称(例如,"Content Manager Event Monitor" ).
  • strComponent 是生成事件的组件名称(例如,"Content Manager 8.4.02.000" )。
  • strExecutionEnvironment 表示执行环境的操作系统和架构(例如,"Windows XP[x86]" )。
  • strLocation 是事件监视器的主机名和 IP 地址(例如,"myhost/1.11.22.33" )。
  • strSubComponent 是子组件的名称(例如,"Event Monitor" )。


清单 3. 查询事件数据

				
            // cast the message to the TextMessage object
            msg = (TextMessage) message;
            // retrieve the raw string from a JMS message
            rawString = msg.getText();

            System.out.println("Reading message: " + rawString);
        
            // retrieve the message properties from a JMS message
            msgDatabase = msg.getStringProperty("DATABASE");
            msgType = msg.getStringProperty("TYPE");
            msgLaunch = msg.getStringProperty("LAUNCH");
            msgID = msg.getStringProperty("ID");
            msgApplication = msg.getStringProperty("APPLICATION");
            msgCount = msg.getStringProperty("COUNT");
        
            // create an input source from the raw string
            ByteArrayInputStream bais = new ByteArrayInputStream
                                        (rawString.getBytes("UTF-8"));
            InputSource is = new InputSource(bais);
            bais.close();
            
            // parse the string
            try {
              _parser.parse(is);
            } catch (SAXParseException se) {
              System.out.println("Encountered a SAX parser error" 
                                 + " - " + se.getMessage());
              se.printStackTrace();
              throw se;
            }
            
            // retrieve the information from a CBE-formatted string
            strGlobalInstanceId = ((myContentHandler)_dh).getGlobalInstanceId();
            strReturnMsg = ((myContentHandler)_dh).getReturnMsg();
            strReturnTime = ((myContentHandler)_dh).getReturnTime();
            strApplication = ((myContentHandler)_dh).getApplication();
            strComponent = ((myContentHandler)_dh).getComponent();
            strExecutionEnvironment = ((myContentHandler)_dh).getExecutionEnvironment();
            strLocation = ((myContentHandler)_dh).getLocation();
            strSubComponent = ((myContentHandler)_dh).getSubComponent();
			

解析事件数据

清单 4 显示了 myEventHandler 程序中解析从消息字符串查询的事件数据的一部分程序。这些代码能从事件数据获取 PID 字符串和 ITEMTYPE 名称。PID 字符串和 ITEMTYPE 名称在后面会用于查询内容数据和创建电子邮件通知。

StringTokenizer 对象是使用事件数据和分隔符 ";" 实例化的。其中,一个 while 循环会遍历所有标识符,如果遇到 "ICMEMEND" 或遍历完所有标识符时,循环结束。每一个标识符都是用一个 3 元组元素 <tag>=<value> 表示的。带有 ITEMTYPE 标签的标识符包含了事件数据所引用的文档的持久化 ID 字符串。


清单 4. 解析事件数据

				
            // Use the StringTokenizer to parse the event message           
            StringTokenizer tokenizer = new StringTokenizer(strReturnMsg, ";");
 
            // variable for PID
            String pid = "";
            // variable for itemtype
            String itemtype = "";
             
            String strPart = ""; 
            int pos = 0;
            String tagPart = "";
            
            // retrieve pid and itemtype from the event message
            while (true) {
              strPart = tokenizer.nextToken();
              if (strPart.equalsIgnoreCase("ICMEMEND")
                  || strPart == null || strPart.equals("")) break;

              pos = strPart.indexOf('=');
              tagPart = "";
 
              if (pos > 0) {      
                  tagPart = strPart.substring(0, pos);
              }
              else continue;
 
              if (tagPart.equalsIgnoreCase("ITEMTYPE")) {
                  pos = strPart.indexOf('=');
 
                  if (pos > 0) {
                      itemtype = strPart.substring(pos + 1);
                  }
              }
              else {
                if (tagPart.equalsIgnoreCase("PID")) {
                    pos = strPart.indexOf('=');
 
                    if (pos > 0) {
                        pid = strPart.substring(pos + 1);
                    }
                }
              }
            } // end while
			

 

查询内容数据

通过指定从事件数据查询到的 PID 字符串,我们就能从 Content Manager 知识库中查询到文档元数据库。清单 5 显示了 myEventHandler 示例程序中从一个 Content Manager 服务器查询一个动态数据对象(DDO 对象)及其属性的一部分代码。一个查询调用能够为 DDO 对象填充它的属性值。

然后,下面这些属性会被查询到:

  • InterestRate 是贷款利率(例如,"0.05" )。
  • FirstName 是贷款人的名字。
  • LastName 是贷款人的姓。
  • LoanAmount 是贷款数额(例如,"500000" )。
  • Duration 是以月为单位的贷款期限。
  • CreditScore 是申请人的信用评分(例如,"310" )。
  • YearlyIncome 是申请人的年收入。
  • Notification 是包含一组电子邮件地址的子组件,它有一个记录接收者电子邮件的属性 EMailAddress (例如,"[email protected]" )。

DDO 对象的属性值在后面将用于创建一个电子邮件通知。


清单 5. 查询内容数据

				
            // retrieve the DDO object with the pid string
            DKDDO ddo  = (DKDDO)dsICM.createDDO(pid);	        
            ddo.retrieve();
            // retrieve the LoanNumber attribute
            short dataId = (short)ddo.dataId("LoanNumber");              
            String loanId = (String)ddo.getData(dataId);
            // retrieve the InterestRate attribute
            dataId = (short)ddo.dataId("InterestRate");                
            float interestRate = ((java.math.BigDecimal)
                                 (ddo.getData(dataId))).floatValue();
            // retrieve the FirstName attribute
            dataId = (short)ddo.dataId("FirstName");              
            String firstName = (String)ddo.getData(dataId);
            // retrieve the LastName attribute
            dataId = (short)ddo.dataId("LastName");              
            String lastName = (String)ddo.getData(dataId);           
            // retrieve the LoanAmount attribute
            dataId = (short)ddo.dataId("LoanAmount");   
            int loanAmount = ((Integer)(ddo.getData(dataId))).intValue();
            // retrieve the Duration attribute
            dataId = (short)ddo.dataId("Duration");   
            short duration = ((Short)(ddo.getData(dataId))).shortValue();
            // retrieve the CreditScore attribute
            dataId = (short)ddo.dataId("CreditScore");   
            short creditScore = ((Short)(ddo.getData(dataId))).shortValue();
            // retrieve the YearlyIncome attribute
            dataId = (short)ddo.dataId("YearlyIncome");   
            int yearlyIncome = ((Integer)(ddo.getData(dataId))).intValue();
            // retrieve the Notification component
            DKChildCollection notification = (DKChildCollection) ddo.getData(
                     (short)ddo.dataId(DKConstant.DK_CM_NAMESPACE_CHILD, 
                     "Notification"));
            dkIterator iterNotification = notification.createIterator();
            int count = notification.cardinality();
            // variable for Notification
            String[] notificationList = new String[count];
            int index = -1;
            // construct the array of e-mail addresses
            while (iterNotification.more()) {
              DKDDO ddoEmail = (DKDDO) iterNotification.next();
              ddoEmail.retrieve();
              dataId = (short) ddoEmail.dataId("EMailAddress");
              String email = (String) ddoEmail.getData(dataId);
              if (email == null || email.trim().equals("")) continue;
              else {
                  index++;              
                  notificationList[index] = email.trim();
              }
            } 

 

执行规则

因为整合体系结构将决策交由一个 ILOG JRules 规则引擎执行,这个定制的事件处理器将用于创建一个 ILOG JRules 连接会话。接着,贷款信息会被传递到规则引擎进行评估。然后评估响应会返回给定制的事件处理器以便执行更进一步处理。

清单 6 显示了 myEventHandler 示例程序中包含执行规则的详细步骤的一部分代码。总而言之,这部分代码执行了以下操作:

  • 创建一个示例程序使用的 J2SE 规则会话。
  • 创建一个允许事件处理器设计规则路径和输入参数的会话请求。
  • 在请求中根据以下语法设置规则路径: "/{RuleApp name}[/{version}]/{ruleset name}[/{version}]"
  • 在请求中设置 borrowerloan 输入参数。
  • 发送请求到 ILOG JRules 规则引擎中执行,然后返回一个包含评估响应的 IlrSessionResponse 对象。


清单 6. 执行规则

				
        // create a J2SE session 
        IlrSessionFactory factory = new IlrJ2SESessionFactory();
        IlrStatelessSession session = factory.createStatelessSession();
        // create a session request
        IlrSessionRequest sessionRequest = factory.createRequest();
        
        //  The rule path is composed of: 
        //      /{RuleApp name}[/{version}]/{ruleset name}[/{version}]
        //
        //  Note that the rule archive is deployed in the file system 
        //  under the res_data subdirectory.
        String rulesetpath = "/miniloanruleapp/1.0/miniloanrules/1.0";

        sessionRequest.setRulesetPath(IlrPath.parsePath(rulesetpath));

        // ensure correct version of the ruleset is taken in account
        sessionRequest.setForceUptodate(true);
        sessionRequest.setTraceEnabled(true);
        sessionRequest.getTraceFilter().setInfoAllFilters(true);

        // set the input parameters for the execution of the rules
        Map inputParameters = new HashMap ();
       
        // miniloan.Borrower borrower = new miniloan.Borrower("Tom", 500, 50000);
        // This expression sets the input parameter to a borrower named Tom
        // with a credit score of 500 and yearly income of 50000.
        miniloan.Borrower borrower = new miniloan.Borrower(lastName+", "+firstName, 
                                                           creditScore, yearlyIncome);

        // miniloan.Loan loan = new miniloan.Loan(500000, 240, 0.05);
        // This expression creates a new loan with an amount of 500000,
        // a duration of 240 months, and an interest rate of 5%.
        miniloan.Loan loan = new miniloan.Loan(loanAmount, duration, interestRate);

        inputParameters.put("borrower", borrower);
        inputParameters.put("loan", loan);

        sessionRequest.setInputParameters(inputParameters);
        // execute the rules
        IlrSessionResponse sessionResponse = session.execute(sessionRequest);
        return sessionResponse;    

 

在过程中移动贷款申请

清单 7 显示了 myEventHandler 示例程序中选择贷款过程中将贷款申请从 SubmitLoan 操作区向前移动的正确路径的一部分代码。如果贷款被批准,它会从 SubmitLoan 操作区移动到 Approved 操作区。如果贷款被拒绝了,它会从 SubmitLoan 操作区移动到 Rejected 操作区。


清单 7. 继续处理贷款申请

				
            // Locate the workpackage referenced by the pid string
            String[] wpList = drICM.listWorkPackagePidStringsWithItem(pid);
            for (int i = 0; i < wpList.length; i++)
            {
                if ((drICM.retrieveWorkPackage(wpList[i], true))
                    .getWorkNodeName().equalsIgnoreCase("SubmitLoan"))
                {
                  if (approved) {
                     // Advance the workpackage to the "Approved" work basket, 
                     // if the workpackage is located at the "SubmitLoan" work basket 
                     // and approved = true.
                     drICM.continueProcess(wpList[i], "Approve", "ICMADMIN");
                  }
                  else
                  {
                     // Advance the workpackage to the "Rejected" work basket, 
                     // if the workpackage is located at the "SubmitLoan" work basket 
                     // and approved = false.
                     drICM.continueProcess(wpList[i], "Reject", "ICMADMIN");
                  }                 
                  ...
                }
                ...                 
            }

 

发送电子邮件通知

清单 8 显示了 myEventHandler 示例程序中使用 JavaMail API 通过简单邮件传输协议(SMTP)发送电子邮件的一部分代码,并显示了程序 sendMail 函数。首先,要指定一个 SMTP 服务器名,并实例化一个会话对象。其次,设置发件人地址和收件人地址数组。这个程序还设置了电子邮件标题、邮件程序和发送日期。最后,程序调用函数 Transport.send 将电子邮件发送到指定的 SMTP 服务器,由服务器将电子邮件发送给接收者。


清单 8. 发送电子邮件通知

				
        Properties props = System.getProperties();
        
        // use SMTP 
        if (mailhost != null) props.put("mail.smtp.host", mailhost);

        // get a Session object
        javax.mail.Session session = javax.mail.Session.getInstance(props, null);
        if (debug) session.setDebug(true);

        // construct the message
        javax.mail.Message msg = new MimeMessage(session);
        // set From address 
        if (from != null)
            msg.setFrom(new InternetAddress(from));
        else
            msg.setFrom();
        
        InternetAddress[] toAddresses = new InternetAddress[to.length];
        for (int i = 0; i < to.length; i++) {
            toAddresses[i] = new InternetAddress(to[i]);
        }
        // set To address
        msg.setRecipients(javax.mail.Message.RecipientType.TO, toAddresses);
        
        // optional
        if (cc != null)
            msg.setRecipients(javax.mail.Message.RecipientType.CC, 
                              InternetAddress.parse(cc, false));
        if (bcc != null)
            msg.setRecipients(javax.mail.Message.RecipientType.BCC, 
                              InternetAddress.parse(bcc, false));
        // set Subject 
        msg.setSubject(subject);
        // set e-mail text
        msg.setText(text);

        msg.setHeader("X-Mailer", mailer);
        msg.setSentDate(new Date());
        // send out the e-mail
        Transport.send(msg);

 

浏览示例贷款申请

本节将通过循序渐进的方式介绍一个特定贷款申请例子来演示示例程序的功能,以及 Content Manager 和 ILOG 之间的整合。

这个例子是从一位贷款代理人创建一个贷款申请文档开始的。文档的创建触发了一个项目创建事件,它通过一个 JMS 队列从事件监听器流到定制的事件处理器。如图 11 所示,这个定制的事件处理器会通过 ILOG JRules 规则引擎决定是否要批准或拒绝这个贷款申请,然后这个事件处理器会根据它被批准或拒绝来继续在贷款过程中流转这个贷款申请,并且发送恰当的电子邮件通知给接收者。


图 11. 贷款场景
图片描述通过 Content Manager / ILOG 整合架构实现的事件流转。

当贷款代理人为他的客户提交了一个贷款申请后,这个提交操作会自动创建一个事件,这个事件会触发定制的事件处理器来处理这个申请。图 12 描述一个完整的输入界面,其中贷款代理人可以输入以下信息:

  • 贷款号: 01-234-5678-8
  • 姓: Doe
  • 名: John
  • 信用评分: 310
  • 年收入: 30000
  • 贷款数额: 500000
  • 利率: 0.05
  • 期限: 240
  • 通知: [email protected]


图 12. 贷款申请信息
填写了上面所示的数据的贷款申请输入界面。

在这个特殊的案例中,根据 ILOG JRules 规则引擎的评估响应,这个贷款申请被拒绝了,因为:

  • 与信用评分相比,债务-收入太高
  • 债务-收入比例太大

结果,定制的事件处理器会将贷款申请从 SubmitLoan 操作区移到 Rejected 操作区。图 13 显示 RejectedWL 工作列表现在包含了这个贷款的贷款申请信息。


图 13. RejectedWL 工作列表
RejectedWL 截图。工作列表包含了贷款号为 01-234-5678-8 的示例贷款申请。

图 14 是一个事件通知例子,在这里它是由定制的事件处理器发送的电子邮件。这个事件通知包含了下面的信息:

  • 事件信息,如主机名、数据库、事件类型、消息 ID 和项目类型
  • 贷款信息,如贷款号、姓、名、信用评分、年收入、贷款数额、利率和期限
  • 带有评估详细信息的审批状态


图 14. 电子邮件通知
由事件处理器生成的一个示例电子邮件截图。

 

结束语

IBM Content Manager Enterprise Edition 支持能整合外部应用的事件基础架构。本文描述了一个场景和一个定制的事件处理器的设计,这个定制的事件处理器整合了 IBM WebSphere ILOG JRules 和 Content Manager。在 下载 小节中提供的示例代码演示了 Content Manager 和 ILOG JRules 之间通过定制的事件处理器实现的交互。通过在快速变化环境中管理业务规则的功能,解决方案开发人员能够在企业内容管理领域开发更高效的业务应用。

 

下载

描述 名字 大小 下载方法
本文样例程序 myEventHandler.zip 10KB HTTP

 

参考资料

学习

 

原文:http://www.ibm.com/developerworks/cn/data/library/techarticles/dm-1005ilogjrulescontentmanager/index.html?ca=drs-

 

 

 

 

 

 

 

你可能感兴趣的:(应用服务器,项目管理,jms,IBM,websphere)