<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Java Votary

      BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
      48 隨筆 :: 1 文章 :: 80 評論 :: 0 Trackbacks

    2005年12月31日 #

    ThreadLocal是什么

    早在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal為解決多線程程序的并發問題提供了一種新的思路。使用這個工具類可以很簡潔地編寫出優美的多線程程序。

    ThreadLocal很容易讓人望文生義,想當然地認為是一個“本地線程”。其實,ThreadLocal并不是一個Thread,而是Thread的局部變量,也許把它命名為ThreadLocalVariable更容易讓人理解一些。

    當使用ThreadLocal維護變量時,ThreadLocal為每個使用該變量的線程提供獨立的變量副本,所以每一個線程都可以獨立地改變自己的副本,而不會影響其它線程所對應的副本。

    從線程的角度看,目標變量就象是線程的本地變量,這也是類名中“Local”所要表達的意思。

    線程局部變量并不是Java的新發明,很多語言(如IBM IBM XL FORTRAN)在語法層面就提供線程局部變量。在Java中沒有提供在語言級支持,而是變相地通過ThreadLocal的類提供支持。

    所以,在Java中編寫線程局部變量的代碼相對來說要笨拙一些,因此造成線程局部變量沒有在Java開發者中得到很好的普及。

    ThreadLocal的接口方法

    ThreadLocal類接口很簡單,只有4個方法,我們先來了解一下:

    • void set(Object value)

    設置當前線程的線程局部變量的值。

    • public Object get()

    該方法返回當前線程所對應的線程局部變量。

    • public void remove()

    將當前線程局部變量的值刪除,目的是為了減少內存的占用,該方法是JDK 5.0新增的方法。需要指出的是,當線程結束后,對應該線程的局部變量將自動被垃圾回收,所以顯式調用該方法清除線程的局部變量并不是必須的操作,但它可以加快內存回收的速度。

    • protected Object initialValue()

    返回該線程局部變量的初始值,該方法是一個protected的方法,顯然是為了讓子類覆蓋而設計的。這個方法是一個延遲調用方法,在線程第1次調用get()或set(Object)時才執行,并且僅執行1次。ThreadLocal中的缺省實現直接返回一個null。

    值得一提的是,在JDK5.0中,ThreadLocal已經支持泛型,該類的類名已經變為ThreadLocal<T>。API方法 也相應進行了調整,新版本的API方法分別是void set(T value)、T get()以及T initialValue()。

    ThreadLocal是如何做到為每一個線程維護變量的副本的呢?其實實現的思路很簡單:在ThreadLocal類中有一個Map,用于存儲每一個線程的變量副本,Map中元素的鍵為線程對象,而值對應線程的變量副本。我們自己就可以提供一個簡單的實現版本:

    代碼清單1 SimpleThreadLocal

    public class SimpleThreadLocal {

    private Map valueMap = Collections.synchronizedMap(new HashMap());

    public void set(Object newValue) {

    valueMap.put(Thread.currentThread(), newValue);①鍵為線程對象,值為本線程的變量副本

    }

    public Object get() {

    Thread currentThread = Thread.currentThread();

    Object o = valueMap.get(currentThread);②返回本線程對應的變量

    if (o == null && !valueMap.containsKey(currentThread)) {③如果在Map中不存在,放到Map

    中保存起來。

    o = initialValue();

    valueMap.put(currentThread, o);

    }

    return o;

    }

    public void remove() {

    valueMap.remove(Thread.currentThread());

    }

    public Object initialValue() {

    return null;

    }

    }

    雖然代碼清單9?3這個ThreadLocal實現版本顯得比較幼稚,但它和JDK所提供的ThreadLocal類在實現思路上是相近的。

    一個TheadLocal實例

    下面,我們通過一個具體的實例了解一下ThreadLocal的具體使用方法。

    代碼清單2 SequenceNumber

    package com.baobaotao.basic;

    public class SequenceNumber {

    通過匿名內部類覆蓋ThreadLocalinitialValue()方法,指定初始值

    private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>(){

    public Integer initialValue(){

    return 0;

    }

    };

    獲取下一個序列值

    public int getNextNum(){

    seqNum.set(seqNum.get()+1);

    return seqNum.get();

    }

    public static void main(String[] args)

    {

    SequenceNumber sn = new SequenceNumber();

    3個線程共享sn,各自產生序列號

    TestClient t1 = new TestClient(sn);

    TestClient t2 = new TestClient(sn);

    TestClient t3 = new TestClient(sn);

    t1.start();

    t2.start();

    t3.start();

    }

    private static class TestClient extends Thread

    {

    private SequenceNumber sn;

    public TestClient(SequenceNumber sn) {

    this.sn = sn;

    }

    public void run()

    {

    for (int i = 0; i < 3; i++) {④每個線程打出3個序列值

    System.out.println("thread["+Thread.currentThread().getName()+

    "] sn["+sn.getNextNum()+"]");

    }

    }

    }

    }

     

    通常我們通過匿名內部類的方式定義ThreadLocal的子類,提供初始的變量值,如例子中①處所示。TestClient線程產生一組序列號, 在③處,我們生成3個TestClient,它們共享同一個SequenceNumber實例。運行以上代碼,在控制臺上輸出以下的結果:

    thread[Thread-2] sn[1]

    thread[Thread-0] sn[1]

    thread[Thread-1] sn[1]

    thread[Thread-2] sn[2]

    thread[Thread-0] sn[2]

    thread[Thread-1] sn[2]

    thread[Thread-2] sn[3]

    thread[Thread-0] sn[3]

    thread[Thread-1] sn[3]

    考察輸出的結果信息,我們發現每個線程所產生的序號雖然都共享同一個SequenceNumber實例,但它們并沒有發生相互干擾的情況,而是各自產生獨立的序列號,這是因為我們通過ThreadLocal為每一個線程提供了單獨的副本。

    Thread同步機制的比較

    ThreadLocal和線程同步機制相比有什么優勢呢?ThreadLocal和線程同步機制都是為了解決多線程中相同變量的訪問沖突問題。

    在同步機制中,通過對象的鎖機制保證同一時間只有一個線程訪問變量。這時該變量是多個線程共享的,使用同步機制要求程序慎密地分析什么時候對變量進行讀寫,什么時候需要鎖定某個對象,什么時候釋放對象鎖等繁雜的問題,程序設計和編寫難度相對較大。

    而ThreadLocal則從另一個角度來解決多線程的并發訪問。ThreadLocal會為每一個線程提供一個獨立的變量副本,從而隔離了多個線 程對數據的訪問沖突。因為每一個線程都擁有自己的變量副本,從而也就沒有必要對該變量進行同步了。ThreadLocal提供了線程安全的共享對象,在編 寫多線程代碼時,可以把不安全的變量封裝進ThreadLocal。

    由于ThreadLocal中可以持有任何類型的對象,低版本JDK所提供的get()返回的是Object對象,需要強制類型轉換。但JDK 5.0通過泛型很好的解決了這個問題,在一定程度地簡化ThreadLocal的使用,代碼清單 9 2就使用了JDK 5.0新的ThreadLocal<T>版本。

    概括起來說,對于多線程資源共享的問題,同步機制采用了“以時間換空間”的方式,而ThreadLocal采用了“以空間換時間”的方式。前者僅提供一份變量,讓不同的線程排隊訪問,而后者為每一個線程都提供了一份變量,因此可以同時訪問而互不影響。

    Spring使用ThreadLocal解決線程安全問題

    我們知道在一般情況下,只有無狀態的Bean才可以在多線程環境下共享,在Spring中,絕大部分Bean都可以聲明為singleton作用 域。就是因為Spring對一些Bean(如RequestContextHolder、 TransactionSynchronizationManager、LocaleContextHolder等)中非線程安全狀態采用 ThreadLocal進行處理,讓它們也成為線程安全的狀態,因為有狀態的Bean就可以在多線程中共享了。

    一般的Web應用劃分為展現層、服務層和持久層三個層次,在不同的層中編寫對應的邏輯,下層通過接口向上層開放功能調用。在一般情況下,從接收請求到返回響應所經過的所有程序調用都同屬于一個線程,如圖9?2所示:

    通通透透理解ThreadLocal

    1同一線程貫通三層

    這樣你就可以根據需要,將一些非線程安全的變量以ThreadLocal存放,在同一次請求響應的調用線程中,所有關聯的對象引用到的都是同一個變量。

    下面的實例能夠體現Spring對有狀態Bean的改造思路:

    代碼清單3 TopicDao:非線程安全

    public class TopicDao {

    private Connection conn;一個非線程安全的變量

    public void addTopic(){

    Statement stat = conn.createStatement();引用非線程安全變量

    }

    }

    由于①處的conn是成員變量,因為addTopic()方法是非線程安全的,必須在使用時創建一個新TopicDao實例(非singleton)。下面使用ThreadLocal對conn這個非線程安全的“狀態”進行改造:

    代碼清單4 TopicDao:線程安全

    import java.sql.Connection;

    import java.sql.Statement;

    public class TopicDao {

    ①使用ThreadLocal保存Connection變量

    private static ThreadLocal<Connection> connThreadLocal = new ThreadLocal<Connection>();

    public static Connection getConnection(){

    ②如果connThreadLocal沒有本線程對應的Connection創建一個新的Connection,

    并將其保存到線程本地變量中。

    if (connThreadLocal.get() == null) {

    Connection conn = ConnectionManager.getConnection();

    connThreadLocal.set(conn);

    return conn;

    }else{

    return connThreadLocal.get();③直接返回線程本地變量

    }

    }

    public void addTopic() {

    ④從ThreadLocal中獲取線程對應的Connection

    Statement stat = getConnection().createStatement();

    }

    }

    不同的線程在使用TopicDao時,先判斷connThreadLocal.get()是否是null,如果是null,則說明當前線程還沒有對 應的Connection對象,這時創建一個Connection對象并添加到本地線程變量中;如果不為null,則說明當前的線程已經擁有了 Connection對象,直接使用就可以了。這樣,就保證了不同的線程使用線程相關的Connection,而不會使用其它線程的 Connection。因此,這個TopicDao就可以做到singleton共享了。

    當然,這個例子本身很粗糙,將Connection的ThreadLocal直接放在DAO只能做到本DAO的多個方法共享Connection時 不發生線程安全問題,但無法和其它DAO共用同一個Connection,要做到同一事務多DAO共享同一Connection,必須在一個共同的外部類 使用ThreadLocal保存Connection。

    小結

    ThreadLocal是解決線程安全問題一個很好的思路,它通過為每個線程提供一個獨立的變量副本解決了變量并發訪問的沖突問題。在很多情況 下,ThreadLocal比直接使用synchronized同步機制解決線程安全問題更簡單,更方便,且結果程序擁有更高的并發性。

    posted @ 2009-07-01 12:15 Dion 閱讀(344) | 評論 (0)編輯 收藏

         摘要: 題記世界是事實的總體,而不是事物的總體。世界為諸事實所規定,為它們既是全部事實所規定。——路德維希·維特根斯坦,《邏輯哲學論》The answer, please?"請回答我..."The stern voice startles you. 一個嚴厲的聲音把你嚇個半死.You were dozing in Mrs. Rosencrantz's high school math class agai...  閱讀全文
    posted @ 2006-03-11 10:02 Dion 閱讀(2111) | 評論 (0)編輯 收藏

    前幾天跟著寫了一個簡單的例子.
    覺得Drools的配置也沒有什么.
    今天在運行house的例子的時候, 無論怎么樣, 總是異常: 沒有定義的SMF.
    顯然沒有找到我定義的drools.config文件.
    官方網站上是這樣寫地:
    String droolsConfigProp = System.getProperty( "drools.conf" );

    if ( droolsConfigProp != null )
    {
        loadConfig( droolsConfigProp );
    }

    ClassLoader cl = Thread.currentThread( ).getContextClassLoader( ); if ( cl == null )
    {
        cl = getClass( ).getClassLoader( );
    }

    Enumeration configUrls = cl.getResources( "META-INF/drools.conf" );

    if ( !configUrls.hasMoreElements( ) )
    {
        cl = getClass( ).getClassLoader( );
        configUrls = cl.getResources( "META-INF/drools.conf" );
    }

    if ( !configUrls.hasMoreElements( ) )
    {
        cl = ClassLoader.getSystemClassLoader( );
        configUrls = cl.getResources( "META-INF/drools.conf" );
    }

    this.classLoader = cl;
    while ( configUrls.hasMoreElements( ) )
    {
        URL configUrl = (URL) configUrls.nextElement( );
        loadConfig( configUrl );
    }

    好像每一個旮旯里面都找了, 為什么沒有找到我的呢?
    System.getProperty指向的位置并不一定和loadFromUrl位置一樣.呵呵.
    posted @ 2006-03-11 10:00 Dion 閱讀(1640) | 評論 (0)編輯 收藏

    內容提要
           在本文的第一部分,我將討論規則引擎如何幫助你從軟件的應用邏輯中分離出商業規則邏輯,以實現商業應用的靈活性。另外,我還將介紹JSR-94規則引擎 API,及其開源實現Drools項目,它是這一新技術的先驅。在第二部分,我們將介紹一個規則引擎例子,并深入地研究Drools引擎及其JSR-94 擴展的復雜性。

    為什么使用規則引擎
           商業世界充滿了關于變化的陳詞濫調,如任何事物都會改變,唯一不變的是變化等等。而在技術領域里,情況正好相反。我們仍然在試圖解決30年前軟件業中同樣 的一堆問題--也許比30年前還要多的問題。在過去的十年,IT從業人員淹沒在軟件方法學的大量文獻中,如快速軟件開發,極限編程,敏捷軟件開發等,它們 無一例外地強調靈活和變化的重要性。
           但商業通常比開發團隊所依賴的軟件過程和技術改變得更加迅速。當商業策劃人員試圖重整IT部門,以支持新的業務轉型時,仍然覺得很費勁。

    Lost in Translation
           雖然IT團隊反應迅速,但他們通常帶來"電話效應"――IT給商業計劃的執行帶來的阻力和它帶來的利益一樣多。不幸的是,在開發團隊完全理解商業決策規則 并實現之前,規則已經改變了。在軟件進入市場前,它已經過時了,需要進行重構以滿足新的業務需求。如果你是一個開發人員,你會知道我在說什么。再也沒有比 在需求變動的情況下構造軟件讓開發人員更沮喪的事情了。作為軟件開發人員,你必須比業務人員更了解業務,有時還要了解更多。
           試想一下你是一位商業決策者。假如公司的成功依賴于你對于市場趨勢敏銳的洞察力,它常常幫助你領先于競爭者利用變化的市場環境獲利。每天你都會得到更多更 好的市場信息,但并不要緊。完成新產品開發可能需要6-9個月,在此期間,對于市場大膽和敏銳的洞察和信息優勢可能已經浪費了。而且,當產品發布時,有這 樣幾種可能:產品沒有什么吸引人的特性,預算超支,過了產品的最佳發布期限,或三者兼而有之。
           情況可能還會更糟,在完成產品開發時,市場環境和規劃產品開發時相比,已經發生了根本變化。現在你必須要遵守新的規則,你已經喪失了你的邊際優勢,而且設 計軟件的五人中的三人已經離開了公司。你必須給接手的新人重新講解復雜的業務。如果事情不順利,你可能發現自己要對付一個缺少文檔,并且你完全不了解的遺 留應用。
           你的戰略在哪出現了問題?你在哪里應該可以做到更好?最近的輕量級軟件過程,如極限編程,敏捷軟件開發等都在強調自動單元測試和軟件功能優先級的重要性。 除此之外,還有其他的原則,你的開發團隊可能也很熟悉,這些原則可以幫助他們對需求的變動作出迅速反應并縮短項目的開發周期。這些原則的大多數,如系統分 解,多年前就已經出現,并得到了Java平臺的支持(如JMX等),還有如面向對象和角色建模,已經內建在Java語言中。
           但Java仍然是一門相當年輕的語言,而且Java平臺遠遠還沒有完備。當前在Java社區,一個引人注目的新技術是,分離商業決策者的商業決策邏輯和應 用開發者的技術決策,并把這些商業決策放在中心數據庫,讓它們能在運行時(即商務時間)可以動態地管理和修改。這是一個你值得考慮的策略。
           為什么你的開發團隊不得不象商業經理人一樣,在代碼中包含復雜微妙的商業決策邏輯呢?你怎樣才能向他們解釋決策推理的微妙之處呢?你這樣做是否謹慎呢?可 能不是。象bottom line一樣,某些東西在解釋的過程中丟失了。為什么要冒這樣的風險,讓應用代碼或測試代碼錯誤地表達你的商業決策邏輯呢?如果這樣做的話,你怎樣檢查它 們的正確性呢――難道你自己想學習如何編程和編寫測試代碼,或者你的客戶會為你測試軟件?你一方面要應付市場,一方面要應付軟件代碼,這實在太困難了。
           如果能將這些商業決策規則集中地放在一個地方,以一種你可以理解的格式定義,讓你可以直接管理,而不是散落在代碼的各個角落,那該有多好。如果你能把商業 決策規則獨立于你的軟件代碼,讓開發團隊作出技術決策,你將會獲得更多好處。你的項目開發周期會更短,軟件對于變動的需求更靈活。

    規則引擎標準Java API
           2003年11月,Java社區通過了Java Rule Engine API規范(JSR-94)的最后草案。這個新的API讓開發人員在運行時訪問和執行規則有了統一的標準方式。隨著新規范產品實現的成熟和推向市場,開發 團隊將可以從應用代碼中抽取出商業決策邏輯。
           這就需要新一代的管理工具,幫助商務經理人可以定義和細化軟件系統的行為。不必通過開發過程來修改應用,并假定可以得到正確的結果,經理人將可以隨時根據需要修改決策規則,并進行測試。
           但這將需要開發人員在設計系統時作出某些改變,并可以得到合適的開發工具。

    分離商務和技術的關注點
           這是一個非常簡單的例子,從經理人的角度,說明如何分離商務和技術的關注點。
           你管理著一個反向投資基金。你公司計算機系統的一部分用于分析股票價格,收益和每股凈資產,并在需要時向你提出預警。這個計算機系統的工作是,識別出PE比率比市場平均值低的股票,并標記出來以便進一步的檢查。
           你的IT部門擁有一大堆數據,并開發了一系列你可以在規則中引用的簡單數據對象。現在,為簡單起見,假設你是一名受過良好教育的,了解技術的管理人,你了解XML的基本知識,可以讓你編寫和修改簡單的XML規則文件。
           你的第一個規則是,給道瓊斯所有的股票估值,并剔除P/E比率大于10的股票(這有點過分簡化,但這里只作為一個例子)。保留下來的股票用來生產一系列報表。對于這個簡單的例子,你的規則文件看起來如下(我們將會過頭來討論這個文件的結構):

    <stock:overvalued>
        <stock:index> DJIA </stock:index>
        <stock:pe> over 10.0 </stock:pe>
    </stock:overvalued>

           一個月后,你接到一家巴西分析師公司的電話,雇傭你的公司生成一系列巴西股市的報表,但他們有更嚴格的標準。而目前在巴西,P/E比率市場平均值是個位 數,因此你用來評估被市場低股票的閾值需要改變。除了較低的P/E比率,你的新客戶還要求以Price-to-Book比率作為參考標準。
           你啟動規則編輯器,并修改規則以匹配新的評估條件。現在,規則引擎剔除巴西股市中P/E比率大于6.5,以及Price to Book 比率小于等于1的股票。完成規則文件修改后,看起來如下:

    <stock:overvalued>
        <stock:index> Brazil </stock:index>
        <stock:pe> over 6.5 </stock:pe>
        <stock:pb> over 1.0 </stock:pb>
    </stock:overvalued>

           你無需為此向開發團隊作任何解釋。你無需等待他們開發或測試程序。如果你的規則引擎的語義足夠強大,讓你描述工作數據,你可以隨時按需修改商業規則。
           如果限制因素是規則的定義語言和數據模型,你可以確信這兩者將會標準化,并出現先進的編輯器和工具,以簡化規則的定義,保存和維護。
           現在,我希望你已經清楚以下的原則:在這個例子中,哪只股票是否被選擇是一個商務決策,而不是技術決策。決定將哪只股票交給你的分析師是經理人的邏輯 ――"logic of the bottom line"。經理人作出這些決策,并可以按需定制應用。這些規則因此變成了一種控制界面,一種新的商業系統用戶界面。

    使用Rule開發
           如果在這個應用場景中,你是一個開發人員,你的工作會稍微輕松一些。一旦你擁有了一種用于分析股票的規則語言,你可以取出數據對象并交給規則引擎執行。我們將會到規則語言的討論,但現在我們繼續剛才的例子。
           你的系統將一系列的stock bean輸入規則引擎。當規則執行后,你可以選出符合條件的股票并可以對它們作進一步處理。也許是把它們輸入報表生成系統。分析師使用這些報表幫助他們分 析股市。同時,老板也可能讓你使用新的技術分析工具,并用Dow理論預測股市的底部和頂部。
           規則引擎可以讓你的系統變得更簡單,因為你無需在代碼中編寫商務邏輯,如怎樣選擇股票,選擇股票過程中奇怪的條件組合等。這些邏輯不再進入你的代碼。你將可以專注于數據模型。
           現在可以這么認為,通過從應用代碼中剝離出易變的商業邏輯,你的效率會更高。但凡是總有例外――簡單應用可能并不能從規則系統中獲益。但如果你開發一個大型系統,有很多易變的商業邏輯,你可以考慮在應用中集成規則引擎。
           除了從應用代碼中剝離出商業決策邏輯外,規則引擎還有其他用處。有時候你需要應用成百上千的規則進行決策,并且有上千個對象和這些規則一起使用。很難想象 有什么先進的人工智能引擎可以處理這種情況。遇到這種情況,你需要一個極快的決策算法或是大型機。大型機并不便宜,但你可以非常便宜的得到效率和可伸縮性 最好的算法。

    Bob McWhirter的Drools項目
           現在,我要介紹Drools項目,Charles Forgy Rete算法的一個增強的Java語言實現。Drools是一個Bob McWhirter開發的開源項目,放在The Codehaus上。在我寫這篇文章時,Drools發表了2.0-beata-14版。在CVS中,已完整地實現了JSR94 Rule Engine API并提供了單元測試代碼。
           Rete算法是Charles Forgy在1979年發明的,是目前用于生產系統的效率最高的算法(除了私有的Rete II)。Rete是唯一的,效率與執行規則數目無關的決策支持算法。For the uninitiated, that means it can scale to incorporate and execute hundreds of thousands of rules in a manner which is an order of magnitude more efficient then the next best algorithm。Rete應用于生產系統已經有很多年了,但在Java開源軟件中并沒有得到廣泛應用(討論Rete算法的文檔參見http://herzberg.ca.sandia.gov/jess/docs/61/rete.html。)。
           除了應用了Rete核心算法,開源軟件License和100%的Java實現之外,Drools還提供了很多有用的特性。其中包括實現了JSR94 API和創新的規則語義系統,這個語義系統可用來編寫描述規則的語言。目前,Drools提供了三種語義模塊――Python模塊,Java模塊和 Groovy模塊。本文余下部分集中討論JSR94 API,我將在第二篇文章中討論語義系統。
           作為使用javax.rules API的開發人員,你的目標是構造一個RuleExecutionSet對象,并在運行時通過它獲得一個RuleSession對象。為了簡化這個過程, 我編寫了一個規則引擎API的fa?ade,可以用來解釋代表Drools的DRL文件的InputStream,并構造一個 RuleExecutionSet對象。
           在上面提到了Drools的三種語義模塊,我接下來使用它們重新編寫上面的例子XML規則文件。這個例子中我選擇Java模塊。使用Java模塊重新編寫的規則文件如下:

    <rule-set name="StockFlagger"
          xmlns="http://drools.org/rules"
          xmlns:java="http://drools.org/semantics/java">
      <rule name="FlagAsUndervalued">
        <parameter identifier="stock">
          <java:class>org.codehaus.drools.example.Stock</java:class>
        </parameter>
        <java:condition>stock.getIndexName().equals("DJIA");</java:condition>
        <java:condition>stock.getPE() > 10 </java:condition>
        <java:consequence>
          removeObject(stock);   ( 譯注:應該是retractObject(stock) )
        </java:consequence>
      </rule>
    </rule-set>

           現在的規則文件并沒有上面的簡潔明了。別擔心,我們將在下一篇文章討論語義模塊。現在,請注意觀察XML文件的結構。其中一個rule-set元素包含了 一個或多個rule元素,rule元素又包含了parameter,condition和consequence元素。Condition和 consequence元素包含的內容和Java很象。注意,在這些元素中,有些事你可以做,有些事你不能做。目前,Drools使用 BeanShell2.0b1作為它的Java解釋器。我在這里并不想詳細的討論DRL文件和Java語義模塊的語法。我們的目標是解釋如何使用 Drools的JSR94 API。
           在Drools項目CVS的drools-jsr94模塊中,單元測試代碼包含了一個ExampleRuleEngineFacade對象,它基于 Brian Topping的Dentaku項目。這個fa?ade對象通過javax.rules API,創建了供RuleExecutionSet和RuleSession使用的一系列對象。它并沒有完全包括了Drools引擎API的所有特性和細 微差別,但可以作為新手使用API的一個簡單例子。
          下面的代碼片斷顯示如何使用規則引擎的facade構造一個RuleExecutionSet對象,并通過它獲得一個RuleSession對象。
     
    import java.io.InputStream;
    import javax.rules.*;
    import org.drools.jsr94.rules.ExampleRuleEngineFacade;
    public class Example {
        private ExampleRuleEngineFacade engine;
        private StatelessRuleSession statelessSession;
        /* place the rule file in the same package as this class */
        private String bindUri = "myRuleFile.drl"
        public Example() {
            /* get your engine facade */
            engine = new ExampleRuleEngineFacade();
            /* get your input stream */
            InputStream inputStream =
                    Example.class.getResourceAsStream(bindUri);
            /* build a RuleExecutionSet to the engine */
            engine.addRuleExecutionSet(bindUri, inputStream);
            /* don't forget to close your InputStream! */
            inputStream.close();
            /* get your runtime session */
            this.statelessSession = engine.getStatelessRuleSession(bindUri);
        }
        ...
    }

           在以上的例子代碼中,你需要處理InputStream的IOException例外,這里為了簡單起見省略了。你要做的只是構建InputStream 對象,并把它輸入ExampleRuleEngineFacade,用來創建一個RuleExecutionSet對象。然后,你可以得到一個 StatelessRuleSession,并用它來執行所有的規則。使用StatelessRuleSession相對簡單。我們可以給上面的類添加一 個方法,用來對一個對象列表執行規則:

    public List getUndervalued(List stocks) {
        return statelessSession.executeRules(stocks);
    }

           該方法輸入一個stock對象列表給規則引擎,然后使用規則評估輸入的股票對象,并剔除那些不符合價值低估標準的股票。它是個簡單的例子,但足以說明問題。
           在ExampleRuleEngineFacade類中,代碼會稍微有些復雜。ExampleRuleEngineFacade類創建了一個 RuleServiceProvider對象,并用它創建RuleAdministrator,RuleExecutionSetProvider和 RuleRuntime對象。RuleExecutionSetProvider負責解釋InputStream,并創建一個 RuleExecutionSet對象。RuleRuntime對象用來得到一個session,RuleAdministrator用來管理所有的對 象。在往下是Drools核心API,它的核心是Rete算法實現。我在這里不打算詳細討論,但你可以看看 ExampleRuleEngineFacade的代碼。
           現在你已經看到了在商業和科研方面使用規則引擎的一些例子,并對Drools項目有了基本的了解。在下一篇文章里,我將討論DRL文件的結構和Java語 義模塊,讓你可以編寫自己的DRL文件。還將向你解釋如何編寫你自己的語義模塊,討論salience和working memory的概念。

    資源
    · Drools Project
    · JSR-94 Specification
     
    作者
          N. Alex Rupp is a freelance software architect and developer from Minneapolis, and the current JSR94 Lead for the Drools project.
    posted @ 2006-03-11 10:00 Dion 閱讀(1750) | 評論 (0)編輯 收藏

    一般情況下, 只顯式引用:

    • drools-all-2.0.jar
    • antlr-2.7.5.jar
    • xercesImpl-2.6.2.jar

    就可以了.當然ClassPath下也要用一些其他的jar.
    下載位置: http://dist.codehaus.org/drools/distributions/drools-2.0-bin-withdeps.zip

    如果, 在DRL文件中定義了Java Function, 這時候就要顯式的引用:

    • janino-2.3.2.jar

    這時候, 引擎是需要janino把DRL中的java function描述轉換成可執行的二進制代碼(?)的.

    posted @ 2006-03-11 09:59 Dion 閱讀(962) | 評論 (0)編輯 收藏

    Drools and Mandarax

    兩個項目做了兩件不同的事情: 一個是Forward Chaining,另一個是 backward chaining. Drools 是forward chaining的,  意味著 它對assert的對象反應, 事件驅動的. Mandarax 是 backward chaining的, 像 prologue一樣, 你問它問題, 它試圖給你它知道的答案. 舉例來說, 在使用Drools的時候, 你可能會先assert 給它今天的日期, 如果它發現有匹配的規則的手,它會用事件的方式通知你"今天是你的生日". 在 backward chaining 的系統, 你可能先問 "今天是我的生日嘛?" 系統會搜索它知道的, 然后告訴你答案.
    For an excellent explanation of forward and backward chaining read Charles Forgey's recent articles at http://rulespower.com/ - Forward and Backward Chaining:
    Parts 1, 2 and 3.
    posted @ 2006-03-11 09:58 Dion 閱讀(1173) | 評論 (0)編輯 收藏

    Open Source Rule Engines Written In Java

    • Drools The drools engine uses a modified form of the Rete algorithm called the Rete-OO algorithm. Internally it operates using the same concepts and methods as Forgy's original but adds some node types required for seemless integration with an object-oriented language.
    • OFBiz Rule Engine Backward chaining is supported. Original code base from "Building Parsers in Java" by Steven John Metsker.
    • Mandarax Based on backward reasoning. The easy integration of all kinds of data sources. E.g., database records can be easily integrated as sets of facts and reflection is used in order to integrate functionality available in the object model.
    • Algernon Efficient and concise KB traversal and retrieval. Straightforward access to ontology classes and instances. Supports both forward and backward chaining.
    • TyRuBa TyRuBa supports higher order logic programming: variables and compound terms are allowed everywhere in queries and rules, also in the position of a functor- or predicate-name. TyRuBa speeds up execution by making specialized copies of the rule-base for each query in the program. It does so incrementally while executing a logic program and builds an index for fast access to rules and facts in the rule base, tuned to the program that is running. The indexing techniques works also for higher-order logic. TyRuBa does 'tabling' of query results.
    • JTP Java Theorem Prover is based on a very simple and general reasoning architecture. The modular character of the architecture makes it easy to extend the system by adding new reasoning modules (reasoners), or by customizing or rearranging existing ones.
    • JEOPS JEOPS adds forward chaining, first-order production rules to Java through a set of classes designed to provide this language with some kind of declarative programming.
    • InfoSapient Semantics of business rules expressed using fuzzy logic.
    • JShop Simple Hierarchical Ordered Planner (SHOP) written in Java.
    • RDFExpert RDF-driven expert system shell. The RDFExpert software uses Brian McBride's JENA API and parser. A simple expert system shell that uses RDF for all of its input: knowledge base, inference rules and elements of the resolution strategy employed. It supports forward and backward chaining.
    • Jena 2 - Jena is a Java framework for writing Semantic Web applications. Jena2 has a reasoner subsystem which includes a generic rule based inference engine together with configured rule sets for RDFS and for the OWL/Lite subset of OWL Full. These reasoners can be used to construct inference models which show the RDF statements entailed by the data being reasoned over. The subsystem is designed to be extensible so that it should be possible to plug a range of external reasoners into Jena, though worked examples of doing so are left to a future release.
    • JLisa - JLisa is a powerful framework for building business rules accessible to Java and it is compatible with JSR-94. JLisa is more powerful than Clips because it has the expanded benefit of having all the features from common lisp available. These features are essential for multi-paradigm software development
    • Euler - Euler is a backward-chaining reasoner enhanced with Euler path detection and will tell you whether a given set of facts and rules supports a given conclusion. Things are described in N3.
    • JLog - JLog is an implementation of a Prolog interpreter, written in Java. It includes built-in source editor, query panels, online help, animation primitives, and a GUI debugger.
    • Pellet OWL Reasoner - Pellet is an open-source Java based OWL DL reasoner. It can be used in conjunction with either Jena or OWL API libraries. Pellet API provides functionalities to see the species validation, check consistency of ontologies, classify the taxonomy, check entailments and answer a subset of RDQL queries (known as ABox queries in DL terminology). Pellet is an OWL DL reasoner based on the tableaux algorithms developed for expressive Description Logics.
    • Prova - Prova is derived from Mandarax Java-based inference system developed by Jens Dietrich. Prova extends Mandarax by providing a proper language syntax, native syntax integration with Java, and agent messaging and reaction rules. The development of this language was supported by the grant provided within the EU project GeneStream. In the project, the language is used as a rules-based backbone for distributed web applications in biomedical data integration.
    posted @ 2006-03-11 09:58 Dion 閱讀(1342) | 評論 (0)編輯 收藏

    始終會用上的Common BeanUtils

    Beanutils用了魔術般的反射技術,實現了很多夸張有用的功能,都是C/C++時代不敢想的。無論誰的項目,始終一天都會用得上它。我算是后知后覺了,第一回看到它的時候居然錯過。

    1.屬性的動態getter、setter

    在這框架滿天飛的年代,不能事事都保證執行getter,setter函數了,有時候屬性是要根據名字動態取得的,就像這樣:  
    BeanUtils.getProperty(myBean,"code");
    而Common BeanUtils的更強功能在于可以直接訪問內嵌對象的屬性,只要使用點號分隔。
    BeanUtils.getProperty(orderBean, "address.city");
    相比之下其他類庫的BeanUtils通常都很簡單,不能訪問內嵌的對象,所以有時要用Commons BeanUtils來替換它們。

    BeanUtils還支持List和Map類型的屬性,如下面的語法即可取得Order的顧客列表中第一個顧客的名字
    BeanUtils.getProperty(orderBean, "customers[1].name");
    其中BeanUtils會使用ConvertUtils類把字符串轉為Bean屬性的真正類型,方便從HttpServletRequest等對象中提取bean,或者把bean輸出到頁面。
    而PropertyUtils就會原色的保留Bean原來的類型。

    2.BeanCompartor 動態排序

    還是通過反射,動態設定Bean按照哪個屬性來排序,而不再需要在實現bean的Compare接口進行復雜的條件判斷。
    List peoples = ...; // Person對象的列表
    Collections.sort(peoples, new BeanComparator("age"));

    如果要支持多個屬性的復合排序,如"Order By lastName,firstName"

    ArrayList sortFields = new ArrayList();
    sortFields.add(new BeanComparator("lastName"));
    sortFields.add(new BeanComparator("firstName"));
    ComparatorChain multiSort = new ComparatorChain(sortFields);
    Collections.sort(rows,multiSort);

    其中ComparatorChain屬于jakata commons-collections包。
    如果age屬性不是普通類型,構造函數需要再傳入一個comparator對象為age變量排序。
    另外, BeanCompartor本身的ComparebleComparator, 遇到屬性為null就會拋出異常, 也不能設定升序還是降序。這個時候又要借助commons-collections包的ComparatorUtils.

       Comparator mycmp = ComparableComparator.getInstance();
       mycmp = ComparatorUtils.nullLowComparator(mycmp);  //允許null
       mycmp = ComparatorUtils.reversedComparator(mycmp); //逆序
       Comparator cmp = new BeanComparator(sortColumn, mycmp);
    3.Converter 把Request或ResultSet中的字符串綁定到對象的屬性

       經常要從request,resultSet等對象取出值來賦入bean中,如果不用MVC框架的綁定功能的話,下面的代碼誰都寫膩了。

       String a = request.getParameter("a");
    bean.setA(a);
    String b = ....
    bean.setB(b);
    ......

    不妨寫一個Binder自動綁定所有屬性:

        MyBean bean = ...;
    HashMap map = new HashMap();
    Enumeration names = request.getParameterNames();
    while (names.hasMoreElements())
    {
    String name = (String) names.nextElement();
    map.put(name, request.getParameterValues(name));
    }
    BeanUtils.populate(bean, map);

        其中BeanUtils的populate方法或者getProperty,setProperty方法其實都會調用convert進行轉換。
         但Converter只支持一些基本的類型,甚至連java.util.Date類型也不支持。而且它比較笨的一個地方是當遇到不認識的類型時,居然會拋 出異常來。 對于Date類型,我參考它的sqldate類型實現了一個Converter,而且添加了一個設置日期格式的函數。
    要把這個Converter注冊,需要如下語句:

        ConvertUtilsBean convertUtils = new ConvertUtilsBean();
       DateConverter dateConverter = new DateConverter();
       convertUtils.register(dateConverter,Date.class);



    //因為要注冊converter,所以不能再使用BeanUtils的靜態方法了,必須創建BeanUtilsBean實例
    BeanUtilsBean beanUtils = new BeanUtilsBean(convertUtils,new PropertyUtilsBean());
    beanUtils.setProperty(bean, name, value);
    4 其他功能
    4.1 ConstructorUtils,動態創建對象
         public static Object invokeConstructor(Class klass, Object arg)
    4.2 MethodUtils,動態調用方法
        MethodUtils.invokeMethod(bean, methodName, parameter);

    4.3 PropertyUtils,當屬性為Collection,Map時的動態讀取:
    Collection: 提供index
       BeanUtils.getIndexedProperty(orderBean,"items",1);
    或者
      BeanUtils.getIndexedProperty(orderBean,"items[1]");
    Map: 提供Key Value
      BeanUtils.getMappedProperty(orderBean, "items","111");//key-value goods_no=111 
    或者
      BeanUtils.getMappedProperty(orderBean, "items(111)") 

    4.4 PropertyUtils,直接獲取屬性的Class類型
         public static Class getPropertyType(Object bean, String name)
    4.5 動態Bean 用DynaBean減除不必要的VO和FormBean 
    posted @ 2006-01-15 20:20 Dion 閱讀(5093) | 評論 (2)編輯 收藏

         摘要: Migrate apps from Internet Explorer to MozillaHow to make Internet Explorer-specific Web applications work in Mozilla-based browsersDocument options Print this page'); //--> Print this page E-ma...  閱讀全文
    posted @ 2006-01-04 19:18 Dion 閱讀(1490) | 評論 (1)編輯 收藏

    以下以 IE 代替 Internet Explorer,以 MF 代替 Mozzila Firefox

    1. document.form.item 問題
        (1)現有問題:
            現有代碼中存在許多 document.formName.item("itemName") 這樣的語句,不能在 MF 下運行
        (2)解決方法:
            改用 document.formName.elements["elementName"]
        (3)其它
            參見 2

    2. 集合類對象問題
        (1)現有問題:
            現有代碼中許多集合類對象取用時使用 (),IE 能接受,MF 不能。
        (2)解決方法:
            改用 [] 作為下標運算。如:document.forms("formName") 改為 document.forms["formName"]。
            又如:document.getElementsByName("inputName")(1) 改為 document.getElementsByName("inputName")[1]
        (3)其它

    3. window.event
        (1)現有問題:
            使用 window.event 無法在 MF 上運行
        (2)解決方法:
            MF 的 event 只能在事件發生的現場使用,此問題暫無法解決。可以這樣變通:
            原代碼(可在IE中運行):
                <input type="button" name="someButton" value="提交" onclick="javascript:gotoSubmit()"/>
                ...
                <script language="javascript">
                    function gotoSubmit() {
                        ...
                        alert(window.event);    // use window.event
                        ...
                    }
                </script>

            新代碼(可在IE和MF中運行):
                <input type="button" name="someButton" value="提交" onclick="javascript:gotoSubmit(event)"/>
                ...
                <script language="javascript">
                    function gotoSubmit(evt) {
                        evt = evt ? evt : (window.event ? window.event : null);
                        ...
                        alert(evt);             // use evt
                        ...
                    }
                </script>
            此外,如果新代碼中第一行不改,與老代碼一樣的話(即 gotoSubmit 調用沒有給參數),則仍然只能在IE中運行,但不會出錯。所以,這種方案 tpl 部分仍與老代碼兼容。

    4. HTML 對象的 id 作為對象名的問題
        (1)現有問題
            在 IE 中,HTML 對象的 ID 可以作為 document 的下屬對象變量名直接使用。在 MF 中不能。
        (2)解決方法
            用 getElementById("idName") 代替 idName 作為對象變量使用。

    5. 用idName字符串取得對象的問題
        (1)現有問題
            在IE中,利用 eval(idName) 可以取得 id 為 idName 的 HTML 對象,在MF 中不能。
        (2)解決方法
            用 getElementById(idName) 代替 eval(idName)。

    6. 變量名與某 HTML 對象 id 相同的問題
        (1)現有問題
            在 MF 中,因為對象 id 不作為 HTML 對象的名稱,所以可以使用與 HTML 對象 id 相同的變量名,IE 中不能。
        (2)解決方法
            在聲明變量時,一律加上 var ,以避免歧義,這樣在 IE 中亦可正常運行。
            此外,最好不要取與 HTML 對象 id 相同的變量名,以減少錯誤。
        (3)其它
            參見 問題4

    7. event.x 與 event.y 問題
        (1)現有問題
            在IE 中,event 對象有 x, y 屬性,MF中沒有。
        (2)解決方法
            在MF中,與event.x 等效的是 event.pageX。但event.pageX IE中沒有。
            故采用 event.clientX 代替 event.x。在IE 中也有這個變量。
            event.clientX 與 event.pageX 有微妙的差別(當整個頁面有滾動條的時候),不過大多數時候是等效的。

            如果要完全一樣,可以稍麻煩些:
            mX = event.x ? event.x : event.pageX;
            然后用 mX 代替 event.x
        (3)其它
            event.layerX 在 IE 與 MF 中都有,具體意義有無差別尚未試驗。


    8. 關于frame
       (1)現有問題
             在 IE中 可以用window.testFrame取得該frame,mf中不行
       (2)解決方法
             在frame的使用方面mf和ie的最主要的區別是:
    如果在frame標簽中書寫了以下屬性:
    <frame src="xx.htm" id="frameId" name="frameName" />
    那么ie可以通過id或者name訪問這個frame對應的window對象
    而mf只可以通過name來訪問這個frame對應的window對象
    例如如果上述frame標簽寫在最上層的window里面的htm里面,那么可以這樣訪問
    ie: window.top.frameId或者window.top.frameName來訪問這個window對象
    mf: 只能這樣window.top.frameName來訪問這個window對象

    另外,在mf和ie中都可以使用window.top.document.getElementById("frameId")來訪問frame標簽
    并且可以通過window.top.document.getElementById("testFrame").src = 'xx.htm'來切換frame的內容
    也都可以通過window.top.frameName.location = 'xx.htm'來切換frame的內容
    關于frame和window的描述可以參見bbs的‘window與frame’文章
    以及/test/js/test_frame/目錄下面的測試
    ----adun 2004.12.09修改

    9. 在mf中,自己定義的屬性必須getAttribute()取得
    10.在mf中沒有  parentElement parement.children  而用
                   parentNode parentNode.childNodes
       childNodes的下標的含義在IE和MF中不同,MF使用DOM規范,childNodes中會插入空白文本節點。
      一般可以通過node.getElementsByTagName()來回避這個問題。
       當html中節點缺失時,IE和MF對parentNode的解釋不同,例如
       <form>
       <table>
            <input/>
       </table>
       </form>
       MF中input.parentNode的值為form, 而IE中input.parentNode的值為空節點

      MF中節點沒有removeNode方法,必須使用如下方法 node.parentNode.removeChild(node)

    11.const 問題
      (1)現有問題:
         在 IE 中不能使用 const 關鍵字。如 const constVar = 32; 在IE中這是語法錯誤。
      (2)解決方法:
         不使用 const ,以 var 代替。

    12. body 對象
       MF的body在body標簽沒有被瀏覽器完全讀入之前就存在,而IE則必須在body完全被讀入之后才存在

    13. url encoding
    在js中如果書寫url就直接寫&不要寫&amp;例如var url = 'xx.jsp?objectName=xx&amp;objectEvent=xxx';
    frm.action = url那么很有可能url不會被正常顯示以至于參數沒有正確的傳到服務器
    一般會服務器報錯參數沒有找到
    當然如果是在tpl中例外,因為tpl中符合xml規范,要求&書寫為&amp;
    一般MF無法識別js中的&amp;


    14. nodeName 和 tagName 問題
      (1)現有問題:
         在MF中,所有節點均有 nodeName 值,但 textNode 沒有 tagName 值。在 IE 中,nodeName 的使用好象
         有問題(具體情況沒有測試,但我的IE已經死了好幾次)。
      (2)解決方法:
         使用 tagName,但應檢測其是否為空。

    15. 元素屬性
       IE下 input.type屬性為只讀,但是MF下可以修改


    16. document.getElementsByName() 和 document.all[name] 的問題
      (1)現有問題:
         在 IE 中,getElementsByName()、document.all[name] 均不能用來取得 div 元素(是否還有其它不能取的元素還不知道)。
    posted @ 2005-12-31 17:55 Dion 閱讀(934) | 評論 (1)編輯 收藏

    主站蜘蛛池模板: 青青青国产色视频在线观看国产亚洲欧洲国产综合 | 国产精成人品日日拍夜夜免费| 亚洲 国产 图片| 免费大片av手机看片高清| 四虎永久成人免费| 在线亚洲精品视频| 亚洲无码高清在线观看| 国产在线观看xxxx免费| 亚洲AV永久青草无码精品| 久久一本岛在免费线观看2020| 亚洲国产精品婷婷久久| 在免费jizzjizz在线播| 亚洲砖码砖专无区2023| 成在线人永久免费视频播放| 日韩少妇内射免费播放| 久久综合九九亚洲一区| 黄色永久免费网站| 日韩亚洲综合精品国产| 亚洲永久精品ww47| 99精品视频在线观看免费播放| 亚洲av午夜精品无码专区| 日本黄页网站免费| 久青草视频97国内免费影视| 亚洲AV色香蕉一区二区| 少妇高潮太爽了在线观看免费| 亚洲av成人片在线观看| 久久亚洲综合色一区二区三区| 免费观看国产网址你懂的| 亚洲AV无码一区二区乱子仑 | 免费福利资源站在线视频| 国产成人A人亚洲精品无码| 在线看片韩国免费人成视频| 色五月五月丁香亚洲综合网| 黑人精品videos亚洲人| 永久免费AV无码国产网站| 一级做a爰黑人又硬又粗免费看51社区国产精品视 | 久久免费的精品国产V∧| 亚洲JIZZJIZZ妇女| 亚洲av日韩av无码| 国产成人高清精品免费软件| 免费A级毛片无码视频|