1

介绍

6.0引入了一种新的配置和约定方法来构建知识库,而不是在5.x中使用编程构建器方法,构建器仍然可以使用,因为它用于工具集成。

构建现在使用Maven,并与Maven实践保持一致,KIE项目或模块只是一个Maven Java项目或模块,另外还有一个元数据文件META-INF/kmodule.xml,kmodule.xml文件是选择资源到知识库并配置这些知识库和会话的描述符,还有通过Spring和OSGi蓝图提供的XML支持。

虽然标准Maven可以构建和打包KIE资源,但它不会在构建时提供验证,有一个Maven插件,推荐使用它来获得构建时验证,插件还生成许多类,使运行时加载速度更快。

示例项目布局和Maven POM描述符在屏幕截图中进行了说明:

defaultkiesession.png

KIE使用默认值来最小化配置的数量,一个空的kmodule.xml是最简单的配置,必须始终有一个kmodule.xml文件,即使是空的,因为它用于发现JAR及其内容。

Maven可以通过“mvn install”将一个KieModule部署到本地机器上,本地机器上的所有其他应用程序都使用它,或者它可以“mvn deploy”将KieModule推到远程Maven存储库中,构建应用程序将拉取KieModule并在过程中填充本地Maven存储库。

maven.png

JAR可以以两种方式部署,要么像Maven依赖项清单中的其他JAR一样添加到类路径,要么在运行时动态加载它们,KIE将扫描类路径以找到所有包含kmodule.xml的jar文件,每个发现的JAR都由KieModule接口表示。术语类路径KieModule和动态KieModule用于引用这两种加载方法,虽然动态模块支持并行版本控制,但是类路径模块不支持,此外,一旦模块在类路径上,就不能动态加载其他版本。

API的详细参考资料将包含在下一节中,没有耐心的人可以直接跳转到示例部分,这对于不同的用例来说是相当容易理解的。

构建

builder.png

创建和构建一个Kie项目

Kie项目具有普通Maven项目的结构,唯一的特点是包含kmodule.xml文件,以声明的方式定义可以从中创建的KieBaseKieSession,这个文件必须放在Maven项目的resources/META-INF文件夹中,而所有其他Kie工件,如DRL或Excel文件,必须存储在resources文件夹中或其下的任何其他子文件夹中。

由于所有配置方面都提供了有意义的默认值,所以最简单的kmodule.xml文件可以仅包含一个空的kmodule标记,如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns="http://www.drools.org/xsd/kmodule"/>

这样,kmodule将包含一个默认的KieBase,存储在resources文件夹或其任何子文件夹下的所有Kie资源都将被编译并添加到其中,为了触发这些工件的构建,为它们创建一个KieContainer就足够了。

KieContainer.png

对于这个简单的例子,创建一个KieContainer就足够了,它可以读取从类路径构建的文件:

KieServices kieServices = KieServices.Factory.get();
KieContainer kContainer = kieServices.getKieClasspathContainer();

KieServices是一个可以访问所有Kie构建和运行时设施的接口:

KieServices.png

通过这种方式,所有Java源和Kie资源都被编译并部署到KieContainer中,而KieContainer使其内容在运行时可用。

kmodule.xml文件

如前一节所述,kmodule.xml文件是可以声明地配置可以从KIE项目创建的KieBaseKieSession的地方。

特别是KieBase是应用程序所有知识定义的存储库,它将包含规则、流程、函数和类型模型。KieBase本身不包含数据,相反,会话是从KieBase创建的,KieBase中可以插入数据,从KieBase中可以启动流程实例。创建KieBase可能很重,而创建会话非常轻,因此建议在可能的情况下缓存KieBase,以允许重复创建会话。然而,终端用户通常不必担心,因为KieContainer已经自动提供了这种缓存机制。

KieBase.png

相反,KieSession存储并在运行时数据上执行,它是由KieBase创建的,如果在kmodule.xml文件中定义了它,则更容易从KieContainer直接创建。

