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",
"true" ); // we want to scan directories, not just files, turning this on turns on file scanning
aconf.setProperty( "drools.agent.newInstance",
"true" ); // resource changes results in a new instance of the KnowledgeBase being built,
// this cannot currently be set to false for incremental building
KnowledgeAgent kagent = KnowledgeAgentFactory.newKnowledgeAgent( "test agent", // the name of the agent
kbase, // the KnowledgeBase to use, the Agent will also monitor any exist knowledge definitions
aconf );
kagent.applyChangeSet( ResourceFactory.newUrlResource( url ) ); // resource to the change-set xml for the resources to add
KnowledgeAgents可以使用一个空的 KnowledgeBase或植入的一个。如果提供了一个植入的KnowledgeBase,KnowledgeAgent会遍历KnowledgeBase,并订阅它发现的资源。虽然KnowledgeBuilder可以构建在一个目录中发现的所有资源,但信息会被KnowledgeBuilder丢弃,因些那些目录不会继续被扫描。只有作为applyChangeSet(Resource)方法的一部分指定的目录才会被监控。
2.2.4 Drools Flow
Drools 4.0有简单的 "RuleFlow"用于组织规则 。Drools 5.0引入了一个强大(可扩大)的工作流引擎。它允许用户使用规则和流程(流程和规则的强力交互是可能的)指定他们的业务逻辑,并提供了一个统一的环境。
2.2.4.1 在一个特殊断点的流程实例视图
图2.31 规则流属性
图2.32 在一个工作流中的一个特殊断点中的当前活动节点
2.2.4.2 新节点
计时器:
可以增加一个计时器节点,它导致执行的节点等待一个特定时期。目前只使用JDK默认的初始延迟和重复延迟,更复杂的计时器可以会用于将来的里程碑中。
人类任务:
流程可以包括需要人类参与者来执行的任务。人类任务包括诸如任务名字、优先权、描述、参与者id等等参数。流程引擎可以使用我们的可插式工作项目(见后)方便地与存在的人类任务组件整合(例如,一个WS-HumanTask实现)。泳道和分配 (Swimlanes and assignment )规则也被支持。
在调色板的截图中显示了两个新组件,并且工作流本身显示了使用的人类任务。也显示了两个"work items",在下一节会被解释。
图2.33 人类任务
2.2.4.3 域特殊工作项目
域特殊工作项目是用户创建的有助于定制任务执行的可插式节点。它们提供了一个api用于在调色板中指定一个新的图标,以及用于该任务属性的gui编辑器。如果没有提供gui编辑器,那么则默认为基于键值对表单的一个文本。然后api允许执行那些指定的工作项目的行为。默认提供了Email 和Log 工作项目。有关如何实现它们,在Drools flow中已被更新。
下图显示了用于一个工作流中的三个不同的工作项目,"Blood Pressure", "BP Medication", "Notify GP":
图 2.34 工作项目
还有一个新的"Notification"工作项目
图 2.35 Notification
2.2.4.4 可扩展流程定义语言(ePDL)
Drools 4.0使用XStream存储它的内容,这是不容易人为编写的。Drools 5.0引入了ePDL,它是一个用于我们流程语言的特殊XML,它也允许域特殊扩展,在博客帖子“Drools的可扩展流程定义语言(ePDL)和语义模块框架(SMF)”中已被详细谈及。如下所示,XML语言的一个例子,使用了DSL扩展。
<process name="process name" id="process name" package-name="org.domain"
xmlns="http://drools.org/drools-4.0/process"
xmlns:mydsl="http://domain/org/mydsl"
xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
xs:schemaLocation="http://drools.org/drools-4.0/process drools-processes-4.0.xsd" >
<nodes>
<start id="0" />
<action id="1" dialect="java">
list.add( "action node was here" );
</action>
<mydsl:logger id="2" type="warn">
This is my message
<mydsl:logger>
<end id="3" />
</nodes>
<connections>
<connection from="0 to="1" />
<connection from="1" to="2" />
<connection from="2" to="3" />
</connections>
</process>
2.2.4.5 可插式节点
用于该框架的基本节点完全是可插式的,使它易于扩展和实现其他执行模式。我们已经有一个OSWorkflow的局部实现,并正和Deigo一起完成它,为OSWorkflow用户提供一个移值路径。其他的增强,包括异常域、在各种节点上包括进入和退出(on-entry and on-exit)行为的能力、集成了我们的用于长期运行流程的状态持久化的二进制持久化机制等等。了解更多内容请看Drools flow文档。
2.2.4.6 人类任务
在流程的上下文中人类任务的管理是十分重要的。因为我们允许用户插入他们喜欢的任何任务组件,所以我们开发了一个人类任务管理组件,用于支持基于WS - HumanTask规范的人类任务的整个生命周期。
2.2.4.7 Drools flow语言的新功能
n 事件节点允许一个流程响应一个外部事件。
n 异常处理器和异常处理器域用于处理可能被抛出的异常。
n ForEach节点允许你多次实例化你的流程段,为一个集合中的每个元素。
n 数据类型的支持已得到扩展。
n 普通的节点类型集成了定时器。
因此,新的节点类型和属性已被增加到了Eclipse中的Drools Flow 编辑器。在集成测试中,你可以发现这些新功能的例子(例如ProcessExceptionHandlerTest,ProcessTimerTest等)。
2.2.4.8 工作项目
我们的可插式工作项目方法,允许你以一个声明的方式,把域特殊(domain-specific )工作插入到你的流程中。我们计划构建一个普通工作项目的库,并且已经提供了用于发送电子邮件、查找文件、归档、执行系统命令、日志和人类任务的实现。
2.2.4.9 JPA
改进了对持久化(JPA)和事务(JTA)的支持。
例子 2..45 如何使用持久化和事件与流程结合的例子
// create a new JPA-based session and specify the JPA entity manager factory
Environment env = KnowledgeBaseFactory.newEnvironment();
env.set( EnvironmentName.ENTITY_MANAGER_FACTORY, Persistence.createEntityManagerFactory( "emf-name" ) );
env.set( EnvironmentName.TRANSACTION_MANAGER, TransactionManagerServices.getTransactionManager() );
StatefulKnowledgeSession ksession = JPAKnowledgeService.newStatefulKnowledgeSession( kbase, null, env ); // KnowledgeSessionConfiguration may be null, and a default will be used
int sessionId = ksession.getId();
// if no transaction boundary is specified, the method invocation is executed in a new transaction automatically
ProcessInstance processInstance = ksession.startProcess( "org.drools.test.TestProcess" );
// the users can also specify the transaction boundary themselves
UserTransaction ut = (UserTransaction) new InitialContext().lookup( "java:comp/UserTransaction" );
ut.begin();
ksession.insert( new Person( "John Doe" ) );
ksession.startProcess( "org.drools.test.TestProcess" );
ksession.fireAllRules();
ut.commit();
2.2.4.10 变量注入
支持在代码约束和动作中,在MVEL和Java中,直接访问流程变量,所以,在你的流程中有一个变量名为"person" ,你现在可以如下这样描述约束:
例子 2.46 变量注入例子
* [Java code constraint] return person.getAge() > 20;
* [MVEL action] System.out.println(person.name);
2.2.4.11 杂项改进
n 流程实例现在可以通过标记事件节点属性"external" 为ture,侦听外部事件。外部事件使用session.signalEvent(type, eventData)单独发送给引擎。在你的流程中如何使用事件,详情请看Drools Flow :https://hudson.jboss.org/hudson/job/drools/lastSuccessfulBuild/artifact/trunk/target/docs/drools-flow/html/ch03.html#d0e917
n 流程实例对多线程是安全的(因为多线程被阻止在相同的流程实例上工作)。
n 流程持久化/事务的支持已被进一步改进。详情请查看 drools-process/drools-process-enterprise项目
n 人类任务组件已被扩展来支持在任务执行期间的输入/输出/异常的各种数据。
例子 2.47 因此,任务客户端的生命周期的方法已经扩展为允许内容的数据
taskClient.addTask(task, contentData, responseHandler)
taskClient.complete(taskId, userId, outputData,responseHandler)
taskFail.complete(taskId, userId, outputData,responseHandler)
long contentId = task.getTaskData().getDocumentContentId();
taskClient.getContent(contentId, responseHandler);
ContentData content = responseHandler.getContent();
n 现在在编辑期间,可以移植老的Drools4 RuleFlows (使用xstream格式的) 到 Drools5 流程 (使用可读式的xml)。当以下系统属性设置:drools.ruleflow.port = ture时,在添加RuleFlow到KnowledgeBase时,移植会自动被执行。
n 增加了一个新类型的结合点(join),它将等待直到它的n中的m个连接已被完成。这个n既可以是在流程中的硬编码,也可以是基于该过程中的一个变量的值。
n 已做了改进,使持久化配置更容易。持久化方法是基于一个命令服务,确保了客户端的调用在一个事务内部被执行,并且在命令成功执行之后状态被存储在数据库中。虽然在M4版中已经可以直接使用该命令,我们还是扩展了它,目的在于除了可以简单地使用配置文件配置持久化外,人们可以使用标准的 StatefulKnowledgeSession接口。详情请查看Drools Flow 文档中有关持久化的部分。