主要對(duì)于運(yùn)行相關(guān)內(nèi)容的介紹,又多了很多新的概念,還沒(méi)有著手去嘗試,感覺(jué)很多內(nèi)容確實(shí)有用,等我把這一章結(jié)束時(shí)候,實(shí)際跑一跑代碼感受一下。
甩鍋聲明:本人英語(yǔ)一般,翻譯只是為了做個(gè)筆記,所以有翻譯錯(cuò)誤的地方,錯(cuò)就錯(cuò)了,如果你想給我糾正,就給我留言,我會(huì)改過(guò)來(lái),如果懶得理我,就直接劃過(guò)即可。
?
運(yùn)行
KieBase
KieBase是所有應(yīng)用的知識(shí)定義倉(cāng)庫(kù)。它包含規(guī)則,過(guò)程,定義和模型類型,KieBase本身不包含數(shù)據(jù);相反,會(huì)話是被KieBase創(chuàng)建的,會(huì)話可以插入數(shù)據(jù)并啟動(dòng)實(shí)例流程。KieBase從包含定義了KieBase的KieModule的KieContainer處獲得。
示例16. 從KieContainer獲取KieBase
KieBase kBase = kContainer.getKieBase();
KieSession
KieSession存儲(chǔ)和執(zhí)行運(yùn)行時(shí)數(shù)據(jù),KieSession由KieBase創(chuàng)建。
圖示15. KieSession
示例17. 通過(guò)KieBase創(chuàng)建KieSession
KieSession ksession = kbase.newKieSession();
KieRuntime
KieRuntime
KieRuntime提供了既適用于規(guī)則又適用于過(guò)程的方法,就像設(shè)置全局變量和注冊(cè)通道。(“出口點(diǎn)”是“通道”的過(guò)時(shí)同義詞。)
圖示16. KieRuntime
全局變量
Globals是對(duì)Drools規(guī)則引擎可視對(duì)象的命名,但是使用這種方式與fact截然不同:全局對(duì)象的更改不會(huì)觸發(fā)規(guī)則的重新運(yùn)行。但是,全局變量對(duì)于提供靜態(tài)信息還是非常有幫助的,比如作為一個(gè)用于規(guī)則的右邊提供服務(wù)的對(duì)象,或者作為Drools規(guī)則引擎的返回值。如果你在一個(gè)規(guī)則的左手邊對(duì)象使用全局變量時(shí),請(qǐng)確保其是不變的,或者,至少意想不到的改變不會(huì)對(duì)你的規(guī)則的行為產(chǎn)生任何影響。
全局變量不許在規(guī)則文件中聲明,并且該全局變量需要有java對(duì)象的支持。
global java.util.List list
?由于Kie base已經(jīng)知道了全局變量的標(biāo)識(shí)和類型,現(xiàn)在可以通過(guò)調(diào)用ksession.setGlobal()方法,通過(guò)傳入全局變量的變量名和對(duì)象給全局變量賦值,對(duì)于任何session,都可以通過(guò)對(duì)象與全局變量關(guān)聯(lián)。DRL代碼中如果全局變量的類型和標(biāo)識(shí)聲明失敗,則會(huì)導(dǎo)致調(diào)用時(shí)拋出異常。
List<String> list = new ArrayList<>();
ksession.setGlobal("list", list);
在運(yùn)行規(guī)則之前,請(qǐng)確保設(shè)置了全局變量。否則會(huì)導(dǎo)致空指針異常。
事件模型
事件包提供了Drools規(guī)則引擎事件的通知方式,包括規(guī)則啟動(dòng),對(duì)象裝配等。這樣就允許將日志,審核與你程序的主要部分分離。
KieRuntimeEventManager接口被KieRuntime類實(shí)現(xiàn),KieRuntimeEventManager提供了兩個(gè)接口,RuleRuntimeEventManager和ProcessEventManager。我們這里只介紹RuleRuntimeEventManager。
圖示 17. KieRuntimeEventManager?
RuleRuntimeEventManager允許添加和移除監(jiān)聽(tīng)器,以便可以監(jiān)聽(tīng)工作內(nèi)存和日程。
?圖示18 RuleRuntimeEventManager
下面的代碼片段展示了,如何把一個(gè)簡(jiǎn)單的日程監(jiān)聽(tīng)器聲明并將其加入到session中去。當(dāng)他啟動(dòng)后,會(huì)打印出匹配的事件。
示例18.添加一個(gè)日程監(jiān)聽(tīng)器
ksession.addEventListener( new DefaultAgendaEventListener() {
public void afterMatchFired(AfterMatchFiredEvent event) {
super.afterMatchFired( event );
System.out.println( event );
}
});
Drools也提供了DebugRuleRuntimeEventListener 和DebugAgendaEventListener,使用調(diào)試打印語(yǔ)句實(shí)現(xiàn)了每一個(gè)方法。打印所有工作內(nèi)存事件,添加一個(gè)監(jiān)聽(tīng)器像下面這樣:
示例19. 添加一個(gè)DebugRuleRuntimeEventListener
ksession.addEventListener( new DebugRuleRuntimeEventListener() );
所有發(fā)出的事件都要實(shí)現(xiàn)KieRuntimeEvent接口,這個(gè)接口用于檢索實(shí)際的KnowkegeRuntime的事件源。
?圖示19. KieRuntimenEvent
當(dāng)前支持的事件有:
-
MatchCreatedEvent
-
MatchCancelledEvent
-
BeforeMatchFiredEvent
-
AfterMatchFiredEvent
-
AgendaGroupPushedEvent
-
AgendaGroupPoppedEvent
-
ObjectInsertEvent
-
ObjectDeletedEvent
-
ObjectUpdatedEvent
-
ProcessCompletedEvent
-
ProcessNodeLeftEvent
-
ProcessNodeTriggeredEvent
-
ProcessStartEvent
KieRuntimeLogger
KieRuntimeLogger使用了Drools中的綜合事件系統(tǒng)創(chuàng)造一個(gè)審計(jì)日志,該日志可以用于記錄應(yīng)用的執(zhí)行情況,然后可以使用類似于Eclipse的審計(jì)器對(duì)其進(jìn)行查看。
?圖示20.KieLoggers
示例20.FileLogger
KieRuntimeLogger logger =
KieServices.Factory.get().getLoggers().newFileLogger(ksession, "logdir/mylogfile");
...
logger.close();
命令和命令執(zhí)行器
KIE有有狀態(tài)會(huì)話和無(wú)狀態(tài)會(huì)話的概念。已經(jīng)介紹了使用標(biāo)準(zhǔn)KieRuntime的有狀態(tài)會(huì)話,會(huì)隨著時(shí)間推移進(jìn)行迭代處理。無(wú)狀態(tài)會(huì)話是對(duì)提供的數(shù)據(jù)集進(jìn)行KieRuntime的一次性執(zhí)行。
?圖示21 CommandExecutor
圖示22 ExecutionResults
CommandExecutor允許命令在會(huì)話中執(zhí)行,唯一不同的是無(wú)狀態(tài)會(huì)話會(huì)在會(huì)話銷毀結(jié)束之前,執(zhí)行fireAllRules方法。命令可以使用CommandExecutor創(chuàng)建。java手冊(cè)提供了允許使用CommandExecutor的命令的完整列表。
setGlobal和getGlobal是兩條跟Drools與jBPM都有關(guān)的命令。
Set Global調(diào)用下面的setGlobal。可選的布爾表示是否命令應(yīng)該返回作為ExecutionResults一部分的全局值。如果是true,則使用與全局名稱相同的名稱。如果需要替代名稱,則可以使用字符串而不是布爾值。
示例21. 設(shè)置Set Global命令
StatelessKieSession ksession = kbase.newStatelessKieSession();
ExecutionResults bresults =
ksession.execute( CommandFactory.newSetGlobal( "stilton", new Cheese( "stilton" ), true);
Cheese stilton = bresults.getValue( "stilton" );
允許已存在的全局變量返回,第二個(gè)可選字符串參數(shù)是返回值的名稱
示例22. Get Global命令
StatelessKieSession ksession = kbase.newStatelessKieSession();
ExecutionResults bresults =
ksession.execute( CommandFactory.getGlobal( "stilton" );
Cheese stilton = bresults.getValue( "stilton" );
前面所有的例子執(zhí)行都是單條命令。BatchExecution表示一條復(fù)雜命令,有一系列的命令組成創(chuàng)建。執(zhí)行時(shí)遍歷命令列表并按順序執(zhí)行。這就意味著你可以插入對(duì)象,開(kāi)啟程序,調(diào)用fireAllRules并且執(zhí)行查詢,所有都是在execute方法中調(diào)用,相當(dāng)強(qiáng)大。
無(wú)狀態(tài)會(huì)話會(huì)在結(jié)束的時(shí)候自動(dòng)執(zhí)行fireAllRules()。然而目光敏銳的讀者可能已經(jīng)注意到fireAllRules命令,并且想知道他是如何與無(wú)狀態(tài)會(huì)話一起工作的。這個(gè)FireAllRules命令允許設(shè)置不在會(huì)話結(jié)束時(shí)自動(dòng)執(zhí)行,可以將它看做是一個(gè)手動(dòng)操作的函數(shù)。
批處理中任何設(shè)置了out標(biāo)識(shí)符的命令都會(huì)將其結(jié)果添加到返回的ExecutionResults實(shí)例中。讓我們看一個(gè)簡(jiǎn)單的例子去了解一下如何工作的。為了說(shuō)明方便,例子中的命令包括了Drools和jBPM。例子的詳細(xì)介紹在Drools和jBPM相關(guān)章節(jié)中。
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" );
在前面的例子中,執(zhí)行了多條命令,其中有兩個(gè)使用了ExecutionResults。查詢命令默認(rèn)使用與查詢名稱相同的標(biāo)識(shí)符。但是也可以映射到不同的標(biāo)識(shí)上。
StatelessKieSession
StatelessKieSession封裝了KieSession,而不是繼承。他的主要關(guān)注點(diǎn)是決策服務(wù)的場(chǎng)景類型。他避免調(diào)用了dispose()方法。無(wú)狀態(tài)會(huì)話不支持迭代插入和從java代碼里調(diào)用fireAllRules方法;調(diào)用execute是一個(gè)單次行為,該行為會(huì)在內(nèi)部實(shí)例化一個(gè)KieSession,添加所有的用戶數(shù)據(jù), 執(zhí)行用戶命令,調(diào)用fireAllRule方法,并且最后調(diào)用dispose方法。雖然使用StatelessKieSession類的主要方式是通過(guò)實(shí)現(xiàn)了CommandExecutor接口的BatchExecutor(Command的子類),但是當(dāng)只是簡(jiǎn)單對(duì)象插入時(shí),會(huì)提供兩種方法使用。CommandExecutor和BatchExecution會(huì)在其各自的章節(jié)進(jìn)行詳細(xì)討論。
圖示23. StatelessKieSession
我們的簡(jiǎn)單示例展示了,無(wú)狀態(tài)會(huì)話使用方便的API執(zhí)行給定的java對(duì)象集合。會(huì)話會(huì)遍歷該集合,按順序插入每一個(gè)元素。
示例24. 使用集合執(zhí)行簡(jiǎn)單的StatelessKieSession
StatelessKieSession ksession = kbase.newStatelessKieSession();
ksession.execute( collection );
如果這種情況是單條命令,應(yīng)該按照下面的例子來(lái)做:
示例25. 使用插入元素命令執(zhí)行簡(jiǎn)單的StatelessKieSession
如果你想要插入集合本身和集合的每一條元素,CommandFactory.newInsert(collection)會(huì)幫你完成這個(gè)工作。
CommandFactory的方法創(chuàng)建支持的命令,所有的命令可以使用XStream和BatchExecutionHelper進(jìn)行編組。BatchExecutionHelper。BatchExecutionHelper提供了有關(guān)XML的詳細(xì)信息,以及如何使用Drools Pipeline自動(dòng)對(duì)BatchExecution和ExecutionResults進(jìn)行編組。
StatelessKieSession支持全局變量,其作用域有很多種。我們首先介紹非命令方式,因?yàn)槊畹淖饔糜蚴翘囟ǖ膱?zhí)行調(diào)用。全局變量的實(shí)現(xiàn)可以有三種方式。
- StatelessKieSessoin的方法getGlobal返回一個(gè)全局變量的實(shí)例,該實(shí)例提供了訪問(wèn)會(huì)話的全局變量。這些全局變量用于所有執(zhí)行調(diào)用。對(duì)于可變?nèi)肿兞恳裢庑⌒模驗(yàn)閳?zhí)行調(diào)用可以同時(shí)在不同的線程中調(diào)用。
實(shí)例26. 會(huì)話范圍的全局變量
StatelessKieSession ksession = kbase.newStatelessKieSession();
// Set a global hbnSession, that can be used for DB interactions in the rules.
ksession.setGlobal( "hbnSession", hibernateSession );
// Execute while being able to resolve the "hbnSession" identifier.
ksession.execute( collection );
- 使用委托是全局量的另一種解決方式。給全局變量賦值(使用setGlobal(String,Object)方法)會(huì)導(dǎo)致全局變量被保存在內(nèi)部集合中,將標(biāo)識(shí)符映射到值。在內(nèi)部集合中的標(biāo)識(shí)符會(huì)比其他委托的全局變量擁有優(yōu)先級(jí)。只有當(dāng)內(nèi)部集合沒(méi)有該標(biāo)識(shí)符的時(shí)候,委托全局變量(如果有的話)才會(huì)被使用。
- 第三種方式是擁有一個(gè)執(zhí)行范圍的全局變量。一個(gè)被設(shè)置了全局變量的命令傳遞給CommandExecutor。
CommandExecutor接口通過(guò)輸出參數(shù),也提供了到處數(shù)據(jù)的能力。插入的事實(shí),全局變量,查詢結(jié)果全部都可以被返回。
示例27. 輸出標(biāo)識(shí)符
// Set up a list of commands
List cmds = new ArrayList();
cmds.add( CommandFactory.newSetGlobal( "list1", new ArrayList(), true ) );
cmds.add( CommandFactory.newInsert( new Person( "jon", 102 ), "person" ) );
cmds.add( CommandFactory.newQuery( "Get People", "getPeople" ) );
// Execute the list
ExecutionResults results =
ksession.execute( CommandFactory.newBatchExecution( cmds ) );
// Retrieve the ArrayList
results.getValue( "list1" );
// Retrieve the inserted Person fact
results.getValue( "person" );
// Retrieve the query as a QueryResults instance.
results.getValue( "Get People" );
編組
KieMarshallers用于編組和解組KieSession。
圖示24. KieMarshallers
KieMarshallers的實(shí)例可以通KieService中檢索。最簡(jiǎn)單的方式如下:
示例28. 簡(jiǎn)單的Marshall例子
// ksession is the KieSession// kbase is the KieBase
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Marshaller marshaller = KieServices.Factory.get().getMarshallers().newMarshaller( kbase );
marshaller.marshall( baos, ksession );
baos.close();
但是,使用marshalling,當(dāng)處理用戶數(shù)據(jù)時(shí),你需要更靈活。使用ObjectMarshallingStrategy接口來(lái)到達(dá)這個(gè)靈活的目的。包里提供了兩種實(shí)現(xiàn)方式,但是用戶也可以自己去實(shí)現(xiàn)。兩種提供的方式是IdentityMarshallingStrategy和SerializeMarshallingStrategy。SerializeMarshallingStrategy是默認(rèn)的,就像上面例子展示的那樣,在用戶實(shí)例上調(diào)用Serializable或者Externalizable方法。
IdentityMarshallingStrategy為每一個(gè)用戶對(duì)象建立了一個(gè)整數(shù)id,并保存在一個(gè)Map里,同時(shí)將id寫(xiě)入流中。當(dāng)解組時(shí),這個(gè)id用來(lái)訪問(wèn)IdentityMarshallingStrategy的map,檢索實(shí)例。這就意味著,如果你使用IdentityMarshallingStrategy,編組實(shí)例的生命周期是有狀態(tài)的,并且創(chuàng)建id,為所有嘗試編組的對(duì)象保留引用。下面就是使用標(biāo)識(shí)編組策略的代碼。
示例29. IdentityMarshallingStrategy
ByteArrayOutputStream baos = new ByteArrayOutputStream();
KieMarshallers kMarshallers = KieServices.Factory.get().getMarshallers()
ObjectMarshallingStrategy oms = kMarshallers.newIdentityMarshallingStrategy()
Marshaller marshaller =
kMarshallers.newMarshaller( kbase, new ObjectMarshallingStrategy[]{ oms } );
marshaller.marshall( baos, ksession );
baos.close();
在大部分情況下,單條策略是不夠的,為了添加靈活,可以使用ObjectMarshallingStrategyAcceptor 接口。編組有一個(gè)策略鏈,當(dāng)讀或?qū)懸粋€(gè)用戶對(duì)象時(shí),這個(gè)策略鏈會(huì)遍歷策略,詢問(wèn)是否他們接受對(duì)用戶對(duì)象編組的責(zé)任。提供的實(shí)現(xiàn)之一是ClassFilterAcceptor。這個(gè)類允許使用字符串和通配符來(lái)匹配類名。默認(rèn)是“.”所以在前一個(gè)例子的標(biāo)識(shí)符編組策略用的就是默認(rèn)的“.”。
假設(shè)我們要序列化除了給定外的所有類,我們可以通過(guò)標(biāo)識(shí)查找,操作如下所示:
示例30. 帶接收者的IdentityMarshallingStrategy
ByteArrayOutputStream baos = new ByteArrayOutputStream();
KieMarshallers kMarshallers = KieServices.Factory.get().getMarshallers()
ObjectMarshallingStrategyAcceptor identityAcceptor =
kMarshallers.newClassFilterAcceptor( new String[] { "org.domain.pkg1.*" } );
ObjectMarshallingStrategy identityStrategy =
kMarshallers.newIdentityMarshallingStrategy( identityAcceptor );
ObjectMarshallingStrategy sms = kMarshallers.newSerializeMarshallingStrategy();
Marshaller marshaller =
kMarshallers.newMarshaller( kbase,
new ObjectMarshallingStrategy[]{ identityStrategy, sms } );
marshaller.marshall( baos, ksession );
baos.close();
注意,接收檢查的順序就是提供元素時(shí)的自然順序。
持久化和事物
使用Drools可以使用JPA實(shí)現(xiàn)開(kāi)箱即用的長(zhǎng)期持久化。需要安裝JTA的一些實(shí)現(xiàn)。用于開(kāi)發(fā)目的時(shí),可以使用Bitronix的事務(wù)管理器,因?yàn)樗脑O(shè)置和嵌入式工作很簡(jiǎn)單,但是對(duì)于生產(chǎn)使用來(lái)說(shuō),推薦使用JBoss Transactions。
示例31. 使用事務(wù)的簡(jiǎn)單例子
KieServices kieServices = KieServices.Factory.get();
Environment env = kieServices.newEnvironment();
env.set( EnvironmentName.ENTITY_MANAGER_FACTORY,
Persistence.createEntityManagerFactory( "emf-name" ) );
env.set( EnvironmentName.TRANSACTION_MANAGER,
TransactionManagerServices.getTransactionManager() );
// KieSessionConfiguration may be null, and a default will be used
KieSession ksession =
kieServices.getStoreServices().newKieSession( kbase, null, env );
int sessionId = ksession.getId();
UserTransaction ut =
(UserTransaction) new InitialContext().lookup( "java:comp/UserTransaction" );
ut.begin();
ksession.insert( data1 );
ksession.insert( data2 );
ksession.startProcess( "process1" );
ut.commit();
使用JPA,必須使用EntityManagerFactory和TransactionManager設(shè)置環(huán)境。如果發(fā)生回滾,ksession的狀態(tài)也需要回滾,所以ksession才能在回滾之后繼續(xù)使用。為了加載一個(gè)之前持久化了的KieSession,你需要那個(gè)KieSession的id, 像下面展示的那樣:
示例32.加載KieSession
KieSession ksession =
kieServices.getStoreServices().loadKieSession( sessionId, kbase, null, env );
為了能夠啟用多個(gè)類的持久化需要添加一個(gè)persistence.xml,像下面例子這樣:
示例33. 配置 JPA
<persistence-unit name="org.drools.persistence.jpa" transaction-type="JTA"><provider>org.hibernate.ejb.HibernatePersistence</provider><jta-data-source>jdbc/BitronixJTADataSource</jta-data-source><class>org.drools.persistence.info.SessionInfo</class><class>org.drools.persistence.info.WorkItemInfo</class><properties><property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/><property name="hibernate.max_fetch_depth" value="3"/><property name="hibernate.hbm2ddl.auto" value="update" /><property name="hibernate.show_sql" value="true" /><property name="hibernate.transaction.manager_lookup_class"value="org.hibernate.transaction.BTMTransactionManagerLookup" /></properties></persistence-unit>
首先jdbc JTA數(shù)據(jù)源必須配置,Bitronix提供了很多種配置方式,請(qǐng)查詢其文檔了解詳情。為了快速開(kāi)始,這里有一個(gè)程序化的方法:
示例34. 配置 JTA 數(shù)據(jù)源
PoolingDataSource fs = new PoolingDataSource();
fs.setUniqueName( "jdbc/BitronixJTADataSource" );
fs.setClassName( "org.h2.jdbcx.JdbcDataSource" );
fs.setMaxPoolSize( 3 );
fs.setAllowLocalTransactions( true );
fs.getDriverProperties().put( "user", "sa" );
fs.getDriverProperties().put( "password", "sasa" );
fs.getDriverProperties().put( "URL", "jdbc:h2:mem:mydb" );
fs.init();
Bitronix也提供了簡(jiǎn)單的JNDI嵌入服務(wù),非常適合測(cè)試。使用該服務(wù),需要添加一個(gè)jndi配置文件到META-INF文件夾中,并且在配置文件中添加下面這一行:文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-553929.html
示例35. JNDI配置文件文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-553929.html
java.naming.factory.initial=bitronix.tm.jndi.BitronixInitialContextFactory
到了這里,關(guān)于Drools用戶手冊(cè)翻譯——第三章 構(gòu)建,部署,應(yīng)用和運(yùn)行(三)運(yùn)行的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!