13.代码中调用规则

概述

在URule Pro当中,是不能直接调用具体的规则文件的,我们需要先将定义好的规则文件放到知识包中,然后才可以对规则文件进行测试和调用。

在代码中调用知识包,需要先通过KnowledgeService接口可获取指定的知识包ID对应的构建好的资源包信息,然后通过知识包来创建具体的KnowledgeSession对象,接下来插入相关业务对象,最后执行具体的规则调用。

KnowledgeService接口源码如下:

package com.bstek.urule.runtime.service;
import java.io.IOException;
import com.bstek.urule.runtime.KnowledgePackage;
/**
 * @author Jacky.gao
 * @since 2015年1月28日
 */
public interface KnowledgeService {
    public static final String BEAN_ID="urule.knowledgeService";
    /**
     * 根据给定的资源包ID获取对应的KnowledgePackage对象
     * @param packageId 项目名称加资源包ID,格式为:projectName/packageId
     * @return 返回与给定的资源包ID获取对应的KnowledgePackage对象
     * @throws IOException
     */
    KnowledgePackage getKnowledge(String packageId) throws IOException;
    /**
     * 根据给定的一个或多个资源包ID获取对应的KnowledgePackage对象的集合
     * @param packageIds 资源包ID数组
     * @return 返回与给定的一个或多个资源包ID获取对应的KnowledgePackage对象集合
     * @throws IOException
     */
    KnowledgePackage[] getKnowledges(String[] packageIds) throws IOException;
}

可以看到,这个接口中有两个方法可供使用,一个是给一个知识包ID(格式为:projectName/packageId)返回一个对应的KnowledgePackage对象;另一个是给一个或多个知识包ID,返回一个集合类型的KnowledgePackage对象。在URule Pro当中,对于一个知识包,在使用时引擎会将其构建成KnowledgePackage对象,在这个KnowledgePackage对象中包含了所有由向决策集、决策表、交叉决策表、决策树、评分卡、复杂评分卡以及决策流等文件构建的RuleSet对象,以及由规则流构成的FlowDefinition对象。

在使用getKnowledge方法获取某个指定的package时,要给一个资源包ID,需要注意的是资源包的ID在定义要要包含资源包所在项目名称,格式为:projectName/packageId

通过KnowledgeService接口获取到KnowledgePackage对象后,接下来就可通过KnowledgePackage对象创建com.bstek.urule.runtime.KnowledgeSession对象,这个对象就是引擎提供的与业务数据交互的接口,通过这个接口,可将需要的业务数据对象插入到引擎当中,最后根据需要执行规则或规则流。

package com.bstek.urule.runtime;

import java.io.IOException;
import java.util.List;
import java.util.Map;

import com.bstek.urule.runtime.agenda.AgendaFilter;
import com.bstek.urule.runtime.response.FlowExecutionResponse;
import com.bstek.urule.runtime.response.RuleExecutionResponse;
import com.bstek.urule.runtime.rete.ReteInstance;

public interface KnowledgeSession extends WorkingMemory{
    /**
     * 执行当前WorkMemory中所有满足条件的规则
     * @return 返回一个ExecutionResponse对象,其中包含规则执行耗时,满足条件的规则,执行的规则等信息
     */
    RuleExecutionResponse fireRules();
    /**
     * 对当前WorkMemory中所有满足条件的规则进行过滤执行
     * @param filter 对满足条件的规则进行过滤
     * @return 返回一个ExecutionResponse对象,其中包含规则执行耗时,满足条件的规则,执行的规则等信息
     */
    RuleExecutionResponse fireRules(AgendaFilter filter);
    /**
     * 对当前WorkMemory中所有满足条件的规则进行过滤执行,并向WorkingMemory中设置一个Map的参数对象
     * @param parameters 向WorkingMemory中设置一个Map的参数对象
     * @param filter 对满足条件的规则进行过滤
     * @return 返回一个ExecutionResponse对象,其中包含规则执行耗时,满足条件的规则,执行的规则等信息
     */
    RuleExecutionResponse fireRules(Map<String,Object> parameters,AgendaFilter filter);
    /**
     * 对当前WorkMemory中所有满足条件的规则进行执行,并定义执行的最大数目,超出后就不再执行
     * @param max 执行规则的最大数目
     * @return 返回一个ExecutionResponse对象,其中包含规则执行耗时,满足条件的规则,执行的规则等信息
     */
    RuleExecutionResponse fireRules(int max);
    /**
     * 对当前WorkMemory中所有满足条件的规则进行执行,并定义执行的最大数目,超出后就不再执行,<br>
     * 并向WorkingMemory中设置一个Map的参数对象
     * @param parameters 向WorkingMemory中设置一个Map的参数对象
     * @param max 执行规则的最大数目
     * @return 返回一个ExecutionResponse对象,其中包含规则执行耗时,满足条件的规则,执行的规则等信息
     */
    RuleExecutionResponse fireRules(Map<String,Object> parameters,int max);
    /**
     * 对当前WorkMemory中所有满足条件的规则进行过滤执行,并定义执行数目的最大值
     * @param filter 对满足条件的规则进行过滤
     * @param max 执行规则的最大数目
     * @return 返回一个ExecutionResponse对象,其中包含规则执行耗时,满足条件的规则,执行的规则等信息
     */
    RuleExecutionResponse fireRules(AgendaFilter filter,int max);
    /**
     * 对当前WorkMemory中所有满足条件的规则进行过滤执行,并定义执行数目的最大值,<br>
     * 并向WorkingMemory中设置一个Map的参数对象
     * @param parameters 向WorkingMemory中设置一个Map的参数对象
     * @param filter 对满足条件的规则进行过滤
     * @param max 执行规则的最大数目
     * @return 返回一个ExecutionResponse对象,其中包含规则执行耗时,满足条件的规则,执行的规则等信息
     */
    RuleExecutionResponse fireRules(Map<String,Object> parameters,AgendaFilter filter,int max);
    /**
     * 对当前WorkMemory中所有满足条件的规则进行执行,并向WorkingMemory中设置一个Map的参数对象
     * @param parameters 向WorkingMemory中设置一个Map的参数对象
     * @return 返回一个ExecutionResponse对象,其中包含规则执行耗时,满足条件的规则,执行的规则等信息
     */
    RuleExecutionResponse fireRules(Map<String,Object> parameters);
    /**
     * 根据规则流ID,执行目标规则流
     * @param processId 要执行的规则流ID
     * @return 返回一个ExecutionResponse对象,其中包含规则流执行耗时信息
     */
    FlowExecutionResponse startProcess(String processId);
    /**
     * 根据规则流ID,执行目标规则流,并向WorkingMemory中设置一个Map的参数对象
     * @param processId 要执行的规则流ID
     * @param parameters 向WorkingMemory中设置一个Map的参数对象
     * @return 返回一个ExecutionResponse对象,其中包含规则流执行耗时信息
     */
    FlowExecutionResponse startProcess(String processId,Map<String,Object> parameters);

