Java規(guī)則引擎的集成 "本文摘自starrynght的blog星月夜,作者首先介紹了javarule,然后提供了spring和商務(wù)規(guī)則引擎的集成。
規(guī)則引擎簡介 Java規(guī)則引擎是推理引擎的一種,它起源于基于規(guī)則的專家系統(tǒng)。 Java規(guī)則引擎將業(yè)務(wù)決策從應(yīng)用程序代碼中分離出來,并使用預(yù)定義的語義模塊編寫業(yè)務(wù)決策。Java規(guī)則引擎接受數(shù)據(jù)輸入,解釋業(yè)務(wù)規(guī)則,并根據(jù)規(guī)則作出業(yè)務(wù)決策。從這個意義上來說,它是軟件方法學(xué)在"關(guān)注點(diǎn)分離"上的一個重要的進(jìn)展。 JSR-94規(guī)范定義了獨(dú)立于廠商的標(biāo)準(zhǔn)API,開發(fā)人員可以通過這個標(biāo)準(zhǔn)的API使用Java規(guī)則引擎規(guī)范的不同產(chǎn)品實(shí)現(xiàn)。但值得注意的是,這個規(guī)范并沒有強(qiáng)制統(tǒng)一規(guī)則定義的語法,因此,當(dāng)需要將應(yīng)用移植到其他的Java規(guī)則引擎實(shí)現(xiàn)時,可能需要變換規(guī)則定義。 基于規(guī)則的專家系統(tǒng)(RBES) 專家系統(tǒng)是人工智能的一個分支,它模仿人類的推理方式,使用試探性的方法進(jìn)行推理,并使用人類能理解的術(shù)語解釋和證明它的推理結(jié)論。專家系統(tǒng)有很多分類:神經(jīng)網(wǎng)絡(luò)、基于案例推理和基于規(guī)則系統(tǒng)等。 規(guī)則引擎則是基于規(guī)則的專家系統(tǒng)的一部分。為了更深入的了解Java規(guī)則引擎,下面簡要地介紹基于規(guī)則的專家系統(tǒng)(RBES)。 RBES的技術(shù)架構(gòu) RBES包括三部分:Rule Base(knowledge base)、Working Memory(fact base)和Rule Engine(推理引擎)。它們的結(jié)構(gòu)如下系統(tǒng)所示: ![]() 如上圖所示,規(guī)則引擎包括三部分:Pattern Matcher、Agenda和Execution Engine。Pattern Matcher決定選擇執(zhí)行哪個規(guī)則,何時執(zhí)行規(guī)則;Agenda管理PatternMatcher挑選出來的規(guī)則的執(zhí)行次序;Execution Engine負(fù)責(zé)執(zhí)行規(guī)則和其他動作。 RBES的推理(規(guī)則)引擎 和人類的思維相對應(yīng),規(guī)則引擎存在兩者推理方式:演繹法(Forward-Chaining)和歸納法(Backward-Chaining)。演繹法從一個初始的事實(shí)出發(fā),不斷地應(yīng)用規(guī)則得出結(jié)論(或執(zhí)行指定的動作)。而歸納法則是從假設(shè)出發(fā),不斷地尋找符合假設(shè)的事實(shí)。 Rete算法是目前效率最高的一個Forward-Chaining推理算法,Drools項(xiàng)目是Rete算法的一個面向?qū)ο蟮腏ava實(shí)現(xiàn)。 規(guī)則引擎的推理步驟如下: 1. 將初始數(shù)據(jù)(fact)輸入Working Memory。 2. 使用Pattern Matcher比較規(guī)則(rule)和數(shù)據(jù)(fact)。 3. 如果執(zhí)行規(guī)則存在沖突(conflict),即同時激活了多個規(guī)則,將沖突的規(guī)則放入沖突集合。 4. 解決沖突,將激活的規(guī)則按順序放入Agenda。 5. 使用規(guī)則引擎執(zhí)行Agenda中的規(guī)則。重復(fù)步驟2至5,直到執(zhí)行完畢所有Agenda中的規(guī)則。 JSR 94:Java規(guī)則引擎API 基于規(guī)則編程是一種聲明式的編程技術(shù),這種技術(shù)讓你可以使用試探性的規(guī)則而不是過程性的指令來解決問題。規(guī)則引擎是一個軟件模塊,它決定了如何將規(guī)則作用于推理數(shù)據(jù)。在保險業(yè)和金融服務(wù)業(yè)都廣泛地使用了基于規(guī)則的編程技術(shù),當(dāng)需要在大量的數(shù)據(jù)上應(yīng)用復(fù)雜的規(guī)則時,規(guī)則引擎技術(shù)特別有用。 Java規(guī)則引擎API由javax.rules包定義,是訪問規(guī)則引擎的標(biāo)準(zhǔn)企業(yè)級API。Java規(guī)則引擎API允許客戶程序使用統(tǒng)一的方式和不同廠商的規(guī)則引擎產(chǎn)品交互,就像使用JDBC編寫?yīng)毩⒂趶S商訪問不同的數(shù)據(jù)庫產(chǎn)品一樣。Java規(guī)則引擎API包括創(chuàng)建和管理規(guī)則集合的機(jī)制,在Working Memory中添加,刪除和修改對象的機(jī)制,以及初始化,重置和執(zhí)行規(guī)則引擎的機(jī)制。 使用Java規(guī)則引擎API Java規(guī)則引擎API把和規(guī)則引擎的交互分為兩類:管理活動和運(yùn)行時活動。管理活動包括實(shí)例化規(guī)則引擎和裝載規(guī)則。而運(yùn)行時活動包括操作Working Memory和執(zhí)行規(guī)則。如果你在J2SE環(huán)境中使用Java規(guī)則引擎,你可能需要在代碼中執(zhí)行以上所有的活動。相反,在J2EE環(huán)境中,Java規(guī)則引擎的管理活動是應(yīng)用服務(wù)器的一部分。JSR 94的參考實(shí)現(xiàn)包括了一個JCA連接器,用于通過JNDI獲得一個RuleServiceProvider。 設(shè)置規(guī)則引擎 Java規(guī)則引擎的管理活動階段開始于查找一個合適的javax.rules.RuleServiceProvider對象,這個對象是應(yīng)用程序訪問規(guī)則引擎的入口。在J2EE環(huán)境中,你可能可以通過JNDI獲得RuleServiceProvider。否則,你可以使用javax.rules.RuleServiceProviderManager類: javax.rules.RuleServiceProviderManager class: String implName = "org.jcp.jsr94.ri.RuleServiceProvider"; Class.forName(implName); RuleServiceProvider serviceProvider = RuleServiceProviderManager.getRuleServiceProvider(implName); 一旦擁有了RuleServiceProvider對象,你可以獲得一個javax.rules.admin.RuleAdministrator類。從RuleAdministrator類中,你可以得到一個RuleExecutionSetProvider,從類名可以知道,它用于創(chuàng)建javax.rules.RuleExecutionSets對象。RuleExecutionSet基本上是一個裝入內(nèi)存的,準(zhǔn)備好執(zhí)行的規(guī)則集合。 包javax.rules.admin包括兩個不同的RuleExecutionSetProvider類。RuleExecutionSetProvider類本身包括了從Serializable對象創(chuàng)建RuleExecutionSets的方法,因此在規(guī)則引擎位于遠(yuǎn)程服務(wù)器的情況下,仍然可以使用RuleExecutionSetProvider類,構(gòu)造器的參數(shù)可以通過RMI來傳遞。另一個類是LocalRuleExecutionSetProvider,包含了其他方法,用于從非Serializable資源(如java.io.Reader-本地文件)創(chuàng)建RuleExectionSets。假設(shè)擁有了一個RuleServiceProvider對象,你可以從本地文件rules.xml文件創(chuàng)建一個RuleExectionSet對象。如以下的代碼所示: RuleAdministrator admin = serviceProvider.getRuleAdministrator(); HashMap properties = new HashMap(); properties.put("name", "My Rules"); properties.put("description", "A trivial rulebase"); FileReader reader = new FileReader("rules.xml"); RuleExecutionSet ruleSet = null; try { LocalRuleExecutionSetProvider lresp = admin.getLocalRuleExecutionSetProvider(properties); ruleSet = lresp.createRuleExecutionSet(reader, properties); } finally { reader.close(); } 接下來,你可以使用RuleAdministrator注冊獲得的RuleExecutionSet,并給它分配一個名稱。在運(yùn)行時,你可以用同一個名稱創(chuàng)建一個RuleSession;該RuleSession使用了這個命名的RuleExecutionSet。參見下面的例子: admin.registerRuleExecutionSet("rules", ruleSet, properties); 執(zhí)行規(guī)則引擎 在運(yùn)行時階段,你可以參見一個RuleSession對象。RuleSession對象基本上是一個裝載了特定規(guī)則集合的規(guī)則引擎實(shí)例。你從RuleServiceProvider得到一個RuleRuntime對象,接下來,從javax.rules.RuleRuntime得到RuleSession對象。 RuleSession分為兩類:stateful和stateless。它們具有不同的功能。StatefulRuleSession的Working Memory能夠在多個方法調(diào)用期間保存狀態(tài)。你可以在多個方法調(diào)用期間在Working Memory中加入多個對象,然后執(zhí)行引擎,接下來還可以加入更多的對象并再次執(zhí)行引擎。相反,StatelessRuleSession類是不保存狀態(tài)的,為了執(zhí)行它的executeRules方法,你必須為Working Memory提供所有的初始數(shù)據(jù),執(zhí)行規(guī)則引擎,得到一個內(nèi)容列表作為返回值。 下面的例子中,我們創(chuàng)建一個StatefulRuleSession實(shí)例,添加兩個對象(一個Integer和一個String)到Working Memory,執(zhí)行規(guī)則,然后得到Working Memory中所有的內(nèi)容,作為java.util.List對象返回。最后,我們調(diào)用release方法清理RuleSession: RuleRuntime runtime = rsp.getRuleRuntime(); StatefulRuleSession session = (StatefulRuleSession) runtime.createRuleSession("rules", properties, RuleRuntime.STATEFUL_SESSION_TYPE); session.addObject(new Integer(1)); session.addObject("A string"); session.executeRules(); List results = session.getObjects(); session.release(); 集成JSR 94產(chǎn)品實(shí)現(xiàn) 支持JSR 94規(guī)范的產(chǎn)品實(shí)現(xiàn)既有收費(fèi)的商業(yè)產(chǎn)品,也有免費(fèi)的開源項(xiàng)目。目前最為成熟,功能最強(qiáng)大的商業(yè)產(chǎn)品是ILOG公司的JRules,該公司也是JSR 94規(guī)范的積極推動者之一。支持JSR 94規(guī)范的開源項(xiàng)目目前很少,只有Drools項(xiàng)目。值得注意的是,Jess不是開源項(xiàng)目,它可以免費(fèi)用于學(xué)術(shù)研究,但用于商業(yè)用途則要收費(fèi)。 JSR 94的產(chǎn)品實(shí)現(xiàn) Java規(guī)則引擎商業(yè)產(chǎn)品有: l. ILOG公司的JRules 2. BlazeSoft公司的Blaze 3. Rules4J 4. Java Expert System Shell (JESS) 開源項(xiàng)目的實(shí)現(xiàn)包括: l. Drools項(xiàng)目 2. OFBiz Rule Engine(不支持JSR 94) 3. Mandarax(目前不支持JSR 94) 使用Spring集成 集成Java規(guī)則引擎的目標(biāo)是,使用標(biāo)準(zhǔn)的Java規(guī)則引擎API封裝不同的實(shí)現(xiàn),屏蔽不同的產(chǎn)品實(shí)現(xiàn)細(xì)節(jié)。這樣做的好處是,當(dāng)替換不同的規(guī)則引擎產(chǎn)品時,可以不必修改應(yīng)用代碼。 封裝JSR94實(shí)現(xiàn) RuleEngineFacade類封裝Java規(guī)則引擎,使用ruleServiceProviderUrl和ruleServiceProviderImpl兩個參數(shù),屏蔽了不同產(chǎn)品的配置。代碼如下: public class RuleEngineFacade { private RuleAdministrator ruleAdministrator; private RuleServiceProvider ruleServiceProvider; private LocalRuleExecutionSetProvider ruleSetProvider; private RuleRuntime ruleRuntime; // configuration parameters private String ruleServiceProviderUrl; private Class ruleServiceProviderImpl; public void setRuleServiceProviderUrl(String url) { this.ruleServiceProviderUrl = url; } public void setRuleServiceProviderImpl(Class impl) { this.ruleServiceProviderImpl = impl; } public void init() throws Exception { RuleServiceProviderManager.registerRuleServiceProvider( ruleServiceProviderUrl, ruleServiceProviderImpl); ruleServiceProvider = RuleServiceProviderManager.getRuleServiceProvider(ruleServiceProviderUrl); ruleAdministrator = ruleServiceProvider.getRuleAdministrator(); ruleSetProvider = ruleAdministrator.getLocalRuleExecutionSetProvider(null); } public void addRuleExecutionSet(String bindUri,InputStream resourceAsStream) throws Exception { Reader ruleReader = new InputStreamReader(resourceAsStream); RuleExecutionSet ruleExecutionSet = ruleSetProvider.createRuleExecutionSet(ruleReader, null); ruleAdministrator.registerRuleExecutionSet(bindUri,ruleExecutionSet,null); } public StatelessRuleSession getStatelessRuleSession(String key) throws Exception { ruleRuntime = ruleServiceProvider.getRuleRuntime(); return (StatelessRuleSession) ruleRuntime.createRuleSession(key, null, RuleRuntime.STATELESS_SESSION_TYPE); } public StatefulRuleSession getStatefulRuleSession(String key) throws Exception { ruleRuntime = ruleServiceProvider.getRuleRuntime(); return (StatefulRuleSession) ruleRuntime.createRuleSession( key, null, RuleRuntime.STATEFUL_SESSION_TYPE); } public RuleServiceProvider getRuleServiceProvider() { return this.ruleServiceProvider; } } 封裝規(guī)則 Rule類封裝了具體的業(yè)務(wù)規(guī)則,它的輸入?yún)?shù)ruleName是定義規(guī)則的配置文件名,并依賴于RuleEngineFacade組件。代碼如下: public class Rule { private String ruleName; private RuleEngineFacade engineFacade; public void init() throws Exception { InputStream is = Rule.class.getResourceAsStream(ruleName); engineFacade.addRuleExecutionSet(ruleName, is); is.close(); } public void setRuleName(String name) { this.ruleName = name; } public void setEngineFacade(RuleEngineFacade engine) { this.engineFacade = engine; } public StatelessRuleSession getStatelessRuleSession() throws Exception { return engineFacade.getStatelessRuleSession(ruleName); } public StatefulRuleSession getStatefuleRuleSession() throws Exception { return engineFacade.getStatefulRuleSession(ruleName); } } 組裝規(guī)則組件 組裝規(guī)則的配置文件如下: http:/// org.drools.jsr94.rules.RuleServiceProviderImpl /test/fibonacci.drl 測試用例 最后,我們編寫測試用例,代碼如下: public class JSRTest extends TestCase { ApplicationContext ctx = null; protected void setUp() throws Exception { super.setUp(); ctx = new FileSystemXmlApplicationContext("testrule.xml"); } public void testGetRuleSession() throws Exception { Rule rule = (Rule)ctx.getBean("fibonacci"); assertNotNull(rule.getStatefuleRuleSession()); assertNotNull(rule.getStatelessRuleSession()); } public void testStatelessRule() throws Exception { Rule rule = (Rule)ctx.getBean("fibonacci"); Fibonacci fibonacci = new Fibonacci(50); List list = new ArrayList(); list.add(fibonacci); StatelessRuleSession session = rule.getStatelessRuleSession(); session.executeRules(list); session.release(); } public void testStatefulRule() throws Exception { Rule rule = (Rule)ctx.getBean("fibonacci"); Fibonacci fibonacci = new Fibonacci(50); StatefulRuleSession session = rule.getStatefuleRuleSession(); session.addObject(fibonacci); session.executeRules(); session.release(); } } 運(yùn)行測試用例,出現(xiàn)綠條,測試通過。 規(guī)則定義語言之間的變換 因?yàn)镴SR 94規(guī)范并沒有強(qiáng)制統(tǒng)一規(guī)則定義的語法,因此,當(dāng)需要將應(yīng)用移植到其他的Java規(guī)則引擎實(shí)現(xiàn)時,可能需要變換規(guī)則定義,如將Drools私有的DRL規(guī)則語言轉(zhuǎn)換成標(biāo)準(zhǔn)的ruleML,Jess規(guī)則語言轉(zhuǎn)換成ruleML等。這個工作一般由XSLT轉(zhuǎn)換器來完成。" |
|
|