camunda工作流(二)BPMN 模型API

camunda bpmn方式使用操作手册指南,对比官方文档及翻译和自己理解

一、导入已有模板信息

1、通过已有模型进行导入,导入方式如下两种
1.1、从指定文件路径获取
1.2、根据流信息获取
2、导入模型和可以按照ID或者元素类型搜索元素
2.1、按照id搜索
2.2、按照类型搜索
3、对于每个元素实例都可以读取和编辑属性值,可以通过程序方法或者XML模型API来完成操作

// 方式一:read a model from a file
File file = new File("PATH/TO/MODEL.bpmn");
BpmnModelInstance modelInstance = Bpmn.readModelFromFile(file);

// 方式二 read a model from a stream
InputStream stream = [...]
BpmnModelInstance modelInstance = Bpmn.readModelFromStream(stream);

// find element instance by ID
StartEvent start = (StartEvent) modelInstance.getModelElementById("start");
// find all elements of the type task
ModelElementType taskType = modelInstance.getModel().getType(Task.class);
Collection taskInstances = modelInstance.getModelElementsByType(taskType);

StartEvent start = (StartEvent) modelInstance.getModelElementById("start");
// read attributes by helper methods
String id = start.getId();
String name = start.getName();

// edit attributes by helper methods
start.setId("new-id");
start.setName("new name");

// read attributes by generic XML model API (with optional namespace)
String custom1 = start.getAttributeValue("custom-attribute");
String custom2 = start.getAttributeValueNs("custom-attribute-2", "http://camunda.org/custom");

// edit attributes by generic XML model API (with optional namespace)
start.setAttributeValue("custom-attribute", "new value");
start.setAttributeValueNs("custom-attribute", "http://camunda.org/custom", "new value");

4、BPMN模型简单过程示例



  
    
      start-task1
    
    
      start-task1
      task1-end
    
    
      task1-end
    
    
    
  

5、通过BPMN模型API来获取ID为start-task1的序列流的源流和目标节点 简单模型如上

// read bpmn model from file
BpmnModelInstance modelInstance = Bpmn.readModelFromFile(new File("/PATH/TO/MODEL.bpmn"));
// find sequence flow by id
SequenceFlow sequenceFlow = (SequenceFlow) modelInstance.getModelElementById("start-task1");
// get the source and target element
FlowNode source = sequenceFlow.getSource();
FlowNode target = sequenceFlow.getTarget();

// get all outgoing sequence flows of the source
Collection outgoing = source.getOutgoing();
assert(outgoing.contains(sequenceFlow));


通过上述参考可以创建不同的辅助模块;
样例:使用以下方法去获取后续的节点信息

public Collection getFlowingFlowNodes(FlowNode node) {
  Collection followingFlowNodes = new ArrayList();
  for (SequenceFlow sequenceFlow : node.getOutgoing()) {
    followingFlowNodes.add(sequenceFlow.getTarget());
  }
  return followingFlowNodes;
}

二、新建流程模型

2.1、模型相关操作
2.1.1、创建模型
2.1.2、创建BPMN定义元素,设置目标名称空间,并将其添加到新创建的模型实例中
2.1.3、向模型新增流程:①、创建BPMN实例;②、设置元素实例的属性和子元素; ③、将新创建的元素实例添加到对应的父元素;
2.1.4、简化操作

// 1、创建模型
BpmnModelInstance modelInstance = Bpmn.createEmptyModel();

//2、创建BPMN定义元素,设置目标名称空间,并将其添加到新创建的模型实例中
Definitions definitions = modelInstance.newInstance(Definitions.class);
definitions.setTargetNamespace("http://camunda.org/examples");
modelInstance.setDefinitions(definitions);

// 3、通常 向模型新增流程,需要以下三个步骤
// ①、创建BPMN实例;②、设置元素实例的属性和子元素; ③、将新创建的元素实例添加到对应的父元素;
Process process = modelInstance.newInstance(Process.class);
process.setId("process");
definitions.addChildElement(process);