    /**
     * 执行将日志信息写入到日志文件操作,要看到日志文件我们需要设置urule.debugToFile属性值为true,<br>
     * 同时定义输出文件目录属性urule.defaultHtmlFileDebugPath,这样在urule.debug属性为true情况下就会向这个目录下写入日志文件,<br>
     * 需要的时候,可以通过实现com.bstek.urule.debug.DebugWriter接口定义自己的日志输出文件,这样就可以将日志输出到任何地方
     * @throws IOException 抛出IO异常
     */
    void writeLogFile() throws IOException;

    /**
     * @return 返回对应的知识包集合
     */
    List<KnowledgePackage> getKnowledgePackageList();

    /**
     * @return 返回Rete实例对象集合
     */
    List<ReteInstance> getReteInstanceList();

    /**
     * @return 返回当前缓存的KnowledgeSession Map对象
     */
    Map<String,KnowledgeSession> getKnowledgeSessionMap();

    /**
     * @return 返回当前KnowledgeSession的父,如果不存在则返回null
     */
    KnowledgeSession getParentSession();
}

可以看到KnowledgeSession接口扩展自WorkingMemory接口,WorkingMemory接口源码如下:

package com.bstek.urule.runtime;

import java.util.List;
import java.util.Map;

import com.bstek.urule.runtime.log.LogManager;
import com.bstek.urule.runtime.rete.Context;

public interface WorkingMemory{
    /**
     * 插入一个业务数据对象,对应到规则当中就是一个变量对象
     * @param fact 目标业务数据对象
     * @return 插入是否成功
     */
    boolean insert(Object fact);

    /**
     * 更新一个在当前WorkingMemory中已存在的业务对象,如果对象存在,那么WorkingMemory会重新评估这个对象
     * @param fact 要更新的对象
     * @return 更新是否成功,如果对象不在WorkingMemory中,则返回false
     */
    boolean update(Object fact);
    /**
     * 获取当前WorkingMemory中的某个参数值
     * @param key 参数对应的key值
     * @return 返回具体的值
     */
    Object getParameter(String key);
    /**
     * @return 返回所有的参数对象
     */
    Map<String,Object> getParameters();

    /**
     * @return 返回当前WorkingMemory中所有类型的业务数据对象
     */
    Map<String,Object> getAllFactsMap();

    /**
     * @return 返回插入到当前WorkingMemory中所有业务对象
     */
    List<Object> getFactList();

    /**
     * 根据knowledgePackageWrapper的id返回对应的KnowledgeSession对象
     * @param id knowledgePackageWrapper的id
     * @return 对应的KnowledgeSession对象
     */
    KnowledgeSession getKnowledgeSession(String id);

    /**
     * 将KnowledgeSession对象放入缓存以备下次调用时使用
     * @param id knowledgePackageWrapper的id
     * @param session 对应的KnowledgeSession对象
     */
    void putKnowledgeSession(String id,KnowledgeSession session);

    /**
     * 向当前Session中放入变量
     * @param key 变量Key
     * @param value 变量值
     */
    void setSessionValue(String key,Object value);

    /**
     * 取出当前Session中对应的变量
     * @param key 变量key
     * @return 变量值
     */
    Object getSessionValue(String key);

    /**
     * @return 返回当前SessionValueMap对象
     */
    Map<String,Object> getSessionValueMap();


    /**
     * 激活某个设置了互斥组属性的具体的规则
     * @param activationGroupName 互斥组属性值
     * @param ruleName 规则名
     */
    void activeRule(String activationGroupName,String ruleName);

    /**
     * 激活指定名称的执行组
     * @param groupName 执行组名称
     */
    void activePendedGroup(String groupName);

    /**
     * 激活指定名称的执行组并立即执行执行组规则对应的动作部分
     * @param groupName 执行组名称
     */
    void activePendedGroupAndExecute(String groupName);

    /**
     * 返回当前上下文对象
     * @return 返回当前上下文对象
     */
    Context getContext();

    /**
     * @return 返回当前LogManager对象
     */
    LogManager getLogManager();

    /**
     * @return 返回当前FactManager对象
     */
    FactManager getFactManager();
}

要通过KnowledgePackage对象或这个对象的数组创建一个KnowledgeSession对象,可以通过com.bstek.urule.runtime.KnowledgeSessionFactory类中下面两个静态方法实现:

/**
 * 创建一个普通的KnowledgeSession对象
 * @param knowledgePackage 创建KnowledgeSession对象所需要的KnowledgePackage对象
 * @return 返回一个新的KnowledgeSession对象
 */
public static KnowledgeSession newKnowledgeSession(KnowledgePackage knowledgePackage){
    return new KnowledgeSessionImpl(knowledgePackage);
}

/**
 * 创建一个普通的KnowledgeSession对象
 * @param knowledgePackage 创建KnowledgeSession对象所需要的KnowledgePackage集合对象
 * @return 返回一个新的KnowledgeSession对象
 */
public static KnowledgeSession newKnowledgeSession(KnowledgePackage[] knowledgePackages){
    return new KnowledgeSessionImpl(knowledgePackages);
}

