公司产品一直用JBPM作为流程驱动核心,之前的主要应用场景为数据审批等变动较小的业务范围,今年新开发了产品模块,用JBPM来驱动协同项目,实现多人协同工作的需求,在WBS分解项目时无法做到确保项目运行过程中不会变动,所以出现了项目运行过程中需要能动态改变流程定义并保证之前已经运行过的项目节点数据不会丢失的需求。
在网上搜索了很多,没有发现类似的流程实例拷贝的功能,自己试着实现了一个版本,当然有诸多限制,譬如已完成节点不可编辑,不可删除,否则流程无法正确流至上个流程实例的当前节点。
数据库中增加了流程过程描述表,主要字段为主流程实例ID,项目ID,流程任务ID,项目任务ID,以及任务的outCome。流程运行过程中,完成任务操作时往这张表中增加记录,记录流程运行过程。
实例拷贝主要分为六步,一、根据项目ID获取最新流程定义。二、根据最新流程定义获取上个版本流程定义。三、根据上个版本流程定义获取上个版本的流程实例。四、根据上个版本流程实例抽取出上个版本中已经运行的任务的描述,包括任务关联数据,任务走向等。五、开始最新流程实例,递归寻找当前任务,从上个版本的任务描述中拷贝关联数据以及确定任务走向。六、清理上个版本流程数据。
实现的重点是要考虑分支,子流程等复杂元素,特变是嵌套分支,嵌套子流程等也要能完美运行通过,主要的业务是在抽取历史任务数据以及递归推动最新流程,绑定历史任务数据的地方。代码的编写过程中,也运用了一些JBMP的源码知识,譬如控制执行先后顺序参开了ExecutionImpl的performAtomicOperation的模式,匹配历史任务时采用了责任链模式,将历史任务按照执行顺序组织成责任链模式,匹配成功后跳过已匹配的任务。
附上关键代码:
public abstract class EDMAtomicOperation implements Serializable {
//serialVersionUID is
private static final long serialVersionUID = 1L;
public static final EDMAtomicOperation GET_LATEST_PDID = new GetLatestPdIdOperation();
public static final EDMAtomicOperation GET_LAST_VERSION_PDID = new GetLastVersionPdIdOperation();
public static final EDMAtomicOperation GET_LAST_VERSION_PIID = new GetLastVersionPiIdOperation();
public static final EDMAtomicOperation GET_LAST_VERSION_TASKDESC = new GetLastVersionTaskDescOperation();
public static final EDMAtomicOperation COPY_PROCESS = new CopyProcessDataOperation();
public static final EDMAtomicOperation CLEAN_OLD_PROCESS = new CleanOldProcessOperation();
public abstract void perform(ProcessCourserImpl processCourse);
}
public interface ProcessCourser {
public void sign();
public String getErrorMessage();
}
public class ProcessCourserImpl implements ProcessCourser,Serializable{
//serialVersionUID is
private static final long serialVersionUID = -3239018905632135241L;
public ProcessCourserImpl(String projectId, ProcessCourseTool processCourseTool, String userShowName){
this.projectId = projectId;
this.processCourseTool = processCourseTool;
this.userShowName = userShowName;
}
/**
*@Function Name: sign
*@Description: 开始工作
*@Date Created: 2013-12-24 下午04:46:35
*@Author: Pan Duan Duan
*@Last Modified: , Date Modified:
*/
@Override
public void sign() {
//如果还未获取最新流程定义
if("".equals(latestPdId)){
performAtomicOperation(EDMAtomicOperation.GET_LATEST_PDID);
}
}
/**
*@Function Name: performAtomicOperation
*@Description: @param operation
*@Date Created: 2013-12-25 上午08:34:54
*@Author: Pan Duan Duan 执行操作
*@Last Modified: , Date Modified:
*/
public synchronized void performAtomicOperation(EDMAtomicOperation operation) {
performAtomicOperationSync(operation);
}
/**
*@Function Name: performAtomicOperationSync
*@Description: @param operation
*@Date Created: 2013-12-25 上午08:35:02
*@Author: Pan Duan Duan 以同步的方式执行操作
*@Last Modified: , Date Modified:
*/
private void performAtomicOperationSync(EDMAtomicOperation operation) {
//初始化
if (atomicOperations==null) {
atomicOperations = new LinkedList<EDMAtomicOperation>();
atomicOperations.offer(operation);
while (! atomicOperations.isEmpty()) {
EDMAtomicOperation atomicOperation = atomicOperations.poll();
atomicOperation.perform(this);
}
}else {
atomicOperations.offer(operation);
}
}
public class CopyProcessDataOperation extends EDMAtomicOperation {
Logger log = Logger.getLogger(this.getClass().getName());
//serialVersionUID is
private static final long serialVersionUID = -6126577335188267494L;
@Override
public void perform(ProcessCourserImpl processCourse) {
log.warn("Copy Process Step 5: copy old process info to the new one");
//最新流程定义
String latestPdId = processCourse.getLatestPdId();
//得到当前用户显示名称
String userShowName = processCourse.getUserShowName();
String latestPiId = processCourse.getProcessCourseTool().getProcessInstanceTool().startProcessInstance(latestPdId,userShowName);
//上个版本流程任务描述集合
List<EDMTaskDesc> lastTaskDescs = processCourse.getTaskDescs();
if(null != lastTaskDescs && lastTaskDescs.size() > 0){
processFlow(latestPiId,processCourse);
}
processCourse.performAtomicOperation(EDMAtomicOperation.CLEAN_OLD_PROCESS);
}
/**
*@Function Name: processFlow
*@Description: @param latestProcessInstance
*@Description: @param processCourse
*@Date Created: 2013-12-26 上午09:26:54
*@Author: Pan Duan Duan 驱动流程 复制数据
*@Last Modified: , Date Modified:
*/
private void processFlow(String latestPiId,
ProcessCourserImpl processCourse) {
//当前流程主流程ID
String mainPiId = latestPiId;
//
ProcessInstanceTool processInstanceTool = processCourse.getProcessCourseTool().getProcessInstanceTool();
//当前任务集合
List<Task> currentTasks = new ArrayList<Task>();
while(currentTasks.isEmpty()){
//获取最新流程的当前任务集合
getNextTaskByPiId(mainPiId, currentTasks, processInstanceTool);
//已完成任务集合
List<Task> completedTask = new ArrayList<Task>();
for(Task currentTask : currentTasks){
//完成任务 拷贝任务数据
boolean completed = complateCurrentTask(currentTask,processInstanceTool,processCourse,mainPiId);
if(completed){
completedTask.add(currentTask);
}
}
//移除已经完成的任务 推动流程
for(Task tempTask : completedTask){
currentTasks.remove(tempTask);
}
}
//
}
/**
*@param processCourse
* @Function Name: complateCurrentTask
*@Description: @param currentTask
*@Description: @param processInstanceTool
*@Date Created: 2013-12-26 上午11:05:06
*@Author: Pan Duan Duan 完成当前任务
*@Last Modified: , Date Modified:
*/
private boolean complateCurrentTask(Task currentTask,
ProcessInstanceTool processInstanceTool, ProcessCourserImpl processCourse, String mainPiId) {
//流程引擎
ProcessEngine processEngine = processInstanceTool.getProcessEngine();
//得到流程实例Service
ExecutionService executionService = processEngine.getExecutionService();
//得到taskService
TaskService taskService = processEngine.getTaskService();
//返回结果
boolean flag = false;
//
try{
//获取任务ID
String taskId = currentTask.getId();
//得到executionId
String executionId = currentTask.getExecutionId();
//得到流程实例
Execution execution = executionService.findExecutionById(executionId);
//得到主线流程
execution = processInstanceTool.findMainExecution(execution);
//获取项目任务ID
String prjTaskId = processInstanceTool.getTaskDataIdByProcessTaskId(taskId);
//复制任务数据
EDMTaskDesc copyedTaskDesc = processCourse.getTaskDescs().get(0).doCopy(prjTaskId, processInstanceTool);
//获取新的变量ID
if(null != copyedTaskDesc){
String newVariableId = copyedTaskDesc.getNewVariableId();
//获取新变量实例
EdmTaskVariable edmTaskVariable = processInstanceTool.getEdmTaskVariableById(newVariableId);
//获取任务执行人
String taskOperator = edmTaskVariable.getOperator();
//转化为登录名称
taskOperator = processInstanceTool.getUserLoginNameByShowName(taskOperator);
//注入流程任务中
processEngine.execute(new TaskDelegateCmd(taskId, taskOperator));
//保存任务变量
executionService.createVariable(execution.getId(), taskId, edmTaskVariable, true);
//得到任务流向
String outCome = copyedTaskDesc.getOutCome();
//完成任务
taskService.completeTask(taskId,outCome);
//保存执行过程
processCourse.getProcessCourseTool().saveTaskCourse(mainPiId, processCourse.getProjectId(), taskId, copyedTaskDesc.getPrjTaskId(), outCome);
//修改任务状态
processCourse.getProcessCourseTool().changePrjTaskState(copyedTaskDesc.getPrjTaskId(), ProjectState.STATE_COMPLETED);
flag = true;
}
}catch (Exception e) {
e.printStackTrace();
}
return flag;
}
/**
*@Function Name: getNextTaskByPiId
*@Description: @param hisPiId
*@Description: @param processEngine
*@Description: @return 得到下一个任务
*@Date Created: 2013-7-24 下午02:48:15
*@Author: Pan Duan Duan
*@Last Modified: , Date Modified:
*/
public void getNextTaskByPiId(String mainPiId, List<Task> tasks, ProcessInstanceTool processInstanceTool) {
ProcessEngine processEngine = processInstanceTool.getProcessEngine();
//任务Serveice
TaskService taskService = processEngine.getTaskService();
//流程实例Service
ExecutionService executionService = processEngine.getExecutionService();
//HistoryService
HistoryService historyService = processEngine.getHistoryService();
RepositoryService repositoryService = processEngine.getRepositoryService();
//得到流程实例
Execution excution = executionService.findExecutionById(mainPiId);
//寻找主流程当前任务
List<Task> taskList = taskService.createTaskQuery().processInstanceId(mainPiId).list();
if(null != taskList && taskList.size() > 0)
{
tasks.addAll(taskList);
}
//尋找 分支 当前任务
Collection<? extends Execution> executions = excution.getExecutions();
if(executions.size() > 0)
{
for(Execution branchExecution : executions){
getNextTaskByPiId(branchExecution.getId(), tasks, processInstanceTool);
}
}
//寻找子流程
String subExecutionId = null;
ExecutionImpl executionImpl = (ExecutionImpl) excution;
//得到activityName
String activityName = executionImpl.getActivityName();
if(null != activityName){
//获取主线流程
String mainBranchExecutionId = processInstanceTool.getProcessInstanceService().getMainPiIdByBranchPiId(excution.getId());
//根据当前流程 得到所有父子流程集合
List<Map<String,Object>> processInfos = processInstanceTool.getProcessInstanceIds(mainBranchExecutionId);
for(Map<String,Object> processInfo : processInfos){
String piId = CommonTools.Obj2String(processInfo.get("PIID"));
//得到流程实例
HistoryProcessInstance hisPi = historyService.createHistoryProcessInstanceQuery().processInstanceId(piId).uniqueResult();
if(null != hisPi){
//得到流程定义
ProcessDefinition pd = repositoryService.createProcessDefinitionQuery().processDefinitionId(hisPi.getProcessDefinitionId()).uniqueResult();
//得到流程定义名称
String pdName = pd.getName();
//得到流程定义
if(activityName.equals(pdName)){
subExecutionId = piId;
break;
}
}
}
if(null != subExecutionId){
getNextTaskByPiId(subExecutionId, tasks, processInstanceTool);
}
}
}
}
public class GetLastVersionTaskDescOperation extends EDMAtomicOperation{
Logger log = Logger.getLogger(this.getClass().getName());
//serialVersionUID is
private static final long serialVersionUID = 1665471796593388193L;
@Override
public void perform(ProcessCourserImpl processCourse) {
log.warn("Copy Process Step 4: get the last version flow task description");
//获取上个版本的 流程实例ID
String lastVersionPiId = processCourse.getLastVersionPiId();
if(!"".equals(lastVersionPiId)){
List<EDMTaskDesc> taskDescs = getLastVersionTaskDesc(lastVersionPiId,processCourse);
//组织责任链
organizeTaskChain(taskDescs);
processCourse.setTaskDescs(taskDescs);
}
processCourse.performAtomicOperation(EDMAtomicOperation.COPY_PROCESS);
}
/**
*@Function Name: organizeTaskChain
*@Description: @param taskDesc
*@Date Created: 2013-12-26 上午10:23:30
*@Author: Pan Duan Duan 组织责任链
*@Last Modified: , Date Modified:
*/
private void organizeTaskChain(List<EDMTaskDesc> taskDescs) {
int taskDescSize = taskDescs.size();
if(taskDescSize > 0){
//临时变量
EDMTaskDesc tempTaskDesc = taskDescs.get(taskDescSize - 1);
//遍历循环
for(int index = taskDescSize - 2; index >= 0; index--){
EDMTaskDesc taskDesc = taskDescs.get(index);
taskDesc.setNextEDMTaskDesc(tempTaskDesc);
tempTaskDesc = taskDesc;
}
}
}
/**
*@Function Name: getLastVersionTaskDesc
*@Description: @param lastVersionPiId
*@Description: @param processCourse
*@Description: @return 获取上个版本的任务描述
*@Date Created: 2013-12-25 上午11:27:22
*@Author: Pan Duan Duan
*@Last Modified: , Date Modified:
*/
private List<EDMTaskDesc> getLastVersionTaskDesc(
String lastVersionPiId, ProcessCourserImpl processCourse) {
List<EDMTaskDesc> retVal = new ArrayList<EDMTaskDesc>();
ProcessInstanceTool processInstanceTool = processCourse.getProcessCourseTool().getProcessInstanceTool();
ProcessEngine processEngine = processInstanceTool.getProcessEngine();
//获取流程历史服务
HistoryService historyService = processEngine.getHistoryService();
//获取父子流程集合
List<Map<String, Object>> processIdDescs = processCourse.getProcessCourseTool().getProcessInstanceTool().getProcessInstanceService().getProcessInstanceIds(lastVersionPiId);
//遍历获取已完成任务集合
Set<String> processInstanceIds = new HashSet<String>();
//加入主流程
processInstanceIds.add(lastVersionPiId);
for(Map<String, Object> processDescMap : processIdDescs){
//得到流程ID
String piId = CommonTools.Obj2String(processDescMap.get("PIID"));
processInstanceIds.add(piId);
}
for(String piId : processInstanceIds){
//得到历史流程
HistoryProcessInstance historyProcessInstance = historyService.createHistoryProcessInstanceQuery().processInstanceId(piId).uniqueResult();
//得到历史流程定义
String hisPdId = historyProcessInstance.getProcessDefinitionId();
//获取任务表单描述
XmlFormReader formReader = processEngine.execute(new GetFormReaderCommand(hisPdId));
List<HistoryActivityInstance> historyActivityInstances = historyService.createHistoryActivityInstanceQuery().processInstanceId(piId).list();;
//遍历任务集合
for(HistoryActivityInstance hisAci : historyActivityInstances){
//过滤掉正在进行的 以及非正常任务节点
if(hisAci.getEndTime() == null || ((HistoryActivityInstanceImpl)hisAci).getType().equals("sub-process")){
continue;
}
//强转为实现类
HistoryActivityInstanceImpl hisAcImpl = (HistoryActivityInstanceImpl) hisAci;
XmlForm xmlForm = formReader.getFormByName(hisAcImpl.getActivityName());
//获取历史任务ID
String taskId = processInstanceTool.getTaskIdByHaiImplId(String.valueOf(hisAcImpl.getDbid()));
//获取历史变量值
String hisVariId = CommonTools.Obj2String(historyService.getVariable(piId, taskId));
//存储
EDMTaskDesc edmTaskDesc = new EDMTaskDesc();
edmTaskDesc.setHisTaskId(taskId);
edmTaskDesc.setVariableId(hisVariId);
edmTaskDesc.setPrjTaskId(xmlForm == null ? "" : xmlForm.getId());
retVal.add(edmTaskDesc);
}
}
//增加outCome并排序
retVal = appendOutCome(retVal,processCourse);
return retVal;
}
/**
*@Function Name: appendOutCome
*@Description: @param retVal
*@Description: @return 增加任务流向 以及 排序
*@Date Created: 2013-12-26 上午08:23:06
*@Author: Pan Duan Duan
*@Last Modified: , Date Modified:
*/
/**
*@param processCourse
* @Function Name: appendOutCome
*@Description: @param source
*@Description: @return
*@Date Created: 2013-12-26 上午08:27:16
*@Author: Pan Duan Duan
*@Last Modified: , Date Modified:
*/
private List<EDMTaskDesc> appendOutCome(List<EDMTaskDesc> source, ProcessCourserImpl processCourse) {
//返回结果
List<EDMTaskDesc> desc = new ArrayList<EDMTaskDesc>();
//获取流程执行过程
StringBuffer querySql = new StringBuffer();
querySql.append("SELECT ID,MAIN_PIID,PROJECT_ID,FLOW_TASK_ID,PRJ_TASK_ID,OUTCOME FROM ");
querySql.append("JBPM4_EDM_COURSE WHERE MAIN_PIID = ? AND PROJECT_ID = ? ORDER BY ID");
//获取查询结果
List<Map<String,Object>> queryList = processCourse.getProcessCourseTool().getProcessInstanceTool().getMetaDaoFactory().getJdbcTemplate().
queryForList(querySql.toString(),new Object[]{processCourse.getLastVersionPiId(),processCourse.getProjectId()});
//遍历组织
for(Map<String,Object> dataMap : queryList){
//流程任务ID
String flowTaskId = CommonTools.Obj2String(dataMap.get("FLOW_TASK_ID"));
//任务流向
String outCome = CommonTools.Obj2String(dataMap.get("OUTCOME"));
//从sources中寻找
for(EDMTaskDesc tempTaskDesc : source){
if(flowTaskId.equals(tempTaskDesc.getHisTaskId())){
tempTaskDesc.setOutCome(outCome);
desc.add(tempTaskDesc);
break;
}
}
}
return desc;
}