imixs / open-bpmn

BPMN Modeler based on Eclipse Graphical Language Server Platform
https://www.open-bpmn.org
Other
55 stars 11 forks source link

Parsing of the xml ignores how the XML namespace are defined #345

Open paule96 opened 2 weeks ago

paule96 commented 2 weeks ago

If you have as an default XML namespace in your bpmn model the bpmn namespace ('xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"') you can't open the document anymore, because you will get the following error:

Invalid BPMN File: Missing root element 'bpmn2:definitions'

There error is from here:

https://github.com/imixs/open-bpmn/blob/48a21aeebc511a39a69b55e8d02eeba8d5605528/open-bpmn.metamodel/src/main/java/org/openbpmn/bpmn/util/BPMNModelFactory.java#L169C13-L179C14

That's is a problem if a 3rd party software generates an bpmn compliant XML and you try to view / debug it with open-bpmn.

Example of a namespace definition with no bpmn2: prefix

<definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="test" expressionLanguage="C# Lambda"
  xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL">
rsoika commented 2 weeks ago

Hi @paule96 , yes of course this should not happen. Can you provide a demo.bpmn file to reproduce the bug? Then I will create a junit test to cover this issue - and hopefully fix the issuer too.

paule96 commented 2 weeks ago

important note for the following XML: This XML will produce a diagram where all elements are on the same position, because I didn't bothered yet to set x/y locations for all diagram elements.