单次调用

由于这两个方法都是静态方法,所以可以直接调用,下面的代码中演示了完整的调用过程:

package tt;
import rete.test.Dept;
import rete.test.Employee;
import com.bstek.urule.Utils;
import com.bstek.urule.runtime.KnowledgePackage;
import com.bstek.urule.runtime.KnowledgeSession;
import com.bstek.urule.runtime.KnowledgeSessionFactory;
import com.bstek.urule.runtime.service.KnowledgeService;
/**
 * @author Jacky.gao
 * @since 2015年3月5日
 */
public class Invoke {
    public void doTest() throws Exception{
        //从Spring中获取KnowledgeService接口实例
        KnowledgeService service=(KnowledgeService)Utils.getApplicationContext().getBean(KnowledgeService.BEAN_ID);
        //通过KnowledgeService接口获取指定的资源包"projectName/test123"
        KnowledgePackage knowledgePackage=service.getKnowledge("projectName/test123");
        //通过取到的KnowledgePackage对象创建KnowledgeSession对象
        KnowledgeSession session=KnowledgeSessionFactory.newKnowledgeSession(knowledgePackage);

        Employee employee=new Employee();
        Dept dept=new Dept();
        dept.setLevel(12);
        employee.setDept(dept);
        employee.setSalary(111000);
        //将业务数据对象Employee插入到KnowledgeSession中
        session.insert(employee);
        //执行所有满足条件的规则
        session.fireRules();
    }
}

在上面的示例当中,获取到KnowledgeSession对象后,向其中插入一个名为Employee业务数据对象,这样引擎在计算时,会直接采用Employee中相关数据,如有条件满足,同时有对Employee中相关数据赋值,那么会直接反映到当前插入的这个Employee对象当中。

在实际使用中,可能还会向KnowledgeSession中添加参数数据(以Map形式添加),对应URule中的参数库文件中定义的信息,引擎计算完成后,我们要通KnowledgeSession中的getParameter来获取具体的参数对象,而不 能通过原添加的Map中获取,如下代码:

package tt;
import java.util.HashMap;
import java.util.Map;
import rete.test.Dept;
import rete.test.Employee;
import com.bstek.urule.Utils;
import com.bstek.urule.runtime.KnowledgePackage;
import com.bstek.urule.runtime.KnowledgeSession;
import com.bstek.urule.runtime.KnowledgeSessionFactory;
import com.bstek.urule.runtime.service.KnowledgeService;
/**
 * @author Jacky.gao
 * @since 2015年3月5日
 */
public class Invoke {
    public void doTest() throws Exception{
        //从Spring中获取KnowledgeService接口实例
        KnowledgeService service=(KnowledgeService)Utils.getApplicationContext().getBean(KnowledgeService.BEAN_ID);
        //通过KnowledgeService接口获取指定的资源包"test123"
        KnowledgePackage knowledgePackage=service.getKnowledge("projectName/test123");
        //通过取到的KnowledgePackage对象创建KnowledgeSession对象
        KnowledgeSession session=KnowledgeSessionFactory.newKnowledgeSession(knowledgePackage);

        Employee employee=new Employee();
        Dept dept=new Dept();
        dept.setLevel(12);
        employee.setDept(dept);
        employee.setSalary(111000);
        //将业务数据对象Employee插入到KnowledgeSession中
        session.insert(employee);
        //执行所有满足条件的规则

        Map<String,Object> parameter=new HashMap<String,Object>();
        parameter.put("count", 10);
        parameter.put("result", true);
        //触发规则时并设置参数
        session.fireRules(parameter);

        //获取计算后的result值,要通过KnowledgeSession,而不能通过原来的parameter对象
        boolean result=(Boolean)session.getParameter("result");
        System.out.println(result);
    }
}

从上面的代码中可以看到,在规则计算完成后,在获取计算后的参数中的result值时,我们并没有用提供参数的parameter,而是通过KnowledgeSession的getParameter来实现,这是因为在向KnowledgeSession设置参数时,引擎会将参数中所有的值取出并放入到引擎中内置的一个Map中,以避免影响原参数的值。所以计算完成后,我们要通过KnowledgeSession的getParameter来获取计算后的参数值。

如果我们的资源包中包含有规则流,那么在插入好相关业务数据对象后,可以通过KnowledgeSession中提供的startProcess来实现规则流的调用,如下面的代码所示:

package tt;
import java.util.HashMap;
import java.util.Map;
import rete.test.Dept;
import rete.test.Employee;
import com.bstek.urule.Utils;
import com.bstek.urule.runtime.KnowledgePackage;
import com.bstek.urule.runtime.KnowledgeSession;
import com.bstek.urule.runtime.KnowledgeSessionFactory;
import com.bstek.urule.runtime.service.KnowledgeService;
/**
 * @author Jacky.gao
 * @since 2015年3月5日
 */
public class Invoke {
    public void doTest() throws Exception{
        //从Spring中获取KnowledgeService接口实例
        KnowledgeService service=(KnowledgeService)Utils.getApplicationContext().getBean(KnowledgeService.BEAN_ID);
        //通过KnowledgeService接口获取指定的资源包"test123"
        KnowledgePackage knowledgePackage=service.getKnowledge("projectName/test123");
        //通过取到的KnowledgePackage对象创建KnowledgeSession对象
        KnowledgeSession session=KnowledgeSessionFactory.newKnowledgeSession(knowledgePackage);

        Employee employee=new Employee();
        Dept dept=new Dept();
        dept.setLevel(12);
        employee.setDept(dept);
        employee.setSalary(111000);
        //将业务数据对象Employee插入到KnowledgeSession中
        session.insert(employee);
        //执行所有满足条件的规则

        Map<String,Object> parameter=new HashMap<String,Object>();
        parameter.put("count", 10);
        parameter.put("result", true);

        //开始规则流并设置参数
        session.startProcess("flow-test",parameter);

        //获取计算后的result值,要通过KnowledgeSession,而不能通过原来的parameter对象
        boolean result=(Boolean)session.getParameter("result");
        System.out.println(result);
    }
}

