jBPM项目总结

转载:jBPM项目总结

原文地址: http://jbpm.group.javaeye.com/group/blog/196057

★使用jbpm提供的eclipse plugin在画流程图的时候, 一般的步骤是先画各种节点(我画流程图的顺序是这样的:start node, end node, task node, common node....),然后保存关闭,再打开继续添加transition,因为插件设计器有一个缺点就是第一次就画好transition之后再打开会" 惨不忍睹", 各种node会摆放的乱七八糟.

★一般我们认为未命名的那个transtion是默认要执行的transtion,其实不是这样的,通过看源代码:
Java代码
  1. public Transition getDefaultLeavingTransition() {  
  2.     Transition defaultTransition = null;  
  3.     if ( (leavingTransitions!=null)  
  4.          && (leavingTransitions.size()>0) ) {  
  5.       defaultTransition = (Transition) leavingTransitions.get(O);  
  6.     } else if ( superState!=null ){  
  7.       defaultTransition = superState.getDefaultLeavingTransition();  
  8.     }  
  9.     return defaultTransition;  
  10.   }  
public Transition getDefaultLeavingTransition() {
    Transition defaultTransition = null;
    if ( (leavingTransitions!=null)
         && (leavingTransitions.size()>0) ) {
      defaultTransition = (Transition) leavingTransitions.get(O);
    } else if ( superState!=null ){
      defaultTransition = superState.getDefaultLeavingTransition();
    }
    return defaultTransition;
  }

我们可以看出,实际上第一个transtion才是默认的transition,所以在画流程图的时候要注意这一点,为了让未命名的transtion作为默认的transtion,必须将其放在第一个.

★jbpm数据表结构分析
不管是task变量还是process变量,都保存在jbpm_variableinstance表中,从该表中我们可以看出, 只能存取byte, date, double, long和string这几种类型的变量,所以如果你设置的变量类型为int类型,那么实际上取出来的是long类型的,所以在使用 getVariable()方法要取得int类型变量,注意要造型为long.

★不管是task变量还是process变量, 如果要取变量值, 有两种方式, 一种是通过中间表jbpm_tokenvariablemap和jbpm_moduleinstance, jbpm使用hibernate就是通过该方式来取得的, 另一种是通过jbpm_variableinstance表的processinstantce_字段来取得, 一般如果我们手工来获取的话,可以通过该字段来取得.

★ContextInstance实例对应的表是jbpm_moduleinstance,ProcessInstance实例对应的表是jbpm_processintance

★进入子流程之前的事件是subprocess-created而不时node-enter

★token类是一个非常底层的东东,在实际的工作流中很少用到,而用到最多的就是task node以及附加在各个节点上的action handler, 因为只有task node才是人能参与流程的地方, 而jBPM工作流的主要任务就是通过人的操作来让流程运行, 如果在很多地方使用token,那说明你的流程不是人在操作,而是机器在操作.所以大部分我看到的使用token的地方都是在测试中,因为需要通过代码来 让流程自动运行.

★state node会将当前的流程挂起,这跟org.jbpm.graph.node.State类有关:
Java代码
  1. public class State extends Node {  
  2.     
  3.   private static final long serialVersionUID = 1L;  
  4.   
  5.   public State() {  
  6.     this(null);  
  7.   }  
  8.     
  9.   public State(String name) {  
  10.     super( name );  
  11.   }  
  12.   
  13.   public void execute(ExecutionContext executionContext) {  
  14.   }  
  15. }  
public class State extends Node {
  
  private static final long serialVersionUID = 1L;

  public State() {
    this(null);
  }
  
  public State(String name) {
    super( name );
  }

  public void execute(ExecutionContext executionContext) {
  }
}

因为它的execute()方法为空, 而其他的节点的execute方法中都会调用leave()方法继续流程的执行,跑到下一个节点,也别指望在state node中通过ProcessInstance.signal()方法来启动流程,因为没有地方提供你放该执行代码,即使通过在event的 actionhandler中来让流程继续执行也不行,因为在执行execute方法之前有一个流程锁定的动作,而在execute方法执行之后则有一个 解锁的处理,而位于action中execute方法中的signal方法也会检查当前节点是否锁定,因为在执行execute方法之前已经锁定所以会抛 出node被锁定的异常.而要让流程继续执行需要在流程之外通过调用ProcessInstance.signal()方法让流程继续执行.

★一般流程图的画法, 一般按照流程的顺序遵循从上到下,从左到右的原则[img]http://macrochen.javaeye.com/upload/picture /pic/14941/61a7b9f3-29e3-322a-9d76-9b927bec236e.jpg [/img]
★申请界面可能千差万别,但是审核页面都基本类似:审核内容,审核历史,审核意见, 所以我做成了通用的页面[img]http://macrochen.javaeye.com/upload/picture/pic/14945 /b3966fb3-2a30-331e-b40a-4fec6cdd9316.png [/img]

