版本 7.9.0
KIE 生态
OptaPlanner 是一个本地搜索和优化的工具,独立于Drools Planner 。
UberFire 是新的workbench工程,提供类似Eclipse工作台功能。
KIE-WB 是整合了Guvnor 、drools、jbpm的uber工作台。jbpm-wb是虚的。
生命周期
- Author 创作
使用DRL、BPMN2、决策表、类进行知识创作 - 构建
将创作的知识构建为可部署的单元, JAR。 - 测试
- 部署
- 使用 、管理
使用maven
使用maven构建, 遵循maven的实践规则。 KIE工程可以作为maven工程或module。 spring 中提供xml配置来代替元数据配置META-INF/kmodule.xml。
maven构建时不提供校验规则的机制, 可以通过插件实现 kie-maven-plugin 。
Kie项目具有普通maven的工程结构,唯一特点是需要包含一个kmodule.xml文件, 此文件必须放在Maven项目的resources / META-INF文件夹中,而所有其他Kie工件(如DRL或Excel文件)必须存储在resources文件夹或其下的任何其他子文件夹中。
由于工程使用默认的配置,因此最简单的kmodule.xml文件如下:
这种情况下,kmodule将包含一个默认的KieBase 。 所有存储在resources文件夹下及其子文件夹下的规则都将被编译进去。
创建KieContainer
从类路径下读取文件创建KieContainer ;
KieServices kieServices = KieServices.Factory.get();
KieContainer kContainer = kieServices.getKieClasspathContainer();
通过该方式,将所有的java 、kie资源都编译部署到KieContainer中,从而可以在运行时使用之。
kmodule.file
KieBase是所有应用程序知识定义的存储库。 它将包含规则,流程,函数和类型模型。 KieBase本身不包含数据;。
KieSession 存储和执行运行时数据。 它从KieBase创建或者当在kmodule.xml中定义时可以直接从KieContainer中创建。
如下实例, kmodule.xml中可以定义和配置多个KieBase,并且对于每个KieBase都可以创建不同的KieSession 。
kbase的属性设置:
Attribute name | Default value | Admitted values | Meaning |
---|---|---|---|
name | none | any | 从KieContainer中获取KieBase的name |
includes | none | 逗号分隔列表 | 逗号分隔的列表,kmodule中定义的其他kbase都将包含在该中 |
packages | all | any comma separated list | 该packages下所有的资源文件都将包含在该kbase中 |
default | false | true, false | 定义该kbase是否是默认的,若是默认的则从KieContainer中可以不传name直接创建,最多只能有一个默认kbase |
equalsBehavior | identity | identity, equality | 定义当新fact插入working memory时drools的行为。 使用identity ,则总是创建一个新的FactHandle,除非同样的对象在workingmemory中不存在。 使用equality ,则只有心插入的对象不同于(根据其提供的equal方法比较)已经存在的fact才创建。 |
eventProcessingMode | cloud | cloud, stream | 当以cloud模式创建时,事件被认为是一般的facts。 当stream时允许进行时间推理 |
declarativeAgenda | disabled | disabled, enabled | 是否启用Declarative Agenda |
Ksession的属性:
- name , KIESession的唯一名称标识。用于从KieContainer中获取KieSession。
- type , stateful、stateless, 默认是stateful ;
- default , 默认false ;当默认true时, 则允许从KieContainer中不传入name获取。
- clockType , realtime、pseudo, 默认realtime ; 定义事件时间戳是由系统时钟还是由应用程序控制的伪时钟确定的。 该时钟对于单元测试时间规则特别有用。
- beliefSystem , simple, jtms, defeasible ; 默认simple ; 定义KieSession使用的信任系统的类型。
如前例所示, 可以在每个KieSession上声明一个日志记录器,一个或多个WorkItemHandlers,以及3中类型的监听器:ruleRuntimeEventListener, agendaEventListener and processEventListener 。
在kmodule.xml中声明之后, 既可以使用其name从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上调用2种不同的方法。 如果向KieContainer请求的KieSession的类型与kmodule.xml文件中声明的类型不对应,则KieContainer将抛出RuntimeException。 此外,由于KieBase和KieSession已被标记为默认值,因此可以从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,然后packaging使用 kjar。
kjar
...
org.kie
kie-maven-plugin
7.9.0.Final
true
该插件支持所有Drools / jBPM。 但是,如果您在Java类中使用特定的KIE注释,例如@ kie.api.Position,则需要将kie-api的编译时依赖项添加到项目中。 我们建议使用provided scope 添加KIE依赖项。 这样kjar尽可能保持轻量级,并且不依赖于任何特定的KIE版本。
在没有Maven插件的情况下构建KIE模块会将所有资源按原样复制到生成的JAR中。 当运行时加载JAR时,它将尝试构建所有资源。 如果存在编译问题,它将返回null KieContainer。 它还将编译开销推送到运行时。 通常不建议这样做,并且应始终使用Maven插件。
通过编程定义KieModule
支持通过编程的方式定义KieBase 和KieSession , 也支持通过编程将资源动态加载到项目中。 这需要使用KieFileSystem ,它是虚拟文件系统,可以通过它添加任意资源。
可以通过KieServices获取KieFileSystem 。 kmodule.xml配置文件是必须的一步, kie提供了KieModuleModel来通过编程添加。
通过KieServices获取KieModuleModel ,配置其KieBases 和 KieSession , 转换为XML , 将XML添加到KieFileSysstem , 如下:
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());
向KieFileSystem添加其他组件:
KieFileSystem kfs = ...
kfs.write( "src/main/resources/KBase1/ruleSet1.drl", stringContainingAValidDRL )
.write( "src/main/resources/dtable.xls",
kieServices.getResources().newInputStreamResource( dtableFileStream ) );
上例显示可以将Kie工件添加为普通String 或 Resource 。 在后一种情况下,资源可以由KieResources工厂创建,也由KieServices提供。 KieResources提供了许多方便的工厂方法,可将InputStream,URL,File或表示文件系统路径的String转换为可由KieFileSystem管理的Resource。
通常,可以从用于将其添加到KieFileSystem的名称的扩展名推断出资源的类型。 但是,也可以不遵循有关文件扩展名的Kie约定,并明确地将特定的ResourceType分配给资源,如下所示:
KieFileSystem kfs = ...
kfs.write( "src/main/resources/myDrl.txt",
kieServices.getResources().newInputStreamResource( drlStream )
.setResourceType(ResourceType.DRL) );
向KieFileSystem中添加resource , 将KieFileSystem传给KieBuilder 来构建之。
当KieFileSystem的内容被成功构建, 结果KieModule 被自动添加到KieRepository 。 KieRepository 是个单例,是所有KieModule的仓库。
在此之后,可以通过KieServices使用其ReleaseId为该KieModule创建一个新的KieContainer。 但是,由于在这种情况下KieFileSystem不包含任何pom.xml文件(可以使用KieFileSystem.writePomXML方法添加一个),因此Kie无法确定KieModule的ReleaseId并为其分配默认值。 可以从KieRepository获取此默认ReleaseId,并用于标识KieRepository内部的KieModule。 以下示例显示了整个过程。
KieServices kieServices = KieServices.Factory.get();
KieFileSystem kfs = ...
kieServices.newKieBuilder( kfs ).buildAll();
KieContainer kieContainer = kieServices.newKieContainer(kieServices.getRepository().getDefaultReleaseId());
此时,可以从KieContainer中获取KieBases并创建新的KieSession,其方式与直接从类路径创建的KieContainer的方式完全相同。
检查编译结果是最佳做法。 KieBuilder报告了3种不同严重程度的编译结果:ERROR,WARNING和INFO。 ERROR表示项目的编译失败,没有生成KieModule,没有任何内容会添加到KieRepository。 警告和INFO结果可以忽略,但可供检查。
KieBuilder kieBuilder = kieServices.newKieBuilder( kfs ).buildAll();
assertEquals( 0, kieBuilder.getResults().getMessages( Message.Level.ERROR ).size() );
更改构建结果默认严重性
当添加一个同名的新规则时,默认会替换旧的规则,并打印出INFO结果。这在大多数时候是可以的,但有些情况不希望这么做,需要阻止规则更新并报告error。
可以通过API调用,系统属性或配置文件来完成。 从此版本开始,Drools支持规则更新和功能更新的可配置结果严重性。 要使用系统属性或配置文件对其进行配置,用户必须使用以下属性:
// sets the severity of rule updates
drools.kbuilder.severity.duplicateRule =
// sets the severity of function updates
drools.kbuilder.severity.duplicateFunction =
部署 Deploy
KieBase是所有知识的仓库,包括rule、process、function、type model。
KieBase本身不包含数据; KieBase可以从包括KieModule的KieContainer中获取。
有时,如在OSGI环境, KieBase需要使用默认classloader加载不了的类型。 这种情况下,需要使用KieBaseConfiguration 来创建附加classloader 并传递KieContainer给他来创建KieBase。
KieServices kieServices = KieServices.Factory.get();
KieBaseConfiguration kbaseConf = kieServices.newKieBaseConfiguration( null, MyType.class.getClassLoader() );
KieBase kbase = kieContainer.newKieBase( kbaseConf );
KieBase创建并返回KieSession对象,它可以选择保留这些对象的引用。 当KieBase发生修改时,这些修改将应用于会话中的数据。 此引用是弱引用,它也是可选的,由布尔标志控制。
KieScanner
KieScanner允许连续监视Maven存储库,以检查是否已安装新版本的Kie项目。 在包装该项目的KieContainer中部署了一个新版本。 使用KieScanner需要kie-ci.jar在类路径上。
KieServices kieServices = KieServices.Factory.get();
ReleaseId releaseId = kieServices.newReleaseId( "org.acme", "myartifact", "1.0-SNAPSHOT" );
KieContainer kContainer = kieServices.newKieContainer( releaseId );
KieScanner kScanner = kieServices.newKieScanner( kContainer );
// Start the KieScanner polling the Maven repository every 10 seconds
kScanner.start( 10000L );
在此示例中,KieScanner配置为以固定的时间间隔运行,但也可以通过在其上调用scanNow()方法按需运行它。如果KieScanner在Maven存储库中找到该KieContainer使用的Kie项目的更新版本,它会自动下载新版本并触发新项目的增量构建。此时,KieContainer控制下的现有KieBases和KieSessions将自动升级 - 特别是那些使用getKieBase()获得的KieBases及其相关的KieSession,以及直接使用KieContainer.newKieSession()获得的任何KieSession因此引用默认值KieBase。此外,从现在开始,从KieContainer创建的所有新KieBase和KieSession都将使用新的项目版本。请注意,在KieScanner升级之前通过newKieBase()获得的任何现有KieBase及其任何相关的KieSession都不会自动升级;这是因为通过newKieBase()获得的KieBases不受KieContainer的直接控制。
如果使用SNAPSHOT,版本范围,LATEST或RELEASE设置,KieScanner将仅对已部署的jar进行拾取更改。固定版本不会在运行时自动更新。
Maven支持许多机制来管理应用程序中的版本控制和依赖关系。 可以使用特定版本号发布模块,也可以使用SNAPSHOT后缀。 依赖关系可以指定要使用的版本范围,或者采用SNAPSHOT机制的优势。
StackOverflow为此提供了非常好的描述,如下所示。
http://stackoverflow.com/questions/30571/how-do-i-tell-maven-to-use-the-latest-version-of-a-dependency
Runing
从KieContainer 获取 KieBase
KieBase kBase = kContainer.getKieBase();
KieSession
KieSession ksession = kbase.newKieSession();
KieRuntime
全局Globals与fact不同,他的修改不会触发规则的重新评估。全局变量用户提供静态信息、作为RHS的服务对象,作为规则引擎的返回对象。
事件模型event model
事件提供了通知规则引擎的方法, 包括触发规则、声明对象等。 这运行将日志记录审计与应用程序分离。
KieRuntimeEventManager接口由KieRuntime实现,它提供两个接口,RuleRuntimeEventManager和ProcessEventManager。 我们这里只介绍RuleRuntimeEventManager。
RuleRuntimeEventManager 支持监听器的添加和删除,因此working memroy 和 agenda的事件可以被监听 :
ksession.addEventListener( new DefaultAgendaEventListener() {
public void afterMatchFired(AfterMatchFiredEvent event) {
super.afterMatchFired( event );
System.out.println( event );
}
});
Drools提供了DebugRuleRuntimeEventListener and DebugAgendaEventListener , 他们实现了打印语句, 使用如下:
ksession.addEventListener( new DebugRuleRuntimeEventListener() );
所有发出的事件都实现了KieRuntimeEvent接口,该接口可用于检索事件源自的实际KnowlegeRuntime。
当前支持的事件由:
The events currently supported are:
MatchCreatedEvent
MatchCancelledEvent
BeforeMatchFiredEvent
AfterMatchFiredEvent
AgendaGroupPushedEvent
AgendaGroupPoppedEvent
ObjectInsertEvent
ObjectDeletedEvent
ObjectUpdatedEvent
ProcessCompletedEvent
ProcessNodeLeftEvent
ProcessNodeTriggeredEvent
ProcessStartEvent
KieRuntimeLogger
KieRuntimeLogger使用Drools中的综合事件系统创建审计日志,该日志可用于记录应用程序的执行,以便以后使用Eclipse审计查看器等工具进行检查。
KieRuntimeLogger logger =
KieServices.Factory.get().getLoggers().newFileLogger(ksession, "logdir/mylogfile");
...
logger.close();
Commands and commandExecutor
KIE拥有有状态或无状态会话的概念。 已经涵盖了使用标准KieRuntime的有状态会话,并且可以随着时间的推移迭代地进行。 Stateless是使用提供的数据集一次性执行KieRuntime。 它可能返回一些结果,会话在最后处理,禁止进一步的迭代交互。
上述的基础是 CommandExecutor 接口, 有状态和无状态接口都会扩展之。
CommandExecutor允许在这些会话上执行命令,唯一的区别是StatelessKieSession在处理会话之前在结束时执行fireAllRules()。 可以使用CommandExecutor创建命令.Javadocs使用CommandExecutor提供允许的命令的完整列表。
setGlobal和getGlobal是两个与Drools和jBPM相关的命令。
在下面设置全局调用setGlobal。 可选的boolean指示命令是否应该返回全局值作为ExecutionResults的一部分。 如果为true,则它使用与全局名称相同的名称。 如果需要替代名称,可以使用String代替布尔值。
StatelessKieSession ksession = kbase.newStatelessKieSession();
ExecutionResults bresults =
ksession.execute( CommandFactory.newSetGlobal( "stilton", new Cheese( "stilton" ), true);
Cheese stilton = bresults.getValue( "stilton" );
StatelessKieSession ksession = kbase.newStatelessKieSession();
ExecutionResults bresults =
ksession.execute( CommandFactory.getGlobal( "stilton" );
Cheese stilton = bresults.getValue( "stilton" );
上述例子都是使用的单条命令。 组合命令使用BatchExecution 。
组合命令是个列表, 他迭代每条命令并执行之。 这意味着可以在一个execute()中insert some objects, start a process, call fireAllRules and execute a query。
StatelessKieSession 会自动在结束时执行fireAllRules() 。
批处理中具有out标识符集的任何命令都会将其结果添加到返回的ExecutionResults实例。 让我们看一个简单的例子来看看它是如何工作的。 出于说明的目的,所呈现的示例包括来自Drools和jBPM的命令。 它们在Drool和jBPM特定部分中有更详细的介绍。
StatelessKieSession ksession = kbase.newStatelessKieSession();
List cmds = new ArrayList();
cmds.add( CommandFactory.newInsertObject( new Cheese( "stilton", 1), "stilton") );
cmds.add( CommandFactory.newStartProcess( "process cheeses" ) );
cmds.add( CommandFactory.newQuery( "cheeses" ) );
ExecutionResults bresults = ksession.execute( CommandFactory.newBatchExecution( cmds ) );
Cheese stilton = ( Cheese ) bresults.getValue( "stilton" );
QueryResults qresults = ( QueryResults ) bresults.getValue( "cheeses" );