在URule Pro当中,规则流中是不存在人工任务的,也就是说规则流的执行是一次性完成的,这点与包含人工任务的工作流引擎不同,比如UFLO,在UFLO中有人工任务,所以开启流程实例后可能需要多次完成人工任务才能完成一个流程实例。

使用GeneralEntity传递值

在代码中调用知识包,对于需要插入到规则中的变量对象,可能当前环境里并不存在,或者说这种变量的属性是动态可变的,对于这两种我们可以使用URule Pro中提供的GeneralEntity来代替实际的业务对象。

GeneralEntity扩展自HashMap,所以可以通过Map的put方法将目标对象的属性及其值在代码中逐个填入,同时GeneralEntity实例化时要求必须要提供一个String类型的构造参数,这个String类型的值表示的就是目标业务对象的完整类名。 上面的通过startProcess来实现规则流的调用代码示例,通过使用GeneralEntity来代替实际业务对象,代码就是下面的样子:

package tt;
import java.util.HashMap;
import java.util.Map;
import com.bstek.urule.Utils;
import com.bstek.urule.model.GeneralEntity;
import com.bstek.urule.runtime.KnowledgePackage;
import com.bstek.urule.runtime.KnowledgeSession;
import com.bstek.urule.runtime.KnowledgeSessionFactory;
import com.bstek.urule.runtime.service.KnowledgeService;
/**
 * @author Jacky.gao
 * @since 2015年3月5日
 */
public class Invoke {
    public void doTest() throws Exception{
        //从Spring中获取KnowledgeService接口实例
        KnowledgeService service=(KnowledgeService)Utils.getApplicationContext().getBean(KnowledgeService.BEAN_ID);
        //通过KnowledgeService接口获取指定的资源包"test123"
        KnowledgePackage knowledgePackage=service.getKnowledge("projectName/test123");
        //通过取到的KnowledgePackage对象创建KnowledgeSession对象
        KnowledgeSession session=KnowledgeSessionFactory.newKnowledgeSession(knowledgePackage);

        GeneralEntity employee=new GeneralEntity("rete.test.Employee");
        GeneralEntity dept=new GeneralEntity("rete.test.Dept");
        dept.setLevel(12);
        employee.setDept(dept);
        employee.setSalary(111000);
        //将业务数据对象Employee插入到KnowledgeSession中
        session.insert(employee);
        //执行所有满足条件的规则

        Map<String,Object> parameter=new HashMap<String,Object>();
        parameter.put("count", 10);
        parameter.put("result", true);

        //开始规则流并设置参数
        session.startProcess("flow-test",parameter);

        //获取计算后的result值,要通过KnowledgeSession,而不能通过原来的parameter对象
        boolean result=(Boolean)session.getParameter("result");
        System.out.println(result);
    }
}

在上面的代码当中,我们就通过GeneralEntity来代替实际的Employee和Dept对象插入到规则当中,运行后我们发现其结果与实际的Employee和Dept对象插入到规则当中运行结果完全一致。

实际上,在URule Pro提供的快速测试、基于JSON的快速测试、防真测试三种测试工具中以及Rest服务里,实际在调用规则传递变量时采用的都是GeneralEntity来代替实际的业务对象, 所以如果我们在代码中调用业务规则实际业务对象不存在时,也可以采用GeneralEntity实现业务数据的传递。

批处理支持

实际业务当中,我们除了会做单条规则计算外,还有可能需要运行规则引擎来处理一大批数据,这些数据可能有几万条,几十万条,甚至更多。在这种情况下,如果我们还是采用普通的KnowledgeSession在一个线程里处理大批量数据的话,那么引擎还是只能在当前线程里运行,这样就会需要很长的时间才能可能将这几十万条甚至更多的数据处理完成,在这个时候,为了充分利用服务器较强的CPU性能,我们可以使用BatchSession利用多线程并行处理这些数据。顾名思义BatchSession是用来做批处理任务的会话对象,它是 URule Pro当中提供的多线程并行处理大批量业务数据的规则会话对象。

要得到一个BatchSession对象,我们也需要提供一个或多个KnowledgePackage对象,获取KnowledgePackage对象的方法与上面介绍的方法相同,有了KnowledgePackage对象后,就可以利用KnowledgeSessionFactory类创建一个BatchSession对象。

在com.bstek.urule.runtime.KnowledgeSessionFactory类中除上面提到的两个构建KnowledgeSession的静态方法外,还提供了另外八个可用于构建BatchSession的静态方法,其源码如下:

