`
snowfox2008
  • 浏览: 125286 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

JBPM最佳实践

阅读更多

原文来自:http://www.mastertheboss.com/en/jbpm/106-jbpm-best-practices.html,原创翻译,转载联系本人

 

JBPM最佳实践


没有完美的规则来对你的工作流进行建模,这依赖于你的流程的需求,涉及到的执行者等等。不管如何,我想同你分享一下开发人员通常认可的关键规则。如果你认可或者你想提些建议,请反馈给我。

1) 保持你的JBPM executionContext整洁
在我曾经见过的正式的第一个JBPM项目中,我注意到开发人员有时使用executionContext来放置很多流程的变量。为了控制执行流,向executionContext中增加变量是基本的法则,但是不管如何,不要被它诱惑的往里面放任何东西!例如:假设你正在设计一个复杂的售票系统:你可能需要存储一些关于执行者的附加信息,例如:名,姓,邮件。因此在执行上下文中你混合了既有模型变量和流程变量!
在一个EJB中构造这些字段,并且在你的executionContext中仅保持一个ticketid。下面是一个使用Seam与业务流程进行交互的EJB的例子:

@Stateful  
@Name("TicketingSystem")  
   
public class TicketBean implements TicketBeanItf {  
   
@PersistenceContext(type=PersistenceContextType.EXTENDED)  
EntityManager em;  
   
@In(create = true)  
@Out  
private Ticket ticket;  
   
// We make this available to the business process  
@Out(scope=ScopeType.BUSINESS_PROCESS, required=false)  
long ticketId;  
   
@CreateProcess(definition="TroubleTicketing")  
 public void createTicket() {  
    em.persist(ticket);  
    // Keep a reference to the ticketId in your biz process  
    ticketId = ticket.getTicketId();  
 }  
    
}  

 记住这执行上下文中添加领域变量,除了被当作一个差的设计选择外,而且也会很大程度上降低你流程的性能。
2) 使用异常处理器,仅用来设置变量或者做错误通知
JBPM有一个内建的异常处理器,能够被应用在单一节点或者整个流程。

 

<exception-handler exception-class="java.lang.Exception">  
    <action class="com.sample.handlers.BPMExceptionHandler"></action>        
</exception-handler>  

 
你或许曾经被诱惑在JBPM中使用异常处理机制来决策执行流:不要这样做!JBPM的机制并不完全与java的异常处理相似,在java中,一个捕获的异常能够对控制流产生影响。而在jBPM的案例中,控制流不能被jBPM的异常处理机制改变。异常可以被捕获或不捕获。不捕获的异常被扔给客户端(例如,调用token.signal()方法的客户端)或者这个异常被jBPM异常处理器捕获。对于捕获的异常,graph execution会当作没有异常发生继续执行。
使用异常处理器的最佳实践是,执行那些相关的action(设置变量,发生邮件,jms消息等)然后或者继续graph的执行(你期望的行为)或者因为事务失败而重新扔出异常,并且结束当前节点回到开始的状态。
依赖于被扔出的异常,在你的Action中捕获业务异常并且设置一些流程变量也是一个好的设计。然后你可以在流程中构造一个决策节点,用来选择一个特定的执行路径。

3) 需要JBPM做失败转发?在一个集群的SLSB(无状态session bean)中封装JBPM的调用
jBPM是一个状态机:流程描述和运行时的状态被持久化到数据库中,但是在一个集群环境中,他们不会自动失败转发。如果一个集群节点失败了,这时正在执行的一些外部触发器(ui,jms,重复执行的计时器)需要停止执行并且不得不重新启动。依赖于你所处的事务上下文,这些动作能够被自动执行(重新传递jms消息,重新执行计时器)或者请求UI交互(如果集群节点down机需要重启时,显示错误消息给用户)。
因此,当流程描述是持久化时,工作流的失败转发必须被手动执行。jBPM能够被用来构建一个完整的失败保护,可集群的支持失败转发的工作流解决方案,但是它不支持开箱即用。
那么,在你的工作流中增加这些特性的最简单的方式是什么?对于大多数的案例,最佳的解决方案是在你的应用中用一个无状态的会话bean来封装jBPM API的调用,并且在最后构建集群。
给你的EJB增加集群能力一点也不复杂,参考下面的方案:
http://www.mastertheboss.com/en/jboss-application-server/49-jboss-ejb3-in-a-cluster.html
4) 在尽可能的地方使用superstates
一个Superstate是一组节点:它是一种将节点组合成相关集合的方便的方式,是在流程中实例阶段的表示。例如,一个应用程序能够分阶段组合流程中的所有节点。动作可以被关联到superstate事件上。一个重要的意义就是一个令牌能够在给定的时间上被多层嵌套。这可以方便地来检查一个流程是否被执行了,例如在开始阶段。
 
因此,当每个状态都代表着一个流程的阶段时,将流程拆分为superstates是一种好的设计,但是为什么我提倡对superstates的使用有更多的理由:JBPM是一个状态机并且本身没有交付一个内建的图形化环境。有个实际的Eclipse插件,可以让你来图形化的流程建模,但是它不是在市场上最好的BPM前端工具:图标太大,太丑陋,并且你不能够自己为不同的节点类型(至少它能够马上实现)来定制图标。如果你曾经用JBPM画一个有100个节点的流程,你可能也做我所做的事情了:因为流程的图片很大并且完全杂乱,我从JBPM中自己设计了一个前端展现层。
如果你不希望为JBPM设计一个新的前端,那么尽可能广泛地使用superstates,它将使你的流程(jpg图)更可读并且当你展现一个5页的流程图时你的老板不会晕。
5) 扩展JBPM的Api而不是搞乱复杂的流程建模
有时候开发人员(我也是)不去寻找更简单的解决方案:或许用jbpm内建的节点来建模流程而导致一个不必要的复杂流程。想更简单…jBPM是非常易扩展的(actionhandlers,定制节点类型)并且有时比用存在的节点做简单的建模还要容易,这可以使得事情没有那么复杂。
例如:假设你有这样一个需求,在一个依赖于地理定位的特定泳道内指派一个任务。(如果一个任务发生在纽约,它指派给用户A,如果它发生在芝加哥那么指派给用户B)
拿到org.jbpm.taskmgmt.def.Task.java这个类并且增加下述的字段和方法:

private String taskLocation;
 
public String getTaskLocation() {
   return taskLocation;
}
public void setTaskLocation(String taskLocation){
   this.taskLocation = taskLocation;
}

 

现在为Task.java修改hibernate配置文件,在Task.hbm.xml中:

<property name="taskLocation" column="TASKLOCATION_" ></property>

 

最后修改JpdlXmlReader.java以使得当流程从数据库中读入时新的属性被注入到Task类中。

String taskLocation = taskElement.attributeValue("taskLocation");

if (taskLocation==null) {
         taskLocation = taskElement.attributeValue("taskLocation");
}
    
if (taskLocation != null){
          task.setLocation(taskLocation);
}

 

另一个定制的例子可以应用到你的查询中:假设你想用很多规则来过滤你的任务:考虑一个用JSF来做渲染的任务列表和一个任务过滤窗口,这个窗口中你可以按照优先级,日期,执行人,任务名称等来过滤任务。
一个不会使得事情更复杂的,实现它的方式是,给任务实例增加过滤器:仅仅是打开任务实例的hibernate文件并且添加一些hibernate过滤器:

<filter name="filterPriority"  condition=":paramPriority = PRIORITY_"/>    
    <filter name="filterDesc"      condition=":paramDesc = DESCRIPTION_"/>        
    <filter name="filterId"        condition="str(ID_) LIKE (:paramId) "/>    
    <filter name="filterCreate"    condition="CREATE_ BETWEEN :paramFrom and :paramTo"/>  

 

注意,传递给过滤器的参数以“ : ”开始,而其它的字段(像_ID)属于TaskInstance。然后当你填充你的数据库时,使被选择的过滤器生效:

String sql  = "from org.jbpm.taskmgmt.exe.TaskInstance";
session.enableFilter("filterPriority").setParameter("paramPriority","2");
Query queryParent = session.createQuery(sql);   
List list = queryParent.list();
Iterator iter = list.iterator();
 while (iter.hasNext()) {   
  TaskInstance taskInstance = (TaskInstance)iter.next();
 }

 

6) 如果你有复杂的规则,雇用一个Drool的开发人员
一个工作流引擎(或者传统的面向图的编程)是关于制定一个代表执行的图。节点可以展现为等待状态。一个规则引擎是关于指定一套规则然后为指定的一套事实库应用一个推理算法。Drools怎样同JBPM相配合呢?一个最佳的实践是用JBPM来使一个规则引擎中的Handlers中的全部或部分逻辑具体化。换句话说,JBPM引擎能够由Drools规则引擎来驱动。
结合其它观点,这一做法也并非适用于所有情况:问问自己几件事:
我的java Action Handlers有多复杂?如果你仅仅需要从数据库中读取一些数据,而不需要更多的,这可能不适合用一个规则引擎。然而,在用java来实现一个适量处理的场合,当实现你的JBPM处理器时是值得考虑用Drools的。这是因为大多数的应用开发随着时间会越来越复杂,而Drools会让你轻松地应付这些,特别是如果你的应用的剩余时间是在中期或更长。更进一步,Drools通过在一个或多个很容易配置的XML文件中指定业务规则可以帮助你对付将来的变化。
Drools的另一个得分点是,Drools指导开发人员正确地编写代码来做正确的事情。同时规则更容易阅读然后编码,因此你的职员将会更适合于它。
更进一步,正确地使用,Drools能记住的不仅仅是信息,而且还有使用这些信息的以前的测试的结果,给整个应用一个快速的提升。
7) 用还是不用BPEL
BPEL是一个XML语言,用来描述长期运行的web服务的交互。它主要被用来集中地编排消息交换,因此在SOA中是一个关键的要素。
BPEL与JPDL有什么共同点呢?
两个语言都有一个过程标记
同外部代理交互
对于活动的调度
异常处理
错误恢复
即使他们有一些共同点,这些元素的具体表达式导致不同的听众:例如讨论过程标记:

JPDL用简单的含义来反应组织过程
BPEL用复杂的场景来描述结构化的组成

而与外部代理的交互也被不同地利用:

BPEL是面向文档的,因此主要在公司边界上使用
JPDL是面向对象的,因此它是公司组件的主干

BPEL将人之间的交互代理给partner service
jPDL提供集成的任务管理

因此我应该什么时候使用BPEL?
当你需要你的流程很轻便的伸展到java平台之外。BPEL流程能够在基于java平台的编排的服务器上被执行或者在任何其它的软件平台上(例如.NET)。这一点在使用不同平台的不同的合作伙伴之间进行商业交互的场景中是特别重要的。当没有直接的人员涉及,你可能更需要对长期运行的业务流程的支持。
当你需要以一个相对简单的方式取得事务补偿时。在已经完全成功的业务流程中的补偿,或者取消步骤是业务流程中最重要的概念。补偿的目标是撤销,作为一个已经被放弃的业务流程的一部分已经被执行的前续节点的影响。
当这些条件不满足时使用JPDL。


 

  • 大小: 50.6 KB
3
2
分享到:
评论
1 楼 whaosoft 2009-03-28  
以前我没用过这个  现在做hr系统我用到了这个 好难写啊

相关推荐

Global site tag (gtag.js) - Google Analytics