//为了简化以上方法  可以通过下面方法
protected  T createElement(BpmnModelElementInstance parentElement, String id, Class elementClass) {
  T element = modelInstance.newInstance(elementClass);
  element.setAttributeValue("id", id, true);
  parentElement.addChildElement(element);
  return element;
}

2.2、将事件、任务等连接起来 可以优化按照以下方法以创建
原文: After you created the elements of your process like start event, tasks, gateways and end event, you have to connect the elements with sequence flows. Again, this follows the same 3 steps of element creation and can be simplified by the following helper method.

/ 如下参考,将事件、任务等连接起来  可以优化以下方法以创建
// After you created the elements of your process like start event, tasks, gateways and end event, you have to connect the elements with sequence flows. 
// Again, this follows the same 3 steps of element creation and can be simplified by the following helper method.
public SequenceFlow createSequenceFlow(Process process, FlowNode from, FlowNode to) {
  String identifier = from.getId() + "-" + to.getId();
  SequenceFlow sequenceFlow = createElement(process, identifier, SequenceFlow.class);
  process.addChildElement(sequenceFlow);
  sequenceFlow.setSource(from);
  from.getOutgoing().add(sequenceFlow);
  sequenceFlow.setTarget(to);
  to.getIncoming().add(sequenceFlow);
  return sequenceFlow;
}

2.3、根据 BPMN 2.0 规范验证模型并将其转换为 XML 字符串或将其保存到文件或流中
原文:Validate the model against the BPMN 2.0 specification and convert it to an XML string or save it to a file or stream.

// validate the model
Bpmn.validateModel(modelInstance);

// convert to string
String xmlString = Bpmn.convertToString(modelInstance);

// write to output stream
OutputStream outputStream = new OutputStream(...);
Bpmn.writeModelToStream(outputStream, modelInstance);

// write to file
File file = new File(...);
Bpmn.writeModelToFile(file, modelInstance);

2.4、使用一个用户任务创建一个简单的过程


camunda工作流(二)BPMN 模型API_第1张图片
简单流程
// create an empty model
BpmnModelInstance modelInstance = Bpmn.createEmptyModel();
Definitions definitions = modelInstance.newInstance(Definitions.class);
definitions.setTargetNamespace("http://camunda.org/examples");
modelInstance.setDefinitions(definitions);

// create the process
Process process = createElement(definitions, "process-with-one-task", Process.class);

// create start event, user task and end event
StartEvent startEvent = createElement(process, "start", StartEvent.class);
UserTask task1 = createElement(process, "task1", UserTask.class);
task1.setName("User Task");
EndEvent endEvent = createElement(process, "end", EndEvent.class);

// create the connections between the elements
createSequenceFlow(process, startEvent, task1);
createSequenceFlow(process, task1, endEvent);

// validate and write model to file
Bpmn.validateModel(modelInstance);
File file = File.createTempFile("bpmn-model-api-", ".bpmn");
Bpmn.writeModelToFile(file, modelInstance);

2.5、创建具有两个并行任务的简单流程


camunda工作流(二)BPMN 模型API_第2张图片
创建具有两个并行任务的简单流程
// create an empty model
BpmnModelInstance modelInstance = Bpmn.createEmptyModel();
Definitions definitions = modelInstance.newInstance(Definitions.class);
definitions.setTargetNamespace("http://camunda.org/examples");
modelInstance.setDefinitions(definitions);

// create elements
StartEvent startEvent = createElement(process, "start", StartEvent.class);
ParallelGateway fork = createElement(process, "fork", ParallelGateway.class);
ServiceTask task1 = createElement(process, "task1", ServiceTask.class);
task1.setName("Service Task");
UserTask task2 = createElement(process, "task2", UserTask.class);
task2.setName("User Task");
ParallelGateway join = createElement(process, "join", ParallelGateway.class);
EndEvent endEvent = createElement(process, "end", EndEvent.class);