<?xml version="1.0" encoding="utf-8"?>
<definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="test" expressionLanguage="C# Lambda" xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL">
  <process id="G7659d650c6ba41ff82bfb5f2ffe75eaf" name="Test" processType="Public">
    <startEvent id="Start_Event" name="Start">
      <q1:outgoing xmlns="" xmlns:q1="http://www.omg.org/spec/BPMN/20100524/MODEL">G94506efc9c90484c94f76ab9d865c3f3</q1:outgoing>
    </startEvent>
    <task id="Task_1" name="Task 1">
      <q2:incoming xmlns="" xmlns:q2="http://www.omg.org/spec/BPMN/20100524/MODEL">G94506efc9c90484c94f76ab9d865c3f3</q2:incoming>
      <q3:outgoing xmlns="" xmlns:q3="http://www.omg.org/spec/BPMN/20100524/MODEL">G3c32d4362fb34202afdd71d832bea159</q3:outgoing>
    </task>
    <sequenceFlow id="G94506efc9c90484c94f76ab9d865c3f3" sourceRef="Start_Event" targetRef="Task_1" />
    <sequenceFlow id="G3c32d4362fb34202afdd71d832bea159" sourceRef="Task_1" targetRef="Gf17b6be0df1a4474ade00c089b0af2a4" />
    <parallelGateway id="Gf17b6be0df1a4474ade00c089b0af2a4" name="Parallel Gateway 1" gatewayDirection="Diverging">
      <q4:incoming xmlns="" xmlns:q4="http://www.omg.org/spec/BPMN/20100524/MODEL">G3c32d4362fb34202afdd71d832bea159</q4:incoming>
      <q5:outgoing xmlns="" xmlns:q5="http://www.omg.org/spec/BPMN/20100524/MODEL">G1368eaa247794e79aba99e78faa68b43</q5:outgoing>
      <q6:outgoing xmlns="" xmlns:q6="http://www.omg.org/spec/BPMN/20100524/MODEL">G690fc96a505e46ac8e1bb836e91a68f7</q6:outgoing>
    </parallelGateway>
    <sequenceFlow id="G1368eaa247794e79aba99e78faa68b43" sourceRef="Gf17b6be0df1a4474ade00c089b0af2a4" targetRef="Task_2" />
    <task id="Task_2" name="Task 2">
      <q7:incoming xmlns="" xmlns:q7="http://www.omg.org/spec/BPMN/20100524/MODEL">G1368eaa247794e79aba99e78faa68b43</q7:incoming>
      <q8:outgoing xmlns="" xmlns:q8="http://www.omg.org/spec/BPMN/20100524/MODEL">G6f4b004c57ff4fd69a2e8d47969a1cd4</q8:outgoing>
    </task>
    <sequenceFlow id="G690fc96a505e46ac8e1bb836e91a68f7" sourceRef="Gf17b6be0df1a4474ade00c089b0af2a4" targetRef="Task_3" />
    <task id="Task_3" name="Task 3">
      <q9:incoming xmlns="" xmlns:q9="http://www.omg.org/spec/BPMN/20100524/MODEL">G690fc96a505e46ac8e1bb836e91a68f7</q9:incoming>
      <q10:outgoing xmlns="" xmlns:q10="http://www.omg.org/spec/BPMN/20100524/MODEL">Gd008894c897342c7a2df989d8512e647</q10:outgoing>
    </task>
    <sequenceFlow id="G6f4b004c57ff4fd69a2e8d47969a1cd4" sourceRef="Task_2" targetRef="G83238fcdcfca426183c01bd67b23e21d" />
    <sequenceFlow id="Gd008894c897342c7a2df989d8512e647" sourceRef="Task_3" targetRef="G83238fcdcfca426183c01bd67b23e21d" />
    <parallelGateway id="G83238fcdcfca426183c01bd67b23e21d" name="Parallel Gateway 2" gatewayDirection="Converging">
      <q11:incoming xmlns="" xmlns:q11="http://www.omg.org/spec/BPMN/20100524/MODEL">G6f4b004c57ff4fd69a2e8d47969a1cd4</q11:incoming>
      <q12:incoming xmlns="" xmlns:q12="http://www.omg.org/spec/BPMN/20100524/MODEL">Gd008894c897342c7a2df989d8512e647</q12:incoming>
      <q13:outgoing xmlns="" xmlns:q13="http://www.omg.org/spec/BPMN/20100524/MODEL">Gf65d4f6da8fe49d8bbeca90cfc23f130</q13:outgoing>
    </parallelGateway>
    <exclusiveGateway id="G31e596bc314146428528d433f401e15b" name="Check Name" default="G0156d62645cd41979c4f60dacadaf76e">
      <q14:incoming xmlns="" xmlns:q14="http://www.omg.org/spec/BPMN/20100524/MODEL">Gf65d4f6da8fe49d8bbeca90cfc23f130</q14:incoming>
      <q15:outgoing xmlns="" xmlns:q15="http://www.omg.org/spec/BPMN/20100524/MODEL">Ge5fdf5a3ace64afeb0391770454d319a</q15:outgoing>
      <q16:outgoing xmlns="" xmlns:q16="http://www.omg.org/spec/BPMN/20100524/MODEL">G0156d62645cd41979c4f60dacadaf76e</q16:outgoing>
    </exclusiveGateway>
    <sequenceFlow id="Gf65d4f6da8fe49d8bbeca90cfc23f130" sourceRef="G83238fcdcfca426183c01bd67b23e21d" targetRef="G31e596bc314146428528d433f401e15b" />
    <sequenceFlow id="G0156d62645cd41979c4f60dacadaf76e" sourceRef="G31e596bc314146428528d433f401e15b" targetRef="End_Event" />
    <sequenceFlow id="Ge5fdf5a3ace64afeb0391770454d319a" sourceRef="G31e596bc314146428528d433f401e15b" targetRef="klaus1">
      <conditionExpression xsi:type="tFormalExpression" id="G10c2af103a6d40e4884add371d3e2c0c">
        <documentation id="G85255209aaf447f59140139eacfd8c97">Name muss Klaus sein</documentation>x =&gt; x.Name == "Klaus"</conditionExpression>
    </sequenceFlow>
    <task id="klaus1" name="Ich bin Klaus">
      <q17:incoming xmlns="" xmlns:q17="http://www.omg.org/spec/BPMN/20100524/MODEL">Ge5fdf5a3ace64afeb0391770454d319a</q17:incoming>
      <q18:outgoing xmlns="" xmlns:q18="http://www.omg.org/spec/BPMN/20100524/MODEL">Gfd21215af7fb4334962afd070b3dafd7</q18:outgoing>
    </task>
    <sequenceFlow id="Gfd21215af7fb4334962afd070b3dafd7" sourceRef="klaus1" targetRef="End_Event" />
    <endEvent id="End_Event" name="End">
      <q19:incoming xmlns="" xmlns:q19="http://www.omg.org/spec/BPMN/20100524/MODEL">G0156d62645cd41979c4f60dacadaf76e</q19:incoming>
      <q20:incoming xmlns="" xmlns:q20="http://www.omg.org/spec/BPMN/20100524/MODEL">Gfd21215af7fb4334962afd070b3dafd7</q20:incoming>
    </endEvent>
  </process>
  <collaboration id="G024b6d714b0e480eab24981a1d1ee8dc">
    <participant id="G465e307c5d934e6ab5656db8cdd45779" name="Klaus" processRef="G7659d650c6ba41ff82bfb5f2ffe75eaf" />
  </collaboration>
  <BPMNDiagram name="test" xmlns="http://www.omg.org/spec/BPMN/20100524/DI">
    <BPMNPlane bpmnElement="G024b6d714b0e480eab24981a1d1ee8dc">
      <BPMNShape id="G465e307c5d934e6ab5656db8cdd45779_di" bpmnElement="G465e307c5d934e6ab5656db8cdd45779">
        <Bounds x="0" y="0" width="1500" height="500" xmlns="http://www.omg.org/spec/DD/20100524/DC" />
        <BPMNLabel />
      </BPMNShape>
      <BPMNShape id="Start_Event_di" bpmnElement="Start_Event">
        <Bounds x="0" y="0" width="50" height="50" xmlns="http://www.omg.org/spec/DD/20100524/DC" />
        <BPMNLabel>
          <Bounds x="0" y="0" width="50" height="50" xmlns="http://www.omg.org/spec/DD/20100524/DC" />
        </BPMNLabel>
      </BPMNShape>
      <BPMNShape id="Task_1_di" bpmnElement="Task_1">
        <Bounds x="0" y="0" width="50" height="50" xmlns="http://www.omg.org/spec/DD/20100524/DC" />
        <BPMNLabel>
          <Bounds x="0" y="0" width="50" height="50" xmlns="http://www.omg.org/spec/DD/20100524/DC" />
        </BPMNLabel>
      </BPMNShape>
      <BPMNShape id="Gf17b6be0df1a4474ade00c089b0af2a4_di" bpmnElement="Gf17b6be0df1a4474ade00c089b0af2a4">
        <Bounds x="0" y="0" width="50" height="50" xmlns="http://www.omg.org/spec/DD/20100524/DC" />
        <BPMNLabel>
          <Bounds x="0" y="0" width="50" height="50" xmlns="http://www.omg.org/spec/DD/20100524/DC" />
        </BPMNLabel>
      </BPMNShape>
      <BPMNShape id="Task_2_di" bpmnElement="Task_2">
        <Bounds x="0" y="0" width="50" height="50" xmlns="http://www.omg.org/spec/DD/20100524/DC" />
        <BPMNLabel>
          <Bounds x="0" y="0" width="50" height="50" xmlns="http://www.omg.org/spec/DD/20100524/DC" />
        </BPMNLabel>
      </BPMNShape>
      <BPMNShape id="Task_3_di" bpmnElement="Task_3">
        <Bounds x="0" y="0" width="50" height="50" xmlns="http://www.omg.org/spec/DD/20100524/DC" />
        <BPMNLabel>
          <Bounds x="0" y="0" width="50" height="50" xmlns="http://www.omg.org/spec/DD/20100524/DC" />
        </BPMNLabel>
      </BPMNShape>
      <BPMNShape id="G83238fcdcfca426183c01bd67b23e21d_di" bpmnElement="G83238fcdcfca426183c01bd67b23e21d">
        <Bounds x="0" y="0" width="50" height="50" xmlns="http://www.omg.org/spec/DD/20100524/DC" />
        <BPMNLabel>
          <Bounds x="0" y="0" width="50" height="50" xmlns="http://www.omg.org/spec/DD/20100524/DC" />
        </BPMNLabel>
      </BPMNShape>
      <BPMNShape id="G31e596bc314146428528d433f401e15b_di" bpmnElement="G31e596bc314146428528d433f401e15b">
        <Bounds x="0" y="0" width="50" height="50" xmlns="http://www.omg.org/spec/DD/20100524/DC" />
        <BPMNLabel>
          <Bounds x="0" y="0" width="50" height="50" xmlns="http://www.omg.org/spec/DD/20100524/DC" />
        </BPMNLabel>
      </BPMNShape>
      <BPMNShape id="klaus1_di" bpmnElement="klaus1">
        <Bounds x="0" y="0" width="50" height="50" xmlns="http://www.omg.org/spec/DD/20100524/DC" />
        <BPMNLabel>
          <Bounds x="0" y="0" width="50" height="50" xmlns="http://www.omg.org/spec/DD/20100524/DC" />
        </BPMNLabel>
      </BPMNShape>
      <BPMNShape id="End_Event_di" bpmnElement="End_Event">
        <Bounds x="0" y="0" width="50" height="50" xmlns="http://www.omg.org/spec/DD/20100524/DC" />
        <BPMNLabel>
          <Bounds x="0" y="0" width="50" height="50" xmlns="http://www.omg.org/spec/DD/20100524/DC" />
        </BPMNLabel>
      </BPMNShape>
      <BPMNEdge id="G94506efc9c90484c94f76ab9d865c3f3_di" bpmnElement="G94506efc9c90484c94f76ab9d865c3f3" sourceElement="Start_Event_di" targetElement="Task_1_di" />
      <BPMNEdge id="G3c32d4362fb34202afdd71d832bea159_di" bpmnElement="G3c32d4362fb34202afdd71d832bea159" sourceElement="Task_1_di" targetElement="Gf17b6be0df1a4474ade00c089b0af2a4_di" />
      <BPMNEdge id="G1368eaa247794e79aba99e78faa68b43_di" bpmnElement="G1368eaa247794e79aba99e78faa68b43" sourceElement="Gf17b6be0df1a4474ade00c089b0af2a4_di" targetElement="Task_2_di" />
      <BPMNEdge id="G690fc96a505e46ac8e1bb836e91a68f7_di" bpmnElement="G690fc96a505e46ac8e1bb836e91a68f7" sourceElement="Gf17b6be0df1a4474ade00c089b0af2a4_di" targetElement="Task_3_di" />
      <BPMNEdge id="G6f4b004c57ff4fd69a2e8d47969a1cd4_di" bpmnElement="G6f4b004c57ff4fd69a2e8d47969a1cd4" sourceElement="Task_2_di" targetElement="G83238fcdcfca426183c01bd67b23e21d_di" />
      <BPMNEdge id="Gd008894c897342c7a2df989d8512e647_di" bpmnElement="Gd008894c897342c7a2df989d8512e647" sourceElement="Task_3_di" targetElement="G83238fcdcfca426183c01bd67b23e21d_di" />
      <BPMNEdge id="Gf65d4f6da8fe49d8bbeca90cfc23f130_di" bpmnElement="Gf65d4f6da8fe49d8bbeca90cfc23f130" sourceElement="G83238fcdcfca426183c01bd67b23e21d_di" targetElement="G31e596bc314146428528d433f401e15b_di" />
      <BPMNEdge id="G0156d62645cd41979c4f60dacadaf76e_di" bpmnElement="G0156d62645cd41979c4f60dacadaf76e" sourceElement="G31e596bc314146428528d433f401e15b_di" targetElement="End_Event_di" />
      <BPMNEdge id="Ge5fdf5a3ace64afeb0391770454d319a_di" bpmnElement="Ge5fdf5a3ace64afeb0391770454d319a" sourceElement="G31e596bc314146428528d433f401e15b_di" targetElement="klaus1_di" />
      <BPMNEdge id="Gfd21215af7fb4334962afd070b3dafd7_di" bpmnElement="Gfd21215af7fb4334962afd070b3dafd7" sourceElement="klaus1_di" targetElement="End_Event_di" />
    </BPMNPlane>
  </BPMNDiagram>
