解决AssignmentHandler 出现异常事务无法回滚问题

转自:http://macrochen.javaeye.com/blog/199590

在使用jBPM做开发的过程中, JbpmContextFilter 是一个非常方便的过滤器, 从源代码中我们可以看到:

Java代码 复制代码
  1. public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {   
  2.   String actorId = null;   
  3.   
  4.   // see if we can get the authenticated swimlaneActorId   
  5.   if (servletRequest instanceof HttpServletRequest) {   
  6.     HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;   
  7.     Principal userPrincipal = httpServletRequest.getUserPrincipal();   
  8.     if (userPrincipal != null) {   
  9.       actorId = userPrincipal.getName();   
  10.     }   
  11.   }   
  12.   
  13.   JbpmContext jbpmContext = getJbpmConfiguration().createJbpmContext(jbpmContextName);   
  14.   try {   
  15.     if (isAuthenticationEnabled) {   
  16.       jbpmContext.setActorId(actorId);   
  17.     }   
  18.     filterChain.doFilter(servletRequest, servletResponse);   
  19.   } finally {   
  20.     jbpmContext.close();   
  21.   }   
  22. }  
  public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    String actorId = null;

    // see if we can get the authenticated swimlaneActorId
    if (servletRequest instanceof HttpServletRequest) {
      HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
      Principal userPrincipal = httpServletRequest.getUserPrincipal();
      if (userPrincipal != null) {
        actorId = userPrincipal.getName();
      }
    }

    JbpmContext jbpmContext = getJbpmConfiguration().createJbpmContext(jbpmContextName);
    try {
      if (isAuthenticationEnabled) {
        jbpmContext.setActorId(actorId);
      }
      filterChain.doFilter(servletRequest, servletResponse);
    } finally {
      jbpmContext.close();
    }
  }


它主要做了两个事情: 一个是在流程继续执行之前为我们创建了一个JbpmContext; 另一个工作就是流程执行之后完成jbpmContext的关闭. 而如果不使用这个Filter的话, 那么在我们的流程处理中,就需要自己做这两件事. 接着我们再看org.jbpm.JbpmContext.close()的代码:

Java代码 复制代码
  1. public void close() {   
  2.     log.debug("closing jbpmContext " + toString());   
  3.     try {   
  4.       if (services!=null) {   
  5.         try {   
  6.           autoSave();   
  7.         } finally {   
  8.           services.close();   
  9.         }   
  10.       }   
  11.     } finally {   
  12.       if (jbpmConfiguration!=null) {   
  13.         jbpmConfiguration.jbpmContextClosed(this);   
  14.       }   
  15.     }   
  16.   }  
public void close() {
    log.debug("closing jbpmContext " + toString());
    try {
      if (services!=null) {
        try {
          autoSave();
        } finally {
          services.close();
        }
      }
    } finally {
      if (jbpmConfiguration!=null) {
        jbpmConfiguration.jbpmContextClosed(this);
      }
    }
  }


在关闭之前都会执行自动保存.
从上面的分析我们可以看到, jbpm似乎没有提供我们处理流程内部异常的地方,它不管是否出现异常, 都会将我们的流程操作节点持久化到对应的jbpm数据库表中.本人就因为这个原因导致流程出现问题, 在我的一个流程运行过程中, 在某个审核步骤完成, 进入下一个task node的时候, 该Task节点的AssignmentHandler不幸抛出了异常, 按照我们的理解, 流程应该停留在前一步审核阶段, 但实际情况是审核节点已经被设置成完成状态, 而下一个task node的taskInstance也已经持久化到jbpm_taskinstance表中(只是处于未完成状态), 因为是在assign的时候出现的问题, 因此这个新创建的任务节点将成为一个无人处理的节点, 所有人都无法见不到它了.
出现上面的情况一点也不奇怪, 因为在TaskMgmtInstance的createTaskInstance方法中中有这样的代码:

Java代码 复制代码
  1. // create the task instance   
  2. taskInstance.create(executionContext);   
  3.   
  4. // if this task instance is created for a task, perform assignment   
  5. if (task!=null) {   
  6.   taskInstance.assign(executionContext);   
  7. }  
