介绍
6.0引入了一种新的配置和约定方法来构建知识库,而不是在5.x中使用编程构建器方法,构建器仍然可以使用,因为它用于工具集成。
构建现在使用Maven,并与Maven实践保持一致,KIE项目或模块只是一个Maven Java项目或模块,另外还有一个元数据文件META-INF/kmodule.xml,kmodule.xml文件是选择资源到知识库并配置这些知识库和会话的描述符,还有通过Spring和OSGi蓝图提供的XML支持。
虽然标准Maven可以构建和打包KIE资源,但它不会在构建时提供验证,有一个Maven插件,推荐使用它来获得构建时验证,插件还生成许多类,使运行时加载速度更快。
示例项目布局和Maven POM描述符在屏幕截图中进行了说明:
KIE使用默认值来最小化配置的数量,一个空的kmodule.xml是最简单的配置,必须始终有一个kmodule.xml文件,即使是空的,因为它用于发现JAR及其内容。
Maven可以通过“mvn install
”将一个KieModule部署到本地机器上,本地机器上的所有其他应用程序都使用它,或者它可以“mvn deploy
”将KieModule推到远程Maven存储库中,构建应用程序将拉取KieModule并在过程中填充本地Maven存储库。
JAR可以以两种方式部署,要么像Maven依赖项清单中的其他JAR一样添加到类路径,要么在运行时动态加载它们,KIE将扫描类路径以找到所有包含kmodule.xml的jar文件,每个发现的JAR都由KieModule接口表示。术语类路径KieModule和动态KieModule用于引用这两种加载方法,虽然动态模块支持并行版本控制,但是类路径模块不支持,此外,一旦模块在类路径上,就不能动态加载其他版本。
API的详细参考资料将包含在下一节中,没有耐心的人可以直接跳转到示例部分,这对于不同的用例来说是相当容易理解的。
构建
创建和构建一个Kie项目
Kie项目具有普通Maven项目的结构,唯一的特点是包含kmodule.xml文件,以声明的方式定义可以从中创建的KieBase
和KieSession
,这个文件必须放在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
就足够了,它可以读取从类路径构建的文件:
KieServices kieServices = KieServices.Factory.get();
KieContainer kContainer = kieServices.getKieClasspathContainer();
KieServices
是一个可以访问所有Kie构建和运行时设施的接口:
通过这种方式,所有Java源和Kie资源都被编译并部署到KieContainer
中,而KieContainer
使其内容在运行时可用。
kmodule.xml文件
如前一节所述,kmodule.xml文件是可以声明地配置可以从KIE项目创建的KieBase
和KieSession
的地方。
特别是KieBase
是应用程序所有知识定义的存储库,它将包含规则、流程、函数和类型模型。KieBase
本身不包含数据,相反,会话是从KieBase
创建的,KieBase
中可以插入数据,从KieBase
中可以启动流程实例。创建KieBase
可能很重,而创建会话非常轻,因此建议在可能的情况下缓存KieBase
,以允许重复创建会话。然而,终端用户通常不必担心,因为KieContainer
已经自动提供了这种缓存机制。
相反,KieSession
存储并在运行时数据上执行,它是由KieBase
创建的,如果在kmodule.xml文件中定义了它,则更容易从KieContainer
直接创建。
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种不同的类型:ruleRuntimeEventListener
,agendaEventListener
,processEventListener
。
在定义了类似于前一个示例中的kmodule.xml之后,现在从KieContainer
可以使用它们的名称简单地检索KieBase
和KieSession
。
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_1
和KSession2_2
属于两种不同的类型(第一种是有状态的,而第二种是无状态的),因此需要根据KieContainer
声明的类型调用两种不同的方法。如果请求KieSession
给KieContainer
的类型与kmodule.xml文件中声明的类型不一致,KieContainer
将抛出一个RuntimeException
。另外,由于KieBase
和KieSession
已经被标记为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的KieBase
和KieSession
,而不是kmodule.xml文件中的声明性定义。同样的编程API还允许显式地添加包含Kie构件的文件,而不是自动从项目的resources文件夹中读取它们。为此,有必要创建一个KieFileSystem
(一种虚拟文件系统),并将项目中包含的所有资源添加到其中。
像所有其他Kie核心组件一样,你可以从KieServices
获得KieFileSystem
的实例,kmodule.xml配置文件必须添加到文件系统中,这是一个强制性的步骤。Kie还提供了一个由KieModuleModel
实现的方便的fluent API,以编程方式创建这个文件。
为了在实践中做到这一点,有必要从KieServices
中创建一个KieModuleModel
,使用所需的KieBase
和KieSession
配置它,转换到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
提供了许多方便的工厂方法来将InputStream
、URL
、File
或表示文件系统路径的String
转换为可以由KieFileSystem
管理的Resource
。
通常,可以从用于将Resource
添加到KieFileSystem
的名称的扩展推断出资源的类型,但是,也可以不遵循Kie约定的文件扩展名,并显式地为Resource
分配特定的ResourceType
,如下所示:
KieFileSystem kfs = ...
kfs.write( "src/main/resources/myDrl.txt",
kieServices.getResources().newInputStreamResource( drlStream )
.setResourceType(ResourceType.DRL) );
将所有资源添加到KieFileSystem
并通过将KieFileSystem
传递给KieBuilder
来构建它。
当成功构建KieFileSystem
的内容时,生成的KieModule
会自动添加到KieRepository
,KieRepository
是一个单例库,充当所有可用KieModule
的存储库。
在此之后,就可以通过KieServices
为KieModule
创建一个新的KieContainer
,使用ReleaseId
,但是,因为在这种情况下,KieFileSystem
并不包含任何pom.xml文件(可以使用KieFileSystem.writePomXML
方法添加xml文件),Kie不能确定KieModule
的ReleaseId
,并给它赋一个默认值,这个默认的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
的情况完全相同。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。