</definitions>
rsoika commented 2 weeks ago

If you like you can test the latest snapshot release : 1.2.1-SNAPSHOT Your feedback is welcome.

paule96 commented 2 weeks ago

@rsoika as far as I understand the current code from your linked commit, it will fix my problem (not tested) but not the problem of random namespace names. For example xmlns:klaus="http://www.omg.org/spec/BPMN/20100524/MODEL" would result in a XML like:

<klaus:definitions>
....
</klaus:definitions>

This would be valid to an XSD

I guess the root problem can't be fixed easily. Because as far as I understand the code base right now (I know it for less than 24h), the code is not aware of XML namespaces overall.

rsoika commented 2 weeks ago

@paule96 , yes you are right at the moment this would not work. I do not really understand this xml structure. For my understanding in BPMN we have the following prefixes

So normally I expect a root element like this:

<bpmn2:definitions  xmlns:xs="http://www.w3.org/2001/XMLSchema" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" 
  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:ext="http://org.eclipse.bpmn2/ext" 
  id="Definitions_1" 
  exporter="org.eclipse.bpmn2.modeler.core" 
  exporterVersion="1.5.3.Final-v20210519-2007-B1" 
  targetNamespace="http://org.eclipse.bpmn2/default/process">

But you are right, the default namespace should be supported as well. And this is what my last change does. So you can left the prefix 'bpmn2:'.

