JBPM是JBOSS公司的一个开源工作流产品,它功能强大,商业工作流所提供的功能它基本上都有。然而在JBPM中没有提供流程监控的功能的相关API,这点让很多使用JBPM做企业应用的开发人员颇为头疼。
其实,在JBPM中我们可以通过手工编码的方式来实现商业工作流中所具有的流程监控功能。
所谓的流程监控就是要实现可以看到当前系统中发布了多少个流程;每一个流程产生了多少个流程实例,有哪些流程实例结束了,哪些处于活动状态,哪些挂起了;同时对于每一个流程实例当前走到什么位置了(流程的进度状况)等等。通过查看JBPM的API我们发现API里并没有提供实现该功能现成的接口,不过我们可以通过自己手工编码的方式来拿到我们需要的监控信息。
通过查看JBPM的表,我们知道要实现流程监控功能就是把JBPM当中的JBPM_PROCESSDEFINITION(已发布的流程表),JBPM_PROCESSINSTANCE(流程实例表),JBPM_TASKINSTANCE(流程产生的任务实例表)这三张表串联起来就可以实现我们所要的监控功能。
首先我们要拿到所有的已发布的流程表的最后一个版本的信息,关于这一块我们可以通过JbpmContext来实现:
JbpmConfiguration config = JbpmConfiguration.getInstance();
JbpmContext jbpm=config.createJbpmContext();
try {
List ls=jbpm.getGraphSession().findLatestProcessDefinitions();
......
} catch (Exception e) {
throw e;
}finally{
jbpm.close();
}
上面代码中的List ls=jbpm.getGraphSession().findLatestProcessDefinitions();是JbpmContext提供的拿到所有的最后一个版本的所有的流程定义信息。
拿到所有的最后一个版本的流程定义信息后,接下来我们需要根据每一个流程定义的名称找到它目前有多少个活动的,或不活动的,或挂起的流程实例。在这里我们来实现根据流程定义名称找到所有的处于活动状态下的流程实例。这一块的功能JBPM的API没有提供现成的,需要我们自己实现。
JBPM的持久层是用Hibernate实现的,因此要实现根据流程定义名称找到所有的处于活动状态下的流程实例我们要写一段HQL来查找所有的由该流程产生的流程实例,然后对得到的流程实例集合进行过滤,拿到我们想要的所有处于活动状态下的流程实例。
查看org.jbpm.db包下面,我们会发现有一个名为:hibernate.queries.hbm.xml文件,它是一个JBPM查询时使用HQL的存储文件,用记事本打开,找到下面的“Query”定义信息:
<query name="GraphSession.findAllProcessInstancesForADefinition">
<![CDATA[
select pi
from org.jbpm.graph.exe.ProcessInstance as pi
where pi.processDefinition.id = :processDefinitionId
order by pi.start desc
]]>
</query>
从名称我们可以看出,这段HQL的作用是用来根据流程ID来查询对应的所有的流程实例,同时按开始时间排倒序。这段HQL只能拿到当前流程的所有实例,并不能区分实例是否处于活动状态,这时我们需要手工区分实例是否是活动的流程实例,所以我们就有下面的代码:
JbpmConfiguration config = JbpmConfiguration.getInstance();
JbpmContext jbpm=config.createJbpmContext();
Session session=jbpm.getSessionFactory().openSession();
try {
String hql = "from ProcessInstance pi where pi.processDefinition.id='"+processId+"' order by pi.start desc";//这里的processId是应用传过来的流程的ID号
List ls = session.createQuery(hql).list();
for (int i=0;i<ls.size();i++) {
ProcessInstance pi = (ProcessInstance) ls.get(i);
if(pi.hasEnded())ls.remove(i); //对结果集进行过滤,把所有已经结束掉的实例remove掉
}
} catch (Exception e) {
throw e;
}finally{
session.close();
jbpm.close();
}
最后,我们需要根据不同的流程实例找到当前流程实例所在的位置,也既流程实例当前所在位置的任务实例。
还是查看hibernate.queries.hbm.xml文件,找到下面这段HQL的定义信息:
<query name="TaskMgmtSession.findTaskInstancesByActorId">
<![CDATA[
select ti
from org.jbpm.taskmgmt.exe.TaskInstance as ti
where ti.actorId = :actorId
and ti.isOpen = true
]]>
</query>
上面的这段HQL表示取指定用户的所有未处理的的任务实例。不符合我们的要求,我们需要在这段HQL的基础上加以改造,于是就有了下面的HQL:
String hql="from TaskInstance ti left join fetch ti.token where ti.isOpen = true and ti.taskMgmtInstance.processInstance="+processInstanceId+"";
解释一下上面的HQL,“left join fetch ti.token”表示在取任务实例时显式的拿到任务实例所对应Token信息,“ti.taskMgmtInstance.processInstance="+processInstanceId+""”表示取任务实例时加上流程实例的限制,这里的processInstanceId是应用里传过来的流程实例ID。JAVA代码如下:
JbpmContext jbpm=this.getJbpmContext();
Session session=jbpm.getSessionFactory().openSession();
try {
String hql="from TaskInstance ti left join fetch ti.token where ti.isOpen = true and ti.taskMgmtInstance.processInstance="+processInstanceId+"";
List ls=session.createQuery(hql).list();
} catch (Exception e) {
throw e;
}finally{
session.close();
jbpm.close();
}
好了,到这里我们已经从理论上实现JBPM的流程监控功能。接下来我们可以在实践中演练一下,看看是否可以实现真正的监控。
这里我们采用的是Bstek公司的Web前端展现产品Dorado5.1(http://www.bstek.com)来做我们的前端展现。加载数据的代码如下:
package org.jbpm.monitor;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.hibernate.Session;
import org.jbpm.JbpmConfiguration;
import org.jbpm.JbpmContext;
import org.jbpm.graph.exe.ProcessInstance;
import com.bstek.dorado.view.DefaultViewModel;
import com.bstek.dorado.view.data.ViewDataset;
/**
* MonitorView
* 该类用来显示某个流程当前激活的流程,每个激活的流程所在节点的位置
*/
public class MonitorView extends DefaultViewModel {
protected void doLoadData(ViewDataset dataset) throws Exception {
String datasetId=dataset.getId();
if(datasetId.equals("datasetProcessDefination")){
JbpmContext jbpm=this.getJbpmContext();
try {
List ls=jbpm.getGraphSession().findLatestProcessDefinitions();
dataset.fromDO(ls);
} catch (Exception e) {
throw e;
}finally{
jbpm.close();
}
}else if(datasetId.equals("datasetProcessInstance")){
String processId=dataset.parameters().getString("processId");
List ls=new ArrayList();
if(processId!=null){
JbpmContext jbpm=this.getJbpmContext();
Session session=jbpm.getSessionFactory().openSession();
try {
String hql = "from ProcessInstance pi where pi.processDefinition.id='"+processId+"' order by pi.start desc";
ls = session.createQuery(hql).list();
for (int i=0;i<ls.size();i++) {
ProcessInstance pi = (ProcessInstance) ls.get(i);
if(pi.hasEnded())ls.remove(i);
}
} catch (Exception e) {
throw e;
}finally{
session.close();
jbpm.close();
}
}
dataset.fromDO(ls);
}else if(datasetId.equals("datasetTaskInstance")){
String processInstanceId=dataset.parameters().getString("processInstanceId");
List ls=new ArrayList();
if(processInstanceId!=null){
JbpmContext jbpm=this.getJbpmContext();
Session session=jbpm.getSessionFactory().openSession();
try {
String hql="from TaskInstance ti left join fetch ti.token where ti.isOpen = true and ti.taskMgmtInstance.processInstance="+processInstanceId+"";
ls=session.createQuery(hql).list();
} catch (Exception e) {
throw e;
}finally{
session.close();
jbpm.close();
}
}
dataset.fromDO(ls);
}
}
public JbpmContext getJbpmContext() {
JbpmConfiguration config = JbpmConfiguration.getInstance();
return config.createJbpmContext();
}
}
页面显示效果如下:
在效果图中有三张表,它们之间由上而下是主从关系,表现出流程中的流程与流程实例,流程实例与任务实例的关系。
这时我们可以尝试开启一个流程或者让流程走动,会发现监控页面会同时做相应的变化,同时点击“流程图”一栏的“查看”链接,我们还可以看到当前流程实例所在的位置。
JBPM是一款功能非常强大的开源工作流产品,使用JBPM开发过程中,因为其开放源代码的特性所以我们可以根据需要对它进行扩展,以满足我们企业应用的需要。
上海锐道信息技术有限公司
高杰
2007.8.6
该文章转载自网络大本营:http://www.xrss.cn/Info/15447.Html