Generating bpmndi for Model API processes

Hello there folks,

Can i get some suggestions on how to i might go about generating the bpmndi section of a process model created using Camunda’s Model API?

Interested in what options are available and if some are better than others.

Hi Niall, this example roughly works (you can open the model in the modeller):

public class ProcessGenerator {

  public BpmnModelInstance modelInstance;

  public static void main(String[] args) throws IOException {
    ProcessGenerator createProcess = new ProcessGenerator();
    createProcess.generateProcess();
  }

  public void generateProcess() throws IOException {
    modelInstance = Bpmn.createEmptyModel();
    Definitions definitions = modelInstance.newInstance(Definitions.class);
    definitions.setTargetNamespace("http://camunda.org/examples");
    modelInstance.setDefinitions(definitions);

    // create the process
    org.camunda.bpm.model.bpmn.instance.Process process = modelInstance.newInstance(Process.class);
    process.setAttributeValue("id", "process-one-task", true);
    definitions.addChildElement(process);

    BpmnDiagram diagram = modelInstance.newInstance(BpmnDiagram.class);
    BpmnPlane plane = modelInstance.newInstance(BpmnPlane.class);
    plane.setBpmnElement(process);
    diagram.setBpmnPlane(plane);
    definitions.addChildElement(diagram);
    
    // create start event, user task and end event
    StartEvent startEvent = createElement(process, "start", "Di generation wanted", 
        StartEvent.class, plane, 15, 15, 50, 50, true);
    
    UserTask userTask = createElement(process, "userTask", "Generate Model with DI", 
        UserTask.class, plane, 100, 0, 80, 100, false);
    
    createSequenceFlow(process, startEvent, userTask, plane, 65, 40, 100, 40);
    
    EndEvent endEvent = createElement(process, "end", "DI generation completed", 
        EndEvent.class, plane, 250, 15, 50, 50, true);
    
    createSequenceFlow(process, userTask, endEvent, plane, 200, 40, 250, 40);
    
    // validate and write model to file
    Bpmn.validateModel(modelInstance);
    File file = File.createTempFile("bpmn-model-api-", ".bpmn");
    Bpmn.writeModelToFile(file, modelInstance);  
    
  }

  protected <T extends BpmnModelElementInstance> T createElement(BpmnModelElementInstance parentElement, 
      String id, String name, Class<T> elementClass, BpmnPlane plane, 
      double x, double y, double heigth, double width, boolean withLabel) {
    T element = modelInstance.newInstance(elementClass);
    element.setAttributeValue("id", id, true);
    element.setAttributeValue("name", name, false);
    parentElement.addChildElement(element);
    
    BpmnShape bpmnShape = modelInstance.newInstance(BpmnShape.class);
    bpmnShape.setBpmnElement((BaseElement) element);
    
    Bounds bounds = modelInstance.newInstance(Bounds.class);
    bounds.setX(x);
    bounds.setY(y);
    bounds.setHeight(heigth);
    bounds.setWidth(width);
    bpmnShape.setBounds(bounds);
    
    if (withLabel) {
      BpmnLabel bpmnLabel = modelInstance.newInstance(BpmnLabel.class);
      Bounds labelBounds = modelInstance.newInstance(Bounds.class);
      labelBounds.setX(x);
      labelBounds.setY(y + heigth);
      labelBounds.setHeight(heigth);
      labelBounds.setWidth(width);
      bpmnLabel.addChildElement(labelBounds);
      bpmnShape.addChildElement(bpmnLabel);
    }
    plane.addChildElement(bpmnShape);
    
    return element;
  }
  
  public SequenceFlow createSequenceFlow(org.camunda.bpm.model.bpmn.instance.Process process, FlowNode from, FlowNode to, BpmnPlane plane,
      int... waypoints) {
    String identifier = from.getId() + "-" + to.getId();
    SequenceFlow sequenceFlow = modelInstance.newInstance(SequenceFlow.class);
    sequenceFlow.setAttributeValue("id", identifier, true);
    process.addChildElement(sequenceFlow);
    sequenceFlow.setSource(from);
    from.getOutgoing().add(sequenceFlow);
    sequenceFlow.setTarget(to);
    to.getIncoming().add(sequenceFlow);
    
    BpmnEdge bpmnEdge = modelInstance.newInstance(BpmnEdge.class);
    bpmnEdge.setBpmnElement(sequenceFlow);
    for (int i = 0; i < waypoints.length / 2; i++) {
      double waypointX = waypoints[i*2];
      double waypointY = waypoints[i*2+1];
      Waypoint wp = modelInstance.newInstance(Waypoint.class);
      wp.setX(waypointX);
      wp.setY(waypointY);
      bpmnEdge.addChildElement(wp);
    }
    plane.addChildElement(bpmnEdge);
    
    return sequenceFlow;
  }
  
}