But I have no glue how I should handle random namespaces. Because Open-BPMN need to search and find elements within the document. And for this a tag name including its optional prefix is mandatory.

I also saw in your example that you use unique prefixes like in this example:

 <exclusiveGateway id="G31e596bc314146428528d433f401e15b" name="Check Name"
      default="G0156d62645cd41979c4f60dacadaf76e">
      <q14:incoming xmlns="" xmlns:q14="http://www.omg.org/spec/BPMN/20100524/MODEL">
        Gf65d4f6da8fe49d8bbeca90cfc23f130</q14:incoming>
      <q15:outgoing xmlns="" xmlns:q15="http://www.omg.org/spec/BPMN/20100524/MODEL">
        Ge5fdf5a3ace64afeb0391770454d319a</q15:outgoing>
      <q16:outgoing xmlns="" xmlns:q16="http://www.omg.org/spec/BPMN/20100524/MODEL">
        G0156d62645cd41979c4f60dacadaf76e</q16:outgoing>
    </exclusiveGateway>

How should we handle this. I can't imaging that I can parse such a XML with the java org.w3c.dom library which I use as the base library to operate on the model.

For example take a look at this line to create a new element within the xml

    public Element createElement(BPMNNS ns, String type) {
        Element element = this.getDoc().createElementNS(getUri(ns), getPrefix(ns) + type);
        return element;
    }