/**
     * 创建一个用于批处理的BatchSession对象,这里默认将开启10个普通的线程池来运行提交的批处理任务,默认将每100个任务放在一个线程里处理
     * @param knowledgePackage 创建BatchSession对象所需要的KnowledgePackage对象
     * @return 返回一个新的BatchSession对象
     */
    public static BatchSession newBatchSession(KnowledgePackage knowledgePackage){
        return new BatchSessionImpl(knowledgePackage,BatchSession.DEFAULT_THREAD_SIZE,BatchSession.DEFAULT_BATCH_SIZE);
    }

    /**
     * 创建一个用于批处理的BatchSession对象,第二个参数来指定线程池中可用线程个数,默认将每100个任务放在一个线程里处理
     * @param knowledgePackage 创建BatchSession对象所需要的KnowledgePackage对象
     * @param threadSize 线程池中可用的线程个数
     * @return 返回一个新的BatchSession对象
     */
    public static BatchSession newBatchSessionByThreadSize(KnowledgePackage knowledgePackage,int threadSize){
        return new BatchSessionImpl(knowledgePackage,threadSize,BatchSession.DEFAULT_BATCH_SIZE);
    }

    /**
     * 创建一个用于批处理的BatchSession对象,这里默认将开启10个普通的线程池来运行提交的批处理任务,第二个参数用来决定单个线程处理的任务数
     * @param knowledgePackage 创建BatchSession对象所需要的KnowledgePackage对象
     * @param batchSize 单个线程处理的任务数
     * @return 返回一个新的BatchSession对象
     */
    public static BatchSession newBatchSessionByBatchSize(KnowledgePackage knowledgePackage,int batchSize){
        return new BatchSessionImpl(knowledgePackage,BatchSession.DEFAULT_THREAD_SIZE,batchSize);
    }

    /**
     * 创建一个用于批处理的BatchSession对象,第二个参数来指定线程池中可用线程个数,第三个参数用来决定单个线程处理的任务数
     * @param knowledgePackage 创建BatchSession对象所需要的KnowledgePackage对象
     * @param threadSize 线程池中可用的线程个数
     * @param batchSize 单个线程处理的任务数
     * @return 返回一个新的BatchSession对象
     */
    public static BatchSession newBatchSession(KnowledgePackage knowledgePackage,int threadSize,int batchSize){
        return new BatchSessionImpl(knowledgePackage,threadSize,batchSize);
    }

    /**
     * 创建一个用于批处理的BatchSession对象,这里默认将开启10个普通的线程池来运行提交的批处理任务,默认将每100个任务放在一个线程里处理
     * @param knowledgePackage 创建BatchSession对象所需要的KnowledgePackage集合对象
     * @return 返回一个新的BatchSession对象
     */
    public static BatchSession newBatchSession(KnowledgePackage[] knowledgePackages){
        return new BatchSessionImpl(knowledgePackages,BatchSession.DEFAULT_THREAD_SIZE,BatchSession.DEFAULT_BATCH_SIZE);
    }

    /**
     * 创建一个用于批处理的BatchSession对象,第二个参数来指定线程池中可用线程个数,默认将每100个任务放在一个线程里处理
     * @param knowledgePackages 创建BatchSession对象所需要的KnowledgePackage集合对象
     * @param threadSize 线程池中可用的线程个数
     * @return 返回一个新的BatchSession对象
     */
    public static BatchSession newBatchSessionByThreadSize(KnowledgePackage[] knowledgePackages,int threadSize){
        return new BatchSessionImpl(knowledgePackages,threadSize,BatchSession.DEFAULT_BATCH_SIZE);
    }

    /**
     * 创建一个用于批处理的BatchSession对象,这里默认将开启10个普通的线程池来运行提交的批处理任务,第二个参数用来决定单个线程处理的任务数
     * @param knowledgePackages 创建BatchSession对象所需要的KnowledgePackage集合对象
     * @param batchSize 单个线程处理的任务数
     * @return 返回一个新的BatchSession对象
     */
    public static BatchSession newBatchSessionByBatchSize(KnowledgePackage[] knowledgePackages,int batchSize){
        return new BatchSessionImpl(knowledgePackages,BatchSession.DEFAULT_THREAD_SIZE,batchSize);
    }

    /**
     * 创建一个用于批处理的BatchSession对象,第二个参数来指定线程池中可用线程个数,第三个参数用来决定单个线程处理的任务数
     * @param knowledgePackages 创建BatchSession对象所需要的KnowledgePackage集合对象
     * @param threadSize 线程池中可用的线程个数
     * @param batchSize 单个线程处理的任务数
     * @return 返回一个新的BatchSession对象
     */
    public static BatchSession newBatchSession(KnowledgePackage[] knowledgePackages,int threadSize,int batchSize){
        return new BatchSessionImpl(knowledgePackages,threadSize,batchSize);
    }

前面介绍规则流中的决策节点时,了解到决策节点中支持百分比分流,这种百分比分流就要求必须是在使用BatchSession处理一批数据的时候,或者是一个用一个普通的KnowledgeSession一次性处理多条数据才有效,否则规则流只会走比例最高的那个分支。

BatchSession接口比较简单,它只定义了两个方法:

/**
     * 添加一个具体要执行Business对象
     * @param business Business对象实例
     */
    void addBusiness(Business business);


    /**
     * 等待线程池中所有业务线程执行完成,在进行批处理操作时一定要以此方法作为方法调用结尾
     */
    void waitForCompletion();

可以看到,它可以接收若干个名为com.bstek.urule.runtime.Business接口实例,Business接口比较简单,它只有一个方法:

package com.bstek.urule.runtime;
/**
 * @author Jacky.gao
 * @since 2015年9月29日
 */
public interface Business {
    void execute(KnowledgeSession session);
}

在Business实现类中,我们的业务写在execute方法当中,在这个方法中,只有一个KnowledgeSession对象,这个session对象就是我们与规则引擎操作的对象,示例代码如下:

//从Spring中获取KnowledgeService接口实例
KnowledgeService service=(KnowledgeService)Utils.getApplicationContext().getBean(KnowledgeService.BEAN_ID);
//通过KnowledgeService接口获取指定的资源包"aaa"
KnowledgePackage knowledgePackage=service.getKnowledge("projectName/aaa");

//通过取的KnowledgePackage对象创建BatchSession对象,在这个对象中,我们将开启5个线程,每个线程最多放置10个Bussiness接口实例运行
BatchSession batchSession=KnowledgeSessionFactory.newBatchSession(knowledgePackage, 5, 10);

for(int i=0;i<100;i++){
    batchSession.addBusiness(new Business(){
        @Override
        public void execute(KnowledgeSession session) {
            Employee employee=new Employee();
            employee.setSalary(11080);
            //将业务数据对象Employee插入到KnowledgeSession中
            session.insert(employee);
            session.startProcess("demo");
        }
    });
}
//等待所有的线程执行完成,对于BatchSession调用来说,此行代码必不可少,否则将导致错误
batchSession.waitForCompletion();

其它API

这里罗列了几个非常用的api,这些api涉及到操作知识库里的文件、操作知识包、查看文件引用等相关api,这里罗列出来,我们可以根据业务需要,通过调用这些api可实现在业务系统中操作知识包或知识库里的文件等。

