因为项目需要,准备引入JBPM 3.1.4,因为我们的项目是基于 Spring 2 + Hibernate 3 + Struts2 的,而Spring Modules整合JBPM 是 3.1.4 版的,所以用了JBPM 3.1.4
高版本的设计器还不兼容 MyEclipse ,所以采用JBPM 3.1.4还是比较明智的
参考网上的资料初步摸清了整合的方式,这里我想说的是,如何降低引入JBPM框架对我们自有应用的影响。
网上资料及JBPM手册里,好像更多的说明了JBPM自身的特性和一些使用规则。
其实就我现在摸索的情况看来,可以采用流程内置脚本的方式实现所有的JBPM Action Bean的效果,某些特定的业务操作,可以通过”先标记、后处理“的方式在业务服务类中编写,这样的好处是:
1、控制点比较单一
2、对原有的业务逻辑侵入性较小
3、可以基于JBPM控制,通过设值操作,模拟流程的进行(因为没有Action Bean的依赖)
缺点是:
1、内置脚本的执行效率可能较低,如果要求高性能,就应该慎用
2、目前只实现了用来完成审批、审核流程下的非侵入代码测试,还没能在更多的工作流情况下加以验证。
3、为了方面查询和联动,我们修改了TaskInstance(或者利用Description字段),存放一些检索关键字和业务主键,这种做法可能不是很规范
简单说来,jbpm例程里的考虑和设计是从 "JBPM为中心”这一出发点考虑,本文中设计及实现是从“业务为中心”这一出发点考虑的。不说了,下面放一些例子代码出来吧。
流程文件,这个流程文件实现了 审核、审批流程,审核、审批流程都是可以通过参数做到可选的效果:
其中,比较特殊的用法
1)通过TrSource变量,了解上一部流程节点的名字,这样可以更好的处理,忽略审核或忽略审批时文档的流向问题
2)通过SetDescription(Docid),将业务单据ID保存到TaskInstance中(实际的项目里,我们在TaskInstance表上加了十个字段)
3)通过skipex="true" 和 skipapp="true" 变量设置,使文档省略审核或省略审批
4)这个流程可以直接挂载到JBPM控制台中执行,不依赖于任何的类
<?xml version="1.0" encoding="UTF-8"?>
<process-definition xmlns="" name="docflow">
<start-state name="create_document">
<task name="create_document">
<controller>
<variable name="docid" access="read,write,required"
mapped-name="docid">
</variable>
</controller>
</task>
<transition name="create_ok" to="create_juge">
<script>
<variable name="trsource" access="write"/>
<expression>
trsource=executionContext.getTransitionSource().getName();
System.out.println(trsource+"1create_ok");
</expression>
</script>
</transition>
</start-state>
<task-node name="update_document">
<task name="update_document" description="测试">
<controller>
<variable name="docid" access="read"
mapped-name="docid">
</variable>
<variable name="addinfo" access="read,write,required"
mapped-name="addinfo">
</variable>
</controller>
<assignment actor-id="createby" />
</task>
<event type="task-create">
<script>
<variable name="docid" access="read"/>
<expression>
taskInstance.setDescription(docid);
</expression>
</script>
</event>
<transition name="update_ok" to="create_juge">
<script>
<variable name="trsource" access="write"/>
<expression>
trsource=executionContext.getTransitionSource().getName();
System.out.println(trsource+"18update_ok");
</expression>
</script>
</transition>
</task-node>
<task-node name="examine_document">
<task name="examine_document">
<controller>
<variable name="docid" access="read"
mapped-name="docid">
</variable>
<variable name="examinememo"
access="read,write,required" mapped-name="examinememo">
</variable>
<variable name="ereply" access="read,write,required"
mapped-name="ereply">
</variable>
</controller>
<assignment actor-id="examineby" />
</task>
<event type="task-create">
<script>
<variable name="docid" access="read"/>
<expression>
taskInstance.setDescription(docid);
</expression>
</script>
</event>
<transition name="to_juge1" to="examine_juge">
<script>
<variable name="trsource" access="write"/>
<expression>
trsource=executionContext.getTransitionSource().getName();
System.out.println(trsource+"2to_juge1");
</expression>
</script>
</transition>
</task-node>
<decision name="examine_juge" >
<event type="node-enter">
<variable name="trsource" access="read,write"/>
<variable name="skipex" access="read"/>
<variable name="test1" access="read,write"/>
<variable name="test2" access="read,write"/>
<script>
trsource=executionContext.getTransitionSource().getName();
//System.out.println(executionContext.getTransition().getName());
//System.out.println(skipex);
System.out.println(trsource);
test1=((trsource.equals("create_juge"))&&(skipapp.equals("false")));
test2=((trsource.equals("create_juge"))&&(skipapp.equals("true")));
System.out.println(test1);
System.out.println(test2);
</script>
</event>
<transition name="examine_ok" to="approve_document">
<condition expression='#{((((trsource=="examine_document") && (ereply=="ok"))) || ((trsource=="create_juge") && (skipex=="true"))) &&(skipapp!="true")}'/>
<script>
<variable name="trsource" access="write"/>
<expression>
trsource=executionContext.getTransitionSource().getName();
//System.out.println(skipapp);
System.out.println(trsource+"3examine_ok");
</expression>
</script>
</transition>
<transition name="examine_cancle" to="cancle_document">
<condition expression='#{((trsource=="examine_document") && (ereply=="cancle"))}'/>
<script>
<variable name="trsource" access="write"/>
<expression>
trsource=executionContext.getTransitionSource().getName();
System.out.println(trsource+"4examine_cancle");
</expression>
</script>
</transition>
<transition name="skip_app" to="approve_juge">
<condition expression='#{((((trsource=="examine_document") && (ereply=="ok"))) || ((trsource=="create_juge") && (skipex=="true"))) &&(skipapp=="true")}'/>
<script>
<variable name="trsource" access="write"/>
<expression>
trsource=executionContext.getTransitionSource().getName();
//System.out.println(skipapp);
System.out.println(trsource+"5skip_app");
</expression>
</script>
</transition>
<transition name="examine_notok" to="create_juge">
<condition expression='#{((trsource=="examine_document") && (ereply=="notok"))||(((trsource=="approve_juge") && (areply=="notok")) &&( skipex=="true"))}'/>
<script>
<variable name="trsource" access="write"/>
<expression>
trsource=executionContext.getTransitionSource().getName();
System.out.println(trsource+"6examine_notok");
</expression>
</script>
</transition>
<transition name="examine_back" to="examine_document">
<condition expression='#{(((trsource=="approve_juge") && (areply=="notok")) &&( skipex!="true"))}'/>
<script>
<variable name="trsource" access="write"/>
<expression>
trsource=executionContext.getTransitionSource().getName();
System.out.println(trsource+"6examine_back");
</expression>
</script>
</transition>
</decision>
<task-node name="approve_document">
<task name="approve_document">
<controller>
<variable name="docid" access="read"
mapped-name="docid">
</variable>
<variable name="examinememo" access="read"
mapped-name="examinememo">
</variable>
<variable name="ereply" access="read"
mapped-name="ereply">
</variable>
<variable name="approvememo"
access="read,write,required" mapped-name="approvememo">
</variable>
<variable name="areply" access="read,write,required"
mapped-name="areply">
</variable>
</controller>
<assignment actor-id="approveby" />
</task>
<event type="task-create">
<script>
<variable name="docid" access="read"/>
<expression>
taskInstance.setDescription(docid);
</expression>
</script>
</event>
<transition name="to_juge2" to="approve_juge">
<script>
<variable name="trsource" access="write"/>
<expression>
trsource=executionContext.getTransitionSource().getName();
System.out.println(trsource+"7to_juge2");
</expression>
</script>
</transition>
</task-node>
<decision name="approve_juge" >
<transition name="approve_notok" to="examine_juge">
<condition expression='#{((trsource=="approve_document") && (areply=="notok"))}'/>
<script>
<variable name="trsource" access="write"/>
<expression>
trsource=executionContext.getTransitionSource().getName();
System.out.println(trsource+"8approve_notok");
</expression>
</script>
</transition>
<transition name="approve_ok" to="apply_document">
<condition expression='#{(((trsource=="approve_document") && (areply=="ok")) ||((trsource=="examine_juge") && (skipapp=="true")))}'/>
<script>
<variable name="trsource" access="write"/>
<expression>
trsource=executionContext.getTransitionSource().getName();
System.out.println(trsource+"9approve_ok");
</expression>
</script>
</transition>
<transition name="approve_cancle" to="cancle_document">
<condition expression='#{((trsource=="approve_document") && (areply=="cancle"))}'/>
<script>
<variable name="trsource" access="write"/>
<expression>
trsource=executionContext.getTransitionSource().getName();
System.out.println(trsource+"10approve_cancle");
</expression>
</script>
</transition>
</decision>
<node name="apply_document">
<event type="node-enter">
<script>
executionContext.getContextInstance().setVariable("docstatus", "apply_document");
</script>
</event>
<!--
<event type="node-enter">
<action name="applydoc" class="org.springmodules.workflow.jbpm31.JbpmHandlerProxy">
<targetBean>applyDoc</targetBean>
<factoryKey>jbpmConfiguration</factoryKey>
</action>
</event>
-->
<transition name="apply_ok" to="end">
<script>
<variable name="trsource" access="write"/>
<expression>
trsource=executionContext.getTransitionSource().getName();
System.out.println(trsource+"11apply_ok");
</expression>
</script>
</transition>
</node>
<node name="cancle_document">
<event type="node-enter">
<script>
executionContext.getContextInstance().setVariable("docstatus", "cancle_document");
</script>
</event>
<!--
<event type="node-enter">
<action name="cancledoc" class="org.springmodules.workflow.jbpm31.JbpmHandlerProxy">
<targetBean>cancleDoc</targetBean>
<factoryKey>jbpmConfiguration</factoryKey>
</action>
</event>
-->
<transition name="cancle_ok" to="end">
<script>
<variable name="trsource" access="write"/>
<expression>
trsource=executionContext.getTransitionSource().getName();
System.out.println(trsource+"13cancle_ok");
</expression>
</script>
</transition>
</node>
<end-state name="end"></end-state>
<decision name="create_juge" >
<event type="node-enter">
<variable name="trsource" access="read,write"/>
<variable name="skipex" access="read"/>
<variable name="test1" access="read,write"/>
<variable name="test2" access="read,write"/>
<script>
trsource=executionContext.getTransitionSource().getName();
test1=((trsource.equals("create_document"))&&(skipex.equals("false")));
test2=((trsource.equals("create_document"))&&(skipex.equals("true")));
System.out.println(test1);
System.out.println(test2);
</script>
</event>
<transition name="to_examine" to="examine_document">
<condition expression='#{((trsource=="create_document")||(trsource=="update_document"))&&(skipex!="true")}'/>
<script>
<variable name="trsource" access="write"/>
<expression>
trsource=executionContext.getTransitionSource().getName();
System.out.println(trsource+"15to_examine");
</expression>
</script>
</transition>
<transition name="skip_ex" to="examine_juge" >
<condition expression='#{((trsource=="create_document")||(trsource=="update_document"))&&(skipex=="true")}'/>
<script>
<variable name="trsource" access="write"/>
<expression>
trsource=executionContext.getTransitionSource().getName();
System.out.println(trsource+"16skip_ex");
</expression>
</script>
</transition>
<transition name="to_update" to="update_document">
<condition expression='#{(trsource=="examine_juge")}'/>
<script>
<variable name="trsource" access="write"/>
<expression>
trsource=executionContext.getTransitionSource().getName();
System.out.println(trsource+"17to_update");
</expression>
</script>
</transition>
</decision>
</process-definition>
下面再贴一个测试类的代码 和 一个JBPM公用服务类的代码(简单封装)
package com.hust.jbpmtest.service;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import junit.framework.TestCase;
import org.jbpm.taskmgmt.exe.TaskInstance;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.hust.buss.service.CommonService;
import com.hust.jbpm.service.JbpmService;
import com.hust.jbpmtest.model.JbpmDoc;
public class JbpmDocflowTest extends TestCase {
private static ApplicationContext ctx;
private static JbpmService mgr;
private static JbpmDocService mgr1;
private static CommonService cmgr;
private static boolean inited = false;
private long tid;
private String docid;
private JbpmDoc doc;
private HashMap compuParams;
public void setUp() throws Exception {
super.setUp();
if (!inited) {
init();
inited = true;
}
// 准备一个业务实体
doc = new JbpmDoc();
docid = cmgr.getPks(JbpmDoc.class.toString(), "ABC");
doc.setDocid(docid);
doc.setTitle("标题内容");
// 计算参量
compuParams = new HashMap();
compuParams.put("age", Long.valueOf("36"));
compuParams.put("year", new Long("5"));
compuParams.put("base", new Long("300"));
compuParams.put("obj", doc);
Object[] obj1 = new Object[] { "a", "b" };
Object[] obj2 = new Object[] { "c", "d" };
List plist = new ArrayList();
plist.add(obj1);
plist.add(obj2);
compuParams.put("plist", plist);
}
protected void tearDown() throws Exception {
super.tearDown();
doc = null;
docid = "";
compuParams = null;
}
public void init() {
ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
mgr = (JbpmService) ctx.getBean("jbpmService");
mgr1 = (JbpmDocService) ctx.getBean("jbpmDocService");
cmgr = (CommonService) ctx.getBean("commonService");
}
public void testDeployProcessDefinition() {
mgr.removePisByName("docflow");
mgr.removePdefsByName("docflow");
assertNull("not deleted all Definition", mgr
.getPdefByName("docflow"));
assertNotNull( "Definition should not be null", mgr
.deployProcessDefinitionByFile("com/hust/jbpmtest/workflow/autocompu/processdefinition.xml"));
}
public void testAllNormal() throws Exception {
// 需要注入的流程变量
HashMap map = new HashMap();
// 配置参量
map.put("pkname", "docid");
map.put("skipex", "false");
map.put("skipapp", "false");
// 业务主键参量
map.put("docid", docid);
map.put("compuparams", compuParams);
// 启动一个正常流程,并设置发起者和注入流程变量
TaskInstance taskInstance = mgr.executeNewTi("docflow", "createby",
null, map);
mgr.saveObject(doc);
// 下一步审核流程任务号
tid = mgr.getTidByBuss("examine_document", docid);
// 正常数据流程 数据
HashMap tiparams = new HashMap();
tiparams.put("ereply", "ok");
tiparams.put("examinememo", "同意");
// 继续下一步正常流程
mgr.executeTi(tid, null, tiparams, null, null);
// 下一步审批流程任务号
tid = mgr.getTidByBuss("approve_document" ,docid);
// 正常数据流程 数据
HashMap tiparams1 = new HashMap();
tiparams1.put("areply", "ok");
tiparams1.put("approvememo", "同意");
// 继续正常的审批流程
mgr.executeTi(tid, null, tiparams1, null, null);
}
}
JBPM服务类的代码
package com.hust.jbpmtest.service;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.jbpm.taskmgmt.exe.TaskInstance;
import com.hust.buss.service.CommonService;
import com.hust.buss.util.Constants;
import com.hust.core.model.PageObject;
import com.hust.core.service.BaseServiceImpl;
import com.hust.core.service.CallBack;
import com.hust.core.util.TextUtil;
import com.hust.jbpm.service.JbpmService;
import com.hust.jbpmtest.model.JbpmDoc;
public class JbpmDocServiceImpl extends BaseServiceImpl implements
JbpmDocService {
JbpmService jbpmService;
CommonService commonService;
public void executeTask(String taskname,JbpmDoc jbpmDoc,HashMap tiparams) throws Exception {
long tid=jbpmService.getTidByBuss(taskname, jbpmDoc.getDocid());
jbpmService.executeTi(tid, null, tiparams, null, null);
String docstatus=jbpmService.getTnameByBuss(jbpmDoc.getDocid());
if (docstatus.equals("")){
jbpmDoc.setDocstatus((String)jbpmService.getTi(tid).getContextInstance().getVariable("docstatus"));
}else{
jbpmDoc.setDocstatus(docstatus);
}
this.updateObject(jbpmDoc);
}
public void setJbpmService(JbpmService jbpmService) {
this.jbpmService = jbpmService;
}
public void createJbpmDoc(JbpmDoc jbpmDoc,HashMap tiparams){
// 启动一个正常流程,并设置发起者和注入流程变量
TaskInstance taskInstance = jbpmService.executeNewTi("docflow", "createby",
null, tiparams);
jbpmDoc.setDocstatus(jbpmService.getTnameByBuss(jbpmDoc.getDocid()));
this.saveObject(jbpmDoc);
}
public PageObject getJbpmList(String taskName,int pageno){
//获得特定环节的业务单据列表 及 附属的TaskInstance列表
//TaskName为空,表示显示所有的待办
//TaskName为all
String hql=" select distinct JbpmDoc from JbpmDoc JbpmDoc,TaskInstance TaskInstance where JbpmDoc.docid=TaskInstance.description ";
String countHql=" select count( distinct JbpmDoc) from JbpmDoc JbpmDoc,TaskInstance TaskInstance where JbpmDoc.docid=TaskInstance.description ";
String todo=" and TaskInstance.isSuspended != true and TaskInstance.isOpen = true ";
String all=" and TaskInstance.name in('create_document','update_document','examine_document','approve_document')";
if (taskName.equals("all")){
//不附加条件
hql=hql+all;
countHql=countHql+all;
} else if (taskName.equals("todo")){
//附加待办条件
hql=hql+todo;
countHql=countHql+todo;
} else{
hql=hql+todo+" and TaskInstance.name="+TextUtil.toSQL(taskName, TextUtil.Text_TYPE);
countHql=countHql+todo+" and TaskInstance.name="+TextUtil.toSQL(taskName, TextUtil.Text_TYPE);
}
PageObject list=getPageList(countHql, hql, Constants.MAX_QUERYRESULT_PER_PAGE, pageno);
return list;
}
public List getTaskInstances(String busscode) {
String hql=" select TaskInstance from TaskInstance TaskInstance where TaskInstance.description=:busscode "
+ " and TaskInstance.name in('create_document','update_document','examine_document','approve_document') order by TaskInstance.create ";
List list=this.findByNamedParam(hql, "busscode", busscode);
commonService.transData(list, null,new CallBack(){
public void execute(Object obj, HashMap map) throws Exception {
TaskInstance ti=(TaskInstance)obj;
String html="";
Map tivars=ti.getVariables();
if (ti.getName().equals("create_document")){
html=tivars.get("actionuser")+"建立文档";
}else if (ti.getName().equals("update_document")){
html=tivars.get("actionuser")+"修订文档,附加说明:"+tivars.get("addinfo");
}else if (ti.getName().equals("examine_document")){
html=tivars.get("actionuser")+"审核文档,审核意见:"+tivars.get("ereply")+",审核说明:"+tivars.get("examinememo");
}else if (ti.getName().equals("approve_document")){
html=tivars.get("actionuser")+"审批文档,审核意见:"+tivars.get("areply")+",审批说明:"+tivars.get("approvememo");
}
ti.setDescription(html);
}
});
return list;
}
public void setCommonService(CommonService commonService) {
this.commonService = commonService;
}
}