Example call:

Element participantNode = createElement(BPMNNS.BPMN2, "participant");

We need to specify the target namespace (URI) and the tagname with optional prefix)

How can we solve this problem with random namespaces?

paule96 commented 1 week ago

@rsoika yes I can understand your problem, even if I guess I can't really help you, because I don't know the Java eco system.

But on the dotnet side you usually pick your XSD of choice. Then you get an XSD to C# converter. This converter will generate something like that:

    [System.CodeDom.Compiler.GeneratedCodeAttribute("XmlSchemaClassGenerator", "2.1.1130.0")]
    [System.SerializableAttribute()]
    [System.Xml.Serialization.XmlTypeAttribute("tText", Namespace="http://www.omg.org/spec/BPMN/20100524/MODEL")]
    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.ComponentModel.DesignerCategoryAttribute("code")]
    [System.Xml.Serialization.XmlRootAttribute("text", Namespace="http://www.omg.org/spec/BPMN/20100524/MODEL")]
    public partial class TText
    {

        [System.Xml.Serialization.XmlAnyElementAttribute()]
        public System.Xml.XmlElement Any { get; set; }

        [System.Xml.Serialization.XmlTextAttribute()]
        public string[] Text { get; set; }
    }

And there you can see that the namespace where this model is defined, is there defined with the full name. This allows the XML parser to parse any XML that is valid for the XSD the model was generated from.