// create flows
createSequenceFlow(process, startEvent, fork);
createSequenceFlow(process, fork, task1);
createSequenceFlow(process, fork, task2);
createSequenceFlow(process, task1, join);
createSequenceFlow(process, task2, join);
createSequenceFlow(process, join, endEvent);

// validate and write model to file
Bpmn.validateModel(modelInstance);
File file = File.createTempFile("bpmn-model-api-", ".bpmn");
Bpmn.writeModelToFile(file, modelInstance);

三、Delegation Code

谷歌翻译: 授权码 如果使用委派代码,则可以访问BPMN模型实例和已执行流程的当前元素。如果访问了BPMN模型,将对其进行缓存以避免冗余的数据库查询。

// Java Delegate
// 如果您的类实现了该org.camunda.bpm.engine.delegate.JavaDelegate接口,则可以访问BPMN模型实例和当前流元素。在下面的示例中,将JavaDelegate其添加到BPMN模型中的服务任务中。因此,返回的流量元素可以转换为ServiceTask。
public class ExampleServiceTask implements JavaDelegate {
  public void execute(DelegateExecution execution) throws Exception {
    BpmnModelInstance modelInstance = execution.getBpmnModelInstance();
    ServiceTask serviceTask = (ServiceTask) execution.getBpmnModelElementInstance();
  }
}

// Execution Listener
// 如果您的类实现了该org.camunda.bpm.engine.delegate.ExecutionListener接口,则可以访问BPMN模型实例和当前流元素。由于可以将执行侦听器添加到流程,事件,任务,网关和序列流等多个元素,因此无法保证流元素将是哪种类型。
public class ExampleExecutionListener implements ExecutionListener {
  public void notify(DelegateExecution execution) throws Exception {
    BpmnModelInstance modelInstance = execution.getBpmnModelInstance();
    FlowElement flowElement = execution.getBpmnModelElementInstance();
  }
}

// Task Listene
// 如果您的类实现了该org.camunda.bpm.engine.delegate.TaskListener接口,则由于只能将任务侦听器添加到用户任务,因此可以访问BPMN模型实例和当前用户任务。
public class ExampleTaskListener implements TaskListener {
  public void notify(DelegateTask delegateTask) {
    BpmnModelInstance modelInstance = delegateTask.getBpmnModelInstance();
    UserTask userTask = delegateTask.getBpmnModelElementInstance();
  }
}

四、Fluent Builder API 创建简单BPMN流程

Fluent Builder API 提供以下基本元素(翻译误差自行理解)

process 处理

start event 开始事件

exclusive gateway 专用网关

parallel gateway 并行网关

script task 脚本任务

service task 服务任务

user task 用户任务

signal event definition 信号事件定义

end event 结束时间

subprocess 子过程

4.1、创建用户任务的简单过程

BpmnModelInstance modelInstance = Bpmn.createProcess()
  .startEvent()
  .userTask()
  .endEvent()
  .done();

4.2、为上述 设置进程的名称并将其标记为可执行文件,并为用户任务指定一个名称

BpmnModelInstance modelInstance = Bpmn.createProcess()
    .name("Example process")
    .executable()
  .startEvent()
  .userTask()
    .name("Some work to do")
  .endEvent()
  .done();

4.3、并行和分支执行路径:在顺序的基础上添加一个并行或者互斥网关,并为第一个路径建模,直到结束事件或另一个网关。然后,调用该moveToLastGateway()方法返回上一个网关,并对下一个路径建模。
原文说明:Just add a parallel or exclusive gateway and model the first path till an end event or another gateway. After that, call the moveToLastGateway() method and you return to the last gateway and can model the next path.

