以.drl为扩展名的文件,是Drools中的规则文件,规则文件的编写,遵循Drools规则语法。下面详细介绍一下Drools规则文件语法。具体参考官方文档: https://docs.jboss.org/drools/release/7.0.0.Final/drools-docs/html_single/index.html#_droolslanguagereferencechapter
DRL文件的总体结构以下:javascript
package package-name imports globals functions queries rules
对于上述元素,其顺序在drl文件中的顺序不重要,除了package-name
,必须是drl文件的第一个元素。上述全部的元素都是可选的,接下来咱们将详细介绍上述元素。php
一个简单的规则结构以下所示:css
rule "name" attributes when LHS then RHSend
一般,规则文件中是不须要标点符号的。即便是规则名“name”
上的双引号也是可选的。attributes
展现规则的执行方式,也是可选的。LHS
是规则的条件部分,遵循一个固定的语法规则,在下面的内容会详细介绍。RHS
一般是一个能够本地执行的代码块。html
从Drools 5开始,提出了“软”关键字和“硬”关键字的概念。硬关键字是保留的,不容许开发人员在编写规则文件时使用。硬关键字有以下几个:java
true
python
false
程序员
null
express
软关键字是在文件内容中公认的一些字,开发人员能够在任何地方使用这些关键字,可是为了消除混淆,并不推荐开发人员在实际中使用这些关键字。软关键字有以下几个:ruby
lock-on-activebash
date-effective
date-expires
no-loop
auto-focus
activation-group
agenda-group
ruleflow-group
entry-point
duration
package
import
dialect
salience
enabled
attributes
rule
extend
when
then
template
query
declare
function
global
eval
not
in
or
and
exists
forall
accumulate
collect
from
action
reverse
result
end
over
init
注释是规则文件中一段会被规则引擎自动忽略的一段文本。drl文件中的注释采用类Java语法的方式,能够分为两类:单行注释和多行注释。
单行注释能够简单的使用双斜杠"//"来标识。语法解析器会自动忽视其后的全部内容。样例以下:
rule "Testing Comments"when // this is a single line comment eval( true ) // this is a comment in the same line of a patternthen // this is a comment inside a semantic code blockend
另外须要注意的是,“#”开头的单行注释在drl文件中已经被弃用。
多行注释主要用于在代码块外对整个文件进行注释,以"/"开头和"/"结尾的之间全部的内容都会被语法解析器解释为注释。样例以下:
rule "Test Multi-line Comments"when /* this is a multi-line comment in the left hand side of a rule */ eval( true ) then /* and this is a multi-line comment in the right hand side of a rule */end
Drools 5 开始提出了标准的错误信息。标准化的目的是为了使开发者能够更准确更简单定位错误问题所在。接下来将介绍如正确理解和识别错误信息。
标准的错误信息格式以下所示。
ErrorMessageFormat.png
错误信息包含如下几个部分:
1st Block: 代表当前错误的错误码。
2st Block: 错误可能发生的行和列。
3st Block: 错误信息描述。
4st Block: 指明错误发生的规则,函数,模板,查询等。
5st Block: 指明错误发生于何种模式。通常不是强制性的。
101: No viable alternative
错误码101指明了最多见的错误,语法解析器没法找到替代方案。下面有一些常见的例子:
1: rule one 2: when 3: exists Foo() 4: exits Bar() // "exits"5: then6: end
上述示例会产生以下错误信息:
[ERR 101] Line 4:4 no viable alternative at input 'exits' in rule one
上述例子中的exits != exists
, 解析器找不到exits
的替代方案,因而报错。下面咱们能够再看一个例子。
1: package org.drools.examples;2: rule3: when4: Object()5: then6: System.out.println("A RHS");7: end
如今,上述的代码会产生以下错误信息:
[ERR 101] Line 3:2 no viable alternative at input 'WHEN'
这里when
是一个关键字,语法解析器在这里会遇到一个问题:rule没有文件名,而when
是一个规则的条件部分。所以报错。下面还有一个相同类型错误的示例:
1: rule simple_rule2: when3: Student( name == "Andy ) 4: then 5: end
这里双引号缺乏了另外一半,所以会报错。
102: Mismatched input
该错误代表,语法解析器在当前输入位置中未找到一个特定的符号。下面有一些例子:
1: rule simple_rule2: when3: foo3 : Bar(
上述示例会产生以下错误:
[ERR 102] Line 0:-1 mismatched input '<eof>' expecting ')' in rule simple_rule in pattern Bar
要解决上述问题,须要完善上述规则语句。接下来的实例会产生多个错误:
1: package org.drools.examples;2:3: rule "Avoid NPE on wrong syntax"4: when5: not( Cheese( ( type == "stilton", price == 10 ) || ( type == "brie", price == 15 ) ) from $cheeseList )6: then7: System.out.println("OK");8: end
上述代码会产生以下错误:
[ERR 102] Line 5:36 mismatched input ',' expecting ')' in rule "Avoid NPE on wrong syntax" in pattern Cheese
[ERR 101] Line 5:57 no viable alternative at input 'type' in rule "Avoid NPE on wrong syntax
该错误信息与前一个错误相关,这里只须要将```,```用```&&```替换就行了。 * **103: Failed predicate** 出现该错误信息的缘由是一个验证的语义谓词被验证为false。一般这些语义谓词用于识别软关键字。如下是一个示例:
1: package nesting;
2: dialect "mvel"
3:
4: import org.drools.compiler.Person
5: import org.drools.compiler.Address
6:
7: fdsfdsfds
8:
9: rule "test something"
10: when
11: p: Person( name=="Michael" )
12: then
13: p.name = "other";
14: System.out.println(p.name);
15: end
咱们能够获得以下错误信息: * ```[ERR 103] Line 7:0 rule 'rule_key' failed predicate: {(validateIdentifierKey(DroolsSoftKeywords.RULE))}? in rule
fdsfdsfds
是个无效的关键字,语法解析器没法将其识别成一个软关键字。
104: Trailing semi-colon not allowed
该错误信息与eval
从句相关,当分号不是出如今其表达式的结尾时可能报此错误。能够看一下以下示例。
1: rule simple_rule2: when3: eval(abc();)4: then5: end
因为在eval
中以分号结尾,咱们能够获得以下错误信息:
[ERR 104] Line 3:4 trailing semi-colon not allowed in rule simple_rule
该错误很容易修复,只要移除eval
中的分号便可。
105: Early Exit
drl文件中的子规则至少能被匹配选择一次,可是当子规则不能匹配任何东西的时候,就会报这个错。
如下是一个示例:
1: template test_error2: aa s 11;3: end
该示例会报如下错误信息:
[ERR 105] Line 2:2 required (…)+ loop did not match anything at input 'aa' in template test_error
Other Messages
开发中可能还会遇到一些其余意想不到的问题,这些问题能够向Drools的开发团队求助。
package
是一系列rule
或其余相关构件如imports, globals
的容器。这个成员之间相互关联,一个package
表明了一个命名空间,其中的每一个rule
的名字都是惟一的,package
名字自己就是命名空间,与实际的文件和文件夹没有任何关系。常见的结构是,一个文件包含多个rule
的文件就定义成一个包。如下的线路图代表了一个包中包含的全部组成元素。
package.png
须要注意的是,一个包必须有一个命名空间,且其声明必须遵照Java命名规范。package
语句必须出如今包的首行,其余组成部分的出现顺序可有可无。其中package
语句结尾的分号;
是可选的。
Drools文件中的import
语句功能与Java中的import
语句功能相似。使用import
时,必须指定对象的全称限定路径和类型名。Drools会自动导入Java相同名字的包中的全部类,也会导入java.lang.*
。
global.png
global
用于定义全局变量。用于使应用对象对一系列规则有效。一般,用于向规则提供全局的数据和服务,特别是一些用于规则序列的应用服务,如日志、规则序列中累加的值等。全局的变量是不会插入Woking Memory
中的,另外,全局变量不要用于创建规则的条件部分,除非它是一个不会改变的常量。所有变量的改变不会通知到规则引擎,规则引擎不跟踪全局变量的变化,由于他们并无加入到Woking Memory
中。全局变量使用不当,会产生不少难以想象的结果。若是多个包中同时定义了相同标识符的全局变量,那么这些全局变量必须是相同类型,并会引用一个相同的全局值。为了更好地使用全局变量,必须遵循如下规则:
在规则文件中定义常量,并使用这些常量。
global java.util.List myGlobalList; rule "Using a global"when eval( true ) then myGlobalList.add( "Hello World" ); end
在工做内存中,为全局变量设值。在从内存中获取全部的fact
以前,最好将全部的全局变量设置。例如:
List list = new ArrayList(); KieSession kieSession = kiebase.newKieSession(); kieSession.setGlobal( "myGlobalList", list );
全局变量并非用来在规则间共享数据,并且最好不要用于在规则间共享数据。规则老是对工做内存的状态产生推理和反应,所以,若是想在规则之间传递数据,能够将这些数据做为facts
传入工做内存。由于规则引擎并不会关心和跟踪这些全局变量的变化。
function.png
function
提供了一种在规则源文件中插入语义代码的方式,与在普通Java类中不一样。他们须要帮助类,不然不能作任何事情。(实际上,编译器会针对这些句子自动产生帮助类。)在规则中使用函数的最主要优势就是你能够把全部逻辑放在一个地方,你能够根据须要更改这些函数的逻辑。函数一般用于在规则的then
部分调用某些动做,特别是一些常常被用到的而传入参数不同的动做。
典型的function
格式以下:
function String hello(String name) { return "Hello "+name+"!"; }
须要注意的是,这里咱们使用了function
关键字,即便它并非Java语言的一部分。参数和返回值的定义和传入与Java语言一致。固然,参数和返回值并非必须的。另外,咱们可使用一个帮助类中的静态方法,如:Foo.hello()
。
Drools支持函数的导入,咱们所要作的就是:
import function my.package.Foo.hello
不须要考虑函数的定义和导入方式,咱们能够直接在须要的地方直接使用函数名调用这个函数。例如:
rule "using a static function"when eval( true )then System.out.println( hello( "Bob" ) );end
在规则引擎中,类型声明有两个目的:容许新的类型声明;容许元数据类型的声明。
声明新类型:Drools工做时会将外部的普通Java对象做为事实。可是有时候使用者想要本身定义一些规则引擎能够直接使用的模型,而不用担忧用Java之类的底层语言来建立对象。另外有时候,当一个域模型已经创建,可是用户想或者须要用一些主要在推理过程当中使用的实体来完善这个模型。
声明元数据:事实每每会有一些与之相关的元数据信息。元信息的样本包含的任何种类的数据都不能表明事实的属性,且在该事实类型的全部实例中都是不变的。这些元信息在规则引擎的运行和推理古城中须要被查询。
为了定义新类型,咱们须要使用关键字``declare,而后是一系列域,最终以
end关键字结尾。一个新的
fact必须有一系列域,不然规则引擎会去
classpath中寻找一个存在的
fact```类,若是没找到,会报错。下面给出定义新类型的几个例子。
Example . Declaring a new fact type: Address
declare Address number : int streetName : String city : Stringend
上面的例子中咱们定义了一个新类型Address
,这个fact
有三个属性,每一个属性都具备一个Java中有效的数据类型。一样的,咱们能够再定义一个数据类型:
Example . Declaring a new fact type: Person
declare Person name : String dateOfBirth : java.util.Date address : Addressend
咱们能够看一下上面的例子,dateOfBirth
是Java中的java.util.Date
类型,address
是咱们刚才声明的类型。为了避免用写全称限定名,咱们能够先使用import
来导入要使用的类型,例如:
Avoiding the need to use fully qualified class names by using import
import java.util.Date declare Person name : String dateOfBirth : Date address : Address end
当咱们声明一个新的fact
类型时,Drools会在编译期间生成实现自一个表示该fact
类型的Java类的字节码。这个生成的Java类
Example : generated Java class for the previous Person fact typedeclaration
public class Person implements Serializable { private String name; private java.util.Date dateOfBirth; private Address address; // empty constructor public Person() {...} // constructor with all fields public Person( String name, Date dateOfBirth, Address address ) {...} // if keys are defined, constructor with keys public Person( ...keys... ) {...} // getters and setters // equals/hashCode // toString}
该类型生成的class是一个普通的Java类,能够在规则中直接使用,就向其余 fact
同样。见以下例子:
Using the declared types in rules
rule "Using a declared Type"when $p : Person( name == "Bob" ) then // Insert Mark, who is Bob's mate. Person mark = new Person(); mark.setName("Mark"); insert( mark ); end
DRL同时支持声明枚举类型。该类型声明须要另一种关键字enum
,而后以都好分割可接收值的列表,最后以分号结束。
declare enum DaysOfWeek SUN("Sunday"),MON("Monday"),TUE("Tuesday"),WED("Wednesday"),THU("Thursday"),FRI("Friday"),SAT("Saturday"); fullName : String end
声明完成以后,该枚举类型能够用于以后的规则中。
rule "Using a declared Enum"when $p : Employee( dayOff == DaysOfWeek.MONDAY )then ...end
在Drools中元数据会被分配给一系列不一样对象的构造:fact
类型,fact
属性和规则。Drools使用@
符号来引出元数据,使用使用以下格式:
@metadata_key( metadata_value )
其中metadata_value
是可选的。
Drools容许声明任何任意元数据属性,可是当其余属性在运行时仅仅对查询有效时,有些属性对于规则引擎来讲具备不一样的意义。Drools容许为fact
类型和fact
属性声明元数据。全部的元数据在该属性被分配到fact
类型前声明,而在向一个特定属性分配值以前声明。
Example 115. Declaring metadata attributes for fact types and attributes
import java.util.Date declare Person @author( Bob ) @dateOfCreation( 01-Feb-2009 ) name : String @key @maxLength( 30 ) dateOfBirth : Date address : Address end
上面的例子中,声明了两个fact
类型(@author
和 @dateOfCreation
)的元数据元素,另外为name
属性声明了两个(@key
和@maxLength
)元数据。其中@key
没有必须值,全部括号和值均被省略了。
rule.png
一个规则,指定当(when
)一系列特定的条件发生时(左手边LHS),而后(then
)作出一系列相应的动做(右手边RHS)。一个常见的问题就是:为何使用when
而不是if
,由于if
一般是执行流程中特定时间点的一部分,是一个须要检查的条件。相反的,when
指明的是一个不绑定于任何特定判断序列或时间点的条件判断,它在规则引擎的声明周期内的任何一个条件发生的状况下均可能触发,无论这个条件是否遇到,这些动做都会执行。
一个规则在一个包中必须具备独一无二的名字。若是在一个DRL文件中重复定义两个相同名字的规则,在加载的时候就会报错。若是向包中添加一个名字已经存在的规则,该规则会覆盖掉以前的同名规则。若是一个规则命中存在空格符,最好使用双引号将规则名包括起来。
规则的属性不是必须的,且属性最好写成一行。
规则中的LHS在关键字when
的后面,一样的,RHS应该在关键字then
的后面,规则最后以关键字end
结尾。另外,规则不许嵌套。
Example . Rule Syntax Overview
rule "<name>" <attribute>*when <conditional element>*then <action>*end
Example . A simple rule
rule "Approve if not rejected" salience -100 agenda-group "approval" when not Rejection() p : Policy(approved == false, policyState:status) exists Driver(age > 25) Process(status == policyState) then log("APPROVED: due to no objections."); p.setApproved(true);end
rule attributes.png
规则属性显式地声明了对规则行为的影响,有些规则属性很简单,有些规则属性是复杂的子系统的一部分,如规则流。为了从Drools中得到更多东西,咱们须要确保对每个规则属性均有正确的认识。
经常使用的规则属性有以下:
no-loop
默认值:false
type: Boolean
当规则序列更改了一个fact
,会致使该规则会被从新触发,以致于产生一个无限循环。当设置为true时,当前规则只会被激活一次。
ruleflow-group
默认值:N/A
type: Stringruleflow
是Drools的特点之一,可让你本身控制规则的命中。同一个ruleflow-group
中的全部规则只有当该组激活时才能被命中。
lock-on-active
默认值:false
type: Boolean
无论什么时候ruleflow-group
和agenda-group
被激活,只要其中的全部规则将lock-on-active
设置为true,那么这些规则都不会再被激活,无论一开始怎么更新,这些匹配的规则都不会被激活。这是no-loop
属性的加强,由于这些变化如今不只仅是规则自身的变化。
salience
默认值:0
type: Integer
任何规则都有一个默认为0的salience
属性,该属性能够为0,正数和负数。salience
表示规则的优先级,值越大其在激活队列中的优先级越高。Drools支持使用动态的salience
,可使用一个包含动态约束变量的表达式来表示。以下所示
Dynamic Salience
rule "Fire in rank order 1,2,.." salience( -$rank ) when Element( $rank : rank,... ) then ...end
agenda-group
默认值:MAIN
type: Stringagenda-group
容许用户将Agenda分割成多个部分以提供更多的运行控制。
auto-focus
默认值:false
type: Boolean
当一个规则被激活时auto-focus
为true,并且该规则的agenda-group
尚未focus,当该agenda-group
focus时,容许该规则潜在命中。
activation-group
默认值:N/A
type: String
属于同一个activation-group的规则会进行惟一命中。也就是说同一个activation-group中的规则,只要有一个命中,其余的规则都会被取消激活状态,这样这些规则就不会被命中。
dialect
默认值:as specified by the package
type: String
dialect用于指明规则中使用的代码的语言种类,目前支持两种语言,"java"或"mvel"。
date-effective
默认值:N/A
type: String (包含日期和时间)
当前系统时间在date-effective以后,该规则才会被激活。
date-effective
默认值:N/A
type: String (包含日期和时间)
当前系统时间在date-effective以后,该规则不会再被激活。
duration
默认值:无
type: long (包含日期和时间)duration
用于表示一个规则在必定时间以后才会被命中,若是它仍是激活状态的话。
LHS是规则的条件部分的统称,由零到多条条件元素组成。若是LHS为空,默认为是条件部分一直为true。当一个新的WorkingMemory session建立的时候,会被激活和触发。
Example. Rule without a Conditional Element
rule "no CEs"when // emptythen ... // actions (executed once)end// The above rule is internally rewritten as:rule "eval(true)"when eval( true ) then ... // actions (executed once)end
LHS中的条件元素基于一个或多个模式,最经常使用的条件元素是and
。固然若是LHS中有多个不互相链接的模式时,默认使用隐式的and
。
Implicit and
rule "2 unconnected patterns"when Pattern1() Pattern2() then ... // actionsend// The above rule is internally rewritten as:rule "2 and connected patterns"when Pattern1() and Pattern2() then ... // actionsend
模式
模式是最终要的条件元素,它能够隐式地匹配全部插入到WorkingMemory中的全部fact
。一个模式具备0个或多个约束条件和一个可选的模式组合。模式的结构图以下所示:
Pattern.png
下面给出一个最简单的模式的例子
Person()
这里的类型为Person,该模式意味着将匹配WorkingMemory中的全部Person对象。该类型不须要是一个真实 fact
对象的类。模式能够指向超类甚至是接口,这样能够匹配多个不一样类的facts
。例如:
Object() // matches all objects in the working memory
模式的括号中条件定义了模式在何种条件下知足。以下所示:
Person( age == 100 )
为了引用匹配的对象,可使用一个模式绑定参数如:$p
。
Example . Pattern with a binding variable
rule ...when $p : Person()then System.out.println( "Person " + $p );end
$符号是非强制性的,只是用于在复杂的规则中方便标识,将其与变量及域区分开来。
约束
什么是约束?
约束是一个返回true
或false
的表达式,以下例所示:
Person( 5 < 6 ) // just an example, as constraints like this would be useless in a real pattern
约束本质上是一个与Java表达式稍微有点不一样的表达式,例如equals()
等价于==
。接下来咱们深刻理解一下。
Java Beans属性获取。
任何一个bean的属性均可以被直接使用,bean属性的获取也可使用标准的Java bean getter: getMyProperty() or isMyProperty()
。例如:
//use directlyPerson( age == 50 )// this is the same as:Person( getAge() == 50 )
同时,Drools还支持嵌套的属性获取方式,如:
//use directlyPerson( address.houseNumber == 50 )// this is the same as:Person( getAddress().getHouseNumber() == 50 )
固然,约束中的条件表达式是支持Java表达式的,下面几个例子都是正确的:
Person( age == 50 ) Person( age > 100 && ( age % 10 == 0 ) ) Person( Math.round( weight / ( height * height ) ) < 25.0 )
逗号分隔符 AND
逗号分隔约束,具备隐含的AND的含义。
// Person is at least 50 and weighs at least 80 kg Person( age > 50, weight > 80 ) // Person is at least 50, weighs at least 80 kg and is taller than 2 meter. Person( age > 50, weight > 80, height > 2 )
逗号运算符不能出如今复合的约束表达式中,如
// Do NOT do this: compile errorPerson( ( age > 50, weight > 80 ) || height > 2 ) // Use this insteadPerson( ( age > 50 && weight > 80 ) || height > 2 )
绑定变量
属性值能够绑定到一个变量中:
// 2 persons of the same agePerson( $firstAge : age ) // bindingPerson( age == $firstAge ) // constraint expression
分组访问嵌套对象属性
能够先看一个例子:
Person( name == "mark", address.city == "london", address.country == "uk" ) Person( name == "mark", address.( city == "london", country == "uk") )
也就是对嵌套对象属性的访问,能够组合在一个括号里面。
内联强制类型转换
当处理嵌套对象时,每每须要将其转换成子类,能够经过使用#
符号来完成。以下例所示:
Person( name == "mark", address#LongAddress.country == "uk" )
在该例子中将 Address
转换成LongAddress
。若是类型转换失败,该值会被认为是false。固然,类型转换也支持全称限定名称。以下所示:
Person( name == "mark", address#org.domain.LongAddress.country == "uk" )
固然,在同一个表达式中使用多级内联转换也是可行的。以下所示:
Person( name == "mark", address#LongAddress.country#DetailedCountry.population > 10000000 )
另外,Drools一样支持instanceof操做。
Person( name == "mark", address instanceof LongAddress, address.country == "uk" )
特殊文字支持
除了正常的Java文字,Drools还支持如下特殊的文字:
日期文字。
做者:圈圈_Master
☆
往期精彩
☆
03 精讲Spring Boot—入门+进阶+实例
关注我
天天进步一点点