这些接口实现都配置在Spring当中,如果我们需要在业务代码中调用这些接口方法,那么可以通过Spring的ApplicationContext引用它们,比如要使用RepositoryService接口,那么可以通过下面的代码获取RepositoryService接口 在Spring中的对象实例引用:

KnowledgeService service=(KnowledgeService)Utils.getApplicationContext().getBean(RepositoryService.BEAN_ID);

上面的代码中的RepositoryService.BEAN_ID是一个定义在RepositoryService接口中的静态常量,用于表示它在Spring中定义的Bean的ID。其实不仅是RepositoryService接口, 后面要介绍除了RefactorService(RefactorService对象实例要通过RepositoryService接口中getRefactorService()方法获取)外, ReferenceService、KnowledgePackageRepositoryService等都有一个名为BEAN_ID的静态常量,也都可以通过它来获取它们在Spring中定义的Bean的ID值。

RepositoryService

RepositoryService接口中定义了针对知识库文件操作的大部分方法,其源码如下:

package com.bstek.urule.console.repository;

import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;

import com.bstek.urule.console.Principal;
import com.bstek.urule.console.repository.model.FileType;
import com.bstek.urule.console.repository.model.RepositoryFile;
import com.bstek.urule.console.repository.refactor.Item;
import com.bstek.urule.console.repository.refactor.RefactorService;

public interface RepositoryService extends RepositoryReader{
    public static final String BEAN_ID="urule.repositoryService";
    /**
     * @param filePath 文件路径
     * @return 返回文件是否存在
     * @throws Exception 抛出异常
     */
    boolean fileExistCheck(String filePath) throws Exception;
    /**
     * @param projectName 项目名称
     * @param user 创建人
     * @param classify 是否采用分类方式返回创建好的项目结果
     * @return 返回创建好的项目结果对象RepositoryFile
     * @throws Exception 抛出异常
     */
    RepositoryFile createProject(String projectName,Principal user,boolean classify) throws Exception;
    /**
     * 创建目录
     * @param path 新目录的路径
     * @param user 创建人
     * @throws Exception 抛出异常
     */
    void createDir(String path,Principal user) throws Exception;
    /**
     * 创建文件
     * @param path 文件路径
     * @param content 文件内容
     * @param user 创建人
     * @throws Exception 抛出异常
     */
    void createFile(String path,String content,Principal user) throws Exception;
    /**
     * 保存文件
     * @param path 文件路径
     * @param content 文件内容
     * @param newVersion 是否以版本形式保存
     * @param versionComment 版本内容,如果不是以版本形式保存,这里给null即可
     * @param user 保存文件的用户
     * @throws Exception 抛出异常
     */
    void saveFile(String path,String content,boolean newVersion,String versionComment,Principal user) throws Exception;
    /**
     * 删除文件
     * @param path 要删除的文件路径
     * @param user 操作的用户
     * @throws Exception 抛出异常
     */
    void deleteFile(String path,Principal user)throws Exception;
    /**
     * 锁定指定文件
     * @param path 文件路径
     * @param user 操作的用户
     * @throws Exception 抛出异常
     */
    void lockPath(String path,Principal user) throws Exception;
    /**
     * 解锁文件
     * @param path 文件路径
     * @param user 操作的用户
     * @throws Exception 抛出异常
     */
    void unlockPath(String path,Principal user) throws Exception;
    /**
     * 加载项目
     * @param project 项目名
     * @param user 操作的用户,会根据这个用户权限过滤加载后的文件
     * @param classify 返回的项目是否以分类形式展示
     * @param types 要加载的文件类型
     * @param searchFileName 要搜索的文件文件名
     * @return 返回项目的Repository对象
     * @throws Exception 抛出异常
     */
    Repository loadRepository(String project,Principal user,boolean classify,FileType[] types,String searchFileName) throws Exception;
    /**
     * 文件重命名
     * @param path 文件原路径
     * @param newPath 新路径
     * @throws Exception 抛出异常
     */
    void fileRename(String path, String newPath) throws Exception;
    /**
     * 将指定项目以xml形式导出
     * @param projectPath 要导出项目
     * @param outputStream 导入后的项目要输出的目的地
     * @throws Exception 抛出异常
     */
    void exportXml(String projectPath,OutputStream outputStream)throws Exception;
    /**
     * 导出所有项目
     * @param outputStream 导入后要输出的目的地
     * @throws Exception 抛出异常
     */
    void exportXml(OutputStream outputStream)throws Exception;
    /**
     * 导入xml文件
     * @param inputStream要导入的xml的数据源
     * @param overwrite 是否覆盖已存在的项目
     * @throws Exception 抛出异常
     */
    void importXml(InputStream inputStream,boolean overwrite)throws Exception;
    /**
     * @param project 项目名称
     * @return 返回指定项目的目录信息
     * @throws Exception 抛出异常
     */
    List<RepositoryFile> getDirectories(String project) throws Exception;
    /**
     * @param project 项目名
     * @param all 是否返回所有的客户端配置信息,包含<b>禁用</b>状态的客户信息
     * @return 返回客户端信息列表
     * @throws Exception 抛出异常
     */
    List<ClientConfig> loadClientConfigs(String project,boolean all)throws Exception;
    /**
     * @param path 项目路径
     * @return 返回项目名,会去除项目名前可能存在的/
     */
    String getProject(String path);
    /**
     * @return 返回项目中配置的ClientProvider接口实现,如果有的话
     */
    ClientProvider getClientProvider();
    /**
     * 重构库文件内容
     * @param path 是重构的库文件路径
     * @param item 要重构的内容
     * @throws Exception 抛出异常
     */
    void refactorContent(String path,Item item) throws Exception;
    /**
     * @param path 文件路径
     * @return 返回文件是否存在
     * @throws Exception 抛出异常
     */
    boolean fileExist(String path) throws Exception;
    /**
     * @param project 指定的项目
     * @param principal 操作人
     * @return 返回指定项目中所有的变量库文件内容信息列表
     * @throws Exception 抛出异常
     */
    List<ProjectVariable> loadProjectLibraries(String project,Principal principal) throws Exception;