// 此示例在开始事件之后使用用户任务对流程进行建模,然后对具有两个并行传出执行路径的并行网关执行并行处理,每个执行路径均具有任务和结束事件。
// 原文: This example models a process with a user task after the start event followed by a parallel gateway with two parallel outgoing execution paths, each with a task and an end event.
BpmnModelInstance modelInstance = Bpmn.createProcess()
  .startEvent()
  .userTask()
  .parallelGateway()
    .scriptTask()
    .endEvent()
  .moveToLastGateway()
    .serviceTask()
    .endEvent()
  .done();

4.4、使用 Fluent Builder API 为指定网关添加条件:condition() 方法,使用恰当标签和表达式

原文:Normally you want to add conditions on outgoing flows of an exclusive gateway which is also simple with the fluent builder API. Just use the method condition() and give it a label and an expression.

BpmnModelInstance modelInstance = Bpmn.createProcess()
  .startEvent()
  .userTask()
  .exclusiveGateway()
  .name("What to do next?")
    .condition("Call an agent", "#{action = 'call'}")
    .scriptTask()
    .endEvent()
  .moveToLastGateway()
    .condition("Create a task", "#{action = 'task'}")
    .serviceTask()
    .endEvent()
  .done();

4.5、当前工作流节点前有多个节点信息,需要使用方法 moveToLastGateway() 跳转到上一个节点,则必须要使用 moveToNode() 并且传入id进行分支跳转;为了防止循环,使用connectTo(elementId)方法

原文:If you want to use the moveToLastGateway() method but have multiple incoming sequence flows at your current position, you have to use the generic moveToNode method with the id of the gateway. This could for example happen if you add a join gateway to your process. For this purpose and for loops, we added the connectTo(elementId) method.

/**
* 以下示例创建了具有三个并行执行的路径,这些路径均加入第二个网关;第一个moveToNode()方法不是必须的,因为此时加入的网关只有一个进入的序列流,使用是为了保持一致性
* This example creates a process with three parallel execution paths which all join in the second gateway. Notice that the first call of moveToNode is not necessary,
* because at this point the joining gateway only has one incoming sequence flow, but was used for consistency.
*/
BpmnModelInstance modelInstance = Bpmn.createProcess()
  .startEvent()
  .userTask()
  .parallelGateway("fork")
    .serviceTask()
    .parallelGateway("join")
  .moveToNode("fork")
    .userTask()
    .connectTo("join")
  .moveToNode("fork")
    .scriptTask()
    .connectTo("join")
  .endEvent()
  .done()
// 本示例在第二个执行路径中创建带有循环回调的并行网关
// this example creates a parallel gateway with a feedback loop in the second execution path.
BpmnModelInstance modelInstance = Bpmn.createProcess()
  .startEvent()
  .userTask()
  .id("question")
  .exclusiveGateway()
  .name("Everything fine?")
    .condition("yes", "#{fine}")
    .serviceTask()
    .userTask()
    .endEvent()
  .moveToLastGateway()
    .condition("no", "#{!fine}")
    .userTask()
    .connectTo("question")
  .done()

4.6、使用fluent builder API创建嵌入式子流程

// directly add it to your process building or you can detach it and create flow elements of the subprocess later on.
// Directly define the subprocess
BpmnModelInstance modelInstance = Bpmn.createProcess()
  .startEvent()
  .subProcess()
    .camundaAsync()
    .embeddedSubProcess()
      .startEvent()
      .userTask()
      .endEvent()
    .subProcessDone()
  .serviceTask()
  .endEvent()
  .done();

// Detach the subprocess building
modelInstance = Bpmn.createProcess()
  .startEvent()
  .subProcess("subProcess")
  .serviceTask()
  .endEvent()
  .done();

SubProcess subProcess = (SubProcess) modelInstance.getModelElementById("subProcess");
subProcess.builder()
  .camundaAsync()
  .embeddedSubProcess()
    .startEvent()
    .userTask()
    .endEvent();

4.7、创建一个抛出信号事件定义并定义该信号将包含的有效负载