★为了将业务表单数据挂到jBPM流程引擎上,一般需要使用一个全局的流程实例变量(我一般采用FORM_ID)将指定的业务表单记录主键保存,这样在流程中的不同节点就可以取得所需要的业务数据,并交给不同的操作对象(Actor)进行处理.

★为了使用指定的页面来处理每个流程任务节点,需要将页面的url保存为任务节点变量, 这样就将jsp页面跟jBPM流程的任务节点联系起来了.而这个工作是放在AssignmentHandler中来处理的.在本人的项目中这是一个通用的 做法,所以写了一个AssignmentHandler抽象类:
Java代码
  1. /** 
  2.  * 审核任务节点对应的分配处理handler 主要设置处理当前审核任务的actor和审核操作页面, 默认的审核页面不能对审核内容进行编辑 
  3.  *  
  4.  * @author Macro Chen 
  5.  * @since Apr 18, 2008 
  6.  */  
  7. public abstract class BaseAssignmentHandler implements AssignmentHandler {  
  8.   
  9.     public void assign(Assignable assignable, ExecutionContext ctx)  
  10.             throws Exception {  
  11.         addContextInstanceVariables(ctx.getContextInstance());  
  12.         String employeeId = getActorId(assignable, ctx);  
  13.         assignable.setActorId(employeeId);  
  14.         addTaskInstanceVariables(assignable, ctx);  
  15.     }  
  16.   
  17.     /** 
  18.      * 添加task instance变量 
  19.      *  
  20.      * @param assignable 
  21.      * @param ctx 
  22.      */  
  23.     protected void addTaskInstanceVariables(Assignable assignable,  
  24.             ExecutionContext ctx) {  
  25.         TaskInstance ti = (TaskInstance) assignable;  
  26.         String url = getOperationUrl(assignable, ctx);  
  27.         url += (url.indexOf("?") != -1 ? "&" : "?") + "taskId=" + ti.getId();  
  28.         ti.setVariable(JbpmConstants.TIV_OPERATION_URL, url);  
  29.     }  
  30.   
  31.     /** 
  32.      * 根据业务需要添加其他的流程变量 
  33.      *  
  34.      * @param ci 
  35.      */  
  36.     protected void addContextInstanceVariables(ContextInstance ci) {  
  37.     }  
  38.   
  39.     /** 
  40.      * 执行任务操作的页面 默认情况下使用通用的审核页面(不可对审核内容进行修改), 子类可以根据需要开发自己的审核页面(如可对审核内容进行修改) 
  41.      *  
  42.      * @return 
  43.      */  
  44.     protected String getOperationUrl(Assignable assignable, ExecutionContext ctx) {  
  45.         return "/workflow/common/common_audit.jsp";  
  46.     }  
  47.   
  48.     /** 
  49.      * 设置当前任务节点的执行者 
  50.      *  
  51.      * @param assignable 
  52.      * @param ctx 
  53.      * @return 
  54.      * @throws Exception 
  55.      */  
  56.     protected abstract String getActorId(Assignable assignable,  
  57.             ExecutionContext ctx) throws Exception;  
  58.   
  59. }  
/**
 * 审核任务节点对应的分配处理handler 主要设置处理当前审核任务的actor和审核操作页面, 默认的审核页面不能对审核内容进行编辑
 * 
 * @author Macro Chen
 * @since Apr 18, 2008
 */
public abstract class BaseAssignmentHandler implements AssignmentHandler {

	public void assign(Assignable assignable, ExecutionContext ctx)
			throws Exception {
		addContextInstanceVariables(ctx.getContextInstance());
		String employeeId = getActorId(assignable, ctx);
		assignable.setActorId(employeeId);
		addTaskInstanceVariables(assignable, ctx);
	}

	/**
	 * 添加task instance变量
	 * 
	 * @param assignable
	 * @param ctx
	 */
	protected void addTaskInstanceVariables(Assignable assignable,
			ExecutionContext ctx) {
		TaskInstance ti = (TaskInstance) assignable;
		String url = getOperationUrl(assignable, ctx);
		url += (url.indexOf("?") != -1 ? "&" : "?") + "taskId=" + ti.getId();
		ti.setVariable(JbpmConstants.TIV_OPERATION_URL, url);
	}

	/**
	 * 根据业务需要添加其他的流程变量
	 * 
	 * @param ci
	 */
	protected void addContextInstanceVariables(ContextInstance ci) {
	}

	/**
	 * 执行任务操作的页面 默认情况下使用通用的审核页面(不可对审核内容进行修改), 子类可以根据需要开发自己的审核页面(如可对审核内容进行修改)
	 * 
	 * @return
	 */
	protected String getOperationUrl(Assignable assignable, ExecutionContext ctx) {
		return "/workflow/common/common_audit.jsp";
	}