    /**
     * @return 返回当前的RefactorService实例对象
     */
    RefactorService getRefactorService();
}

RepositoryService接口扩展自RepositoryReader,在RepositoryReader接口中定义了一些用于读取知识库文件的相关操作,其源码如下:

package com.bstek.urule.console.repository;

import java.io.InputStream;
import java.util.List;

import com.bstek.urule.console.repository.model.RepositoryFile;
import com.bstek.urule.console.repository.model.VersionFile;

public interface RepositoryReader {
    /**
     * 加载指定的companyId下创建的所有的项目信息
     * @param companyId 指定的公司ID
     * @return 返回所有的项目信息
     * @throws Exception 抛出异常
     */
    List<RepositoryFile> loadProjects(String companyId) throws Exception;
    /**
     * 读取指定的最新版本的文件
     * @param path 文件考路径
     * @return 返回文件内容
     * @throws Exception 抛出异常
     */
    InputStream readFile(String path) throws Exception;

    /**
     * 获取指定路径文件的所有版本信息
     * @param path 文件路径
     * @return 返回版本信息列表
     * @throws Exception 抛出异常
     */
    List<VersionFile> getVersionFiles(String path) throws Exception;

    /**
     * 读取指定版本文件
     * @param path 文件路径
     * @param version 文件版本号
     * @return 返回文件内容
     * @throws Exception 抛出异常
     */
    InputStream readFile(String path, String version) throws Exception;
}

RefactorService

RefactorService的作用是用于实现对规则文件名以及库文件中定义的信息值的重命名操作,也就是我们通常说的重构操作。RefactorService接口比较特殊, 它的实例要通过RepositoryService接口中提供的getRefactorService()方法获取,RefactorService接口源码如下:

package com.bstek.urule.console.repository.refactor;
import com.bstek.urule.console.repository.Repository;

public interface RefactorService {
    /**
     * 重构文件名操作,路径要以jcr:开头
     * @param oldPath 旧路径
     * @param newPath 新路径
     * @throws Exception 抛出异常
     */
    void refactorFile(String oldPath,String newPath) throws Exception;
    /**
     * 重构指定Repository下所有文件,路径要以jcr:开头
     * @param oldPath 旧路径
     * @param newPath 新路径
     * @param repo 指定的Repository
     * @throws Exception 抛出异常
     */
    void refactorFile(String oldPath,String newPath,Repository repo) throws Exception;
    /**
     * 重构某个变量库文件或常量库文件或参数库文件或动作库文件下某具体值
     * @param path 具体值所在库文件路径,路径要以jcr:开头
     * @param item 要重构的库文件内容的具体值
     * @throws Exception 抛出异常
     */
    void refactorItem(String path,Item item) throws Exception;
}

ReferenceService

顾名思义,ReferenceService接口作用是用于查看库文件以及库文件定义在其它文件中引用的接口,基实例定义在Spring中,在Spring中的Bean的ID为ReferenceService.BEAN_ID, ReferenceService接口源码如下:

package com.bstek.urule.console.repository.reference;

import java.util.List;

public interface ReferenceService {
    public static final String BEAN_ID="urule.referenceService";
    /**
     * @param path 被引用的文件路径
     * @param item 包含的内容对象
     * @return 返回引用了当前库文件指定内容的其它文件列表
     * @throws Exception 抛出异常
     */
    List<RefFile> loadReferenceFiles(String path,SearchItem item) throws Exception;
    /**
     * @param path 被引用的文件路径
     * @return 返回引用了当前文件的其它文件列表
     * @throws Exception 抛出异常
     */
    List<RefFile> loadReferenceFiles(String path) throws Exception;
}

KnowledgePackageRepositoryService

KnowledgePackageRepositoryService操作的对象是定义在项目中的知识包对象,基实例定义在Spring中,在Spring中的Bean的ID为KnowledgePackageRepositoryService.BEAN_ID,其源码如下:

package com.bstek.urule.console.repository;

import java.io.InputStream;
import java.util.List;
import java.util.Map;

import com.bstek.urule.console.repository.model.ResourcePackage;
import com.bstek.urule.console.repository.model.VersionFile;
import com.bstek.urule.model.library.variable.VariableCategory;
import com.bstek.urule.runtime.KnowledgePackage;