KieSession.png

kmodule.xml允许定义和配置一个或多个KieBase,并为每个KieBase创建所有不同的KieSession,如下面的示例所示:

<kmodule xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://www.drools.org/xsd/kmodule">
  <configuration>
    <property key="drools.evaluator.supersetOf" value="org.mycompany.SupersetOfEvaluatorDefinition"/>
  </configuration>
  <kbase name="KBase1" default="true" eventProcessingMode="cloud" equalsBehavior="equality" declarativeAgenda="enabled" packages="org.domain.pkg1">
    <ksession name="KSession2_1" type="stateful" default="true"/>
    <ksession name="KSession2_2" type="stateless" default="false" beliefSystem="jtms"/>
  </kbase>
  <kbase name="KBase2" default="false" eventProcessingMode="stream" equalsBehavior="equality" declarativeAgenda="enabled" packages="org.domain.pkg2, org.domain.pkg3" includes="KBase1">
    <ksession name="KSession3_1" type="stateful" default="false" clockType="realtime">
      <fileLogger file="drools.log" threaded="true" interval="10"/>
      <workItemHandlers>
        <workItemHandler name="name" type="org.domain.WorkItemHandler"/>
      </workItemHandlers>
      <listeners>
        <ruleRuntimeEventListener type="org.domain.RuleRuntimeListener"/>
        <agendaEventListener type="org.domain.FirstAgendaListener"/>
        <agendaEventListener type="org.domain.SecondAgendaListener"/>
        <processEventListener type="org.domain.ProcessListener"/>
      </listeners>
    </ksession>
  </kbase>
</kmodule>

在这里的标签包含一个键-值对列表,这些键-值对是用于配置KieBase构建过程的可选属性,例如,这个样例kmodule.xml文件定义了一个额外的自定义操作符,名为supersetOf,并由org.mycompany.SupersetOfEvaluatorDefinition类实现。

在定义了这两个KieBase之后,就可以从第一个KieBase中实例化两个不同类型的KieSession,而从第二个KieBase中实例化一个,可以在kbase标记上定义的属性列表,以及它们的含义和默认值如下:

属性名称 默认值 认可的值 含义
name none 任何值 从KieContainer检索此KieBase的名称,这是唯一的强制属性。
includes none 任何逗号分隔的列表 这个kmodule中包含的其他KieBase的逗号分隔列表,所有这些KieBase的构件也将包括在这。
packages all 任何逗号分隔的列表 默认情况下,resources文件夹下的所有Drools构件(任何级别)都包含在KieBase中,此属性允许将在此KieBase中编译的构件限制为仅属于包列表的构件。
default false true,false 定义这个KieBase是否是这个模块的默认值,因此可以从KieContainer创建它,而不向它传递任何名称,每个模块中最多可以有一个默认的KieBase。
equalsBehavior identity identity,equality 定义当一个新事实插入到工作内存中时,Drools的行为。使用identity,它总是创建一个新的FactHandle,除非同一个对象还没有出现在工作内存中,而只有在新插入的对象与已经存在的事实不相等(根据其相等的方法)时,它才相等。
eventProcessingMode cloud cloud,stream 在云模式下编译时,KieBase将事件视为正常事实,而在流模式下则允许对它们进行时间推理。
declarativeAgenda disabled disabled,enabled 定义声明性议程是否启用。

类似地,ksession标签的所有属性(当然名字除外)都有有意义的默认值,下表列出并描述了它们:

