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

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

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

    零全零美(www.zzgwt.com)
    生活中的很多事情,并不像If...Else那么簡單!
    posts - 96,comments - 52,trackbacks - 0
           前面我有一篇《JBPM源碼解讀之:Fork》,大致分析了JBPM對于Fork的實現方式,其實Fork和Join是不可分割的一對,Fork實現分拆,Join實現匯集。先讓我們看一下《JBPM 3.2.3 User Guide》中關于Join的描述:
     The default join assumes that all tokens that arrive in the join are children of the same parent. This situation is created when using the fork as mentioned above and when all tokens created by a fork arrive in the same join. A join will end every token that enters the join. Then the join will examine the parent-child relation of the token that enters the join. When all sibling tokens have arrived in the join, the parent token will be propagated over the (unique!) leaving transition. When there are still sibling tokens active, the join will behave as a wait state.
         下面就讓我們從JBPM源碼的角度分析一下Join是如何關閉每一個到達它的Token,是如何檢查各個子Token與父Token的父子關系,又是如何重新激活父Token實現流程的繼續流轉,更重要的也是我寫這篇文章的原因:我們如何能從Join默認的實現方式中得到啟發,讓我們能夠更好的理解JBPM整個運轉流程,更好的駕馭整個項目。

     我們來看Join類的成員變量:

     1    /**
     2     * specifies wether what type of hibernate lock should be acquired. null
     3     * value defaults to LockMode.Force
     4     */

     5    String parentLockMode;
     6
     7    /**
     8     * specifies if this joinhandler is a discriminator. a descriminator
     9     * reactivates the parent when the first concurrent token enters the join.
    10     * 注:
    11     */

    12    boolean isDiscriminator = false;
    13
    14    /**
    15     * a fixed set of concurrent tokens.
    16     */

    17    Collection tokenNames = null;
    18
    19    /**
    20     * a script that calculates concurrent tokens at runtime.
    21     */

    22    Script script = null;
    23
    24    /**
    25     * reactivate the parent if the n-th token arrives in the join.
    26     */

    27    int nOutOfM = -1;

          parentLockMode用于控制Hibernate的鎖機制,這點我們暫且不去深究,有意思的是下面幾個變量:isDiscriminator、tokenNames、script、nOutOfM共同決定了Join節點內被的具體行為,本文稍后會具體解說。

    1public void read(Element element, JpdlXmlReader jpdlReader) {
    2        String lock = element.attributeValue("lock");
    3        if ((lock != null&& (lock.equalsIgnoreCase("pessimistic"))) {
    4            parentLockMode = LockMode.UPGRADE.toString();
    5        }

    6    }

           Join的read方法很簡單,只是起到了讀取JPDL中對lock的配置,并沒有關心其他成員變量的初始化,這也就直接說明了isDiscriminator、tokenNames、script、nOutOfM這幾個變量均屬于運行期,也就是我們沒有辦法在配置文件里頭像Fork一樣配置Script,就算配了Join也不認。
     execute(ExecutionContext executionContext)方法是每個繼承自Node的類的核心方法,Join類也是在這個方法中實現了Join的控制機制。在解讀這個方法之前必須應該明白的一件事是:Join的execute方法就像Fork的Node-Leave 事件一樣是會執行多次的,同樣這取決于與Join搭配的Fork具體產生了幾個子Token。
     1      //取得Token
     2        Token token = executionContext.getToken();
     3        //得到Token的isAbleToReactivateParent屬性
     4        boolean isAbleToReactivateParent = token.isAbleToReactivateParent();
     5
     6        //如果當前Token沒有結束,則結束該Token,到這里Fork產生的子Token的生命周期也就結束了
     7        if (!token.hasEnded()) {
     8            token.end(false);
     9        }

    10        //檢查該Token是否具有激活父Token的能力,如果有方法繼續,如果沒有方法結束。
    11        if (isAbleToReactivateParent) {
    12            
    13        }

      Join的這種處理方式,讓我們可以方便的實現一種生活中經常用到的抄送機制,例如:在某個流程中有一個審批的環節,這個審批的默認執行人為李副處長,既定情況下,如果這位李副處長審批完畢,流程就應該繼續,但是現在王正處長要求所有審批過的文件都要自己親自過目,但是只是過目,王處長的這個“過目”的行為要求并不會影響流程的運行,意思就是說,王處長完全有可能在流程都已經結束過了才去過目。稍后我會另有文章具體介紹我的使用Fork+Join實現抄送的思路。
      繼續,呵呵,接下我們分析的方法都是在if (isAbleToReactivateParent)塊內的,剛才我們說如果isAbleToReactivateParent == false那么整個方法就結束了。
     1            // the token arrived in the join and can only reactivate
     2            // the parent once
     3            token.setAbleToReactivateParent(false);
     4            總感覺這句是多余的,因為好像一個子Token只有一次機會進入Join節點,然后他的生命周期也就結束了,再者在execute方法的后面有
     5                    Iterator iter = parentToken.getChildren().values().iterator();
     6                    while (iter.hasNext()) {
     7                        ((Token) iter.next()).setAbleToReactivateParent(false);
     8                    }

     9            略去關于處理Hibernate鎖機制的代碼,因為它并不直接影響這個流程的運作.
    10            
    11                boolean reactivateParent = true;
    12
    13                // if this is a discriminator
    14                if (isDiscriminator) {
    15                    // reactivate the parent when the first token arrives in the
    16                    // join. this must be the first token arriving because
    17                    // otherwise
    18                    // the isAbleToReactivateParent() of this token should have
    19                    // been false
    20                    // above.
    21                    reactivateParent = true;
    22
    23                    // if a fixed set of tokenNames is specified at design
    24                    // time
    25                }
     else if (tokenNames != null{
    26                    // check reactivation on the basis of those tokenNames
    27                    reactivateParent = mustParentBeReactivated(parentToken, tokenNames.iterator());
    28
    29                    // if a script is specified
    30                }
     else if (script != null{
    31
    32                    // check if the script returns a collection or a boolean
    33                    Object result = null;
    34                    try {
    35                        result = script.eval(token);
    36                    }
     catch (Exception e) {
    37                        this.raiseException(e, executionContext);
    38                    }

    39                    // if the result is a collection
    40                    if (result instanceof Collection) {
    41                        // it must be a collection of tokenNames
    42                        Collection runtimeTokenNames = (Collection) result;
    43                        reactivateParent = mustParentBeReactivated(parentToken, runtimeTokenNames.iterator());
    44
    45                        // if it's a boolean
    46                    }
     else if (result instanceof Boolean) {
    47                        // the boolean specifies if the parent needs to be
    48                        // reactivated
    49                        reactivateParent = ((Boolean) result).booleanValue();
    50                    }

    51
    52                    // if a nOutOfM is specified
    53                }
     else if (nOutOfM != -1{
    54
    55                    int n = 0;
    56                    // wheck how many tokens already arrived in the join
    57                    Iterator iter = parentToken.getChildren().values().iterator();
    58                    while (iter.hasNext()) {
    59                        Token concurrentToken = (Token) iter.next();
    60                        if (this.equals(concurrentToken.getNode())) {
    61                            n++;
    62                        }

    63                    }

    64                    if (n < nOutOfM) {
    65                        reactivateParent = false;
    66                    }

    67
    68                    // if no configuration is specified..
    69                }
     else {
    70                    // the default behaviour is to check all concurrent tokens
    71                    // and reactivate
    72                    // the parent if the last token arrives in the join
    73                    reactivateParent = mustParentBeReactivated(parentToken, parentToken.getChildren().keySet().iterator());
    74                }

    75
    76                // if the parent token needs to be reactivated from this join
    77                // node
    78                if (reactivateParent) {
    79
    80                    // write to all child tokens that the parent is already
    81                    // reactivated
    82                    Iterator iter = parentToken.getChildren().values().iterator();
    83                    while (iter.hasNext()) {
    84                        ((Token) iter.next()).setAbleToReactivateParent(false);
    85                    }

    86
    87                    // write to all child tokens that the parent is already
    88                    // reactivated
    89                    ExecutionContext parentContext = new ExecutionContext(parentToken);
    90                    leave(parentContext);
    91                }

    92                

        讀了這段邏輯很少,但是注釋很多的代碼相信已經明白了Join內部的核心了,有一種豁然開朗的感覺,JBPM的設計思想真的很迷人:幾句簡單的If、Else
     就實現了讓人看來很神秘的功能。這個時候我們可以來分析一下他的幾個成員變量的作用了。
       如果isDiscriminator為True,這個時候Join節點其實是起到一種選擇器的作用:當第一個子Token到達Join之后,Join就會馬上取消其他子Token執行自身execute方法的能力,而且流程會馬上繼續而不會再理會其他的子Token有沒有到達Join或結束,因為根據我們上面的分析,isDiscriminator被設置為false的子token是不具備激活父Token的能力的。原來實現網上經常爭論的用Join實現多選一是那么簡單(呵呵)!
       下一個有意思的是nOutOfM,代碼寫的很明白,當子Token到達Join的時候n就加1,如果n<nOutOfM Join就一個處于等待狀態,直到n > nOutOfM 流程馬上繼續,這就Join實現多選多的機制,真的很簡單,當然如果我們把nOutOfM設置為1,那么他所起到的作用就跟isDiscriminator一樣了。
       在說明其他兩個變量之前,讓我們先來看一下public boolean mustParentBeReactivated(Token parentToken, Iterator childTokenNameIterator)方法
     1    /**
     2     * 檢查有沒有子Token的isAbleToReactivateParent為真,如果有一個子Token的isAbleToReactivateParent為真,就返回false
     3     */

     4    public boolean mustParentBeReactivated(Token parentToken, Iterator childTokenNameIterator) {
     5        boolean reactivateParent = true;
     6        while ((childTokenNameIterator.hasNext()) && (reactivateParent)) {
     7            String concurrentTokenName = (String) childTokenNameIterator.next();
     8
     9            Token concurrentToken = parentToken.getChild(concurrentTokenName);
    10
    11            if (concurrentToken.isAbleToReactivateParent()) {
    12                log.debug("join will not yet reactivate parent: found concurrent token '" + concurrentToken + "'");
    13                reactivateParent = false;
    14            }

    15        }

    16        return reactivateParent;
    17    }


        isDiscriminator和nOutOfM實現了多選一和多選多,但是這兩個變量起到的控制作用是死的,也就是我們并不能指定從Fork那里產生的子Token中哪幾個到Join之后流程可繼續。例如Fork創建了A、B、C、D、E五個Token,如果用前面那兩個控制機制,這五個哪幾個到Join之后流程會繼續完全是不可控的,如果我們要實現必須是A、C、E到達Join之后流程才可繼續,這樣的需求用isDiscriminator和nOutOfM是無法實現的。別著急,tokenNames是專為解決這個問題而設置的,我們選定幾個子Token塞給tokenNames,那么Join就會自動為我們做這些事情了。
     相比tokenNames Script為我們提供了更靈活的控制機制。如果Script返回Collection類型,那么Script起到了tokenNames的作用,如果返回Bolean類型,那么Script起到isDiscriminator的作用,源碼上注釋的很清楚,我就不在這羅嗦了。
         到這Join的代碼我們也就讀完了,如果上述四個成員變量都為默認值,那么Join也就按默認的行為執行,Join是JBPM源碼中少數注釋很全的類,這也說明這是JBPM開發組的得意之作。通過四個屬性我們可以非常靈活的使用Join實現很多實用的效果。

              文章原創,轉載請注明出處!

     

    posted on 2008-11-14 23:55 零全零美 閱讀(1915) 評論(2)  編輯  收藏 所屬分類: jbpm

    FeedBack:
    # re: [原創]JBPM源碼解讀之:Join[未登錄]
    2008-11-15 11:09 | moon
    不錯工作之余來逛逛居家生活網 <a href="life126.com">life126.com</a>  回復  更多評論
      
    # re: [原創]JBPM源碼解讀之:Join[未登錄]
    2008-11-15 11:10 | life126.com
    好文章 ,工具之余來逛逛居家生活網  回復  更多評論
      
    主站蜘蛛池模板: 免费看无码自慰一区二区| 全免费A级毛片免费看网站| 亚洲国产成人精品无码一区二区 | 国产精品亚洲专区无码不卡| 久久久久国产亚洲AV麻豆| 日本免费A级毛一片| 国产精品高清视亚洲一区二区| 亚洲福利在线播放| 国产成人精品免费视频动漫 | 亚洲人成免费电影| 美女的胸又黄又www网站免费| 国产V亚洲V天堂无码| 67194成是人免费无码| 99精品视频在线观看免费| 亚洲黄页网在线观看| 亚洲精品字幕在线观看| 成年男女男精品免费视频网站| 中文在线观看国语高清免费| 亚洲色精品VR一区区三区| 亚洲一区精品无码| 国产嫩草影院精品免费网址| 9277手机在线视频观看免费| 污污视频免费观看网站| 亚洲人成7777| 亚洲精品tv久久久久久久久| 国产精品久久香蕉免费播放| 四虎成年永久免费网站| a级毛片免费观看视频| 激情婷婷成人亚洲综合| 亚洲人成毛片线播放| 亚洲va久久久噜噜噜久久天堂| 四虎AV永久在线精品免费观看| 成视频年人黄网站免费视频| 成人电影在线免费观看| 免费一级特黄特色大片| 亚洲国产成人无码AV在线| 亚洲一级毛片在线播放| 猫咪免费观看人成网站在线| 亚洲AV无码乱码麻豆精品国产| 亚洲精品高清国产一久久| 亚洲精品国产美女久久久|