原文: The example below shows how to create a throwing signal event definition and define the payload that this signal will contain. By using the camundaIn methods, it is possible to define which process variables will be included in the signal payload, define an expression that will be resolved in the signal-catching process instances, or declare that all of the process variables in the signal-throwing process instance should be passed. It is also possible to define a business key that will be assigned to the signal-catching process instances.

BpmnModelInstance modelInstance = Bpmn.createProcess()
  .startEvent()
  .intermediateThrowEvent("throw")
    .signalEventDefinition("signal")
      .camundaInSourceTarget("source", "target1")
      .camundaInSourceExpressionTarget("${'sourceExpression'}", "target2")
      .camundaInAllVariables("all", true)
      .camundaInBusinessKey("aBusinessKey")
      .throwEventDefinitionDone()
  .endEvent()
  .done();

五、fluent builder API扩展流程

5.1、通过继承存在的流程进行扩展

/**
* 每一个新建的任务都需要执行 一个 gateway 其id为 "gateway"
* imagine a process containing a parallel gateway with the id gateway. You now want to add another execution path to it for a new service task which has to be executed every time.
*/
BpmnModelInstance modelInstance = Bpmn.readModelFromFile(new File("PATH/TO/MODEL.bpmn"));
ParallelGateway gateway = (ParallelGateway) modelInstance.getModelElementById("gateway");

gateway.builder()
  .serviceTask()
    .name("New task")
  .endEvent();


/**
*
* 在现有元素之间插入新任务。想象一个包含用户任务的进程,该任务的ID是 "task1"。现在,您要在这两者之间添加脚本任务和用户任务。
*  Another use case is to insert new tasks between existing elements. Imagine a process containing a user task with the id task1 which is 
* followed by a service task. And now you want to add a script task and a user task between these two.
*/
BpmnModelInstance modelInstance = Bpmn.readModelFromFile(new File("PATH/TO/MODEL.bpmn"));
UserTask userTask = (UserTask) modelInstance.getModelElementById("task1");
SequenceFlow outgoingSequenceFlow = userTask.getOutgoing().iterator().next();
FlowNode serviceTask = outgoingSequenceFlow.getTarget();
userTask.getOutgoing().remove(outgoingSequenceFlow);

userTask.builder()
  .scriptTask()
  .userTask()
  .connectTo(serviceTask.getId());

5.2、生成图信息

/**
* 为了呈现过程,BPMN图元素是必需的。流利的生成器将生成BPMN形状和BPMN边缘,并自动将它们放置在流节点和序列流中。
* To render the process, BPMN diagram elements are necessary. The fluent builder generates BPMN Shapes and BPMN Edges and places them automatically for flow nodes and sequence flows.
*/
final BpmnModelInstance myProcess = Bpmn.createExecutableProcess("process-payments")
      .startEvent()
      .serviceTask()
          .name("Process Payment")
      .endEvent()
      .done();
    
System.out.println(Bpmn.convertToString(myProcess));


/**
* 此示例创建一个BPMN,其中包含语义元素(例如,服务任务等)和图元素:
* This example creates a BPMN containing both semantic elements (e.g., service task etc.) and diagram elements:
*/



  
  
    
      sequenceFlow_b1eec5b5-889d-4e75-854d-59768fbdc8a2
    
    
      sequenceFlow_b1eec5b5-889d-4e75-854d-59768fbdc8a2
      sequenceFlow_5839394a-c0c2-4a5b-aa81-9412f169cc35
    
    
    
      sequenceFlow_5839394a-c0c2-4a5b-aa81-9412f169cc35
    
    
  

  
  
    
      
        
      
      
        
      
      
        
        
      
      
        
      
      
        
        
      
    
  

默认行为是将每个新添加的流元素放置在前一个流元素旁边。

