Java規則引擎及JSR-94[轉]

 

 

規則引擎簡介

      Java規則引擎是推理引擎的一種,它起源於基於規則的專家系統。       Java規則引擎將業務決策從應用程序代碼中分離出來,並使用預約義的語義模塊編寫業務決策。Java規則引擎接受數據輸入,解釋業務規則,並根據規則做出業務決策。從這個意義上來講,它是軟件方法學在"關注點分離"上的一個重要的進展。       JSR-94規範定義了獨立於廠商的標準API,開發人員能夠經過這個標準的API使用Java規則引擎規範的不一樣產品實現。但值得注意的是,這個規範並無強制統一規則定義的語法,所以,當須要將應用移植到其餘的Java規則引擎實現時,可能須要變換規則定義。

基於規則的專家系統(RBES)

       專家系統是人工智能的一個分支,它模仿人類的推理方式,使用試探性的方法進行推理,並使用人類能理解的術語解釋和證實它的推理結論。專家系統有不少分類:神經網絡、基於案例推理和基於規則系統等。        規則引擎則是基於規則的專家系統的一部分。爲了更深刻的瞭解Java規則引擎,下面簡要地介紹基於規則的專家系統(RBES)。

RBES的技術架構

       RBES包括三部分:Rule Base(knowledge base)、Working Memory(fact base)和Rule Engine(推理引擎)。它們的結構以下所示:
 
圖1.基於規則的專家系統組成  
       如上圖所示,規則引擎包括三部分:Pattern Matcher、Agenda和Execution Engine。Pattern Matcher決定選擇執行哪一個規則,什麼時候執行規則;Agenda管理PatternMatcher挑選出來的規則的執行次序;Execution Engine負責執行規則和其餘動做。
 

RBES的推理(規則)引擎

       和人類的思惟相對應,規則引擎存在二者推理方式:演繹法(Forward-Chaining)和概括法(Backward-Chaining)。演繹法從一個初始的事實出發,不斷地應用規則得出結論(或執行指定的動做)。而概括法則是從假設出發,不斷地尋找符合假設的事實。        Rete算法是目前效率最高的一個Forward-Chaining推理算法,Drools項目是Rete算法的一個面向對象的Java實現。        規則引擎的推理步驟以下:        1. 將初始數據(fact)輸入Working Memory。        2. 使用Pattern Matcher比較規則(rule)和數據(fact)。        3. 若是執行規則存在衝突(conflict),即同時激活了多個規則,將衝突的規則放入衝突集合。        4. 解決衝突,將激活的規則按順序放入Agenda。        5. 使用規則引擎執行Agenda中的規則。重複步驟2至5,直到執行完畢全部Agenda中的規則。
 
JSR-94:Java規則引擎API        基於規則編程是一種聲明式的編程技術,這種技術讓你可使用試探性的規則而不是過程性的指令來解決問題。規則引擎是一個軟件模塊,它決定了如何將規則做用於推理數據。在 保險業和金融服務業都普遍地使用了基於規則的編程技術,當須要在大量的數據上應用複雜的規則時,規則引擎技術特別有用。        Java規則引擎API由javax.rules包定義,是訪問規則引擎的標準企業級API。Java規則引擎API容許客戶程序使用統一的方式和不一樣廠商的規則引擎產品交互,就像使用JDBC編寫獨立於廠商訪問不一樣的數據庫產品同樣。Java規則引擎API包括建立和管理規則集合的機制,在Working Memory中添加,刪除和修改對象的機制,以及初始化,重置和執行規則引擎的機制。
使用Java規則引擎API        Java規則引擎API把和規則引擎的交互分爲兩類:管理活動和運行時活動。管理活動包括實例化規則引擎和裝載規則。而運行時活動包括操做Working Memory和執行規則。若是你在J2SE環境中使用Java規則引擎,你可能須要在代碼中執行以上全部的活動。相反,在J2EE環境中,Java規則引擎的管理活動是應用服務器的一部分。JSR 94的參考實現包括了一個JCA鏈接器,用於經過JNDI得到一個RuleServiceProvider。
設置規則引擎        Java規則引擎的管理活動階段開始於查找一個合適的javax.rules.RuleServiceProvider對象,這個對象是應用程序訪問規則引擎的入口。在J2EE環境中,你可能能夠經過JNDI得到RuleServiceProvider。不然,你可使用javax.rules.RuleServiceProviderManager類:           
String implName = "org.jcp.jsr94.ri.RuleServiceProvider"; Class.forName(implName); RuleServiceProvider serviceProvider = RuleServiceProviderManager.getRuleServiceProvider(implName);

 

       一旦擁有了RuleServiceProvider對象,你能夠得到一個javax.rules.admin.RuleAdministrator類。從RuleAdministrator類中,你能夠獲得一個RuleExecutionSetProvider,從類名能夠知道,它用於建立javax.rules.RuleExecutionSets對象。RuleExecutionSet基本上是一個裝入內存的,準備好執行的規則集合。        包javax.rules.admin包括兩個不一樣的RuleExecutionSetProvider類。RuleExecutionSetProvider類自己包括了從Serializable對象建立RuleExecutionSets的方法,所以在規則引擎位於遠程服務器的狀況下,仍然可使用RuleExecutionSetProvider類,構造器的參數能夠經過RMI來傳遞。另外一個類是LocalRuleExecutionSetProvider,包含了其餘方法,用於從非Serializable資源(如java.io.Reader-本地文件)建立RuleExectionSets。假設擁有了一個RuleServiceProvider對象,你能夠從本地文件rules.xml文件建立一個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,並給它分配一個名稱。在運行時,你能夠用同一個名稱建立一個RuleSession;該RuleSession使用了這個命名的RuleExecutionSet。參見下面的例子: admin.registerRuleExecutionSet("rules", ruleSet, properties);