	/**
	 * 设置当前任务节点的执行者
	 * 
	 * @param assignable
	 * @param ctx
	 * @return
	 * @throws Exception
	 */
	protected abstract String getActorId(Assignable assignable,
			ExecutionContext ctx) throws Exception;

}


★以下是本人总结的一些公共的,方便的jBPM静态方法,在整个项目中通用:
Java代码
  1. /** 
  2.  * @author Macro Chen 
  3.  * @since Apr 8, 2008 
  4.  */  
  5. public class JbpmUtils {  
  6.   
  7.     public static Long getLongVariableOfProcess(IJbpmProvider provider,  
  8.             String name) {  
  9.         ContextInstance ci = provider.getContextInstance();  
  10.         if (ci == null) {  
  11.             return (long)0;  
  12.         }  
  13.         return (Long)ci.getVariable(name);  
  14.     }  
  15.   
  16.     public static JbpmContext getJbpmContext() {  
  17.         JbpmConfiguration config = JbpmConfiguration.getInstance();  
  18.         return config.getCurrentJbpmContext();  
  19.     }  
  20.   
  21.     /** 
  22.      * 新建一个pi 
  23.      *  
  24.      * @param name 
  25.      * @return 
  26.      */  
  27.     public static ProcessInstance newProcessInstance(String name) {  
  28.         ProcessDefinition pd = getJbpmContext().getGraphSession()  
  29.                 .findLatestProcessDefinition(name);  
  30.         return new ProcessInstance(pd);  
  31.     }  
  32.   
  33.     /** 
  34.      * 根据taskId取得pi 
  35.      *  
  36.      * @param taskId 
  37.      * @return 
  38.      */  
  39.     public static ProcessInstance getProcessInstanceByTaskId(String taskId) {  
  40.         TaskInstance ti = getTaskInstance(taskId);  
  41.         return ti.getTaskMgmtInstance().getProcessInstance();  
  42.     }  
  43.   
  44.     public static ProcessInstance getProcessInstanceByTaskId(Long taskId) {  
  45.         TaskInstance ti = getTaskInstance(taskId);  
  46.         return ti.getTaskMgmtInstance().getProcessInstance();  
  47.     }  
  48.   
  49.     public static ContextInstance getContextInstance(String name) {  
  50.         return newProcessInstance(name).getContextInstance();  
  51.     }  
  52.   
  53.     public static ContextInstance getContextInstanceByTaskId(String taskId) {  
  54.         TaskInstance ti = getTaskInstance(taskId);  
  55.         if (ti == null)  
  56.             return null;  
  57.   
  58.         return ti.getTaskMgmtInstance().getProcessInstance()  
  59.                 .getContextInstance();  
  60.     }  
  61.   
  62.     public static ContextInstance getContextInstanceByTaskId(Long taskId) {  
  63.         TaskInstance ti = getTaskInstance(taskId);  
  64.         if (ti == null)  
  65.             return null;  
  66.         return ti.getTaskMgmtInstance().getProcessInstance()  
  67.                 .getContextInstance();  
  68.     }  
  69.   
  70.     public static TaskInstance createStartTaskInstance(ProcessInstance pi) {  
  71.         return pi.getTaskMgmtInstance().createStartTaskInstance();  
  72.     }  
  73.   
  74.     public static Session getSession() {  
  75.         return getJbpmContext().getSessionFactory().openSession();  
  76.     }  
  77.   
  78.     public static List getQueryList(String hql) {  
  79.         return getSession().createQuery(hql).list();  
  80.     }  
  81.   
  82.     public static TaskMgmtSession getTaskMgmt() {  
  83.         return getJbpmContext().getTaskMgmtSession();  
  84.     }  
  85.   
  86.     public static TaskInstance loadTaskInstance(long taskId) {  
  87.         return getTaskMgmt().loadTaskInstance(taskId);  
  88.     }  
  89.   
  90.     public static TaskInstance getTaskInstance(String taskId) {  
  91.         if (StringUtils.isEmpty(taskId))  
  92.             return null;  
  93.         return getJbpmContext().getTaskInstance(Long.parseLong(taskId));  
  94.     }  
  95.   
  96.     public static TaskInstance getTaskInstance(long taskId) {  
  97.         return getJbpmContext().getTaskInstance(taskId);  
  98.     }  
  99.   
  100.     public static List<TaskInstance> findTaskInstances(String actorId) {  
  101.         return getTaskMgmt().findTaskInstances(actorId);  
  102.     }  
  103.   
  104.     public static List<TaskInstance> findPooledTaskInstances(String actorId) {  
  105.         return getTaskMgmt().findPooledTaskInstances(actorId);  
  106.     }  
  107.   
  108.     public static void saveTaskInstance(TaskInstance ti) {  
  109.         getJbpmContext().save(ti);  
  110.     }  
  111.   
  112.     public static String getTaskId() {  
  113.         return DoradoUtils.getRequestParameter(JbpmConstants.TASK_ID);  
  114.     }  
  115.   

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值