The reading then starts with the root object of the XML (in the case of BPMN it's the definitions). So the reading code is like:

var xmlReader = new XmlSerializer(typeof(TDefinitions));
// use here some sample bpmn from openbpmn
using var reader = new StreamReader(@"demo-multiline.bpmn");
var output = xmlReader.Deserialize(reader);

This is how in C# the decoppling of namespace definitions and namespaces names are handled.

For my code I also use a thrid party generator that you can find here if you need inspiration.

rsoika commented 1 week ago

I am not sure if I can follow your thoughts. Yes of course you can build a XML Parser/Builder based on a XSD. This is in Java not much different form C#. But the question here is to parse and understand an BPMN file in a generic way. And this requires the definition of the BPMN namespaces.

Look again on your gateway example above. In this example you define new namespaces for each tag which inflates the content. A gateway definition normally should look this:

    <bpmn2:exclusiveGateway id="ExclusiveGateway_1" name="Exclusive Gateway 1" gatewayDirection="Diverging">
      <bpmn2:incoming>SequenceFlow_1</bpmn2:incoming>
      <bpmn2:outgoing>SequenceFlow_2</bpmn2:outgoing>
      <bpmn2:outgoing>SequenceFlow_3</bpmn2:outgoing>
    </bpmn2:exclusiveGateway>

The namespace bpmn2 is unique defined in the root element and is reused in the content tags. Open-BPMN can search for the incoming an outgoing edges and generate new ones.

Apart from the fact that your XML is of course valid, it is impossible for our project to parse it in a meaningful way.

paule96 commented 1 week ago

@rsoika if you look close to the c# there is no definition in the code how the namespace will be called. In the Attribut is only the Uri defined of the namespace not its name. And then the parser should be able to parse all versions that you can produce with the xml, that are valid for the xsd. I don't know if it's just a missing option you need to set in your XSD to Java objects generator, so it will produce more verbose Java classes that define for each object from which namesspace uri it's coming from.

rsoika commented 1 week ago

Yes I was aware of that. Such a declaration means that the URI defines the default namespace:

<definitions ...
  xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL">

This is fine and you can use any tag without a namespace prefix like you have done in your example:

  <process id="G7659d650c6ba41ff82bfb5f2ffe75eaf" name="Test" processType="Public">
    <startEvent id="Start_Event" name="Start">
 ...

And the Open-BPMN Meta model did not supported this before. But with the last fix we now also supporting default namespaces. I added a new Test Method here: https://github.com/imixs/open-bpmn/blob/f3fb07ab549e8896dd0d9a02f94877b053a1209c/open-bpmn.metamodel/src/test/java/org/openbpmn/dom/TestNameSpaces.java#L79

This method adds to your model a new task element with the following code:

        model.openDefaultProces().addTask("task-example-002", "Example Task 002", BPMNTypes.TASK);

And the result is a new tag at the and of your process without the 'bpmn2:' prefix :

 ...
    </endEvent>
    <task id="task-example-002" name="Example Task 002"/>
  </process>
 .....

So this all is fine. (And thanks again for your hint!)

But please be aware that elements in your file with custom namespaces like this one:

G0156d62645cd41979c4f60dacadaf76e Are not recognized by Open-BPMN. They are simply ignored. But of course even with this kind of elements you can work using the Open-BPMN meta model. This is because we support direct access to the Dom Tree. Through the Dom Tree you can directly call any method of the XML Parser. For example with the following call you can find any element: ```java model.getDoc().getElementsByTagName(....) ```