作者:jBPM组
版本:5.0.0
原文版权归作者所有,该翻译稿仅供学习交流使用,引用或转载请保证该部分的完整
章节4 BPMN2.0
4.1业务处理模式与注释(BPMN)2.0规范
4.2示例
4.3支持的元素(elements)与属性(attributes)
4.1业务处理模式与注释(BPMN)2.0规范
BPMN是目标是为所有业务人员提供一套可读易懂的注记,从业务分析,到开发,再到流程的管理监督人员都能适用。
业务处理模式及注记(BPMN)2.0规范是一个OMG规范,不光定义了如何用图形化方式呈现业务流程(就像BPMN1.x),也包括所定义元素的执行语义及存储(交换)流程定义的XML格式。
jBPM支持用BPMN2.0 XML格式定义流程,可以按照你的业务过程使用各种jBPM组件建模、执行、监督管理流程。实际上,全部的BPMN2.0规范还包括如何表示编排、协作之类的事情,当然,jBPM项目只注重规范中与流程调度执行有关的部分。
jBPM没有实现BPMN2.0规范中的全部元素与属性,只提供了一个有效的子集,包括流程运行会涉及到的大部分节点类型。这些元素在规范的"Common Executable"中定义,此外还有其他一些上下文中有用的扩展元素。
流对象(Flow objects)
事件(Events) 开始事件(Start Events)(空None,条件Conditional,信号Signal,消息Message,计时器Timer)结束事件(End Events)(空None,终止Terminate,错误Error,升级Escalation,信号Signal,消息Message,补偿Compensation)中间捕获事件(信号Signal,计时器Timer,条件Conditional,消息Message)中间抛出事件(空None,信号Signal,升级Escalation,消息Message,补偿Compensation)不可中断(Non-interrupting)边界事件(Boundary Event)(升级Escalation,计时器Timer)可中断边界事件(升级Escalation,错误Error,计时器Timer,补偿Compensation) 活动(Activities) 脚本任务(Script Task)(Java或者MVEL表达式语言)任务(Task)服务任务(Service Task)用户任务(User Task)业务规则任务(Business Rule Task)手工任务(Manual Task)发送任务(Send Task)接受任务(Receive Task)可复用子流程(Reusable Sub-Process)(调用活动Call-Activity)嵌入式子流程(Embedded Sub-Process)Ad-Hoc子流程数据对象(Data-Object) 网关(Gateways) 发散(Diverging) 独占(Exclusive)(Java,MVEL,XPath表达式语言)包容(Inclusive)(Java,MVEL,XPath表达式语言)并行(Parallel)基于事件(Event-Based) 聚合(Converging) 独占(Exclusive)并行(Parallel) 路线(Lines) 数据(Data) Java类型流程属性嵌入式子流程属性活动属性 连接对象(Connecting objects) 顺序流(Sequence flow)下面这个名为"Hello World"的BPMN2.0流程,启动之后即输出"Hello World"。
该流程的BPMN2.0 XML格式定义如下:
<?xml version="1.0" encoding="UTF-8"?> <definitions id="Definition" targetNamespace="http://www.example.org/MinimalExample" typeLanguage="http://www.java.com/javaTypes" expressionLanguage="http://www.mvel.org/2.0" xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xs="http://www.w3.org/2001/XMLSchema-instance" xs:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:tns="http://www.jboss.org/drools"> <process processType="Private" isExecutable="true" id="com.sample.HelloWorld" name="Hello World" > <!-- nodes --> <startEvent id="_1" name="StartProcess" /> <scriptTask id="_2" name="Hello" > <script>System.out.println("Hello World");</script> </scriptTask> <endEvent id="_3" name="EndProcess" > <terminateEventDefinition/> </endEvent> <!-- connections --> <sequenceFlow id="_1-_2" sourceRef="_1" targetRef="_2" /> <sequenceFlow id="_2-_3" sourceRef="_2" targetRef="_3" /> </process> <bpmndi:BPMNDiagram> <bpmndi:BPMNPlane bpmnElement="Minimal" > <bpmndi:BPMNShape bpmnElement="_1" > <dc:Bounds x="15" y="91" width="48" height="48" /> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="_2" > <dc:Bounds x="95" y="88" width="83" height="48" /> </bpmndi:BPMNShape> <bpmndi:BPMNShape bpmnElement="_3" > <dc:Bounds x="258" y="86" width="48" height="48" /> </bpmndi:BPMNShape> <bpmndi:BPMNEdge bpmnElement="_1-_2" > <di:waypoint x="39" y="115" /> <di:waypoint x="75" y="46" /> <di:waypoint x="136" y="112" /> </bpmndi:BPMNEdge> <bpmndi:BPMNEdge bpmnElement="_2-_3" > <di:waypoint x="136" y="112" /> <di:waypoint x="240" y="240" /> <di:waypoint x="282" y="110" /> </bpmndi:BPMNEdge> </bpmndi:BPMNPlane> </bpmndi:BPMNDiagram></definitions>
可以按如下步骤用BPMN2.0格式创建流程:
用Drools Eclipse插件向导创建一个新的流程文件,在向导的最后一页确保选择了兼容Drools 5.1 代码。新创建的文件中会使用BPMN2.0 XML格式。因为使用了不同的属性名称等,这还不是一个BPMN2.0的编辑器。在保存时需要选择BPMN2.0 语法。需要注意的是编辑器还没有全部支持执行引擎所支持的节点类型与属性。Oryx是一个开源的支持全部BPMN2规范的Web编辑器。我们把它嵌入到Guvnor中,支持BPMN2.0流程的呈现、编辑。可以用独立或者集成的Oryx编辑器创建/编辑BPMN2.0流程再导出成BPMN2.0格式,然后导入到Gunvor中,这样流程就可以运行了。现在正在编写一个支持BPMN2.0全部规范的Eclipse插件。目前还处于开发阶段,只能支持一定数量的构造或属性,创建简单的BPMN2流程没问题。用所带的向导创建BPMN2文件会生成一个扩展名为.bpmn2的文件,及另外一个包含图形信息扩展名为.prd的文件。双击.prd文件就可以打开图形编辑器进行编辑。下面这段代码显示如何向Knowledge base添加BPMN2流程:
private static KnowledgeBase createKnowledgeBase() throws Exception { KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder(); kbuilder.add(ResourceFactory.newClassPathResource("sample.bpmn2"), ResourceType.BPMN2); return kbuilder.newKnowledgeBase(); }
...如何执行该流程:
KnowledgeBase kbase = createKnowledgeBase(); StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession(); ksession.startProcess("com.sample.HelloWorld");
4.2示例
BPMN2.0规范为每个节点类型(或者其他元素)定义了属性与语义。
jbpm-bpmn2模块为每种不同类型的节点提供了许多junit 测试单元。这些可以作为示例使用,虽然它们不表示真实的业务逻辑,但是通过它们可以了解到那些可使用的特性。下面的列显示了其中的一些示例,完成的列表可以在jbpm-bpmn2 模块的src/main/resources 目录下找到(显示)。
4.3支持的元素(elements)与属性(attributes)
表4.1关键字
ElementSupported attributesSupported elementsExtension attributesdefinitions rootElement BPMNDiagram processprocessType isExecutable name idproperty laneSet flowElementpackageNamesequenceFlowsourceRef targetRef isImmediate name idconditionExpressionbendpointsinterfacename idoperation operationname idinMessageRef laneSet lane lanename idflowNodeRef     Events   startEventname iddataOutput dataOutputAssociation outputSet eventDefinitionx y width heightendEventname iddataInput dataInputAssociation inputSet eventDefinitionx y width heightintermediateCatchEventname iddataOutput dataOutputAssociation outputSet eventDefinitionx y width heightintermediateThrowEventname iddataInput dataInputAssociation inputSet eventDefinitionx y width heightboundaryEventcancelActivity attachedToRef name ideventDefinitionx y width heightterminateEventDefinition   cancelEventDefinition   compensateEventDefinitionactivityRefdocumentation extensionElements conditionalEventDefinition condition errorEventDefinitionerrorRef  errorerrorCode id  escalationEventDefinitionescalationRef  escalationescalationCode id  messageEventDefinitionmessageRef  messageitemRef id  signalEventDefinitionsignalRef  timerEventDefinition timeCycle     Activities   taskname idioSpecification dataInputAssociation dataOutputAssociationtaskName x y width heightscriptTaskscriptFormat name idscriptx y width heightscript text[mixed content] userTaskname idioSpecification dataInputAssociation dataOutputAssociation resourceRolex y width heightpotentialOwner resourceAssignmentExpression resourceAssignmentExpression expression businessRuleTaskname id x y width height ruleFlowGroupmanualTaskname id x y width heightsendTaskmessageRef name idioSpecification dataInputAssociationx y width heightreceiveTaskmessageRef name idioSpecification dataOutputAssociationx y width heightserviceTaskoperationRef name idioSpecification dataInputAssociation dataOutputAssociationx y width heightsubProcessname idflowElement property loopCharacteristicsx y width heightadHocSubProcesscancelRemainingInstances name idcompletionCondition flowElement propertyx y width heightcallActivitycalledElement name idioSpecification dataInputAssociation dataOutputAssociationx y width height waitForCompletion independentmultiInstanceLoopCharacteristics loopDataInputRef inputDataItem     Gateways   parallelGatewaygatewayDirection name id x y width heighteventBasedGatewaygatewayDirection name id x y width heightexclusiveGatewaydefault gatewayDirection name id x y width heightinclusiveGatewaydefault gatewayDirection name id x y width height    Data   propertyitemSubjectRef id  dataObjectitemSubjectRef id  itemDefinitionstructureRef id  ioSpecification dataInput dataOutput inputSet outputSet dataInputname id  dataInputAssociation sourceRef targetRef assignment dataOutputname id  dataOutputAssociation sourceRef targetRef assignment inputSet dataInputRefs outputSet dataOutputRefs assignment from to formalExpressionlanguagetext[mixed content]     BPMNDI   BPMNDiagram BPMNPlane BPMNPlanebpmnElementBPMNEdge BPMNShape BPMNShapebpmnElementBounds BPMNEdgebpmnElementwaypoint Boundsx y width height  waypointx y Â
章节5 API
5.1jBPM API
5.1.1Knowledge Base
5.1.2会话(Session)
5.1.3事件(Events)
5.2Knowledge-based API
在与引擎交互之前需要先建立一个会话,与引擎的通信通过会话进行,而会话会引用Knowledge base,需要时可以通过Knowledge base查找流程定义。流程启动时会根据相应的定义创建一个保存状态的流程实例。
譬如,你想写一个处理销售订单的应用程序。首先你需要建立一个或者多个流程来定义如何处理订单。启动程序时你需要创建一个包含这些流程定义的knowledge base,再在该knowledge base 上建立会话,当有新的订单时为就可以为该订单生成一个新的流程实例了。
Knowledge base在程序启动时只需要创建一次,可以跨多个会话进行共享,在创建时可以解析、编译流程定义,也可以动态地更改Knowledge base(可以在运行时添加或者删除流程定义)。
会话在knowledge base上创建,用于执行流程以及与引擎交互。你可以创建一个尽可能轻巧的独立的会话。至于创建多少个会话则完全取决于你自己,一般来说会创建一个会话,应用程序中所有的调用都基于该会话。如果你想要有多个独立的处理单元(比如你想为每一个用户进行一对一的完整的处理),或者从伸缩性考虑,也可以创建多个会话。否则你只需要创建一个包含全部流程定义的knowledge base,然后再建立一个执行所有流程的会话即可。
5.1 jBPM API
用户使用的API与具体的实现类是分开的。公开的API为一般用户提供了充足的功能,在版本之间也尽量保持稳定。专家级用户则依然可以使用内部了类,但是需要充分了解它们的执行过程以及未来可能发生的更改。
如上所述,jBPM API可以用来(1)创建包含流程定义的knowledge base,(2)创建会话,启动流程,发送信号,注册监听器等。
5.1.1 Knowledge Base
首先需要创建的对象是Knowledge Base,该对象包含了需要执行的流程定义。下面这个代码片段显示了如何创建仅包含一个流程定义的Knowledge Base(使用Knowledge构造,从类路径上加载资源)。
KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder(); kbuilder.add(ResourceFactory.newClassPathResource("MyProcess.bpmn"), ResourceType.BPMN2); KnowledgeBase kbase = kbuilder.newKnowledgeBase();
5.1.2会话
接下去创建用来跟引擎交互的会话。下面这个代码片段显示了如何在前一步得到的Knowledge上创建会话,以及根据标识启动流程。
StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession(); ProcessInstance processInstance = ksession.startProcess("com.sample.MyProcess");
ProcessRuntime接口定义了全部的会话方法,如下所示:
/** * Start a new process instance. The process (definition) that should * be used is referenced by the given process id. * * @param processId The id of the process that should be started * @return the ProcessInstance that represents the instance of the process that was started */ ProcessInstance startProcess(String processId); /** * Start a new process instance. The process (definition) that should * be used is referenced by the given process id. Parameters can be passed * to the process instance (as name-value pairs), and these will be set * as variables of the process instance. * * @param processId the id of the process that should be started * @param parameters the process variables that should be set when starting the process instance * @return the ProcessInstance that represents the instance of the process that was started */ ProcessInstance startProcess(String processId, Map<String, Object> parameters); /** * Signals the engine that an event has occurred. The type parameter defines * which type of event and the event parameter can contain additional information * related to the event. All process instances that are listening to this type * of (external) event will be notified. For performance reasons, this type of event * signaling should only be used if one process instance should be able to notify * other process instances. For internal event within one process instance, use the * signalEvent method that also include the processInstanceId of the process instance * in question. * * @param type the type of event * @param event the data associated with this event */ void signalEvent(String type, Object event); /** * Signals the process instance that an event has occurred. The type parameter defines * which type of event and the event parameter can contain additional information * related to the event. All node instances inside the given process instance that * are listening to this type of (internal) event will be notified. Note that the event * will only be processed inside the given process instance. All other process instances * waiting for this type of event will not be notified. * * @param type the type of event * @param event the data associated with this event * @param processInstanceId the id of the process instance that should be signaled */ void signalEvent(String type, Object event, long processInstanceId); /** * Returns a collection of currently active process instances. Note that only process * instances that are currently loaded and active inside the engine will be returned. * When using persistence, it is likely not all running process instances will be loaded * as their state will be stored persistently. It is recommended not to use this * method to collect information about the state of your process instances but to use * a history log for that purpose. * * @return a collection of process instances currently active in the session */ Collection<ProcessInstance> getProcessInstances(); /** * Returns the process instance with the given id. Note that only active process instances * will be returned. If a process instance has been completed already, this method will return * null. * * @param id the id of the process instance * @return the process instance with the given id or null if it cannot be found */ ProcessInstance getProcessInstance(long processInstanceId); /** * Aborts the process instance with the given id. If the process instance has been completed * (or aborted), or the process instance cannot be found, this method will throw an * IllegalArgumentException. * * @param id the id of the process instance */ void abortProcessInstance(long processInstanceId); /** * Returns the WorkItemManager related to this session. This can be used to * register new WorkItemHandlers or to complete (or abort) WorkItems. * * @return the WorkItemManager related to this session */ WorkItemManager getWorkItemManager();
5.1.3 事件
会话提供了注册及移除监听器的方法。可以用ProcessEventListener对象监听流程相关的事件,比如流程的启动与完成,进入节点或者离开节点等。下面列举了ProcessEventListener类上的不同方法。可以从事件对象中访问相关的信息,如流程实例、节点实例。
public interface ProcessEventListener { void beforeProcessStarted( ProcessStartedEvent event ); void afterProcessStarted( ProcessStartedEvent event ); void beforeProcessCompleted( ProcessCompletedEvent event ); void afterProcessCompleted( ProcessCompletedEvent event ); void beforeNodeTriggered( ProcessNodeTriggeredEvent event ); void afterNodeTriggered( ProcessNodeTriggeredEvent event ); void beforeNodeLeft( ProcessNodeLeftEvent event ); void afterNodeLeft( ProcessNodeLeftEvent event ); }
可以用流程监听得到的信息建立审核日志,我们提供了多种默认的日志实现:
Console logger:向控制台输出各种事件File logger:该日志以XML方式将事件写入文件。在IDE中打开该文件可以以树视图的方式查看执行过程中发生的事件。Threaded file logger:因为文件日志只有在日志关闭或者需要记录的事件数达到指定量时才会向磁盘写入,所以无法用于运行期调试。线程化的文件日志则会在指定时间片之后将事件写入文件,这样就可以在调试时准实时的显示进度。调用KnowledgeRuntimeLoggerFactory对象可以向会话中添加日志,创建控制台日志需要将会话对象以参数形式传入,文件日志则需要指定日志文件名,而线程化文件日志需要指定时间间隔(以毫秒为单位)。
KnowledgeRuntimeLogger logger = KnowledgeRuntimeLoggerFactory.newFileLogger( ksession, "test" ); // add invocations to the process engine here, // e.g. ksession.startProcess(processId); ... logger.close();
日志文件可以在Eclipse中用Drools插件打开,在审计视图中以树状方式显示。如下图所示:
5.2 Knowledge-base API
或许你已经注意到了,jBPM项目公开的API称为Knowledge API,也就是意味着它不光是聚焦在流程上,潜在地允许加载其他类型的knowledge。事实上对流程感兴趣的用户仅有小部分。对你而言就是用KnowledgeBase及KnowledgeSession代替ProcessBase及ProcessSession。
如果你计划在应用程序中使用业务规则或者复杂的事件处理,knowledge-based API可以让用户向相同的knowledge中添加不同的资源,如流程、规则等。这样熟悉jBPM的用户也可以使用Drools Expert(用于处理业务规则)或者Drools Fusion(用于事件处理)等由knowledge统一的不同类型的工具。