業(yè)務(wù)應用常常需要頻繁更改邏輯以滿足企業(yè)的動態(tài)需求。雖然這種邏輯可以使用Java來進行編碼,但所得到的應用程序卻不能快速適應變化。BEA WebLogic Portal包括一個規(guī)則引擎,該引擎與WebLogic Integration業(yè)務(wù)流程一起提供了一種替代解決方案,允許業(yè)務(wù)邏輯快速變化且無需更改Java代碼。在本文中,我們將討論規(guī)則引擎的工作方式以及將其合并到WebLogic Integration (WLI)流程中所需的步驟。我們還將介紹如何在規(guī)則引擎中使用JavaBean為要實施的規(guī)則描述對象的隨機集合并為后端處理提供動作。最后,我們將描述如何通過WebLogic Portal的Datasync機制在運行中的系統(tǒng)中更新一組規(guī)則而無需重新部署應用程序。
業(yè)務(wù)應用的需求總是隨著業(yè)務(wù)環(huán)境的變化趨勢而不斷地改變。決策很少是一成不變的,并且競爭壓力要求業(yè)務(wù)邏輯的設(shè)計和實現(xiàn)具有靈活性,以快速地適應不斷變化的需求。通常,對業(yè)務(wù)邏輯的更改必須由開發(fā)人員來完成,然后進行多次徹底的測試,而這將是一個很耗時的過程。在應用程序的修改工作完成后,需要將其重新部署到服務(wù)器,需要留出預定的停機時間,以防應用程序?qū)τ脩舨豢捎谩?/p>
對于這個問題,更好的解決方案是通過應用程序之外的一組規(guī)則來實現(xiàn)某些業(yè)務(wù)決策。這些規(guī)則并沒有被編譯到應用程序中,而是在運行時讀取并應用。通過這種方式,無需更改代碼或者停止正在運行的應用程序就可以改變這些規(guī)則。
WebLogic Portal包括一個基本的規(guī)則引擎,用于使WebLogic Platform應用程序從規(guī)則獲益。盡管該引擎不是功能完備的產(chǎn)品,但我們將展示如何將其與WebLogic Integration Business Processes (JPDs)一起使用來為業(yè)務(wù)邏輯提供一種靈活且動態(tài)的實現(xiàn)機制,從而無需只為了修改規(guī)則而重新部署應用程序。
首先我們來看一下將在全文中使用的示例應用程序,然后介紹如何將規(guī)則引擎注入到WLI流程中以實現(xiàn)業(yè)務(wù)邏輯。然后,我們將更仔細地研究這些規(guī)則本身以及如何為業(yè)務(wù)邏輯定義這些規(guī)則,最后將描述在運行中的系統(tǒng)中更改業(yè)務(wù)規(guī)則所使用的機制。
我們將開發(fā)一個示例交易應用程序作為在業(yè)務(wù)流程中使用規(guī)則的例子。該交易應用程序是一個金融交易流程的簡化版本,該流程使用調(diào)用規(guī)則引擎的JPD業(yè)務(wù)流程而構(gòu)建。該示例應用程序采用了不同的有價證券交易集合,并根據(jù)一組由業(yè)務(wù)定義的規(guī)則將其分組成交易塊以便執(zhí)行,或許是為了減少傭金。當然,這里給出的應用程序并不完整,但它已經(jīng)足以展示如何在現(xiàn)實世界應用程序中使用規(guī)則引擎。有完整的源代碼可供下載,其中的readme文件提供了構(gòu)建和運行該應用程序的說明。
在解釋如何開發(fā)這樣的應用程序之前,我們先通過對門戶規(guī)則引擎的一些特性的簡要概括來了解其工作方式。這里假定讀者熟悉一般的規(guī)則技術(shù)。
圖1說明了規(guī)則引擎的基本情況。該引擎根據(jù)一組規(guī)則來處理初始的事實集,而這些規(guī)則由引擎從外部庫中獲得。初始事實用于為該引擎填充工作內(nèi)存。由規(guī)則來對工作內(nèi)存中的事實進行評估,如果滿足某條規(guī)則的條件,則將執(zhí)行對應的動作。通常,一個規(guī)則動作將向工作內(nèi)存中添加一條新的事實,并重復該流程直到應用完所有的規(guī)則。然后通過可選的過濾器來選擇特定類的對象以返回給調(diào)用方??梢酝ㄟ^Controls界面訪問規(guī)則引擎,還可以用它來設(shè)置屬性(比如規(guī)則集文件的位置)。
圖1:該規(guī)則引擎是一個由控件包裝的EJB。儲存庫中的規(guī)則反復地應用于工作內(nèi)存中的事實,以獲得新的事實。在無法進行繼續(xù)推理的情況下,對工作內(nèi)存進行過濾以返回感興趣的項目。
讓我們從被實現(xiàn)為JPD的交易業(yè)務(wù)流程開始,來看看如何添加對規(guī)則引擎的調(diào)用。要在WLI流程中添加規(guī)則,可以使用作為WebLogic Portal的一部分而提供的Rules Executor Control(規(guī)則執(zhí)行器控件)。對于本例,我們只使用該控件中所提供的方法和特性的一個子集。關(guān)于規(guī)則控件的附加說明文檔可以在參考資料部分找到。
此處假定開發(fā)人員使用WebLogic Workshop集成開發(fā)環(huán)境來創(chuàng)建新的流程應用程序。然后可在該應用程序中創(chuàng)建一個流程項目。因為默認情況下門戶控件在流程項目中不可用,所以需要將這些控件和規(guī)則引擎的EJB導入到應用程序。然后,將控件輸入和輸出插入到JPD中。在WLI流程中使用門戶規(guī)則引擎的基本步驟如下:
后面我們將更詳細地討論其中的每一個步驟。
規(guī)則引擎包含在下面的文件中:
/weblogic81/p13n/lib/p13n_ejb.jar
要在應用程序中包含該引擎,請右擊Workshop集成開發(fā)環(huán)境中的Modules文件夾,并選擇Add Module。導航到該jar文件,并選擇Open。
要使得門戶規(guī)則控件在應用程序中可用,請右擊Workshop集成開發(fā)環(huán)境中的Libraries文件夾,并選擇Add Library。該控件位于:
</weblogic81/p13n/lib/p13n_controls.jar
導航到此文件,單擊 Open 按鈕。
這里使用的Rules Executor控件方法需要一個對象數(shù)組作為輸入并返回一個結(jié)果的迭代器。在Workshop集成開發(fā)環(huán)境中為這些值創(chuàng)建變量,這樣我們就可以在下一步中通過圖形用戶界面來創(chuàng)建控件。要完成該任務(wù),請為Data Palette中的變量單擊Add按鈕,鍵入輸入變量的名稱,并鍵入Java類型java.lang.Object[]。使用同樣的方式創(chuàng)建Java類型為java.util.Iterator的輸出變量。
要創(chuàng)建規(guī)則控件,請單擊Data Palette中控件的Add按鈕。從菜單中選擇Portal Controls -> Rules Executor。為控件鍵入名稱,并按下Create按鈕。
將剛剛創(chuàng)建的控件拖放到流程中以創(chuàng)建一個控件節(jié)點來實際調(diào)用規(guī)則引擎。在示例中,我們將使用控件的evaluateRuleSet()方法。從Send Data面板中,選擇前面為方法的輸入?yún)?shù)而創(chuàng)建的輸入變量。使用Receive Data面板選擇返回變量來獲取規(guī)則執(zhí)行的結(jié)果。在Property Editor窗口中為控件屬性鍵入相應的值。
在創(chuàng)建了輸入變量后,我們還沒有對它賦值,所以需要編寫代碼來完成該任務(wù)。這個變量是一個Java對象數(shù)組,它提供了輸入到規(guī)則條件中的初始事實??梢詣?chuàng)建一個新的Perform節(jié)點來初始化該數(shù)組,或者通過使用Source View在Control Send節(jié)點中添加代碼來設(shè)置該變量的值。
Rules Executor控件的每一個計算方法都將返回一個結(jié)果的迭代器。編寫代碼,使用該值實現(xiàn)對規(guī)則執(zhí)行結(jié)果的迭代。如果沒有指定過濾器類,這個迭代器將返回規(guī)則引擎工作內(nèi)存中的所有值。其中包括原始輸入以及任何在執(zhí)行滿足條件的規(guī)則的動作時添加到工作內(nèi)存中的值。對于添加的對象,迭代器返回一個Result類的對象,該類的getObject()方法可以返回在規(guī)則動作中所添加的實際對象。
使用XML編輯器,在/META-INF/data目錄中創(chuàng)建一個擴展名為.rls的文件。規(guī)則通常添加到子目錄rulesets中。
我們剛剛展示了如何在業(yè)務(wù)流程中插入規(guī)則引擎?,F(xiàn)在讓我們來看看如何利用該規(guī)則引擎以及如何編寫映射到業(yè)務(wù)規(guī)則的規(guī)則。
規(guī)則包括兩個部分:應用該規(guī)則時必須為真的條件,以及當條件滿足時將執(zhí)行的動作。因此,要在應用程序中使用規(guī)則,設(shè)計人員必須首先定義哪些對象和屬性在測試規(guī)則條件時對規(guī)則編寫者是可見的。規(guī)則引擎允許在一個條件中調(diào)用任意多個方法。這種構(gòu)造方式便于定義JavaBean作為組成初始事實集合的對象,規(guī)則引擎使用這些初始事實來進行推理。可以使用bean的 get方法來獲得條件測試的值。
規(guī)則所引用的Java對象需要從創(chuàng)建它的WLI流程以及從規(guī)則引擎本身中都可見。這就避免了這些對象與流程JPD位于同一包中,更確切地說,它們應該被創(chuàng)建于作為同一應用程序的一部分的Java項目中。然后,這些對象可以通過package.class標記在規(guī)則文件(.rls)和流程JPD中引用。
在我們的交易示例中,將把不同的交易分組以便可以成塊執(zhí)行。為實現(xiàn)該目標,我們定義兩個bean來表示相關(guān)的對象。第一個是Trade bean,它表示單個的交易訂單。這個bean的屬性表示交易的份額、股份數(shù)目以及所期望的價格等。任何對于決定交易所屬的塊來說可能有用的值,都應該作為這個具有公有g(shù)et方法的bean的屬性,以便能夠在規(guī)則中使用它。第二個bean是Block bean,通過它可以存儲所有根據(jù)某個屬性集聚合在一起的不同交易。這個bean的屬性包括規(guī)則中任何可用于判定塊大得足以執(zhí)行訂單的的信息。這些屬性可以是平均價格、交易的總美元數(shù)或總的份額數(shù)等等。
為了在我們的應用程序中實現(xiàn)塊功能,首先使用規(guī)則來定義某項交易是否只需要執(zhí)行其自身就足夠了(也就是說,它是僅包含單個交易的塊),或者如果不是這樣的話,那么應該使用什么屬性將其與其他交易聚集以形成一個塊。在一項交易聚集到適當?shù)膲K中后,就會第二次調(diào)用規(guī)則引擎來判斷該塊是否完成。例如,假設(shè)我們想要得到這樣的規(guī)則:
調(diào)用與規(guī)則條件中的對象相關(guān)聯(lián)的方法很容易,如下面的示例所示,這是規(guī)則1的條件:
<cr:conditions><greater-than-or-equal-to><instance-method><variable><type-alias>Beans.Trade</type-alias></variable><name>getQuantity</name><!-- getQuantity (and any other bean property) takesno arguments. If it did, theywould go here<arguments>...</arguments>--></instance-method><literal:integer>5000</literal:integer></greater-than-or-equal-to></cr:conditions>
在這個示例中,如果在我們的事實中有一個Trade對象,那么規(guī)則引擎就會調(diào)用它的getQuantity()方法并且將結(jié)果與整型5000進行比較。如果它大于或等于5000,則該條件為真。
規(guī)則的第二部分是條件滿足時執(zhí)行的動作的列表。最常見的動作是:創(chuàng)建一個新對象,把它添加到規(guī)則引擎用來評估條件的事實集中。規(guī)則引擎繼續(xù)對規(guī)則進行迭代,直到無法從事實中得出更多的推理;向動作添加新對象會導致另一輪的條件評估循環(huán)。正如我們將要看到的那樣,可以創(chuàng)建任意類型的對象,并定義對應用程序具有特定意義的各種類型。這里的技巧是,應用程序設(shè)計者可以定義一組足夠豐富的動作,以包含那些可由規(guī)則編寫者調(diào)用以滿足各種業(yè)務(wù)需求的任務(wù)。
在我們的交易應用程序示例中,所有動作都會創(chuàng)建將添加到由規(guī)則引擎使用的工作集中的新對象。有些規(guī)則向該集合中添加簡單的String對象。這些對象表示了從原始事實中演繹出來的中間事實,它們可以在規(guī)則引擎中得到進一步的推理,但流程JPD不會以任何形式解釋它們。其他的規(guī)則將創(chuàng)建Beans.Action類的對象。這些對象包括當規(guī)則條件滿足時流程將執(zhí)行的實際命令。流程JPD和支持類將實施已知的動作命令來聚集交易并執(zhí)行塊交易。在這個簡單的示例中,實際上只有兩個已知的命令:創(chuàng)建(并執(zhí)行)訂單、使用指定的屬性聚集一項交易。前面規(guī)則2的動作是使用屬性symbol和manager來進行聚集,該動作如下:
<cr:actions><new-instance><type-alias>Beans.Action</type-alias><arguments><literal:string>symbol, manager</literal:string></arguments></new-instance></cr:actions>
響應該動作,流程JPD及其支持類為當前交易查詢symbol和投資manager,找出具有相同的symbol和投資manager的未執(zhí)行交易,并將這些交易聚集到相同的塊。
在完成一項交易的聚集后,將從第二個Rules Executor控件再次調(diào)用規(guī)則引擎,以評估規(guī)則,決定是否應該執(zhí)行產(chǎn)生的塊交易。根據(jù)業(yè)務(wù)規(guī)則3,該規(guī)則如下:
<cr:conditions><greater-than ><instance-method><variable><type-alias>Beans.Block</type-alias></variable><name>getAmount</name></instance-method><literal:float>50000.00</literal:float></greater-than ></cr:conditions><cr:actions><new-instance><type-alias>Beans.Action</type-alias><arguments><literal:string>create</literal:string></arguments></new-instance></cr:actions>
這一次,我們分析Beans.Block對象,獲取amount屬性并與閾值進行比較。如果該條件滿足,則使用create命令向工作集中添加一個Beans.Action對象,這是通知流程執(zhí)行該塊訂單的信號。
讓我們仔細分析一下流程JPD。下面有用于調(diào)用規(guī)則引擎的Control Send節(jié)點的代碼。正如我們可以看到的,該節(jié)點使用一個Rules Executor控件來評估規(guī)則集,該控件返回一個迭代器。通過其屬性(沒有給出),控件將過濾結(jié)果,僅返回Beans.Action類的對象。通過這些對象,代碼將提取動作命令并執(zhí)行所請求的動作。正如前面所提到的,如果動作是聚集該交易,則流程將使用更新后的塊作為輸入,對規(guī)則引擎開始第二次調(diào)用。通過執(zhí)行適當?shù)膭幼鳎瑢Y(jié)果進行第二次迭代循環(huán)。
public void rulesExecutorControlEvaluateRuleSet()throws Exception{// Execute the Rules using facts as the input//#START: CODE GENERATED - PROTECTED SECTION - you can safely// Add code above this comment in this method. #//// Input transform// Return method callthis.results =rulesExecutorControl.evaluateRuleSet(this.facts);// Output transform// Output assignments//#END : CODE GENERATED - PROTECTED SECTION - you can safely// Add code below this comment in this method. #///* Iterate over the results of rules execution. This assumes thatresults are filtered to return only items of the Beans.Action class.The command property from the Action is expected to be either thestring "create," in which case a Block trade can be executed fromthe single discrete Trade, or it is expected to be a list ofattributes describing the Block that this Trade should beincorporated into.*/while (results.hasNext()){String action =((Action)results.next()).getCommand();if (action.equals("create"))(new Block(trade)).execute(); // single-tradeelse{// Aggregate trade into an intermediate Blocktrade.aggregate(blockStorage, action);/* Call the rules engine a second time, this time usingthe resulting Block as the only input. This is todetermine if the resulting Block now meets the criteriato execute the order. Again, results are assumed to befiltered by the control to return only the Actions.*/Block block = trade.getBlock();Object blockFacts[] = new Object[1];blockFacts[0] = block;Iterator blockResults =blockRulesCntl.evaluateRuleSet(blockFacts);while (blockResults.hasNext()){action =((Action)blockResults.next()).getCommand();if (action.equals("create"))block.execute();}}}}
WebLogic Portal的特性之一是Datasync功能,通過它可以將修改后的數(shù)據(jù)重新部署到集群中的門戶應用程序中。因為門戶規(guī)則引擎從Datasync存儲區(qū)中獲得其規(guī)則文件,所以應用程序的業(yè)務(wù)規(guī)則可以在運行的系統(tǒng)中進行更改,而無需停止應用程序。關(guān)于Datasync以及用于更新數(shù)據(jù)的Datasync Web應用程序的其他信息,請參閱WebLogic Portal文檔。
默認情況下,Datasync Web應用程序?qū)⒉渴鹪诔R?guī)門戶應用程序中。然而,我們的示例應用程序是一個WLI流程應用程序,因此必須在其中手動地包含Datasync。要在Workshop集成開發(fā)環(huán)境中完成這些工作,請右擊Modules文件夾,并選擇Add Module。然后,選擇下面的文件:
/weblogic81/p13n/lib/datasync.war
在集群中,Datasync應用程序應該僅部署到管理服務(wù)器。
為了闡明如何在運行中的應用程序中更改規(guī)則,示例應用程序中包括了兩個不同的規(guī)則文件,這兩個文件中包括了用于將交易聚集成塊的可選規(guī)則。前面已經(jīng)描述了默認規(guī)則(定義于traderules.rls文件中),第二個集合(在altrules.rls中)定義如下規(guī)則:
要看到動態(tài)規(guī)則的執(zhí)行,首先使用所提供的測試數(shù)據(jù)運行示例應用程序。無需停止服務(wù)器或者重新部署應用程序,直接將原始規(guī)則保存到一個新文件中,并將替代的規(guī)則文件從:
/META-INF/data/rulesets/altrules.rls
拷貝到:
/META-INF/data/rulesets/traderules.rls
再次運行示例數(shù)據(jù),這時會發(fā)現(xiàn)產(chǎn)生了不同的訂單。
注意,在示例域中不需要重新運行Datasync Web應用程序來更新規(guī)則。這是因為示例平臺域以開發(fā)模式運行。在這種模式下,Datasync自動輪詢 /META-INF/data目錄及其子目錄以搜索更改,更改過的文件可以自動地重新部署到應用程序。在生產(chǎn)域中,需要使用Web應用程序來實現(xiàn)數(shù)據(jù)的重新部署。推薦的步驟是,首先為所有更新過的文件創(chuàng)建一個jar文件,jar文件的根目錄應該是data。然后,使用Datasync Web應用程序的Bootstrap Data功能來重新部署包含了新規(guī)則的jar文件。
您可以下載本文中使用的示例程序的源代碼:SampleApp.zip (2MB)
在與WLI流程應用程序一起使用時,Portal Rules Engine是實現(xiàn)業(yè)務(wù)邏輯的強大工具。它很容易合并到WLI流程中,并且可以使用JavaBean來評估規(guī)則條件,或者擴展作為規(guī)則評估結(jié)果而執(zhí)行的動作。最后,Datasync特性所提供的動態(tài)更新能力使得業(yè)務(wù)邏輯更靈活,并能夠在不改變Java代碼的前提下適應不斷變化的需求,所以不需要重新部署應用程序。
聯(lián)系客服