// create the task instance
taskInstance.create(executionContext);

// if this task instance is created for a task, perform assignment
if (task!=null) {
  taskInstance.assign(executionContext);
}


它先创建taskInstance, 完成taskInstance的属性设置, 然后执行assign动作(异常就出现在这里), 在出现异常之后程序会回到JbpmContextFilter中,然后执行JbpmContext的close方法(该方法中完成所有新建和修改对象的持久化工作).

为了解决这个问题,就需要我们自己来处理jbpm引擎内部抛出的异常, 这一点jbpm做的非常好, 因为jpbm抛出的异常都是继承自JbpmException, 所以我们只要捕获该异常就可以了.而我们结束流程操作一般都是出现在这样几个地方:调用TaskInstance.end(), ProcessInstance.signal()以及相关的同名方法, 因此只要在调用这些方法的地方对异常进行捕获就可以了.
我将这几个方法放到工具类, 以方便调用:

Java代码 复制代码
  1. public static void end(TaskInstance ti) {   
  2.     end(ti, (Transition) null);   
  3. }   
  4.   
  5. public static void end(final TaskInstance ti, final String transitionName) {   
  6.     new JbpmOperation() {   
  7.         @Override  
  8.         void doExecute(JbpmContext ctx) {   
  9.             ti.end(transitionName);   
  10.         }   
  11.     }.execute();   
  12. }   
  13.   
  14. public static void end(final TaskInstance ti, final Transition transition) {   
  15.     new JbpmOperation() {   
  16.         @Override  
  17.         void doExecute(JbpmContext ctx) {   
  18.             ti.end(transition);   
  19.         }   
  20.     }.execute();   
  21. }   
  22.   
  23. public static void signal(final ProcessInstance pi) {   
  24.     new JbpmOperation() {   
  25.         @Override  
  26.         void doExecute(JbpmContext ctx) {   
  27.             pi.signal();   
  28.         }   
  29.     }.execute();   
  30. }  
public static void end(TaskInstance ti) {
	end(ti, (Transition) null);
}

public static void end(final TaskInstance ti, final String transitionName) {
	new JbpmOperation() {
		@Override
		void doExecute(JbpmContext ctx) {
			ti.end(transitionName);
		}
	}.execute();
}

public static void end(final TaskInstance ti, final Transition transition) {
	new JbpmOperation() {
		@Override
		void doExecute(JbpmContext ctx) {
			ti.end(transition);
		}
	}.execute();
}

public static void signal(final ProcessInstance pi) {
	new JbpmOperation() {
		@Override
		void doExecute(JbpmContext ctx) {
			pi.signal();
		}
	}.execute();
}



然后定义了一个回调的操作类用来处理在遇到JbpmException异常的时候告诉JbpmContext进行rollback:

Java代码 复制代码
  1. /**  
  2.  * 用来处理流程异常, 如果流程本身出现异常(这些异常可能来自我们定义的各种assignmentHandler, actionHandler,  
  3.  * decisionHandler等),通过设置rollbackOnly来阻止其持久化  
  4.  *   
  5.  * @author Macro Chen  
  6.  * @since Jun 3, 2008  
  7.  */  
  8. abstract class JbpmOperation {   
  9.     void execute() {   
  10.         JbpmContext ctx = JbpmUtils.getJbpmContext();   
  11.         try {   
  12.             doExecute(ctx);   
  13.         } catch (JbpmException e) {   
  14.             // 通过该方法让ctx提交的时候rollback   
  15.             ctx.setRollbackOnly();   
  16.             throw e;   
  17.         } finally {   
  18.             // 在filter中close   
  19.             // ctx.close();   
  20.         }   
  21.     }   
  22.   
  23.     abstract void doExecute(JbpmContext ctx);   
  24.   
  25. }  

  说明一下:本人只是借用了文章作者里面的一种解决思路,即setRollbackOnly();   ,但并未把作者的方法搬过来,因为我对他的代码还存在几个疑问:

第一:execute方法没有throws异常,他是如何throw 异常的?

第二:JbpmOperation 是一个抽象类,他是如何可以new实例化一个对象的?

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值