将流元素添加到嵌入式子流程时,当达到子流程边界时,将调整子流程的大小。因此,建议首先将所有新元素添加到子流程中,然后再创建以下元素。否则,可能导致图中的元素重叠。
网关的分支位于另一个分支之下。没有提供自动布局,因此不同分支的元素可能会重叠。
原文:
The default behavior is that each newly added flow element will be placed next to the previous flow element.
When flow elements are added to an embedded subprocess, then the subprocess is resized when the subprocess border is reached. Therefore, it is recommended to first add all new elements to the subprocess and to then create the following elements. Otherwise it could lead to overlapping elements in the diagram.Branches of gateways are placed one below the other. Auto layout is not provided, therefore the elements of different branches may overlap.

六、Repository Service

可以通过流程定义的ID访问BPMN流程实例,以下是不完整代码,参考 generate-jsf-form 获取完整实例
原文:It is also possible to access the BPMN model instance by the process definition id using the Repository Service, as the following incomplete test sample code shows. Please see the generate-jsf-form quickstart for a complete example.

public void testRepositoryService() {
  runtimeService.startProcessInstanceByKey(PROCESS_KEY);
  String processDefinitionId = repositoryService.createProcessDefinitionQuery()
    .processDefinitionKey(PROCESS_KEY).singleResult().getId();
  BpmnModelInstance modelInstance = repositoryService.getBpmnModelInstance(processDefinitionId);
}

七、Extension Elements 扩展元素

定制扩展元素是扩展BPMN模型的标准化方法。该Camunda扩展元素的BPMN模型API的全面实施,但未知扩展元素也可以很容易地访问和添加。
每个BPMN BaseElement可以具有类型的子元素extensionElements。该元素可以包含各种扩展元素。要访问扩展元素,您必须调用getExtensionElements()方法,如果不存在此类子元素,则必须先创建一个。
原文:
Custom extension elements are a standardized way to extend the BPMN model. The Camunda extension elements are fully implemented in the BPMN model API, but unknown extension elements can also easily be accessed and added.
Every BPMN BaseElement can have a child element of the type extensionElements. This element can contain all sorts of extension elements. To access the extension elements you have to call the getExtensionElements() method and, if no such child element exists, you must create one first.

StartEvent startEvent = modelInstance.newInstance(StartEvent.class);
ExtensionElements extensionElements = startEvent.getExtensionElements();
if (extensionElements == null) {
  extensionElements = modelInstance.newInstance(ExtensionElements.class);
  startEvent.setExtensionElements(extensionElements);
}
Collection elements = extensionElements.getElements();

//向集合添加或删除扩展元素。
CamundaFormData formData = modelInstance.newInstance(CamundaFormData.class);
extensionElements.getElements().add(formData);
extensionElements.getElements().remove(formData);
//访问类似查询的界面来过滤扩展元素。
extensionElements.getElementsQuery().count();
extensionElements.getElementsQuery().list();
extensionElements.getElementsQuery().singleResult();
extensionElements.getElementsQuery().filterByType(CamundaFormData.class).singleResult();
//此外,还有一些添加新扩展元素的快捷方式。您可以使用namespaceUri和elementName来添加自己的扩展元素。或者,您可以使用class已知扩展元素类型的,
//例如camunda扩展元素。将扩展元素添加到BPMN元素并返回,以便您可以设置属性或添加子元素。
ModelElementInstance element = extensionElements.addExtensionElement("http://example.com/bpmn", "myExtensionElement");
CamundaExecutionListener listener = extensionElements.addExtensionElement(CamundaExecutionListener.class);
// fluent builder API还存在另一个帮助程序方法,该方法允许您添加预先定义的扩展元素。
CamundaExecutionListener camundaExecutionListener = modelInstance.newInstance(CamundaExecutionListener.class);
camundaExecutionListener.setCamundaClass("org.camunda.bpm.MyJavaDelegte");
startEvent.builder()
  .addExtensionElement(camundaExecutionListener);

翻译原创不易,欢迎支出文中错误,欢迎转载-请附上原文链接,谢谢!

你可能感兴趣的:(camunda工作流(二)BPMN 模型API)