属性名称 默认值 认可的值 含义
name none 任何值 KieSession的唯一名称,用于从KieContainer提取KieSession,这是唯一的强制属性。
type stateful stateful,stateless 有状态会话允许迭代地使用工作内存,而无状态会话是使用提供的数据集一次性在工作内存中执行。
default false true,false 定义这个KieSession是否是这个模块的默认值,因此它可以从KieContainer创建,而无需传递任何名称,在每个模块中,每种类型最多可以有一个默认的KieSession。
clockType realtime realtime,pseudo 定义事件时间戳是由系统时钟决定还是由应用程序控制的psuedo时钟决定,这个时钟对于时间规则的单元测试特别有用。
beliefSystem simple simple,jtms,defeasible 定义KieSession所使用的信赖系统的类型。

正如前面的kmodule.xml示例所述,还可以在每个KieSession上声明创建一个文件(或控制台)记录器、一个或多个WorkItemHandler和一些监听器,这些监听器可以是3种不同的类型:ruleRuntimeEventListeneragendaEventListenerprocessEventListener

在定义了类似于前一个示例中的kmodule.xml之后,现在从KieContainer可以使用它们的名称简单地检索KieBaseKieSession

KieServices kieServices = KieServices.Factory.get();
KieContainer kContainer = kieServices.getKieClasspathContainer();

KieBase kBase1 = kContainer.getKieBase("KBase1");
KieSession kieSession1 = kContainer.newKieSession("KSession2_1");
StatelessKieSession kieSession2 = kContainer.newStatelessKieSession("KSession2_2");

需要注意的是,由于KSession2_1KSession2_2属于两种不同的类型(第一种是有状态的,而第二种是无状态的),因此需要根据KieContainer声明的类型调用两种不同的方法。如果请求KieSessionKieContainer的类型与kmodule.xml文件中声明的类型不一致,KieContainer将抛出一个RuntimeException。另外,由于KieBaseKieSession已经被标记为default,所以在不传递任何名字的情况下,可以从KieContainer那里得到它们。

KieContainer kContainer = ...

KieBase kBase1 = kContainer.getKieBase(); // returns KBase1
KieSession kieSession1 = kContainer.newKieSession(); // returns KSession2_1

由于Kie项目也是Maven项目,因此在pom.xml文件中声明的groupId、artifactId和version用于生成一个ReleaseId,用于在应用程序中唯一标识该项目,这允许通过简单地将ReleaseId传递给KieServices来从项目中创建一个新的KieContainer

KieServices kieServices = KieServices.Factory.get();
ReleaseId releaseId = kieServices.newReleaseId( "org.acme", "myartifact", "1.0" );
KieContainer kieContainer = kieServices.newKieContainer( releaseId );

使用Maven构建

Maven的KIE插件确保构件资源得到验证和预编译,建议在任何时候都使用它,要使用该插件,只需将其添加到Maven pom的构建部分。xml并通过使用包装kjar激活它。要使用该插件,只需将其添加到Maven pom.xml的build部分,并通过使用打包kjar激活它。

<packaging>kjar</packaging>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.kie</groupId>
        <artifactId>kie-maven-plugin</artifactId>
        <version>7.7.0.Final</version>
        <extensions>true</extensions>
      </plugin>
    </plugins>
  </build>

这个插件支持所有Drools/jBPM knowledge资源。但是,如果你在Java类中使用特定的KIE注解,例如@kie.api.Position,你需要在kie-api上添加编译时依赖到你的项目。我们建议对所有其他的KIE依赖项使用提供的作用域,这样,kjar就会尽可能保持轻量级,而不依赖于任何特定的KIE版本。

在不使用Maven插件的情况下构建KIE模块将把所有资源复制到生成的JAR中,当运行时加载JAR时,它将尝试构建所有资源,如果存在编译问题,它将返回null KieContainer,它还将编译开销推到运行时,一般来说,这是不推荐的,而且Maven插件应该总是被使用。

以编程方式定义KieModule

还可以通过编程方式定义属于KieModule的KieBaseKieSession,而不是kmodule.xml文件中的声明性定义。同样的编程API还允许显式地添加包含Kie构件的文件,而不是自动从项目的resources文件夹中读取它们。为此,有必要创建一个KieFileSystem(一种虚拟文件系统),并将项目中包含的所有资源添加到其中。

