以.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—入門+進階+實例
關注我
天天進步一點點