说说 jBPM 工作流引擎的设计原理
1服务 API 设计
jBPM4 工作流引擎的核心 PVM 主要依靠 4 组服务 API :
- 流程定义服务 - Process Service。
- 流程执行服务- Execution Service。
- 流程管理服务 - Managerment Service。
- 指令服务 - Command Service。
应用通过这些服务与 PVM 进行数据交互,这些都是在支持事务的持久化模式下运行的。比如:
* ExecutionService.startProcessInstanceByKey - 发起流程实例。
* TaskService.completeTask - 完成任务。
客户端 API 是核心工作流模型对象对外暴露的公共方法,我们可以直接使用客户端 API 来执行一些流程操作,客户端 API 不会进行任何持久化操作,它操作的结果是通过调用相应服务的 API 后才会被持久化。比如:
* ProcessInstance.getName - 获取流程实例名称。
* Task.setAssignee - 设置任务分配者。
1.1 活动 API
活动 API 用于实现流程活动在运行时的行为。所有的活动类型都要实现 ActivityBehaviour 接口,它提供了控制流程执行的方法,接口定义如下:
public interface ActivityBehaviour extends Serializable {/** invoked when an execution arrives in an activity.* * <p>An ActivityBehaviour can control the propagation * of execution. ActivityBehaviour's can become external activities when they * invoke {@link ActivityExecution#waitForSignal()}. That means the * activity will become a wait state. In that case, {@link ExternalActivityBehaviour} * should be implemented to also handle the external signals. * </p> */void execute(ActivityExecution execution) throws Exception;
}
执行对象的类型需要实现 ActivityExecution 接口,这个接口定义了控制流程推进的方法:
活动定义 | 说明 |
---|---|
String getActivityName() | 获取当前活动名称。 |
void waitForSignal() | 等待执行信号。 |
void takeDefaultTransition() | 选择一个默认的流出转移。 |
void take(String transitionName) | 选择一个指定名称的流出转移。 |
void execute(String activityName) | 执行子活动。 |
void end() | 结束当前流程(包括子流程)。 |
void end(String state) | 结束当前流程(包括子流程),并为子流程指定结束状态。 |
void setPriority(int priority) | 设置活动优先级。 |
1.2 事件监听 API
事件监听 API 用于自定义事件监听器,它可以用来处理被监听到的流程事件。
它与活动 API 的区别是:它不能控制流程的执行。假设一个活动通过 execution 已经确定了一个转移,这时就会触发它所对应的事件监听器,因为转移已经先被确定,所以事件监听器必然无法改变流程的推进路线。
自定义的事件监听器,需要实现 EventListener
接口,这个接口定义如下:
public interface EventListener extends Serializable {/** is invoked when an execution crosses the event on which this listener is registered */void notify(EventListenerExecution execution) throws Exception;}
这里的 notify 方法需要一个 EventListenerExecution 类型的参数,它与 ActivityExecution 的相同之处是,它们都继承自 OpenExecution 接口,但它只定义了一个设置优先级的方法:
public interface EventListenerExecution extends OpenExecution {/** setter for the priority. The default priority is 0, which means * NORMAL. Other recognized named priorities are HIGHEST (2), HIGH (1), * LOW (-1) and LOWEST (-2). For the rest, the user can set any other * priority integer value, but then, the UI will have to display it as * an integer and not the named value.*/void setPriority(int priority);
}
再次强调:事件监听器无法改变流程的推进路径。
2 运行环境设计
为了让流程可以在不同的事务环境(Java EE 或 Spring )中运行,PVM 定义了运行环境对象,它会根据配置的环境,执行服务延迟加载与获取事务管理等操作。
运行环境是 EnvironmentFactory 对象,它有两个实现:
- ProcessEngineImpl - 默认的 Java EE 环境。
- SpringProcessEngine - 基于 Spring 框架的环境。
通过以下方式获取默认环境工厂对象,从而执行任意流程操作:
ConfigurationImpl cfg = new ConfigurationImpl();
cfg.setResource("jbpm.cfg.xml");//指定配置文件//创建环境工厂对象
EnvironmentFactory factory=new ProcessEngineImpl(cfg);//执行任意流程操作
Environment environment=factory.openEnvironment();
try {RepositoryService repositoryService = environment.get(RepositoryService.class);
} finally {factory.close();
}
注意:通过 Environment 对象获取的流程服务受到事务的控制。
也可以通过 Configuration 类加载默认的配置文件,获取各项流程服务,这种方式更方便:
ProcessEngine engine= Configuration.getProcessEngine();
RepositoryService repositoryService=engine.getRepositoryService();
3 命令设计模式
命令设计模式是 jBPM4 实现流程逻辑的核心思想。所有的命令都需要实现 Command 接口,并在 execute() 方法中实现逻辑:
public interface Command<T> extends Serializable {T execute(Environment environment) throws Exception;
}
注意: 每个命令都是独立的事务操作,即每一个 execute() 方法的实现都被一个 Hibernate 事务所包含。
public class CustomCommand implements Command<Void> {private String executionId;@Overridepublic Void execute(Environment environment) throws Exception {//从环境对象中获取执行服务ExecutionService executionService = environment.get(ExecutionService.class);//执行服务,完成流程逻辑executionService.signalExecutionById(executionId);return null;}}
命令定义后,可以通过流程引擎对象来执行自定义的命令:
ProcessEngine engine = Configuration.getProcessEngine();
engine.execute(new CustomCommand());
4 服务设计
外部应用程序(比如客户端)会调用服务 API 来作为操作工作流引擎,也可以通过它来持久化 PVM 的操作。
三个基本的服务接口:
服务类 | 说明 |
---|---|
RepositoryService | 流程定义及其相关资源的服务 |
ExecutionService | 流程实例及其执行的服务 |
ManagementService | Job 相关服务 |
所有的流程逻辑都被封装为命令,因此上述的三个服务类的方法实现执行的都是命令。比如 ManagementService 中的 createJobQuery 的实现:
public JobQuery createJobQuery() {JobQueryImpl query = commandService.execute(new CreateJobQueryCmd());query.setCommandService(commandService);return query;}
所有的 PVM 命令都统一委派给 CommandService,由它来执行这些命令:
public interface CommandService {String NAME_TX_REQUIRED_COMMAND_SERVICE = "txRequiredCommandService";String NAME_NEW_TX_REQUIRED_COMMAND_SERVICE = "newTxRequiredCommandService";/*** @throws JbpmException if command throws an exception.*/<T> T execute(Command<T> command);
}
CommandService 有两种工作模式:
* NAME_TX_REQUIRED_COMMAND_SERVICE :在同一线程中使用一个事务来执行所有的命令。
* NAME_NEW_TX_REQUIRED_COMMAND_SERVICE :一个命令执行一个事务。
CommandService 只定义了一个用于执行命令方法 execute()。
在默认的配置文件 jbpm.default.cfg.xml 中,预设了以下这些服务:
<repository-service />
<repository-cache />
<execution-service />
<history-service />
<management-service />
<identity-service />
<task-service />
CommandService 的设计采用了职责链的设计模式,它是环绕在命令周围的一群拦截器所组成的一条职责链。我们可以组合不同的拦截器,按照不同的顺序,在不同的环境下实现不同的持久化事务策略。
在 jbpm.tx.hibernate.cfg.xml 中,描述了 CommandService 的实现策略:
<command-service name="txRequiredCommandService"><skip-interceptor /><retry-interceptor /><environment-interceptor /><standard-transaction-interceptor />
</command-service><command-service name="newTxRequiredCommandService"><retry-interceptor /><environment-interceptor policy="requiresNew" /><standard-transaction-interceptor />
</command-service>
这就是我们之前所说的 CommandService 存在的两种工作模式的配置方式。
各个服务会按照需要来选择合适的 CommandService 工作模式来执行命令。各个拦截器继承自 Interceptor 抽象类,而它实现的就是 CommandService 接口:
public abstract class Interceptor implements CommandService {protected CommandService next;public CommandService getNext() {return next;}public void setNext(CommandService next) {this.next = next;}
}
多个 CommandService 被配置为一条职责链来拦截命令,这样各个服务就通过职责链来选择不同的策略,而无须改变命令本身啦O(∩_∩)O哈哈~
我们以 newTxRequiredCommandService 的 CommandService 实现为例,来说明这条职责链的作用,调用一条命令后,它会依次执行以下的拦截器——
- retry-interceptor:在数据库的乐观锁失败时,捕获 Hibernate 的 StaleObjectException,并尝试重新调用命令。
- environment-interceptor:为命令的调用提供一个环境对象。
- standard-transaction-interceptor:初始化标准事务对象(StandardTransaction)。
- 最后,由 DefaultCommandService 来调用命令。
也可以在此通过配置,使用其他的方式来调用命令——
- EjbLocalCommandService:把命令委派给一个本地的 EJB,这样可以启动一个 EJB 内容管理事务。
- EjbRemoteCommandService:把命令委派给一个远程的 EJB,这样命令可以在另一个 JVM 上被执行。
- AsyncCommandService:命令被包装为一个异步消息,这样命令就会在一个新的事务中被异步执行。
5 流程历史库
在整个流程实例执行过程的各个关键阶段,都设计了历史事件触发器,它会把流程实例数据存入历史库,实现了运行中的流程数据与历史流程数据的分离。
在流程实例的运行过程中,或触发历史流程事件,然后根据分类被分发到配置好的 HistorySession 中,HistorySession 的默认实现 HistorySessionImpl 会调用相应的历史事件对象 (HistoryEvent )的 process 方法来执行相应的历史事件处理逻辑:
public class HistorySessionImpl implements HistorySession {public void process(HistoryEvent historyEvent) {historyEvent.process();}
}
抽象类 HistoryEvent 的事件本身不会被持久化,它的抽象方法 process() 在它的实现类中,创建了历史实体,比如 HistoryEvent 的一个实现类 ActivityStart:
public void process() {DbSession dbSession = EnvironmentImpl.getFromCurrent(DbSession.class);long processInstanceDbid = execution.getProcessInstance().getDbid();HistoryProcessInstance historyProcessInstanceImpl = dbSession.get(HistoryProcessInstanceImpl.class, processInstanceDbid);HistoryActivityInstanceImpl historyActivityInstance = createHistoryActivityInstance(historyProcessInstanceImpl);String activityType = execution.getActivity().getType();historyActivityInstance.setType(activityType);dbSession.save(historyActivityInstance);execution.setHistoryActivityInstanceDbid(historyActivityInstance.getDbid());
}
这里创建了 HistoryActivityInstanceImpl ,并执行了持久化操作。
在 process() 中历史事件创建的实体与当前的流程实体是对应、归并的关系,比如 ProcessInstanceCreate 事件会创建与持久化 HistoryProcessInstance;而 ProcessInstanceEnd 事件会设置与持久化对应的 HistoryProcessInstance 对象的状态(结束)。
历史流程库维护着过往流程的归档信息。但流程实例或活动实例结束时,就会在历史流程库中写入数据,因为这些数据对于当前运行着的流程来说,是历史(过时)信息。
历史流程库使用 5 张表维护着 4 种实体历史信息:
实体 | 表名 |
---|---|
历史流程实例 | jbpm4_hist_procinst |
历史活动实例 | jbpm4_hist_actinst |
历史任务 | jbpm4_hist_task |
历史流程变量 | jbpm4_hist_var |
最后一张是 jbpm4_hist_detail,它记录着上述这些实体的历史明细表。
可以使用 HistoryService 的 createHistroyXxxQuery() 方法来获取上述实体的查询对象,来获取历史流程实体信息:
在 HistoryService 中还提供了一些用于数据分析的方法,比如:
方法 | 说明 |
---|---|
avgDurationPerActivity(String processDefinitionId) | 获取活动的平均执行时间。 |
choiceDistribution(String processDefinitionId, String activityName) | 获取流程转移的选择次数。 |
需要的话,也可以根据历史明细表 jbpm4_hist_detail,扩展出我们自己的流程数据分析方法哦O(∩_∩)O哈哈~
说说 jBPM 工作流引擎的设计原理相关推荐
- JBPM工作流引擎内核设计思想及构架
1 前言 流程引擎内核仅是"满足Process基本运行"的最微小结构,而整个引擎则要复杂很多,包括"状态存储"."事件处理"."组 ...
- java activiti jbpm_activiti和jbpm工作流引擎哪个比较好?
原标题:activiti和jbpm工作流引擎哪个比较好? 在常用的ERP系统.OA系统的开发中,工作流引擎是一个必不可少的工具.之前在选择工作流引擎时曾经在activiti和jbpm之间有过比较,当时 ...
- 工作流引擎的设计与实现
第四章 工作流引擎的设计与实现(一) (2009-03-24 17:26:26) 转载▼ 标签: 工作流数据模型 工作流数据结构 工作流定义 工作流实例 it 分类: 基于工作流的政务系统--设计 ...
- 可自管理的分布式工作流引擎的设计与实现
<script type="text/javascript"></script> <script src="http://pagead2.g ...
- 工作流系列之可自管理的分布式工作流引擎的设计与实现
这篇文章是偶在清华读研究生时发表在国家核心期刊CIMS("Design and implementation of self-managed distributed workflow eng ...
- 角色扮演游戏引擎的设计原理--转自MOVE2008
角色扮演游戏引擎的设计原理--转自MOVE2008 角色扮演游戏引擎的设计原理 角色扮演游戏(RPG)是深受广大游戏迷们喜爱的一种游戏, 它以独特的互动性和故事性吸引了无数的玩家.它向人们提供了超出现 ...
- 角色扮演游戏引擎的设计原理
角色扮演游戏引擎的设计原理 角色扮演游戏(RPG)是深受广大游戏迷们喜爱的一种游戏, 它以独特的互动性和故事性吸引了无数的玩家.它向人们提供了超出现实生活的广阔的虚拟世界,使人们能够尝试扮演不同的角色 ...
- 基于J2EE的柔性工作流引擎的设计与实现
摘要 工作流的柔性问题日益成为研究的热点,本文在分析工作流和分布式计算技术J2EE的基础上,给出一个基于J2EE的柔性工作流引擎的设计方案及其关键部分的实现技术. 关键词 工作流引擎,柔性,J2EE ...
- 轻量级工作流引擎的设计与实现
工作中,基于实际情况的需要,自研了一款工作流引擎,期间有不少收获,愿与同学们分享,听我娓娓道来- 一.什么是工作流引擎 简而言之,工作流引擎就是驱动工作流执行的一套代码. 至于什么是工作流.为什么要有 ...
最新文章
- 【Win10 应用开发】语音命令与App Service集成
- 2013福建高职单招计算机类专业,福建省2013高职单招计算机类试题及答案.doc
- 编写 Debugging Tools for Windows 扩展,第 3 部分:客户端和回调 (windbg 插件 扩展)
- java怎么解决页面乱码问题_java页面中文乱码的解决办法
- mysql数据库业务逻辑_Mysql业务设计(逻辑设计)
- Fibonacci数列第n项的第7种计算方法:Python列表
- 出现Field 'ssl_cipher' doesn't have a default value错误怎么解决
- python 求点到线段距离
- win7中竟然没有telnet.exe??
- html表格基础及案例示图代码。
- SAP中用户上传附件出错处理实例
- 虚幻4地形怎么增加层_虚幻周报20200114 | 新春快乐!
- 3_5.网络文件系统
- 有位程序员写了部「修仙小说版」编程教程!网友:蚌埠住了 ....
- 2021年,让你看透世界的8个底层逻辑
- 金融直播有哪些好处?直播平台有哪些?
- openni+linux+arm,Jetson tk1 安装OpenNI 1 +Xtion Pro +NiTE
- 【软路由】esxi 通过ping实现停电自动关机
- 【系统分析师之路】 第八章 复盘软件测试与维护(测试与过程改进)
- 火狐插件 打开html 死机,火狐浏览器打开过多Flash网页时死机怎么样解决