public interface KnowledgePackageRepositoryService {
    /**
     * 根据指定格式的知识包信息,获取当前知识包的所有已发布的版本信息
     * @param packageInfo 知识包信息,格式为:项目名/知识包ID,如:testProject/testPackage
     * @return 返回所有已发布的知识包版本集合信息
     */
    List<VersionFile> getKnowledgePackges(String packageInfo);
    /**
     * 返回已发布的知识包指定版本对应的文件信息
     * @param packageInfo 知识包信息,格式为:项目名/知识包ID,如:testProject/testPackage
     * @param version 指定的已发布的知识包版本
     * @return 返回对应的文件信息Map
     */
    Map<String,Object> loadKnowledgePackageFiles(String packageInfo,String version);
    /**
     * @param packageInfo 知识包信息,格式为:项目名/知识包ID,如:testProject/testPackage
     * @return 返回已发布的当前处于激活状态下的知识包的版本信息
     */
    VersionFile getActivedKnowledgePackge(String packageInfo);
    /**
     * @param packageInfo 知识包信息,格式为:项目名/知识包ID,如:testProject/testPackage
     * @param version 指定的已发布的版本
     * @return 返回对应版本的已发布的知识包编译后的byte信息
     */
    byte[] getKnowledgePackgeBytes(String packageInfo,String version);
    /**
     * @param packageInfo 知识包信息,格式为:项目名/知识包ID,如:testProject/testPackage
     * @param version 指定的已发布的版本
     * @return 返回对应版本的已发布的知识包编译后的KnowledgePackage对象信息
     */
    KnowledgePackage getKnowledgePackge(String packageInfo,String version);
    /**
     * @param packageInfo 知识包信息,格式为:项目名/知识包ID,如:testProject/testPackage
     * @param version 指定的已发布的版本
     * @return 返回对应版本的已发布的知识包中包含的变量库信息集合
     */
    List<VariableCategory> getKnowledgePackgeLib(String packageInfo,String version);
    /**
     * @param packageInfo 知识包信息,格式为:项目名/知识包ID,如:testProject/testPackage
     * @param version 指定的已发布的版本
     * @return 返回对应版本的已发布的知识包编译后的InputStream对象信息
     */
    InputStream getKnowledgePackgeData(String packageInfo,String version);
    /**
     * 删除指定版本已发布的知识包信息
     * @param packageInfo 知识包信息,格式为:项目名/知识包ID,如:testProject/testPackage
     * @param version 指定的已发布的版本
     */
    void removeKnowledgePackge(String packageInfo,String version);
    /**
     * 删除指定知识所有已发布的知识包信息
     * @param packageInfo 知识包信息,格式为:项目名/知识包ID,如:testProject/testPackage
     */
    void removeDeployKnowledgePackge(String packageInfo);
    /**
     * 将指定版本的已发布的知识包定义为处于激活状态的知识包
     * @param packageInfo 知识包信息,格式为:项目名/知识包ID,如:testProject/testPackage
     * @param version 指定的已发布的版本
     */
    void activeKnowledgePackage(String packageInfo,String version);
    /**
     * 修改指定知识的状态,将状态改为<b>停用</b>或<b>启用</b>
     * @param packageInfo 知识包信息,格式为:项目名/知识包ID,如:testProject/testPackage
     * @param state true表示<b>启用</b>,false表示<b>停用</b
     */
    void knowledgePackageStateChange(String packageInfo,boolean state);
    /**
     * 项目重命名时触发修改当前项目下知识包的存储路径
     * @param oldName 旧项目名
     * @param newName 新项目名
     */
    void projectRename(String oldName,String newName);
    /**
     * 重置知识包时间戳
     * @param project 项目名称
     */
    void resetProjectResourcePackagesTag(String project);
    /**
     * @param project 项目名称
     * @return 返回指定项目的知识包时间戳
     */
    String getProjectResourcePackagesTag(String project);
    /**
     * @param project 项目名称
     * @return 返回指定项目的知识包信息集合
     */
    List<ResourcePackage> loadProjectResourcePackages(String project);

    /**
     * @param file 要查找的文件
     * @return 返回包含当前文件的知识包列表
     */
    List<ResourcePackage> loadResourcePackagesByFile(String file);
    /**
     * 发布一个新的知识包版本
     * @param packageInfo 知识包信息,格式为:项目名/知识包ID,如:testProject/testPackage
     * @param knowledgePackage 发布的编译后知识包对象
     * @param comment 知识包版本备注
     * @param createUser 发布人
     * @param active 是否激活
     */
    void saveKnowledgePackage(String packageInfo,KnowledgePackage knowledgePackage,String comment,String createUser,boolean active);
}

AuthorityRepositoryService

AuthorityRepositoryService接口实现针对规则项目及文件的权限配置操作,基实例定义在Spring中,在Spring中的Bean的ID为AuthorityRepositoryService.BEAN_ID,其源码如下:

package com.bstek.urule.console.repository.authority;

import java.util.List;

import com.bstek.urule.console.Principal;
import com.bstek.urule.console.repository.OperateType;

public interface AuthorityRepositoryService {
    /**
     * 保存授权信息
     * @param p 当前操作的登录对象信息
     * @param user 要授权的对象的用户名
     * @param authority 要授权的信息
     * @param type 操作类型
     * @throws Exception 抛出异常
     */
    void saveAuthority(Principal p,String user,Authority authority,OperateType type) throws Exception;
    /**
     * 加载指定companyId下所有的权限信息
     * @param companyId 指定的companyId,应与当前登录用户的companyId保持一致
     * @return 返回权限信息集合
     * @throws Exception 抛出异常
     */
    List<AuthorityUnit> loadAuthorityUnits(String companyId) throws Exception;
    /**
     * 重置某个companyId下对应的权限信息的时间戳,系统内部调用
     * @param companyId 指定的companyId
     * @return 返回新的重置后的时间戳
     * @throws Exception 抛出异常
     */
    long resetAuthorityTag(String companyId) throws Exception;
    /**
     * 检查指定companyId下权限信息有没有变化,系统内部调用
     * @param companyId 指定的companyId
     * @param timestamp 给定的时间戳
     * @return 如果有变化返回新的时间戳,没变化则返回0
     * @throws Exception 抛出异常
     */
    long check(String companyId,long timestamp) throws Exception;
    /**
     * 保存权限信息文件,系统内部调用
     * @param content 文件xml格式内容
     * @param user 保存操作的调用人
     * @return 返回保存后的时间戳
     * @throws Exception 抛出异常
     */
    long saveAuthoritiesFile(String content,Principal user) throws Exception;
}

RepositoryInteceptor

RepositoryInteceptor接口的作用是拦截对所有规则文件的的添加、修改、删除操作,实现好该接口后配置到spring中即可生效,其源码内容如下:

package com.bstek.urule.console;

public interface RepositoryInteceptor {
    void readFile(String file);
    void saveFile(String file,String content);
    void createFile(String file,String content);
    void deleteFile(String file);
    void renameFile(String oldFileName,String newFileName);
    void createDir(String dir);
    void createProject(String project);
}

KnowledgePackagePublishListener

KnowledgePackagePublishListener接口是用来拦截发布知识包操作,实现好该接口后配置到spring中即可生效,其源码内容如下:

package com.bstek.urule.console.servlet.respackage;

import com.bstek.urule.runtime.KnowledgePackage;

public interface KnowledgePackagePublishListener {
    void beforePublish(String packageId,String comment,boolean active);
    void afterPublish(KnowledgePackage knowledgePackage,String comment,boolean active);
    void beforeActive(String packageId,String version);
    void afterActive(KnowledgePackage knowledgePackage,String version);
}

results matching ""

    No results matching ""