在我曾经见过的正式的第一个JBPM项目中,我注意到开发人员有时使用 executionContext 来放置很多流程的变量。为了控制执行流,向 executionContext 中增加变量是基本的法则,但是不管如何,不要被它诱惑的往里面放任何东西!例如:假设你正在设计一个复杂的售票系统:你可能需要存储一些关于执行者的附加信息,例如:名、姓、邮件。因此在执行上下文中你混合了业务变量和流程变量!
一个好的解决思路是:在一个EJB中构造这些字段,并且在你的 executionContext 中仅保持一个ticketid。
下面是一个使用Seam与业务流程进行交互的EJB的例子:
view plaincopy to clipboardprint?
@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();
}
}
@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();
}
} < type="application/x-shockwave-flash" width="14" height="15" src=" http://snowfox2008.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" src=" http://snowfox2008.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" flashvars="clipboard=%40Stateful%20%20%0A%40Name(%22TicketingSystem%22)%20%20%0A%20%20%20%0Apublic%20class%20TicketBean%20implements%20TicketBeanItf%20%7B%20%20%0A%20%20%20%0A%40PersistenceContext(type%3DPersistenceContextType.EXTENDED)%20%20%0AEntityManager%20em%3B%20%20%0A%20%20%20%0A%40In(create%20%3D%20true)%20%20%0A%40Out%20%20%0Aprivate%20Ticket%20ticket%3B%20%20%0A%20%20%20%0A%2F%2F%20We%20make%20this%20available%20to%20the%20business%20process%20%20%0A%40Out(scope%3DScopeType.BUSINESS_PROCESS%2C%20required%3Dfalse)%20%20%0Along%20ticketId%3B%20%20%0A%20%20%20%0A%40CreateProcess(definition%3D%22TroubleTicketing%22)%20%20%0A%20public%20void%20createTicket()%20%7B%20%20%0A%20%20%20%20em.persist(ticket)%3B%20%20%0A%20%20%20%20%2F%2F%20Keep%20a%20reference%20to%20the%20ticketId%20in%20your%20biz%20process%20%20%0A%20%20%20%20ticketId%20%3D%20ticket.getTicketId()%3B%20%20%0A%20%7D%20%20%0A%20%20%20%20%0A%7D%20%20%0A" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage=" http://www.macromedia.com/go/getflashplayer" width="14" height="15">
@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();
}
}
@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();
}
}
@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();
}
} < type="application/x-shockwave-flash" width="14" height="15" src=" http://snowfox2008.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" src=" http://snowfox2008.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" flashvars="clipboard=%40Stateful%20%20%0A%40Name(%22TicketingSystem%22)%20%20%0A%20%20%20%0Apublic%20class%20TicketBean%20implements%20TicketBeanItf%20%7B%20%20%0A%20%20%20%0A%40PersistenceContext(type%3DPersistenceContextType.EXTENDED)%20%20%0AEntityManager%20em%3B%20%20%0A%20%20%20%0A%40In(create%20%3D%20true)%20%20%0A%40Out%20%20%0Aprivate%20Ticket%20ticket%3B%20%20%0A%20%20%20%0A%2F%2F%20We%20make%20this%20available%20to%20the%20business%20process%20%20%0A%40Out(scope%3DScopeType.BUSINESS_PROCESS%2C%20required%3Dfalse)%20%20%0Along%20ticketId%3B%20%20%0A%20%20%20%0A%40CreateProcess(definition%3D%22TroubleTicketing%22)%20%20%0A%20public%20void%20createTicket()%20%7B%20%20%0A%20%20%20%20em.persist(ticket)%3B%20%20%0A%20%20%20%20%2F%2F%20Keep%20a%20reference%20to%20the%20ticketId%20in%20your%20biz%20process%20%20%0A%20%20%20%20ticketId%20%3D%20ticket.getTicketId()%3B%20%20%0A%20%7D%20%20%0A%20%20%20%20%0A%7D%20%20%0A" quality="high" allowscriptaccess="always" type="application/x-shockwave-flash" pluginspage=" http://www.macromedia.com/go/getflashplayer" width="14" height="15">
@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();
}
}
随意在执行上下文(executionContext)中添加领域变量,除了被当作一个不好的设计选择外,也会在很大程度上降低你流程运行的性能。
使用jBPM异常处理器,仅用来设置变量或者做错误通知
JBPM有一个内建的异常处理器,能够被应用在单一节点或者整个流程。
JBPM有一个内建的异常处理器,能够被应用在单一节点或者整个流程。
view plaincopy to clipboardprint?
<exception-handler exception-class = "java.lang.Exception">
<action class = "com.sample.handlers.BPMExceptionHandler">
</action>
</exception-handler>
<exception-handler exception-class = "java.lang.Exception">
<action class = "com.sample.handlers.BPMExceptionHandler">
</action>
</exception-handler>
<exception-handler exception-class = "java.lang.Exception">
<action class = "com.sample.handlers.BPMExceptionHandler">
</action>
</exception-handler>
<exception-handler exception-class = "java.lang.Exception">
<action class = "com.sample.handlers.BPMExceptionHandler">
</action>
</exception-handler>
<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中捕获业务异常并且设置一些流程变量也是一个好的设计。然后你可以在流程中构造一个决策节点,用来选择一个特定的执行路径。
依赖于被扔出的异常,在你的Action中捕获业务异常并且设置一些流程变量也是一个好的设计。然后你可以在流程中构造一个决策节点,用来选择一个特定的执行路径。
需要JBPM做失败转发?在一个集群的SLSB(stateless session bean)中封装JBPM的调用
jBPM是一个状态机:流程描述和运行时的状态被持久化到数据库中,但是在一个集群环境中,他们不会自动失败转发。如果一个集群节点失败了,这时正在执行的一些触发器(UI,JMS,重复执行的计时器)需要停止执行并且不得不重新启动。依赖于你所处的事务上下文,这些动作能够被自动执行(重新传递JMS消息,重新执行计时器)或者请求UI交互(如果集群节点宕机需要重启时,显示错误消息给用户)。
jBPM是一个状态机:流程描述和运行时的状态被持久化到数据库中,但是在一个集群环境中,他们不会自动失败转发。如果一个集群节点失败了,这时正在执行的一些触发器(UI,JMS,重复执行的计时器)需要停止执行并且不得不重新启动。依赖于你所处的事务上下文,这些动作能够被自动执行(重新传递JMS消息,重新执行计时器)或者请求UI交互(如果集群节点宕机需要重启时,显示错误消息给用户)。
因此,当流程描述是持久化时,工作流的失败转发必须被手动执行。jBPM能够被用来构建一个完整的失败保护,可集群的支持失败转发的工作流解决方案,但是它不支持开箱即用。
那么,在你的工作流中增加这些特性的最简单的方式是什么?对于大多数的案例,最佳的解决方案是在你的应用中用一个无状态的会话bean来封装jBPM API的调用,并且在最后构建集群。
那么,在你的工作流中增加这些特性的最简单的方式是什么?对于大多数的案例,最佳的解决方案是在你的应用中用一个无状态的会话bean来封装jBPM API的调用,并且在最后构建集群。
给你的EJB增加集群能力一点也不复杂,参考下面的方案:
在尽可能的地方使用super states
一个Superstate是一组节点:它是一种将节点组合成相关集合的方便的方式,是在流程中实例阶段的表示。例如,一个应用程序能够分阶段组合流程中的所有节点。动作(Action)可以被关联到superstate事件上。一个重要的意义就是一个令牌(token)能够在给定的时间上被多层嵌套。这可以方便地来检查一个流程是否被执行了,例如在开始阶段。
一个Superstate是一组节点:它是一种将节点组合成相关集合的方便的方式,是在流程中实例阶段的表示。例如,一个应用程序能够分阶段组合流程中的所有节点。动作(Action)可以被关联到superstate事件上。一个重要的意义就是一个令牌(token)能够在给定的时间上被多层嵌套。这可以方便地来检查一个流程是否被执行了,例如在开始阶段。
因此,当每个状态都代表着一个流程的阶段时,将流程拆分出super states是一种好的设计,但是为什么我提倡对super states的使用还有更多的理由:
JBPM是一个状态机并且本身没有交付一个内建的图形化环境。官方的Eclipse插件,可以让你来图形化的流程建模,但是它不是在市场上最好的BPM前端工具:图标太大,太丑陋,并且你不能够自己为不同的节点类型来定制图标。如果你曾经用JBPM画一个有100个节点的流程,你可能也做我所做的事情了:因为流程的图片很大并且很杂乱,我从JBPM中自己设计了一个前端展现层。
如果你不希望为JBPM设计一个新的前端,那么尽可能广泛地使用superstates,它将使你的流程(图)更可读,并且当你展现一个5页的流程图时你老板不会晕。
尝试扩展JBPM的API而不是搞乱复杂的流程建模
有时候开发人员(我也是)不去寻找更简单的解决方案,或许用jBPM内建的节点来建模流程而导致一个不必要的复杂流程。
有时候开发人员(我也是)不去寻找更简单的解决方案,或许用jBPM内建的节点来建模流程而导致一个不必要的复杂流程。
想更简单……
jBPM是非常易扩展的(action handlers,定制节点类型)并且有时比用存在的节点做简单的建模还要容易!这可以使得事情没有那么复杂。
例如:假设你有这样一个需求,在一个依赖于地理定位的特定泳道内指派一个任务。(如果一个任务发生在纽约,它指派给用户A,如果它发生在芝加哥那么指派给用户B)
拿到 org.jbpm.taskmgmt.def.Task.java 这个类并且增加下述的字段和方法:
view plaincopy to clipboardprint?
private String taskLocation;
public String getTaskLocation() {
return taskLocation;
}
public void setTaskLocation(String taskLocation){
this.taskLocation = taskLocation;
}
private String taskLocation;
public String getTaskLocation() {
return taskLocation;
}
public void setTaskLocation(String taskLocation){
this.taskLocation = taskLocation;
}
private String taskLocation;
public String getTaskLocation() {
return taskLocation;
}
public void setTaskLocation(String taskLocation){
this.taskLocation = taskLocation;
}
private String taskLocation;
public String getTaskLocation() {
return taskLocation;
}
public void setTaskLocation(String taskLocation){
this.taskLocation = taskLocation;
}
private String taskLocation;
public String getTaskLocation() {
return taskLocation;
}
public void setTaskLocation(String taskLocation){
this.taskLocation = taskLocation;
}
public String getTaskLocation() {
return taskLocation;
}
public void setTaskLocation(String taskLocation){
this.taskLocation = taskLocation;
}
现在为 Task.java 修改hibernate配置文件,在 Task.hbm.xml 中:
view plaincopy to clipboardprint?
<property name="taskLocation" column= "TASKLOCATION_"></property>
<property name="taskLocation" column= "TASKLOCATION_"></property>
<property name="taskLocation" column= "TASKLOCATION_"></property>
<property name="taskLocation" column= "TASKLOCATION_"></property>
最后修改 JpdlXmlReader.java 以使得当流程从数据库中读入时新的属性被注入到Task类中。
view plaincopy to clipboardprint?
String taskLocation = taskElement.attributeValue("taskLocation");
if (taskLocation==null) {
taskLocation = taskElement.attributeValue("taskLocation");
}
if (taskLocation != null){
task.setLocation(taskLocation);
}
String taskLocation = taskElement.attributeValue("taskLocation");
if (taskLocation==null) {
taskLocation = taskElement.attributeValue("taskLocation");
}
if (taskLocation != null){
task.setLocation(taskLocation);
}
String taskLocation = taskElement.attributeValue("taskLocation");
if (taskLocation==null) {
taskLocation = taskElement.attributeValue("taskLocation");
}
if (taskLocation != null){
task.setLocation(taskLocation);
}
String taskLocation = taskElement.attributeValue("taskLocation");
if (taskLocation==null) {
taskLocation = taskElement.attributeValue("taskLocation");
}
if (taskLocation != null){
task.setLocation(taskLocation);
}
String taskLocation = taskElement.attributeValue("taskLocation");
if (taskLocation==null) {
taskLocation = taskElement.attributeValue("taskLocation");
}
if (taskLocation != null){
task.setLocation(taskLocation);
}
taskLocation = taskElement.attributeValue("taskLocation");
}
if (taskLocation != null){
task.setLocation(taskLocation);
}
另一个定制的例子可以应用到你的查询中。假设你想用很多规则来过滤你的任务:考虑一个用JSF来做渲染的任务列表和一个任务过滤窗口,这个窗口中你可以按照优先级、日期、执行人、任务名称等来过滤任务。
一个不会使得事情更复杂的、实现它的方式是,给任务实例增加过滤器:仅仅是打开任务实例的hibernate文件并且添加一些hibernate过滤器:
view plaincopy to clipboardprint?
<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" />
<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" />
<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" />
<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。然后当你填充你的数据库时,使被选择的过滤器生效:
view plaincopy to clipboardprint?
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();
}
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();
}
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();
}
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();
}
<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"/>
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();
}
如果你有复杂的规则,雇用一个Drools开发人员
首先要理解:
一个工作流引擎(或者传统的面向图的编程)是关于制定一个代表执行的图。节点可以展现为等待状态。
一个规则引擎是关于指定一套规则然后为指定的一套事实库应用一个推理算法。
Drools怎样同JBPM相配合呢?一个最佳的实践是用JBPM来使一个规则引擎中的Handlers中的全部或部分逻辑具体化。换句话说,JBPM引擎能够由Drools规则引擎来驱动。
结合其它观点,这一做法也并非适用于所有情况,所以问问自己几件事:
我的java Action Handlers 有多复杂?如果你仅仅需要从数据库中读取一些数据,而不需要更多的,这可能不适合用一个规则引擎。然而,在使用java来实现一个处理大量业务规则的场合,当使用JBPM引擎做你的流程管理时是值得考虑用Drools的。这是因为大多数的应用开发随着时间会越来越复杂,而Drools会让你轻松地应付这些,特别是如果你的应用的生命周期比较长。更进一步,Drools通过在一个或多个很容易配置的XML文件中指定业务规则可以帮助你对付将来的业务规则变化。
Drools的另一个得分点是,Drools“指引”开发人员正确地编写代码来“做正确的事情”。同时,规则比编码更容易阅读,因此你的员工在“阅读业务”时会更舒服。
更进一步,在正确使用的情况下,Drools能记住的不仅仅是信息,而且还有以前使用这些信息的测试结果,从而给整个应用一个快速的提升。
用还是不用BPEL?
BPEL是一个XML语言,用来描述长期运行的web服务的交互。它主要被用来集中地编排消息交换,因此在SOA中是一个关键的要素。
BPEL是一个XML语言,用来描述长期运行的web服务的交互。它主要被用来集中地编排消息交换,因此在SOA中是一个关键的要素。
BPEL与JPDL有什么共同点呢?
两个语言都可以描述过程
同外部代理交互
对于活动的调度
异常处理
错误恢复
即使它们有一些共同点,但这些元素的具体表达式导致不同的受众,讨论它们对于过程的描述:
同外部代理交互
对于活动的调度
异常处理
错误恢复
即使它们有一些共同点,但这些元素的具体表达式导致不同的受众,讨论它们对于过程的描述:
JPDL用更加简单的语义来描述组织过程
BPEL用复杂的场景来描述结构化的组成
而与外部代理的交互也被不同地实现:
BPEL用复杂的场景来描述结构化的组成
而与外部代理的交互也被不同地实现:
BPEL是基于面向文档的,因此可以在整个公司范围上使用
JPDL是面向对象的,因此它可以作为公司应用组件的主干
BPEL将人之间的交互代理给 Partner Service 实现
jPDL提供集成的任务管理
那么我应该什么时候使用BPEL?
JPDL是面向对象的,因此它可以作为公司应用组件的主干
BPEL将人之间的交互代理给 Partner Service 实现
jPDL提供集成的任务管理
那么我应该什么时候使用BPEL?
当你需要你的流程很轻便的伸展到java平台之外——BPEL流程能够在基于java平台的服务器或任何其它的软件平台(例如.NET)上被执行。这一点在使用不同平台的合作伙伴之间进行商业交互的场景中是特别重要的。
当没有直接的人员涉及(即无人工干预的流程),同时你更需要对长时间运行的业务流程很好支持的情况下。
当你需要以一个相对简单的方式取得事务补偿时。在已经完全成功的业务流程中的补偿,或者取消步骤是业务流程中最重要的概念。补偿的目标是撤销,即消除一个业务流程中已被放弃的一部分已经被执行的节点所造成的影响。
当这些条件不满足时使用JPDL。
当没有直接的人员涉及(即无人工干预的流程),同时你更需要对长时间运行的业务流程很好支持的情况下。
当你需要以一个相对简单的方式取得事务补偿时。在已经完全成功的业务流程中的补偿,或者取消步骤是业务流程中最重要的概念。补偿的目标是撤销,即消除一个业务流程中已被放弃的一部分已经被执行的节点所造成的影响。
当这些条件不满足时使用JPDL。
本文来自CSDN博客,转载请标明出处:
http://blog.csdn.net/kthq/archive/2009/08/05/4411258.aspx
因此,当每个状态都代表着一个流程的阶段时,将流程拆分出super states是一种好的设计,但是为什么我提倡对super states的使用还有更多的理由:
JBPM是一个状态机并且本身没有交付一个内建的图形化环境。官方的Eclipse插件,可以让你来图形化的流程建模,但是它不是在市场上最好的BPM前端工具:图标太大,太丑陋,并且你不能够自己为不同的节点类型来定制图标。如果你曾经用JBPM画一个有100个节点的流程,你可能也做我所做的事情了:因为流程的图片很大并且很杂乱,我从JBPM中自己设计了一个前端展现层。
如果你不希望为JBPM设计一个新的前端,那么尽可能广泛地使用superstates,它将使你的流程(图)更可读,并且当你展现一个5页的流程图时你老板不会晕。
尝试扩展JBPM的API而不是搞乱复杂的流程建模
有时候开发人员(我也是)不去寻找更简单的解决方案,或许用jBPM内建的节点来建模流程而导致一个不必要的复杂流程。
有时候开发人员(我也是)不去寻找更简单的解决方案,或许用jBPM内建的节点来建模流程而导致一个不必要的复杂流程。
想更简单……
jBPM是非常易扩展的(action handlers,定制节点类型)并且有时比用存在的节点做简单的建模还要容易!这可以使得事情没有那么复杂。
例如:假设你有这样一个需求,在一个依赖于地理定位的特定泳道内指派一个任务。(如果一个任务发生在纽约,它指派给用户A,如果它发生在芝加哥那么指派给用户B)
拿到 org.jbpm.taskmgmt.def.Task.java 这个类并且增加下述的字段和方法:
view plaincopy to clipboardprint?
private String taskLocation;
public String getTaskLocation() {
return taskLocation;
}
public void setTaskLocation(String taskLocation){
this.taskLocation = taskLocation;
}
private String taskLocation;
public String getTaskLocation() {
return taskLocation;
}
public void setTaskLocation(String taskLocation){
this.taskLocation = taskLocation;
}
private String taskLocation;
public String getTaskLocation() {
return taskLocation;
}
public void setTaskLocation(String taskLocation){
this.taskLocation = taskLocation;
}
private String taskLocation;
public String getTaskLocation() {
return taskLocation;
}
public void setTaskLocation(String taskLocation){
this.taskLocation = taskLocation;
}
private String taskLocation;
public String getTaskLocation() {
return taskLocation;
}
public void setTaskLocation(String taskLocation){
this.taskLocation = taskLocation;
}
public String getTaskLocation() {
return taskLocation;
}
public void setTaskLocation(String taskLocation){
this.taskLocation = taskLocation;
}
现在为 Task.java 修改hibernate配置文件,在 Task.hbm.xml 中:
view plaincopy to clipboardprint?
<property name="taskLocation" column= "TASKLOCATION_"></property>
<property name="taskLocation" column= "TASKLOCATION_"></property>
<property name="taskLocation" column= "TASKLOCATION_"></property>
<property name="taskLocation" column= "TASKLOCATION_"></property>
最后修改 JpdlXmlReader.java 以使得当流程从数据库中读入时新的属性被注入到Task类中。
view plaincopy to clipboardprint?
String taskLocation = taskElement.attributeValue("taskLocation");
if (taskLocation==null) {
taskLocation = taskElement.attributeValue("taskLocation");
}
if (taskLocation != null){
task.setLocation(taskLocation);
}
String taskLocation = taskElement.attributeValue("taskLocation");
if (taskLocation==null) {
taskLocation = taskElement.attributeValue("taskLocation");
}
if (taskLocation != null){
task.setLocation(taskLocation);
}
String taskLocation = taskElement.attributeValue("taskLocation");
if (taskLocation==null) {
taskLocation = taskElement.attributeValue("taskLocation");
}
if (taskLocation != null){
task.setLocation(taskLocation);
}
String taskLocation = taskElement.attributeValue("taskLocation");
if (taskLocation==null) {
taskLocation = taskElement.attributeValue("taskLocation");
}
if (taskLocation != null){
task.setLocation(taskLocation);
}
String taskLocation = taskElement.attributeValue("taskLocation");
if (taskLocation==null) {
taskLocation = taskElement.attributeValue("taskLocation");
}
if (taskLocation != null){
task.setLocation(taskLocation);
}
taskLocation = taskElement.attributeValue("taskLocation");
}
if (taskLocation != null){
task.setLocation(taskLocation);
}
另一个定制的例子可以应用到你的查询中。假设你想用很多规则来过滤你的任务:考虑一个用JSF来做渲染的任务列表和一个任务过滤窗口,这个窗口中你可以按照优先级、日期、执行人、任务名称等来过滤任务。
一个不会使得事情更复杂的、实现它的方式是,给任务实例增加过滤器:仅仅是打开任务实例的hibernate文件并且添加一些hibernate过滤器:
view plaincopy to clipboardprint?
<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" />
<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" />
<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" />
<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。然后当你填充你的数据库时,使被选择的过滤器生效:
view plaincopy to clipboardprint?
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();
}
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();
}
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();
}
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();
}
<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"/>
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();
}
如果你有复杂的规则,雇用一个Drools开发人员
首先要理解:
一个工作流引擎(或者传统的面向图的编程)是关于制定一个代表执行的图。节点可以展现为等待状态。
一个规则引擎是关于指定一套规则然后为指定的一套事实库应用一个推理算法。
Drools怎样同JBPM相配合呢?一个最佳的实践是用JBPM来使一个规则引擎中的Handlers中的全部或部分逻辑具体化。换句话说,JBPM引擎能够由Drools规则引擎来驱动。
结合其它观点,这一做法也并非适用于所有情况,所以问问自己几件事:
我的java Action Handlers 有多复杂?如果你仅仅需要从数据库中读取一些数据,而不需要更多的,这可能不适合用一个规则引擎。然而,在使用java来实现一个处理大量业务规则的场合,当使用JBPM引擎做你的流程管理时是值得考虑用Drools的。这是因为大多数的应用开发随着时间会越来越复杂,而Drools会让你轻松地应付这些,特别是如果你的应用的生命周期比较长。更进一步,Drools通过在一个或多个很容易配置的XML文件中指定业务规则可以帮助你对付将来的业务规则变化。
Drools的另一个得分点是,Drools“指引”开发人员正确地编写代码来“做正确的事情”。同时,规则比编码更容易阅读,因此你的员工在“阅读业务”时会更舒服。
更进一步,在正确使用的情况下,Drools能记住的不仅仅是信息,而且还有以前使用这些信息的测试结果,从而给整个应用一个快速的提升。
用还是不用BPEL?
BPEL是一个XML语言,用来描述长期运行的web服务的交互。它主要被用来集中地编排消息交换,因此在SOA中是一个关键的要素。
BPEL是一个XML语言,用来描述长期运行的web服务的交互。它主要被用来集中地编排消息交换,因此在SOA中是一个关键的要素。
BPEL与JPDL有什么共同点呢?
两个语言都可以描述过程
同外部代理交互
对于活动的调度
异常处理
错误恢复
即使它们有一些共同点,但这些元素的具体表达式导致不同的受众,讨论它们对于过程的描述:
同外部代理交互
对于活动的调度
异常处理
错误恢复
即使它们有一些共同点,但这些元素的具体表达式导致不同的受众,讨论它们对于过程的描述:
JPDL用更加简单的语义来描述组织过程
BPEL用复杂的场景来描述结构化的组成
而与外部代理的交互也被不同地实现:
BPEL用复杂的场景来描述结构化的组成
而与外部代理的交互也被不同地实现:
BPEL是基于面向文档的,因此可以在整个公司范围上使用
JPDL是面向对象的,因此它可以作为公司应用组件的主干
BPEL将人之间的交互代理给 Partner Service 实现
jPDL提供集成的任务管理
那么我应该什么时候使用BPEL?
JPDL是面向对象的,因此它可以作为公司应用组件的主干
BPEL将人之间的交互代理给 Partner Service 实现
jPDL提供集成的任务管理
那么我应该什么时候使用BPEL?
当你需要你的流程很轻便的伸展到java平台之外——BPEL流程能够在基于java平台的服务器或任何其它的软件平台(例如.NET)上被执行。这一点在使用不同平台的合作伙伴之间进行商业交互的场景中是特别重要的。
当没有直接的人员涉及(即无人工干预的流程),同时你更需要对长时间运行的业务流程很好支持的情况下。
当你需要以一个相对简单的方式取得事务补偿时。在已经完全成功的业务流程中的补偿,或者取消步骤是业务流程中最重要的概念。补偿的目标是撤销,即消除一个业务流程中已被放弃的一部分已经被执行的节点所造成的影响。
当这些条件不满足时使用JPDL。
当没有直接的人员涉及(即无人工干预的流程),同时你更需要对长时间运行的业务流程很好支持的情况下。
当你需要以一个相对简单的方式取得事务补偿时。在已经完全成功的业务流程中的补偿,或者取消步骤是业务流程中最重要的概念。补偿的目标是撤销,即消除一个业务流程中已被放弃的一部分已经被执行的节点所造成的影响。
当这些条件不满足时使用JPDL。
原文:http://www.mastertheboss.com/en/jbpm/106-jbpm-best-practices.html
本文转自胡奇 51CTO博客,原文链接:http://blog.51cto.com/huqicto/280665,如需转载请自行联系原作者