整體來說實現的非常清晰:
1、引擎解析流程定義xml時,給相應的事件掛接上create-timer 和 cancel-timer動作
2、流程實例實際運轉時,create-timer動作在相應事件觸發時執行
3、create-timer在job表里插入相應時間job記錄,給該job記錄附上計算完畢的執行時間
4、JobExecutorServlet在后臺啟動一到多個JobExecutorThread線程
5、JobExecutorThread線程不停的每隔一段時間對job表掃描一次,找出需要執行的job記錄,執行之
6、只執行一次的job記錄,執行完畢后刪除之;重復執行的job記錄,寫入新的執行時間,更新之
7、相應事件觸發cancel-timer動作,將對應job記錄從job表里刪除
下面具體用代碼來說話(掛接到node節點):
1、引擎解析流程定義xml
JpdlXmlReader.java
protected void readNodeTimer(Element timerElement, Node node) {
String name = timerElement.attributeValue("name", node.getName());
CreateTimerAction createTimerAction = new CreateTimerAction();
createTimerAction.read(timerElement, this);
createTimerAction.setTimerName(name);
createTimerAction.setTimerAction(readSingleAction(timerElement));
addAction(node, Event.EVENTTYPE_NODE_ENTER, createTimerAction);
CancelTimerAction cancelTimerAction = new CancelTimerAction();
cancelTimerAction.setTimerName(name);
addAction(node, Event.EVENTTYPE_NODE_LEAVE, cancelTimerAction);
}
可以看到,引擎把xml中timer節點解析成了兩個ACTION:CreateTimerAction和CancelTimerAction
CreateTimerAction會在進入該節點時觸發,而CancelTimerAction會在令牌離開該節點時觸發。
2、看看CreateTimerAction和CancelTimerAction究竟在做些什么
CreateTimerAction.java
public void execute(ExecutionContext executionContext) throws Exception {
Timer timer = createTimer(executionContext);
SchedulerService schedulerService = (SchedulerService) Services.getCurrentService(Services.SERVICENAME_SCHEDULER);
schedulerService.createTimer(timer);
}
很明顯,是通過一個職責集中的schedulerService向job表中插入了一條job記錄,注意到這個方法:
protected Timer createTimer(ExecutionContext executionContext) {
Timer timer = new Timer(executionContext.getToken());
.
if (dueDate!=null) {
Duration duration = new Duration(dueDate);
Date dueDateDate = businessCalendar.add( new Date(), duration );
timer.setDueDate(dueDateDate);
}
.
return timer;
}
這里利用JBPM提供的工作時間計算組件計算了job的執行時間。
CancelTimerAction就很簡單了,刪除相應的job記錄。
CancelTimerAction.java
public void execute(ExecutionContext executionContext) throws Exception {
SchedulerService schedulerService = (SchedulerService) Services.getCurrentService(Services.SERVICENAME_SCHEDULER);
schedulerService.deleteTimersByName(timerName, executionContext.getToken());
}
3、JobExecutorServlet是干什么的
啟動線程
public void init() throws ServletException {
.
jbpmConfiguration.startJobExecutor();
}
4、線程是如何工作
public void run() {
try {
currentIdleInterval = idleInterval;
while (isActive) {
try {
Collection acquiredJobs = acquireJobs(); //從job表里獲得將要執行的job記錄
if (! acquiredJobs.isEmpty()) { //如果記錄不為空,則開始執行
Iterator iter = acquiredJobs.iterator();
while (iter.hasNext() && isActive) {
Job job = (Job) iter.next();
executeJob(job); //執行
}
} else { // no jobs acquired //如果沒有可執行的job,則等待一段時間
if (isActive) {
long waitPeriod = getWaitPeriod(); //等待的時間是找出即將執行的job離現在最近的時間間隔
if (waitPeriod>0) {
synchronized(jobExecutor) {
jobExecutor.wait(waitPeriod);
}
}
}
}
.
} catch (Throwable t) {
t.printStackTrace();
} finally {
log.info(getName()+" leaves cyberspace");
}
}
看看實際執行的方法
protected void executeJob(Job job) {
JbpmContext jbpmContext = jbpmConfiguration.createJbpmContext();
try {
JobSession jobSession = jbpmContext.getJobSession();
job = jobSession.loadJob(job.getId());
try {
log.debug("executing job "+job);
if (job.execute(jbpmContext)) { //交由Job對象本身去完成執行的邏輯,并決定是否刪除job記錄
jobSession.deleteJob(job);
}
.
}
5、著重關注Time對象
在上面我們看到實際執行的代碼是job.execute(jbpmContext);
Time 是Job的子類,看看它的實現:
public boolean execute(JbpmContext jbpmContext) throws Exception {
boolean deleteThisJob = true; //執行完畢后是否刪除job記錄
ExecutionContext executionContext = new ExecutionContext(token);
executionContext.setTimer(this);
if (taskInstance!=null) {
executionContext.setTaskInstance(taskInstance);
}
// 觸發timer事件
if (graphElement!=null) {
graphElement.fireAndPropagateEvent(Event.EVENTTYPE_TIMER, executionContext);
}
// 如果timer節點上掛有action則執行之
if (action!=null) {
try {
log.debug("executing timer '"+this+"'");
action.execute(executionContext);
} catch (Exception actionException) {
.
}
// 如果定義了transition屬性,則流程順著定義的路徑流轉
if ( (transitionName!=null)
&& (exception==null) // and if no unhandled exception occurred during the action
) {
if (token.getNode().hasLeavingTransition(transitionName)) {
token.signal(transitionName);
}
}
// 如果定義了repeat屬性則job記錄不容許刪除,同時計算新的執行時間
if (repeat!=null) {
deleteThisJob = false;
while (dueDate.getTime()<=System.currentTimeMillis()) {
dueDate = businessCalendar
.add(dueDate,
new Duration(repeat));
}
log.debug("updated timer for repetition '"+this+"' in '"+(dueDate.getTime()-System.currentTimeMillis())+"' millis");
}
return deleteThisJob;
}
posted @
2007-06-22 17:13 ronghao 閱讀(1949) |
評論 (0) |
編輯 收藏
JBPM時間服務的使用主要體現在對timer節點的使用。timer節點有兩種使用方式:一種是掛接到node節點下,在進入node節點時觸發,在離開node節點時終止;另外一種是掛接到task節點下,在任務創建時觸發,默認在任務完成后終止。下面舉例說明:
一、掛接到node節點
<state name='catch crooks'>
<timer name='reminder'
duedate='3 business hours'
repeat='10 business minutes'
transition='time-out-transition' >
<action class='the-remainder-action-class-name' />
<transition name='time-out-transition' to='next' />
</timer>
</state>
解釋:timer將會在流程令牌進入節點catch crooks時觸發,延遲3 business hours開始執行動作,每10 business minutes重復執行一次,直到令牌離開catch crooks節點。
對time節點來說 name、repeat、transition都是可選屬性。對一個流程定義來說,每一個time節點的name必須唯一,如果你不定義name屬性,引擎會默認把node節點的name賦給timer。在上面這個例子里,如果你不定義timer節點的name,則它的name就會是catch crooks。說說repeat屬性,如果你不定義它,則timer就會只執行一次動作不會重復執行。transition屬性,如果定義了這個屬性,流程令牌會在timer執行動作完畢后,順著這個路徑離開node節點。所以在上面這個例子里,盡管定義了repeat屬性,action還是會只執行一次。
action節點,可選,即timer節點在時間到時執行的動作,可以是任意action類型,包括script。注意與時間有關的兩種action類型:create-timer 和 cancel-timer。其實一個timer節點在被引擎解釋時就是被分解為create-timer 和 cancel-timer兩個action,create-timer掛接到node-enter事件中,cancel-timer掛接到node-leave事件中。action節點最多只可以掛一個。
說說整個過程:
1、令牌進入節點catch crooks
2、timer被觸發(實際這時是在執行create-timer動作)
3、3 business hours后 timer 事件觸發
4、定義的action被執行
5、令牌順著time-out-transition路徑離開catch crooks節點
6、cancel-timer動作被執行即timer終止(沒有給repeat的機會)
二、掛接到task節點
<task-node name="Evaluate web order">
<task swimlane="salesman">
<timer duedate="20 seconds" repeat="10 seconds" cancel-event='task-start'>
<action class="org.jbpm.websale.RemindActor">
<swimlaneName>salesman</swimlaneName>
</action>
</timer>
</task>
<transition name="OK" to="salefork" />
<transition name="More info needed" to="Fix web order data" />
</task-node>
與掛接到node 的區別是:這里可以定義一個屬性cancel-event,可以指定那些事件可以終止timer的執行,默認是task-end。可以指定多個事件,以','分割,任一事件觸發timer即終止。
可以看到jbpm對任務實例和節點執行時的時間服務還是支持的很好,可以做出很多的擴展,但是它沒有對整個流程實例本身提供更多的服務,比如說定時的流程啟動和整個流程的時間控制等等。以及對精確時間點的支持還不夠。
posted @
2007-06-21 12:00 ronghao 閱讀(1724) |
評論 (2) |
編輯 收藏
jbpm BusinessCalendar是一個很好用的計算工作日設定的時間服務組件,美中不足的是它的工作日設定是寫死在配置文件中,不能靈活的由用戶修改。另外hongsoft在他的博客中提到jbpm BusinessCalendar可能存在的一個bug:
http://blog.csdn.net/hongbo781202/archive/2006/02/28/612541.aspx這個bug在我的測試中沒有重現,我的jbpm版本是3.2,可以認為jbpm已經修復這個bug。另外在
jbpm BusinessCalendar的配置文件中有這么一行
weekday.thuesday= 9:00-12:00 & 12:30-17:00
可以理解為是一個疏忽,應該是tuesday,禮拜二,呵呵。
posted @
2007-06-15 17:58 ronghao 閱讀(668) |
評論 (0) |
編輯 收藏
工作流時間管理按功能分類:
1. 時間事件啟動工作流流程實例(指定時間點、時間間隔、周期時間)
2. 任務掛起恢復(指定時間點、時間間隔)
3. 任務預警、報警、超時通知
4. 工作流流程實例超時通知
5. 非工作日、節假日設定
6. 流程、任務的處理時間統計
具體說明:
1、工作流流程實例在設置的時間自動啟動,設置時間包括下面兩種方式:
a、指定一個固定的時間點,然后設置周期時間,例如每天、每周的周一、每月的第一天;
b、指定一個固定的時間點,然后設置時間間隔,例如20分鐘后,2小時后,一天后,一個月后。
2、任務在上一個任務節點完成后多長時間啟動。并發任務之間的時間啟動關系。任務在指定時間點啟動。
舉例:財務每周五下午2點開始集中處理報銷事務,所有流程實例流轉到財務報銷節點處于等待狀態,直到周五下午2點任務才啟動,財務才在任務列表里看到待處理的報銷事務并集中處理。
3、舉例說明:經理審批這個任務節點設置完成時間1小時
預警:時間過去預定完成時間一定百分比比如50%還未完成,則在任務發出半小時后系統發出預警信息,按一定時間間隔循環發出。直到任務報警或任務超時或任務完成。
報警:時間過去預定完成時間一定百分比比如90%還未完成,則在任務發出54分鐘后系統發出報警信息,預警自動終止。報警信息只發送一次。
超時通知:任務在規定時間內未完成,系統發出超時通知。同時任務超時存在業務或流程處理,任務
超時應當可以掛上javabean處理一定業務邏輯,同時流程可以選擇繼續等待或是跳轉。
4、和任務超時類似,系統發送超時通知,同時應該存在業務和流程的處理。比如說流程自動終止。
5、主要提供時間計算時對非工作日、非工作時間和節假日的考慮。這里的時間計算僅僅針對于輸入一個時間計算一定時間間隔后輸出一個時間,比如說現在是周五2點,輸入,兩天時間間隔,輸入,周日2點,輸出。考慮非工作日,則輸出應該為下周二2點。用途主要體現在對任務和流程的時間完成期限限定計算上。
6、統計,報表。
大家提提自己的意見。
posted @
2007-06-14 11:42 ronghao 閱讀(1816) |
評論 (3) |
編輯 收藏
這是我前年AXIS學習的筆記,最早寫在了JR上,是分散的五篇(其中一篇是轉載)。按內容分別是:
1、
介紹AXIS2、
使用Handler來增強Web服務的功能(這是一篇轉載)3、
建立安全的AXIS服務(上)4、
建立安全的AXIS服務(下)5、
AXIS服務間傳遞JavaBean及其安全解決其實很多內容現在自己看來寫的都很淺顯,包括輸出大量使用了System.out以及沒有測試。這里也不打算
修改了,簡單整理了一下并提供源代碼的下載。希望能給需要的人一些幫助。
代碼下載
posted @
2007-06-12 16:36 ronghao 閱讀(2258) |
評論 (0) |
編輯 收藏