2.2.3 Drools Expert
2.2.3.1 非对称Rete算法实现
不再需要影子代理。影子代理保护引擎免受有关实事的信息变化的影响,如果发生在引擎控件的外部,它可能不会被修改或撤消。
2.2.3.2 包构建器现在可以构建多个命名空间
你不再需要对一个包命名空间构建一个PackageBuilder 。只要保持为所有命名空间增加你的DRLs,并且getPackages()为每个使用的命名空间返加一个包数组。
例子 2.26 获得多个包
Package[] packages = pkgBuilder.getPackages();
2.2.3.3 规则库连接包构建器
现在可以连接一个 RuleBase到一个 PackageBuilder,这意味着规则被构建,并且同时被添加到规则库。PackageBuilder使用现行的RuleBase的Package实例作为它的资源,取消了发生在现有方法中的Package创造和融合。
例子 2.27 连接规则库(RuleBase)到包构建器(PackageBuilder)
RuleBase ruleBase = RuleBaseFactory.newRuleBase();
PackageBuilder pkgBuilder = new PackageBuilder( ruleBase, null );
2.2.3.4 有状态会话的二进制编码
有状态会话现在可以保存,并可在以后的日期中恢复。预加载数据会话现在可以被创建。对用户对象的持久化可使用可插式策略,例如,hibernate或特征(identity)映射。
2.2.3.5 类型声明
Drools现在支持一种新的基础结构,称为类型声明。这个结构达到两个目的:能够声明实事元数据,以及能够为规则引擎动态地产生局部的新的事实类型。Guvnor模拟工具在底层使用了它。下面是该结构的一个例子:
例子 2.28 声明StockTick
declare StockTick
@role( event )
@timestamp( timestampAttr )
companySymbol : String
stockPrice : double
timestampAttr : long
end
2.2.3.6 声明实事元数据
要声明和关联事实的元数据,只需要对你想声明的每个元数据ID使用@符号。例子:
例子 2.29 声明元数据
declare StockTick
@role( event )
end
2.2.3.7 触发Bean产生
要激活动态bean产生,仅为你的类型声明添加字段和类型。
例子 2.30 声明Person
declare Person
name : String
age : int
end
2.2.3.8 DSL的改进
一系列DSL的改进被实现,包括一个完善的新解析器,并且能够为匹配的变量声明匹配的掩码(mask)。例如,它可限定一个电话号码字段为2位数的国家代码+ 3位区号+ 8位数字电话号码,所有连接以“ - ”(破折号)连接,通过象这样声明DSL映射:电话号码为{number:/d{2}-/d{3}-/d{8}},所有有效的Java正则表达式可以用于变量的掩码中。
2.2.3.9 fireUntilHalt()
Drools现在支持fireUntilHalt()功能,它以一种被动模式启动引擎,在那儿规则会被连续引发,直到调用了halt()。这尤其对CEP(complex event processing)场景有用,CEP场景需要俗称的“活动查询”。
2.2.3.10 规则库分区和多线程传播
Drools ReteOO算法现在支持一个选项,用于以多线程模式启动规则库,在那儿Drools ReteOO网络被划分为多个部分,然后规则被多个线程并发计算。对通常有几个独立规则并发运行的CEP,它也有一个要求,接近实时性能/吞吐量的要求,并且一个计算不能干扰其他的计算。
2.2.3.11 XSD模式支持
Drools现在支持XSD模式。记住虽然XSD模式以用于Drools类加载器的本地POJO类生成。在包构建器中存在有一个帮助类用于模式的产生。一旦数据模式被生成,你通常使用JAXB数据加载器插入数据。
2.2.3.12 数据加载器
Drools现在支持两种数据加载器,Smooks和JAXB。Smooks是一个用于ETL的开源数据转换工具,JAXB是一个标准的Sun数据映射工具。单元测试显示Smooks和JAXB均可在这里找到。
2.2.3.13 类型安全配置
在Drools中,除了能够通过配置文件配置选项外,也可用系统属性配置,通过API的 setProperty()方法设置属性,Drools-API现在支持类型安全配置。我们不希望为每个可能的配置方法增加特殊的方法,有两个原因:它污染了API,并且每次都有一个新选项增加到Drools,API将不得不改变。而这种方式,我们遵循模块化,类基于配置,在此处为每个可能的配置,一个新的选项类增加到了API,除了灵活之外,也维持了API的稳定。所以,现在为了设置配置选项,你只需要使用枚举或者提供每个选项的工厂。例如,如果你希望为断言行为"equality" 配置知识库,并且自动从模式匹配中删除特征(identities),你只需要使用下面的枚举:
例子2.31 配置
KnowledgeBaseConfiguration config = KnowledgeBaseFactory.newKnowledgeBaseConfiguration();
config.setOption( AssertBehaviorOption.EQUALITY );
config.setOption( RemoveIdentitiesOption.YES );
对于选项,我们不需要预定义约束,或者可以假设多个值,提供一个工厂方法。例如,配置alpha值为5,只使用get()工厂方法:
例子 2.32 配置alpha值
config.setOption( AlphaThresholdOption.get(5) );
正如你所见,为每个不同的可能配置,使用了相同的setOption()方法,然而它们仍然是类型安全的。
2.2.3.14 新的累积函数:collectSet和collectList
有时候,有必要收集来自实事属性的值的集合或列表,而不是实事本身。在这种情况下,不可能使用collect CE。所以对这种情况,现在Drools有两个新的累积函数:collectSet用于收集值的集合(即,无重复的值),collectList用于收集值的列表(即,允许重复的值):
例子 2.33 新的累积函数
# collect the set of unique names in the working memory
$names : Set() from accumulate( Person( $n : name, $s : surname ),
collectSet( $n + " " + $s ) )
# collect the list of alarm codes from the alarms in the working memory
$codes : List() from accumulate( Alarm( $c : code, $s : severity ),
collectList( $c + $s ) )
2.2.3.15 用于类型声明的新元数据:@propertyChangeSupport
事实实现了象定义在Javabean(tm)规范中的属性改变的支持。现在可以注释,让引擎注册自身来侦听事实属性的变化。在Drools 4 API的insert()方法中使用的布尔参数已过时,并且不存在于drools-aip模块中了。
例子 2.34 @propertyChangeSupport
declare Person
@propertyChangeSupport
end
2.2.3.16 批处理器
批处理器允许一个知识会话使用命令脚本,此外,无论是StatelessKnowledgeSession 还是 StatefulKnowledgeSession实现都可以使用CommandFactory创建这个接口命令,且使用"execute" 方法执行,如下所示:
例子 2.35 使用CommandFactory
ksession.execute( CommandFactory.newInsert( person ) );
尽管这样,你通常会希望执行一个批处理命令,通过组合命令 BatchExecution可以完成它。BatchExecutionResults现在用来处理结果,某些命令可以使用"out"标识符,用它来添加结果到BatchExecutionResult。现在可以轻松地执行查询,并把结果添加到BatchExecutionResult。这个结果进一步被限定到这个执行调用,并且通过BatchExecutionResults返回。
例子 2.36 使用BatchExecutionResult
List<Command> cmds = new ArrayList<Command>();
cmds.add( CommandFactory.newSetGlobal( "list1", new ArrayList(), true ) );
cmds.add( CommandFactory.newInsert( new Person( "jon", 102 ), "person" ) );
cmds.add( CommandFactory.newQuery( "Get People" "getPeople" );
BatchExecutionResults results = ksession.execute( CommandFactory.newBatchExecution( cmds ) );
results.getValue( "list1" ); // returns the ArrayList
results.getValue( "person" ); // returns the inserted fact Person
results.getValue( "Get People" );// returns the query as a QueryResults instance.
End
CommandFactory详细描述支持的命令,它们所有都可以使用XStream和BatchExecutionHelper进行编码。可以使用管道组合它们来自动化会话脚本。
例子 2.37 使用PipelineFactory
Action executeResultHandler = PipelineFactory.newExecuteResultHandler();
Action assignResult = PipelineFactory.newAssignObjectAsResult();
assignResult.setReceiver( executeResultHandler );
Transformer outTransformer = PipelineFactory.newXStreamToXmlTransformer( BatchExecutionHelper.newXStreamMarshaller() );
outTransformer.setReceiver( assignResult );
KnowledgeRuntimeCommand batchExecution = PipelineFactory.newBatchExecutor();
batchExecution.setReceiver( outTransformer );
Transformer inTransformer = PipelineFactory.newXStreamFromXmlTransformer( BatchExecutionHelper.newXStreamMarshaller() );
inTransformer.setReceiver( batchExecution );
Pipeline pipeline = PipelineFactory.newStatelessKnowledgeSessionPipeline( ksession );
pipeline.setReceiver( inTransformer );
对一个规则集使用上面所述的,会更新一个Cheese事实的价格,下面给定的xml会插入一个使用了输出标识符(out-identifier)的Cheese实例。
例子 2.38 更新Cheese实事
<batch-execution>
<insert out-identifier='outStilton'>
<org.drools.Cheese>
<type>stilton</type>
<price>25</price>
<oldPrice>0</oldPrice>
</org.drools.Cheese>
</insert>
</batch-execution>
然后我们会得到BatchExecutionResults:
例子 2.39 更新Cheese实事
<batch-execution-results>
<result identifier='outStilton'>
<org.drools.Cheese>
<type>stilton</type>
<oldPrice>0</oldPrice>
<price>30</price>
</org.drools.Cheese>
</result>
</batch-execution-results>
2.2.3.17 编码
MarshallerFactory被用来编码和解码StatefulKnowledgeSessions。最简单的,它可以象下面这样使用:
例子 2.40 使用MarshallerFactory
// ksession is the StatefulKnowledgeSession
// kbase is the KnowledgeBase
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Marshaller marshaller = MarshallerFactory.newMarshaller( kbase );
marshaller.marshall( baos, ksession );
baos.close();
然而,在处理引用的用户数据时,你需要更有弹性地使用编码。要达成它,我们有一个ObjectMarshallingStrategy接口。我们提供了两个实现,但是用户可以自己实现它。提供的两个是IdentityMarshallingStrategy和SerializeMarshallingStrategy。默认为SerializeMarshallingStrategy,如上例所示,它只在一个用户实例上调用Serializable或Externalizable方法。而IdentityMarshallingStrategy为每个用户对象创建了一个整数id,并存储它们在一个映射中,该id被写入到该流中。在解码时,它只简单地查看IdentityMarshallingStrategy映射,取回该实例。这意味着,如果你使用IdentityMarshallingStrategy,它对编码实例的生命周期是有状态的,并且会创建ids,保持它企图编码的所有对象的引用。
例子 2.41 使用IdentityMarshallingStrategy编码
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Marshaller marshaller = MarshallerFactory.newMarshaller( kbase, new ObjectMarshallingStrategy[] { MarshallerFactory.newIdentityMarshallingStrategy() } );
marshaller.marshall( baos, ksession );
baos.close();
为增加弹性,我们不能想当然地认为单策略更合适,所以我们增加了一个ObjectMarshallingStrategyAcceptor 接口,每个ObjectMarshallingStrategy都有。编码器有一个策略链,并且当它企图读或写一个用户对象时,它遍历策略,询问它们是否承担负责编码用户对象。提供了一个实现为ClassFilterAcceptor。它允许使用字符和通匹符匹配类名。默认为"*.*",所以在上面使用的IdentityMarshallingStrategy,它有一个默认的"*.*"接收器。然而,比方说,我们希望序列化所有类,一个给定的包除外,这种情况,我们会使用身份查询,如下所示:
例子 2.42 使用身份查询
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectMarshallingStrategyAcceptor identityAceceptor = MarshallerFactory.newClassFilterAcceptor( new String[] { "org.domain.pkg1.*" } );
ObjectMarshallingStrategy identityStratetgy = MarshallerFactory.newIdentityMarshallingStrategy( identityAceceptor );
Marshaller marshaller = MarshallerFactory.newMarshaller( kbase, new ObjectMarshallingStrategy[] { identityStratetgy, MarshallerFactory.newSerializeMarshallingStrategy() } );
marshaller.marshall( baos, ksession );
baos.close();
2.2.3.18 知识代理
KnowlegeAgent由 KnowlegeAgentFactory创建。KnowlegeAgent提供自动加载、缓存和重新加载资源,并且根据一个属性文件配置它。当KnowlegeBase使用的资源更改时,KnowlegeAgent可以更新或重构KnowlegeBase。通过给定工厂的配置确定KnowlegeAgent的策略,但通常使用基于标准轮询的拉策略。我们希望增加基于推的更新,并在将来的版本中重构它。下面的例子,构建了一个代理,它根据在路径字符串中指定的文件构建了一个新的KnowledgeBase。它会每30秒拉这些文件,而不是更新存在的一个,因为 "newInstance" 设置为了"true" (然而,目前只支持"true" 值,并且很难编码到引擎中)。
例子 2.43 构建一个代理
// Set the interval on the ResourceChangeScannerService if you are to use it and default of 60s is not desirable.
ResourceChangeScannerConfiguration sconf = ResourceFactory.getResourceChangeScannerService().newResourceChangeScannerConfiguration();
sconf.setProperty( "drools.resource.scanner.interval",
"30" ); // set the disk scanning interval to 30s, default is 60s
ResourceFactory.getResourceChangeScannerService().configure( sconf );
KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
KnowledgeAgentConfiguration aconf = KnowledgeAgentFactory.newKnowledgeAgentConfiguration();
aconf.setProperty( "drools.agent.scanDirectories",