KieFileSystem.png

像所有其他Kie核心组件一样,你可以从KieServices获得KieFileSystem的实例,kmodule.xml配置文件必须添加到文件系统中,这是一个强制性的步骤。Kie还提供了一个由KieModuleModel实现的方便的fluent API,以编程方式创建这个文件。

KieModuleModel.png

为了在实践中做到这一点,有必要从KieServices中创建一个KieModuleModel,使用所需的KieBaseKieSession配置它,转换到XML,并将XML添加到KieFileSystem,这个过程如下例所示:

KieServices kieServices = KieServices.Factory.get();
KieModuleModel kieModuleModel = kieServices.newKieModuleModel();

KieBaseModel kieBaseModel1 = kieModuleModel.newKieBaseModel( "KBase1 ")
        .setDefault( true )
        .setEqualsBehavior( EqualityBehaviorOption.EQUALITY )
        .setEventProcessingMode( EventProcessingOption.STREAM );

KieSessionModel ksessionModel1 = kieBaseModel1.newKieSessionModel( "KSession1" )
        .setDefault( true )
        .setType( KieSessionModel.KieSessionType.STATEFUL )
        .setClockType( ClockTypeOption.get("realtime") );

KieFileSystem kfs = kieServices.newKieFileSystem();
kfs.writeKModuleXML(kieModuleModel.toXML());

此时,还需要通过它的fluent API向KieFileSystem添加其他所有构成项目的Kie构件,这些构件必须添加到相应的Maven项目的相同位置。

KieFileSystem kfs = ...
kfs.write( "src/main/resources/KBase1/ruleSet1.drl", stringContainingAValidDRL )
        .write( "src/main/resources/dtable.xls",
                kieServices.getResources().newInputStreamResource( dtableFileStream ) );

这个示例表明,添加Kie构件可以同时作为纯字符串和Resource,在后者中,Resource可以由KieResources工厂创建,也可以由KieServices提供,KieResources提供了许多方便的工厂方法来将InputStreamURLFile或表示文件系统路径的String转换为可以由KieFileSystem管理的Resource

KieResources.png

通常,可以从用于将Resource添加到KieFileSystem的名称的扩展推断出资源的类型,但是,也可以不遵循Kie约定的文件扩展名,并显式地为Resource分配特定的ResourceType,如下所示:

KieFileSystem kfs = ...
kfs.write( "src/main/resources/myDrl.txt",
           kieServices.getResources().newInputStreamResource( drlStream )
                      .setResourceType(ResourceType.DRL) );

将所有资源添加到KieFileSystem并通过将KieFileSystem传递给KieBuilder来构建它。

KieBuilder.png

当成功构建KieFileSystem的内容时,生成的KieModule会自动添加到KieRepositoryKieRepository是一个单例库,充当所有可用KieModule的存储库。

KieRepository.png

在此之后,就可以通过KieServicesKieModule创建一个新的KieContainer,使用ReleaseId,但是,因为在这种情况下,KieFileSystem并不包含任何pom.xml文件(可以使用KieFileSystem.writePomXML方法添加xml文件),Kie不能确定KieModuleReleaseId,并给它赋一个默认值,这个默认的ReleaseId可以从KieRepository获得,用来识别KieRepository内部的KieModule,下面的示例展示了整个过程。

KieServices kieServices = KieServices.Factory.get();
KieFileSystem kfs = ...
kieServices.newKieBuilder( kfs ).buildAll();
KieContainer kieContainer = kieServices.newKieContainer(kieServices.getRepository().getDefaultReleaseId());

在这一点上,可以从这个KieContainer获得KieBase并创建新的KieSession,这与直接从类路径创建KieContainer的情况完全相同。


博弈
2.5k 声望1.5k 粉丝

态度决定一切


引用和评论

1 篇内容引用
0 条评论