import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import BusinessLayer;
/**
* Sample Struts action with Pseudocode
* 使用伪代码的Struts行ؓCZ
*/
public class SampleStrutsAction extends Action{
/**
* Standard Struts doPerfom method
* 标准的Struts doPerformҎ(gu)
*/
public ActionForward doPerform(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws InvalidEntryPointException {
//Local Variables
//本地变量
StockOffer userOffer =null;
//Get any previous values from the session
//从session取得以前的数?br /> userOffer=(StockOffer)request.getSession()
.getAttribute("PREVIOUS_STOCK_OFFER");
//create this object if it is null
//如ؓnull则创建新对象
if (null==userOffer){
userOffer = new StockOffer();
}
//Update with the incoming values
//用上送的数据更新
//These values match those on the form
//q些数据是与form中的数据相对应的
userOffer.setStockName(request.
getParameterValue("STOCK_NAME"));
userOffer.setStockPrice(request
.getParameterValue("STOCK_PRICE"));
userOffer.setStockQuantity(request
.getParameterValue("STOCK_QTY"));
//Reset the output value
//重置输出数据
userOffer.setRecommendPurchase(null);
//Call the Business Layer
//调用商业?br /> BusinessLayer
.evaluateStockPurchase(userOffer);
//Forward to the appropriate page
//转向合适的面
if ("YES".equals(
testOffer.getRecommendPurchase()){
return mapping.findForward("YES_WEB_PAGE");
}
//otherwise default to the no page
//否则指向无此面
return mapping.findForward("NO_WEB_PAGE");
}
}
/**
* Defines a Data Access Object - a non data
* source specific way of obtaining data.
* 定义一个数据存取对象-一U非数据源获取数据的Ҏ(gu)
*/
public interface StockNameDao {
/**
* Get a list of stock names for the application
* @return String[] array of stock names
* 得到一个股名字的列表
* q回股票名称的String[]数组
*/
public String [] getStockNames();
/**
* Check if our stock is on the list
* 查股是否在列表?br /> * @param stockName
* @return
*/
public boolean isOnStockList(String stockName);
}
And here's the DaoImplementation:
q是DaoImplementation:
/**
* Concrete Definition of a Data Access Object
* 数据存取对象的具体定?br /> */
public class DaoImplementation
implements StockNameDao {
/**
* Constructor with package level access only
* to encourage use of factory method
* q里的构造器只是让你使用工厂(factory)Ҏ(gu)
*/
DaoImplementation(){}
/**
* Get a list of stock names for the app.
* This is a hard coded sample
* normally we would get this from
* a database or other datasource.
* 得到一个股名字的列表Q这只是一个硬~码的例子,一般来
* 说我们应该从数据库或其它数据源取得数?br /> * @return String[] array of stock names
*/
public String[] getStockNames() {
String[] stockNames=
{"XYZ","ABC","MEGACORP","SOMEOTHERCOMPANY"};
return stockNames;
}
/**
* Check if our stock is on the list
* 查我们的股票是否在列表中
* @param stockName
* @return true / false as appropriate
*/
public boolean isOnStockList(String stockName){
//Get our list of stocks
//获取股票列表
String stockList[] = getStockNames();
//Loop and see if our stock is on it
// done this way for clarity . not speed!
//循环看股是否存在,q样做是Z清晰不是速度!
for (int a=0; a<stockList.length;a++){
if(stockList[a].equals(stockName)){
return true;
}
}
//Default return value
return false;
}
}
package net.firstpartners.rp;
/**
* Factory Method to get the Data Access Object.
* Normally we could replace this with a
* framework like Spring or Hibernate
* 得到数据存取对象的工厂方法,通常我们可以它替换为像Spring?br />* Hibernatteq样的框?br /> */
public class DaoFactory {
/**
* Get the stock name Dao
* This sample is hardcoded - in reality
* we would make this configurable / cache
* instances of the Dao as appropriate
* 得到股票名字的Dao,q个例子是硬~码的-实际上我们可以让它成?br /> * 可配的,~存的合适的Dao对象?br /> * @return an instance of StockNameDao
*/
public static StockNameDao getStockDao(){
return new DaoImplementation();
}
}
<?xml version="1.0"?>
<rule-set name="BusinessRulesSample"
xmlns="http://drools.org/rules"
xmlns:java="http://drools.org/semantics/java"
xmlns:xs="
http://www.w3.org/2001/XMLSchema-instance"
xs:schemaLocation="
http://drools.org/rules rules.xsd
http://drools.org/semantics/java java.xsd">
<!-- Import the Java Objects that
we refer to in our rules -->
<!-- 导入规则中用的对象 -->
<java:import>
java.lang.Object
</java:import>
<java:import>
java.lang.String
</java:import>
<java:import>
net.firstpartners.rp.StockOffer
</java:import>
<java:import>
net.firstpartners.rp.DaoFactory
</java:import>
<java:import>
net.firstpartners.rp.StockNameDao
</java:import>
<!-- Application Data not associated -->
<!-- with any particular rule -->
<!-- In this case it's our factory -->
<!-- object which gives us back -->
<!-- a handle to whatever Dao (Data -->
<!-- access object) that we need -->
<!-- 没有和Q何规则联pȝ应用数据Q这里是我们的工厂对象,-->
<!—它向我们提供向后的操作,告诉我们什么Dao 是我们需要的?->
<application-data
identifier="daoFactory">DaoFactory
</application-data>
<!-- A Java (Utility) function -->
<!-- 一个JavaҎ(gu) -->
<!-- we reference in our rules -->
<!-- 在我们的规则中打印跟t信?-->
<java:functions>
public void printStock(
net.firstpartners.rp.StockOffer stock)
{
System.out.println(
"Name:"+stock.getStockName()
+" Price: "+stock.getStockPrice()
+" BUY:"+stock.getRecommendPurchase());
}
</java:functions>
<!-- Check for XYZ Corp-->
<!-- 查XYZ公司 -->
<rule name="XYZCorp" salience="-1">
<!-- Parameters we can pass into-->
<!-- the business rule -->
<!-- 可以传入规则中的参数 -->
<parameter identifier="stockOffer">
<class>StockOffer</class>
</parameter">
<!-- Conditions that must be met for -->
<!-- business rule to fire -->
<!-- Ȁz规则必L的条g -->
<java:condition>
stockOffer.getStockName().equals("XYZ")
</java:condition>
<java:condition>
stockOffer.getRecommendPurchase() == null
</java:condition>
<java:condition>
stockOffer.getStockPrice() > 10
</java:condition>
<!-- What happens when the business -->
<!-- rule is activated -->
<!-- 规则Ȁzd执行的步?-->
<java:consequence>
stockOffer.setRecommendPurchase(
StockOffer.NO);
printStock(stockOffer);
</java:consequence>
</rule>
<!-- Ensure that negative prices -->
<!-- are not accepted -->
<!-- 定负数不被接受 -->
<rule name="Stock Price Not Negative">
<!-- Parameters we can pass into the -->
<!-- business rule -->
<!-- 可以传入规则中的参数 -->
<parameter identifier="stockOffer">
<class>StockOffer</class>
</parameter>
<!-- Conditions for rule to fire -->
<!-- Ȁz规则必L的条g -->
<java:condition>
stockOffer.getStockPrice() < 0
</java:condition>
<!--When rule is activated then ... -->
<!-- 规则Ȁzd执行的步?-->
<java:consequence>
stockOffer.setRecommendPurchase
(StockOffer.NO);
printStock(stockOffer);
</java:consequence>
</rule>
<!-- Check for Negative Prices-->
<!-- L低h(hun) -->
<rule name="Stock Price Low Enough">
<!-- Parameters for the rule -->
<!-- 可以传入规则中的参数 -->
<parameter identifier="stockOffer">
<class>StockOffer</class>
</parameter>
<!-- Now uses Dao to get stock list -->
<!-- 现在使用Dao获取股票列表 -->
<java:condition>
daoFactory.getStockDao().isOnStockList(
stockOffer.getStockName())
</java:condition>
<java:condition>
stockOffer.getRecommendPurchase() == null
</java:condition>
<java:condition>
stockOffer.getStockPrice() < 100
</java:condition>
<!-- When rule is activated do this -->
<!-- 规则Ȁzd执行的步?-->
<java:consequence>
stockOffer.setRecommendPurchase(
StockOffer.YES);
printStock(stockOffer);
</java:consequence>
</rule>
</rule-set>
if ((user.isMemberOf(AdministratorGroup)
&& user.isMemberOf(teleworkerGroup))
|| user.isSuperUser(){
// 更多对特D案例的?br /> if((expenseRequest.code().equals("B203")
||(expenseRequest.code().equals("A903")
&&(totalExpenses<200)
&&(bossSignOff> totalExpenses))
&&(deptBudget.notExceeded)) {
//付款
} else if {
//查许多其他的条g
}
} else {
//更多商务逻辑
}
import junit.framework.TestCase;
/*
* 应用中商务规则的JUnit试
* q也扮演商务规则的“模拟器?让我们说明输入,验输出;q在代码?布前看看是否辑ֈ我的期望倹{?br />*/
public class BusinessRuleTest extends TestCase {
/**
*股票购买试
*/
public void testStockBuy() throws Exception{
//用模拟值创?br /> StockOffer testOffer = new StockOffer();
testOffer.setStockName("MEGACORP");
testOffer.setStockPrice(22);
testOffer.setStockQuantity(1000);
//q行规则
BusinessLayer.evaluateStockPurchase(testOffer);
//辑ֈ我们的期望吗Q?br /> assertTrue(
testOffer.getRecommendPurchase()!=null);
assertTrue("YES".equals(
testOffer.getRecommendPurchase()));
}
}
/**
*CZ商务逻辑的正?br />*q个单示例里Q所有的商务逻辑都包含在一个类中?br />*但在现实中,按需要代理给其他的类?br />*/
public class BusinessLayer {
/**
*评h(hun)购买q支股票是否是个好主?br /> *@参数 stockToBuy
*@return 如果推荐购买股票q回真,否则q回?br /> */
public static void evaluateStockPurchase
(StockOffer stockToBuy){
return false;
}
}
StockOffercd下所C:
/**
* 单的JavaBean保存StockOffer倹{?br />* 一个’股出价’就是别人卖(公司股䆾Q所l出的h(hun)根{?br />*/
public class StockOffer {
//帔R
public final static String YES="YES";
public final static String NO="NO";
//内部变量
private String stockName =null;
private int stockPrice=0;
private int stockQuantity=0;
private String recommendPurchase = null;
/**
* @q回股票名称
*/
public String getStockName() {
return stockName;
}
/**
* @参数 stockName 讄股票名称.
*/
public void setStockName(String stockName) {
this.stockName = stockName;
}
/**
* @return q回股票h.
*/
public int getStockPrice() {
return stockPrice;
}
/**
* @参数 stockPrice讄股票h.
*/
public void setStockPrice(int stockPrice) {
this.stockPrice = stockPrice;
}
/**
* @return q回股票数量.
*/
public int getStockQuantity() {
return stockQuantity;
}
/**
* @参数 stockQuantity 讄股票数量.
*/
public void setStockQuantity(int stockQuantity){
this.stockQuantity = stockQuantity;
}
/**
* @return q回购买.
*/
public String getRecommendPurchase() {
return recommendPurchase;
}
}
import java.io.IOException;
import org.drools.DroolsException;
import org.drools.RuleBase;
import org.drools.WorkingMemory;
import org.drools.event.DebugWorkingMemoryEventListener;
import org.drools.io.RuleBaseLoader;
import org.xml.sax.SAXException;
/**
*CZ商务逻辑的正?br />*q个单示例里Q所有的商务逻辑都包含在一个类中?br />*但在现实中,按需要代理给其他的类?br />*@作?~省
*/
public class BusinessLayer {
//包含规则文g的名?br /> private static final String BUSINESS_RULE_FILE=
"BusinessRules.drl";
//内部处理的规则基
private static RuleBase businessRules = null;
/**
* 如果q没有装载商务规则的话就装蝲它?br />*@抛出异常 -通常从这里恢?br />*/
private static void loadRules()
throws Exception{
if (businessRules==null){
businessRules = RuleBaseLoader.loadFromUrl(
BusinessLayer.class.getResource(
BUSINESS_RULE_FILE ) );
}
}
/**
*评h(hun)是否购买q支股票
*@参数 stockToBuy
*@return 如果推荐购买股票q回真,否则q回?br /> *@抛出异常
*/
public static void evaluateStockPurchase
(StockOffer stockToBuy) throws Exception{
//保商务规则被装?br /> loadRules();
//一些程序进行的日志
System.out.println( "FIRE RULES" );
System.out.println( "----------" );
//了解以前q行的状?br /> WorkingMemory workingMemory
= businessRules.newWorkingMemory();
//规则集可以d调试侦听?br /> workingMemory.addEventListener(
new DebugWorkingMemoryEventListener());
//让规则引擎了解实?br /> workingMemory.assertObject(stockToBuy);
//让规则引擎工?br /> workingMemory.fireAllRules();
}
}
<?xml version="1.0"?>
<rule-set name="BusinessRulesSample"
xmlns="http://drools.org/rules"
xmlns:java="http://drools.org/semantics/java"
xmlns:xs
="http://www.w3.org/2001/XMLSchema-instance"
xs:schemaLocation
="http://drools.org/rules rules.xsd
http://drools.org/semantics/java java.xsd">
<!-- Import the Java Objects that we refer
to in our rules -->
<java:import>
java.lang.Object
</java:import>
<java:import>
java.lang.String
</java:import>
<java:import>
net.firstpartners.rp.StockOffer
</java:import>
<!-- A Java (Utility) function we reference
in our rules-->
<java:functions>
public void printStock(
net.firstpartners.rp.StockOffer stock)
{
System.out.println("Name:"
+stock.getStockName()
+" Price: "+stock.getStockPrice()
+" BUY:"
+stock.getRecommendPurchase());
}
</java:functions>
<rule-set>
<!-- Ensure stock price is not too high-->
<rule name="Stock Price Low Enough">
<!-- Params to pass to business rule -->
<parameter identifier="stockOffer">
<class>StockOffer</class>
</parameter>
<!-- Conditions or 'Left Hand Side'
(LHS) that must be met for
business rule to fire -->
<!-- note markup -->
<java:condition>
stockOffer.getRecommendPurchase() == null
</java:condition>
<java:condition>
stockOffer.getStockPrice() < 100
</java:condition>
<!-- What happens when the business
rule is activated -->
<java:consequence>
stockOffer.setRecommendPurchase(
StockOffer.YES);
printStock(stockOffer);
</java:consequence>
</rule>
</rule-set>
/**
*试买股确保系l不接受负?br />*/
public void testNegativeStockBuy()
throws Exception{
//用模拟值创?br /> StockOffer testOffer = new StockOffer();
testOffer.setStockName("MEGACORP");
testOffer.setStockPrice(-22);
testOffer.setStockQuantity(1000);
//q行规则
BusinessLayer
.evaluateStockPurchase(testOffer);
//是否辑ֈ我们的期望?
assertTrue("NO".equals(
testOffer.getRecommendPurchase()));
}
<!-- Ensure that negative prices
are not accepted-->
<rule name="Stock Price Not Negative">
<!-- Parameters we can pass into
the business rule -->
<parameter identifier="stockOffer">
<class>StockOffer</class>
</parameter>
<!-- Conditions or 'Left Hand Side' (LHS)
that must be met for rule to fire -->
<java:condition>
stockOffer.getStockPrice() < 0
</java:condition>
<!-- What happens when the business rule
is activated -->
<java:consequence>
stockOffer.setRecommendPurchase(
StockOffer.NO);
printStock(stockOffer);
</java:consequence>
</rule>
FIRE RULES
----------
[ConditionTested: rule=Stock Price Not Negative;
condition=[Condition: stockOffer.getStockPrice()
< 0]; passed=true; tuple={[]}]
[ActivationCreated: rule=Stock Price Not Negative;
tuple={[]}]
[ObjectAsserted: handle=[fid:2];
object=net.firstpartners.rp.StockOffer@16546ef]
[ActivationFired: rule=Stock Price Low Enough;
tuple={[]}]
[ActivationFired: rule=Stock Price Not Negative;
tuple={[]}]
Name:MEGACORP Price: -22 BUY:YES
Name:MEGACORP Price: -22 BUY:NO
/**
*保pȝpȝ在XYZ公司股h(hun)便宜时就购买他们的股?br />*/
public void testXYZStockBuy() throws Exception{
//用模拟值创?br /> StockOffer testOfferLow = new StockOffer();
StockOffer testOfferHigh = new StockOffer();
testOfferLow.setStockName("XYZ");
testOfferLow.setStockPrice(9);
testOfferLow.setStockQuantity(1000);
testOfferHigh.setStockName("XYZ");
testOfferHigh.setStockPrice(11);
testOfferHigh.setStockQuantity(1000);
//q行规则
BusinessLayer.evaluateStockPurchase(
testOfferLow);
assertTrue("YES".equals(
testOfferLow.getRecommendPurchase()));
BusinessLayer.evaluateStockPurchase(
testOfferHigh);
assertTrue("NO".equals(
testOfferHigh.getRecommendPurchase()));
}
<rule name="XYZCorp" salience="-1">
<!-- Parameters we pass to rule -->
<parameter identifier="stockOffer">
<class>StockOffer</class>
</parameter>
<java:condition>
stockOffer.getStockName().equals("XYZ")
</java:condition>
<java:condition>
stockOffer.getRecommendPurchase() == null
</java:condition>
<java:condition>
stockOffer.getStockPrice() > 10
</java:condition>
<!-- What happens when the business
rule is activated -->
<java:consequence>
stockOffer.setRecommendPurchase(
StockOffer.NO);
printStock(stockOffer);
</java:consequence>
</rule>
//生成冲突解决者的列表
ConflictResolver[] conflictResolvers =
new ConflictResolver[] {
SalienceConflictResolver.getInstance(),
RecencyConflictResolver.getInstance(),
SimplicityConflictResolver.getInstance(),
LoadOrderConflictResolver.getInstance()
};
//包装成合成解册?br /> CompositeConflictResolver resolver =
new CompositeConflictResolver(
conflictResolvers);
//当装载规则时Q说明这个解册?br /> businessRules = RuleBaseLoader.loadFromUrl(
BusinessLayer.class.getResource(
BUSINESS_RULE_FILE),resolver);
复杂企业U项目的开发以及其中随外部条g不断变化的业务规?business logic),q切需要分d业决{者的商业决策逻辑和应用开发者的技术决{,q把q些商业决策攑֜中心数据库或其他l一的地方,让它们能在运行时Q即商务旉Q可以动态地理和修改从而提供Y件系l的柔性和适应性。规则引擎正是应用于上述动态环境中的一U解x法?/p>
本文W一部分要介l了规则引擎的生背景和Z规则的专家系l,W二部分介绍了什么是规则引擎及其架构和算法,W三部分介绍了商业品和开源项目实现等各种Java规则引擎Q第四部分对Java规则引擎APIQJSR-94Q作了详l介l,讲解了其体系l构Q管理API和运行时API及相兛_全问题,W五部分则对规则语言及其标准化作了探讨,W六部分l出了一个用Java规则引擎API的简单示例,W七部分l予结和展望?/p>
1?介绍
1.1 规则引擎产生背景
企业理者对企业UITpȝ的开发有着如下的要求:(1)为提高效率,理程必须自动化,即C商业规则异常复杂(2)市场要求业务规则l常变化QITpȝ必须依据业务规则的变化快速、低成本的更?3)Z快速、低成本的更斎ͼ业务人员应能直接理ITpȝ中的规则Q不需要程序开发h员参与?/p>
而项目开发h员则到了以下问?(1)E序=法+数据l构Q有些复杂的商业规则很难推导出算法和抽象出数据模?2)软g工程要求从需?>设计->~码Q然而业务规则常常在需求阶D可能还没有明确Q在设计和编码后q在变化Q业务规则往往嵌在pȝ各处代码?3)对程序员来说Q系l已l维护、更新困难,更不可能让业务h员来理?/p>
Z规则的专家系l的出现l开发h员以解决问题的契机。规则引擎由Z规则的专家系l中的推理引擎发展而来。下面简要介l一下基于规则的专家pȝ?/p>
1.2 Z规则的专家系l?RBES)
专家pȝ是h工智能的一个分支,它模仿hcȝ推理方式Q用试探性的Ҏ(gu)q行推理Qƈ使用人类能理解的术语解释和证明它的推理结论。专家系l有很多分类Q神l网l、基于案例推理和Z规则pȝ{?/p>
RBES包括三部分:Rule BaseQknowledge baseQ、Working MemoryQfact baseQ和Inference EngineQ推理引擎)。它们的l构如下所C:
?.Z规则的专家系l组?/p>
如上图所C,推理引擎包括三部分:Pattern Matcher、Agenda和Execution Engine。Pattern Matcher何时执行哪个规则QAgenda理PatternMatcher挑选出来的规则的执行次序;Execution Engine负责执行规则和其他动作?/p>
推理引擎通过军_哪些规则满事实或目标,q授予规则优先Q满事实或目标的规则被加入议程。存在两者推理方式:演绎法(Forward-Chaining正向链)和归UxQBackward-Chaining反向链)。演l法从一个初始的事实出发Q不断地应用规则得出l论Q或执行指定的动作)。而归Ux则是从假讑և发,不断地寻扄合假讄事实?/p>
2?规则引擎
2.1 业务规则
一个业务规则包含一l条件和在此条g下执行的操作Q它们表CZ务规则应用程序的一D业务逻辑。业务规则通常应该׃务分析h员和{略理者开发和修改Q但有些复杂的业务规则也可以由技术h员用面向对象的技术语a或脚本来定制。业务规则的理论基础?讄一个或多个条gQ当满q些条g时会触发一个或多个操作?/p>
2.2 规则引擎
什么是规则引擎Q规则引擎是如何执行规则的?q可以称之ؓ"什??如何"的问题。到底规则引擎是什么还是目前业界一个比较有争议的问题,在JSR-94U也几乎没有定义。可以这栯为充分定义和解决?如何"的问题,"什?问题本质上也q刃而解。也许这又是一U?先有蛋还是先有鸡"哲学争论。今后标准规则语a的定义和推出及相x准的制定应该可以l这L问题和争论划上一个句受本文中Q暂且这栯说什么是规则引擎Q规则引擎由推理引擎发展而来Q是一U嵌入在应用E序中的lgQ实C业务决{从应用E序代码中分d来,q用预定义的语义模块编写业务决{。接受数据输入,解释业务规则QƈҎ(gu)规则做出业务决策?/p>
2.3 规则引擎的用方?/strong>
׃规则引擎是Y件组Ӟ所以只有开发h员才能够通过E序接口的方式来使用和控制它Q规则引擎的E序接口臛_包含以下几种APIQ加载和卸蝲规则集的APIQ数据操作的APIQ引擎执行的API。开发h员在E序中用规则引擎基本遵循以?个典型的步骤Q创则引擎对象;向引擎中加蝲规则集或更换规则集;向引擎提交需要被规则集处理的数据对象集合Q命令引擎执?导出引擎执行l果Q从引擎中撤出处理过的数据。用了规则引擎之后Q许多涉及业务逻辑的程序代码基本被q五个典型步骤所取代?
一个开攄业务规则引擎应该可以"嵌入"在应用程序的M位置Q不同位|的规则引擎可以使用不同的规则集Q用于处理不同的数据对象。此外,对用引擎的数量没有限制?/p>
2.4 规则引擎架构与推?/strong>
规则引擎的架构如下图所C:
?. 业务规则引擎架构
规则引擎的推理步骤如下:a. 初始数据(factQ输入至工作内存(Working Memory)。b. 使用Pattern Matcher规则库(Rules repository)中的规则QruleQ和数据QfactQ比较。c. 如果执行规则存在冲突QconflictQ,卛_时激zM多个规则Q将冲突的规则放入冲H集合。d. 解决冲突Q将Ȁzȝ规则按顺序放入Agenda。e. 执行Agenda中的规则。重复步骤b至eQ直到执行完毕Agenda中的所有规则?/span>
M一个规则引擎都需要很好地解决规则的推理机制和规则条g匚w的效率问题?/p>
当引擎执行时Q会Ҏ(gu)规则执行队列中的优先序逐条执行规则执行实例Q由于规则的执行部分可能会改变工作区的数据对象,从而会佉K列中的某些规则执行实例因为条件改变而失效,必须从队列中撤销Q也可能会激zd来不满条g的规则,生成新的规则执行实例q入队列。于是就产生了一U?动?的规则执行链QŞ成规则的推理机制。这U规则的"铑ּ"反应完全是由工作Z的数据驱动的?
规则条g匚w的效率决定了引擎的性能Q引擎需要迅速测试工作区中的数据对象Q从加蝲的规则集中发现符合条件的规则Q生成规则执行实例?982q美国卡耐基·梅隆大学的Charles L. Forgy发明了一U叫Rete法Q很好地解决了这斚w的问题。目前世界顶的商用业务规则引擎产品基本上都使用Rete法?/p>
2.5 规则引擎的算?/strong>
大部分规则引擎品的法Q基本上都来自于Dr. Charles Forgy?979q提出的RETE法及其变体QRete法是目前效率最高的一个Forward-Chaining推理法QDrools目是Rete法的一个面向对象的Java实现QRete法其核心思想是将分离的匹配项Ҏ(gu)内容动态构造匹配树Q以辑ֈ显著降低计算量的效果?/p>
3?Java规则引擎
目前L的规则引擎组件多是基于Java和C++E序语言环境Q已l有多种Java规则引擎商业产品与开源项目的实现Q其中有的已l支持JSR94Q有的正朝这个方向做出努力,列出如下Q?/p>
3.1 Java规则引擎商业产品
Java规则引擎商业产品主要有(Jess不是开源项目,它可以免费用于学术研IӞ但用于商业用途则要收费)Q?/p>
3.2 Java规则引擎开源项?/span>
开源项目的实现主要包括Q?/p>
Drools - Drools规则引擎应用Rete法的改qŞ式Rete-II法。从内部机制上讲Q它使用了和Forgy的算法相同的概念和方法,但是增加了可与面向对象语a无缝q接的节点类型?/p>
Mandarax Z反向推理Q归UxQ。能够较Ҏ(gu)地实现多个数据源的集成。例如,数据库记录能方便地集成ؓ事实?facts sets)Qreflection用来集成对象模型中的功能。目前不支持JSR 94
OFBiz Rule Engine - 支持归纳?Backward chaining).最初代码基于Steven John Metsker?Building Parsers in Java"Q不支持JSR 94
JLisa - JLisa是用来构Z务规则的强大框架Q它有着扩展了LISP优秀特色的优?比Clipsq要强大.q些特色对于多范例Y件的开发是臛_重要?支持JSR 94
其它的开源项目实现有诸如Algernon, TyRuBa, JTP, JEOPS, InfoSapient, RDFExpert, Jena 2, Euler, JLog, Pellet OWL Reasoner, Prova, OpenRules, SweetRules, JShop2{等?/p>
4?Java规则引擎API(JSR-94)
4.1 ?/strong>
q去大部分的规则引擎开发ƈ没有规范化,有其自有的APIQ这使得其与外部E序交互集成不够灉|。{而用另外一U品时往往意味需要重写应用程序逻辑和API调用Q代仯大。规则引擎工业中标准的缺乏成Zo人关注的重要斚w?003q?1月定Eƈ?004q?月最l发布的JSR 94QJava规则引擎APIQ得Java规则引擎的实现得以标准化?/p>
Java规则引擎API由javax.rules包定义,是访问规则引擎的标准企业UAPI。Java规则引擎API允许客户E序使用l一的方式和不同厂商的规则引擎品交互,像使用JDBC~写独立于厂商访问不同的数据库品一栗Java规则引擎API包括创徏和管理规则集合的机制Q在Working Memory中添加,删除和修改对象的机制Q以及初始化Q重|和执行规则引擎的机制?/p>
4.2 介Java规则引擎API体系l构
Java规则引擎API分ؓ两个主要部分:q行时客户API(the Runtime client API)和规则管理API(the rules administration API)?/p>
4.2.1规则理API
规则理API在javax.rules.admin中定?包括装蝲规则以及与规则对应的动作(执行?execution sets)以及实例化规则引擎。规则可以从外部资源中装?比如说URI,Input streams, XML streams和readers{等.同时理API提供了注册和取消注册执行集以及对执行集进行维护的机制。用admin包定义规则有助于对客戯问运行规则进行控制管?它通过在执行集上定义许可权使得未经授权的用h法访问受控规则?/p>
理API使用cRuleServiceProvider来获得规则管?RuleAdministrator)接口的实?规则理接口提供Ҏ(gu)注册和取消注册执行集.规则理?RuleAdministrator)提供了本地和q程的RuleExecutionSetProvider.在前面已提及,RuleExecutionSetProvider负责创徏规则执行?规则执行集可以从如XML streams, input streams{来源中创徏.q些数据来源及其内容l汇集和序列化后传送到q程的运行规则引擎的服务器上.大多数应用程序中,q程规则引擎或远E规则数据来源的情况q不多见.Z避免q些情况中的|络开销,API规定了可以从q行在同一JVM中规则库中读取数据的本地RuleExecutionSetProvider.
规则执行集接口除了拥有能够获得有兌则执行集的方?q有能够索在规则执行集中定义的所有规则对?q得客戯够知道规则集中的规则对象q且按照自己需要来使用它们?
4.2.2 q行时API
q行时API定义在javax.rules包中,则引擎用戯行规则获得结果提供了cdҎ(gu)。运行时客户只能讉K那些使用规则理API注册q的规则Q运行时API帮助用户获得规则对话q且在这个对话中执行规则?/p>
q行时API提供了对厂商规则引擎API实现的类gJDBC的访问方?规则引擎厂商通过cRuleServiceProvider(cRuleServiceProvider提供了对具体规则引擎实现的运行时和管理API的访?其规则引擎实现提供l客?q获得RuleServiceProvider唯一标识规则引擎的URL.
URL推荐标准用法是用类?com.mycompany.myrulesengine.rules.RuleServiceProvider"q样的Internet域名I间,q将有助于访问URL的唯一?cRuleServiceProvider内部实现了规则管理和q行时访问所需的接?所有的RuleServiceProvider要想被客h讉K都必ȝRuleServiceProviderManagerq行注册。注册方式类gJDBC API的DriverManager和Driver?/p>
q行时接口是q行时API的关键部?q行时接口提供了用于创徏规则会话(RuleSession)的方?规则会话如前所q是用来q行规则?q行时API同时也提供了讉K在service provider注册q的所有规则执行集(RuleExecutionSets).规则会话接口定义了客户用的会话的类?客户Ҏ(gu)自己q行规则的方式可以选择使用有状态会话或者无状态会话?/p>
无状态会话的工作方式像一个无状态会话bean.客户可以发送单个输入对象或一列对象来获得输出对象.当客户需要一个与规则引擎间的专用会话?有状态会话就很有?输入的对象通过addObject() Ҏ(gu)可以加入C话当?同一个会话当中可以加入多个对?对话中已有对象可以通过使用updateObject()Ҏ(gu)得到更新.只要客户与规则引擎间的会话依然存?会话中的对象׃会丢失?/p>
RuleExecutionSetMetaData接口提供l客戯其查找规则执行集的元数据(metadata).元数据通过规则会话接口(RuleSession Interface)提供l用戗?/p>
使用q行时Runtime API的代码片断如下所C?
RuleServiceProvider ruleProvider = RuleServiceProviderManager.getRuleServiceProvider ("com.mycompany.myrulesengine.rules. RuleServiceProvider"); RuleRuntime ruleRuntime = ruleProvider.getRuleRuntime(); StatelessRuleSession ruleSession = (StatelessRuleSession)ruleRuntime.createRuleSession(ruleURL, null, RuleRuntime.STTELESS_SESSION_TYPE); List inputRules = new ArrayList(); inputRules.add(new String("Rule 1")); inputRules.add(new Integer(1)); List resultRules = ruleSession.executeRules(inputRules); |
4.3 Java规则引擎API安全问题
规则引擎API管理API和运行时API加以分开,从而ؓq些包提供了较好_度的安全控?规则引擎APIq没有提供明昄安全机制,它可以和J2EE规范中定义的标准安全API联合使用.安全可以׃下机制提?如Java authentication and authorization service (JAAS),the Java cryptography extension (JCE),Java secure Socket Extension (JSSE),或者其它定制的安全API.JAAS能被用来定义规则执行集的许可权限,从而只有授权用h能访问?/p>
4.4 异常与日?/strong>
规则引擎API定义了javax.rules.RuleException作ؓ规则引擎异常层次的根c?所有其它异帔Rl承于这个根c?规则引擎中定义的异常都是受控制的异常(checked exceptions),所以捕获异常的d׃l了规则引擎。规则引擎API没有提供明确的日志机?但是它徏议将Java Logging API用于规则引擎API?/p>
4.5 JSR 94 结
JSR 94 则引擎提供了公用标准API,仅仅为实现规则管理API和运行时API提供了指D?q没有提供规则和动作该如何定义以及该用什么语a定义规则,也没有ؓ规则引擎如何d评h(hun)规则提供技术性指?JSR 94规范?yu)上q问题留l了规则引擎的厂?在下一节我简要介l一下规则语a?/p>
5?规则语言
JSR 94中没有涉及用来创则和动作的语a.规则语言是规则引擎应用程序的重要l成部分,所有的业务规则都必ȝ某种语言定义q且存储于规则执行集?从而规则引擎可以装载和处理他们?/p>
׃没有关于规则如何定义的公用规?市场上大多数行的规则引擎都有其自己的规则语aQ目前便有许多种规则语言正在应用Q因此,当需要将应用UL到其他的Java规则引擎实现Ӟ可能需要变换规则定义,如将DroolsU有的DRL规则语言转换成标准的ruleMLQJess规则语言转换成ruleML{。这个工作一般由XSLT转换器来完成?/p>
多种规则语言的用得不同规则引擎实C间的兼容性成为问?通用的规则引擎API或许可以减轻不同厂家API之间的问?但公用规则语a的缺乏将仍然ȝ不同规则引擎实现之间的互操作?管业界在提出公用规则语a上做Z一些努? 比如说RuleML,SRML的出?但距获得绝大部分规则引擎厂商同意的公用标准q有很长的\要走?/p>
6?Java规则引擎API使用CZ
6.1 讄规则引擎
Java规则引擎的管理活动阶D开始于查找一个合适的javax.rules.RuleServiceProvider对象Q这个对象是应用E序讉K规则引擎的入口。在J2EE环境中,你可能可以通过JNDI获得RuleServiceProvider。否则,你可以用javax.rules.RuleServiceProviderManagerc:
javax.rules.RuleServiceProviderManager class: String implName = "org.jcp.jsr94.ri.RuleServiceProvider"; Class.forName(implName); RuleServiceProvider serviceProvider = RuleServiceProviderManager.getRuleServiceProvider |
拥有了RuleServiceProvider对象Q你可以获得一个javax.rules.admin.RuleAdministratorcR从RuleAdministratorcMQ你可以得到一个RuleExecutionSetProviderQ从cd可以知道Q它用于创徏javax.rules.RuleExecutionSets对象。RuleExecutionSet基本上是一个装入内存的Q准备好执行的规则集合?/p>
包javax.rules.admin包括两个不同的RuleExecutionSetProvidercRRuleExecutionSetProvidercLw包括了从Serializable对象创徏RuleExecutionSets的方法,因此在规则引擎位于远E服务器的情况下Q仍然可以用RuleExecutionSetProviderc,构造器的参数可以通过RMI来传递。另一个类是LocalRuleExecutionSetProviderQ包含了其他Ҏ(gu)Q用于从非Serializable资源Q如java.io.ReaderQ本地文Ӟ创徏RuleExectionSets。假设拥有了一个RuleServiceProvider对象Q你可以从本地文件rules.xml文g创徏一个RuleExectionSet对象。如以下的代码所C:
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注册获得的RuleExecutionSetQƈl它分配一个名U。在q行Ӟ你可以用同一个名U创Z个RuleSessionQ该RuleSession使用了这个命名的RuleExecutionSet。参见下面的用法Qadmin.registerRuleExecutionSet("rules", ruleSet, properties);
6.2 执行规则引擎
在运行时阶段Q你可以参见一个RuleSession对象。RuleSession对象基本上是一个装载了特定规则集合的规则引擎实例。你从RuleServiceProvider得到一个RuleRuntime对象Q接下来Q从javax.rules.RuleRuntime得到RuleSession对象?/p>
RuleSession分ؓ两类Qstateful和stateless。它们具有不同的功能。StatefulRuleSession的Working Memory能够在多个方法调用期间保存状态。你可以在多个方法调用期间在Working Memory中加入多个对象,然后执行引擎Q接下来q可以加入更多的对象q再ơ执行引擎。相反,StatelessRuleSessioncL不保存状态的Qؓ了执行它的executeRulesҎ(gu)Q你必须为Working Memory提供所有的初始数据Q执行规则引擎,得到一个内容列表作回倹{?/p>
下面的例子中Q我们创Z个StatefulRuleSession实例Q添加两个对象(一个Integer和一个StringQ到Working MemoryQ执行规则,然后得到Working Memory中所有的内容Q作为java.util.List对象q回。最后,我们调用releaseҎ(gu)清理RuleSessionQ?/p>
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(); |
7?l束?/strong>
Java规则引擎API(JSR-94)允许客户E序使用l一的方式和不同厂商的规则引擎品交互,一定程度上l规则引擎厂商提供了标准化规范。但其几乎没有定义什么是规则引擎Q当然也没有深入到规则是如何构徏和操U늚Q规则调用的效用Q规则与Java语言的绑定等斚w。ƈ且JSR-94在对J2EE的支持上也不뀂规则语a的标准化QJSR-94的进一步的充实深化都有待研I?/p>
当然我不知道他们公司是怎么想的Q不q我惛_中国x真正3-4q工作经验的.Net开发h员恐怕基本上找不刎ͼ是扑ֈ几个恐怕也有一大半是吹的。因?Net从成型到现在估计也就3?q时_可能q不刎ͼ。更不要说大规模开始应用了?/p>
以前q看q一个招聘高UJ2EE工程师的q告Q当然了Q“高U”就是不一P要求臛_?Q?qJ2EE开发经验。而大家当焉知道Q?q前?999q_J2EE才刚刚诞生?/p>
我记得毕业时候看那些公司的招聘广告,都是牛得不得了,恨不得把所有的条g都列上。英语要nU,要懂q个懂那个,什么c++,c,rup,oracle,sql server,linux,unix之类Q反正是能想到的东西都要懂,地球上存在地东西都要会。我当时看着那么多的招聘q告Q觉得自己没有几个能够得上h家的标准的。问问n边认识的人,好像也没有几个能够得上这L标准?/p>
其实我们当然知道Q这些都只是夸张而已。招聘的在招聘广告上掺水Q应聘的在简历上掺水。等到大家真的互怺解了Q对视嘿嘿一W,什么n多的技如云般飘散?/p>
只是我有时候真不知道,q样是好q是坏?/p>