I think it’s worth to mention the DI generation in the UserGuide.

Cheers, Ingo

Here is the complete class with imports… (as text file)

ProcessGenerator.txt (5.3 KB)

1 Like

This gives following on Bpmn.writeModelToFile:

org.xml.sax.SAXParseException; cvc-complex-type.2.4.a: Invalid content was found starting with element ‘bpmndi:BPMNLabel’. One of ‘{“http://www.omg.org/spec/DD/20100524/DI”:extension, “http://www.omg.org/spec/DD/20100524/DC”:Bounds}’ is expected.

My mistake. I was missing the bpmnShape.setBounds call to set x,y and width, height.

@gchristiansen
Do you know if there is a way to arrange an element in the plane in a “dynamic way”? I mean without setting the position manually but automatically…
Instead of putting coordinates manually, like piutting int values manually, I want the program understand that each element, must be distant from the previous one for about a certain measure… can it be possible?

Here below I post my code:

import org.camunda.bpm.model.bpmn.Bpmn;
import org.camunda.bpm.model.bpmn.BpmnModelInstance;
import org.camunda.bpm.model.bpmn.instance.BpmnModelElementInstance;
import org.camunda.bpm.model.bpmn.instance.Definitions;
import org.camunda.bpm.model.bpmn.instance.EndEvent;
import org.camunda.bpm.model.bpmn.instance.FlowNode;
import org.camunda.bpm.model.bpmn.instance.ParallelGateway;
import org.camunda.bpm.model.bpmn.instance.ServiceTask;
import org.camunda.bpm.model.bpmn.instance.StartEvent;
import org.camunda.bpm.model.bpmn.instance.UserTask;
import org.camunda.bpm.model.bpmn.instance.Process;
import org.camunda.bpm.model.bpmn.instance.SequenceFlow;

import java.io.File;
import java.io.IOException;

import org.camunda.bpm.*;

public class TextBPM {

public static void main(String[] args) throws IOException {

	HelpClass helpClass = new HelpClass();
	helpClass.create();

	// create the elements

	StartEvent startEvent = helpClass.createElement(helpClass.process, "start", "Start label", StartEvent.class,
			helpClass.plane, 15, 15, 50, 50, true);

	UserTask task = helpClass.createElement(helpClass.process, "userTask", "Task label", UserTask.class,
			helpClass.plane, 100, 0, 80, 100, false);

	EndEvent endEvent = helpClass.createElement(helpClass.process, "end", "End label", EndEvent.class,
			helpClass.plane, 250, 15, 50, 50, true);

	// create the connections between the elements

	helpClass.createSequenceFlow(helpClass.process, startEvent, task, helpClass.plane, 65, 40, 100, 40);
	helpClass.createSequenceFlow(helpClass.process, task, endEvent, helpClass.plane, 200, 40, 250, 40);

	// validate and write model to file

	Bpmn.validateModel(helpClass.modelInstance);
	File file = File.createTempFile("bpmn-model-api-", ".bpmn");
	Bpmn.writeModelToFile(file, helpClass.modelInstance);

}

}

See the creation of the task in my class. As you can see, i had to set the parameter manually to locate it in the plane. I would like to locate it automatically and tell the program something like: "let’s put the task element after the startEvent element but distant about a tot. and before the endEvent element distant about a tot.
Is that clear?
Is it possible?
Thanks.

Hi @Niall / @Ingo_Richtsmeier ,
Can I get JavaScript for the same ?

I’m using Camunda 7.18 and Modeler 5.7.

Hi @vinothkumar,

maybe here: bpmn-js-examples/modeling-api at master · bpmn-io/bpmn-js-examples · GitHub

Hope this helps, Ingo