整体来说实现的非常清晰:
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 阅读(1939) |
评论 (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 阅读(1713) |
评论 (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 阅读(658) |
评论 (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 阅读(1804) |
评论 (3) |
编辑 收藏
这是我前年AXIS学习的笔记,最早写在了JR上,是分散的五篇(其中一篇是转载)。按内容分别是:
1、
介绍AXIS2、
使用Handler来增强Web服务的功能(这是一篇转载)3、
建立安全的AXIS服务(上)4、
建立安全的AXIS服务(下)5、
AXIS服务间传递JavaBean及其安全解决其实很多内容现在自己看来写的都很浅显,包括输出大量使用了System.out以及没有测试。这里也不打算
修改了,简单整理了一下并提供源代码的下载。希望能给需要的人一些帮助。
代码下载
posted @
2007-06-12 16:36 ronghao 阅读(2242) |
评论 (0) |
编辑 收藏