執行規則引擎        在運行時階段,你能夠參見一個RuleSession對象。RuleSession對象基本上是一個裝載了特定規則集合的規則引擎實例。你從RuleServiceProvider獲得一個RuleRuntime對象,接下來,從javax.rules.RuleRuntime獲得RuleSession對象。        RuleSession分爲兩類:stateful和stateless。它們具備不一樣的功能。StatefulRuleSession的Working Memory可以在多個方法調用期間保存狀態。你能夠在多個方法調用期間在Working Memory中加入多個對象,而後執行引擎,接下來還能夠加入更多的對象並再次執行引擎。相反,StatelessRuleSession類是不保存狀態的,爲了執行它的executeRules方法,你必須爲Working Memory提供全部的初始數據,執行規則引擎,獲得一個內容列表做爲返回值。        下面的例子中,咱們建立一個StatefulRuleSession實例,添加兩個對象(一個Integer和一個String)到Working Memory,執行規則,而後獲得Working Memory中全部的內容,做爲java.util.List對象返回。最後,咱們調用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產品實現        支持JSR 94規範的產品實現既有收費的商業產品,也有免費的開源項目。目前最爲成熟,功能最強大的商業產品是ILOG公司的JRules,該公司也是JSR 94規範的積極推進者之一。支持JSR 94規範的開源項目目前不多,只有Drools和JLisa項目。值得注意的是,Jess不是開源項目,它能夠免費用於學術研究,但用於商業用途則要收費。
JSR-94的產品實現         Java規則引擎商業產品有:            l. ILOG公司的JRules           2. BlazeSoft公司的Blaze           3. Rules4J           4. Java Expert System Shell (JESS)         開源項目的實現包括:           l. Drools項目
         2. JLisa項目          3. OFBiz Rule Engine(不支持JSR 94)          4. Mandarax(目前不支持JSR 94)
使用Spring集成        集成Java規則引擎的目標是,使用標準的Java規則引擎API封裝不一樣的實現,屏蔽不一樣的產品實現細節。這樣作的好處是,當替換不一樣的規則引擎產品時,能夠沒必要修改應用代碼。
封裝JSR-94實現
       RuleEngineFacade類封裝Java規則引擎,使用ruleServiceProviderUrl和ruleServiceProviderImpl兩個參數,屏蔽了不一樣產品的配置。代碼以下:
複製代碼
       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; } }
複製代碼

 

       
封裝規則 Rule類封裝了具體的業務規則,它的輸入參數ruleName是定義規則的配置文件名,並依賴於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); } }
複製代碼

 

組裝規則組件 組裝規則的配置文件以下:
複製代碼
<bean id="ruleEngine" class="spring.RuleEngineFacade" init-method="init" singleton="false"> <property name="ruleServiceProviderUrl"> <value>http://drools.org/</value> </property> <property name="ruleServiceProviderImpl"> <value>org.drools.jsr94.rules.RuleServiceProviderImpl</value> </property> </bean> <bean id="fibonacci" class="spring.Rule" init-method="init"> <property name="ruleName"> <value>/test/fibonacci.drl</value> </property> <property name="engineFacade"> <ref local="ruleEngine"/> </property> </bean>
複製代碼

 

測試用例 最後,咱們編寫測試用例,代碼以下:
複製代碼
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(); } }
複製代碼

 

 
運行測試用例,出現綠條,測試經過。
規則定義語言之間的變換        由於 JSR-94規範並無強制統一規則定義的語法,所以,當須要將應用移植到其餘的Java規則引擎實現時,可能須要變換規則定義,如將Drools私有的DRL規則語言轉換成標準的ruleML,Jess規則語言轉換成ruleML等。這個工做通常由XSLT轉換器來完成。
相關文章
相關標籤/搜索