BPMN Converter¶
Converts BPMN 2.0 XML to RDF using the OMG CMOF metamodel for type resolution.
Ontology¶
Types align to the published BPMN 2.0.2 Ontology (bpmn: prefix).
Type derivation is automatic from CMOF — no configuration needed for full output:
<bpmn:userTask>→ CMOF classUserTask→bpmn:UserTask<bpmn:sequenceFlow>→ CMOF classSequenceFlow→bpmn:SequenceFlow- Relationship detection via superclass analysis (SequenceFlow, MessageFlow, Association, etc.)
The implicit end of a data association¶
A data association is the one relationship BPMN does not write both ends of. It states one as a child element and leaves the other to containment:
<bpmn:userTask id="UserTask_1" name="Capture order">
<bpmn:dataOutputAssociation id="DataOutputAssociation_1">
<bpmn:targetRef>DataObjectReference_1</bpmn:targetRef> <!-- stated -->
</bpmn:dataOutputAssociation> <!-- source: UserTask_1 -->
</bpmn:userTask>
The enclosing activity is therefore taken as the missing end — the source of a
dataOutputAssociation, the target of a dataInputAssociation:
<…/relationship/DataOutputAssociation_1> a bpmn:DataOutputAssociation , arch:QualifiedRelationship ;
arch:source <…/element/UserTask_1> ;
arch:target <…/element/DataObjectReference_1> .
This is inferred only when the file is silent. The specification permits sourceRef on a
dataOutputAssociation, pointing at one of the activity's dataOutputs, and an end stated
explicitly is more precise than the activity — so it is left as written. Adding to it would give
the relationship two sources and break core-shapes#QualifiedRelationshipShape from the other
side.
A plain <bpmn:association> is unaffected: it carries no direction, so there is no implicit end to
infer.
References are checked against the metamodel¶
The CMOF declares the type of every cross-reference — Lane::flowNodeRefs must be a FlowNode,
DataAssociation::targetRef an ItemAwareElement — and the XML schema does not enforce it. Both ways
a file can break that are reported, and they are treated differently:
| The reference | What happens |
|---|---|
| resolves to the declared type | kept, nothing said |
| resolves to something else | kept and reported — it resolves, and the author may have meant it |
| resolves to nothing in the file | dropped and reported |
[WARN] <Lane_1> flowNodeRefs points at <DataObjectReference_1>, which is a DataObjectReference
rather than a FlowNode. …
[WARN] <Lane_1> flowNodeRefs points at 'Task_Absent', which is not in this file. The reference was
dropped …
The drop is the part worth knowing. Every IRI in the notation layer is minted as {base}#{id} and
re-minted into the model namespace from a table of the document's subjects, so a target that is
never a subject was not in that table and its raw {base}#{id} reached the output — an edge pointing
into a namespace the model does not own. Dropping it keeps the graph sound.
Neither case fails the run: both are defects in the file rather than the conversion, and a usable graph can still be written.
The endpoint inferred for a data association above is exempt, because it is not a claim the file made.
It points sourceRef at the enclosing activity where the CMOF declares ItemAwareElement — a knowing
departure, since the activity is the only endpoint available when a file declares no
ioSpecification.
The label of a shape or an edge is a label node¶
BPMN records where a label is drawn separately from where its owner is drawn, because a tool lets an
author drag the text off the shape. BPMNShape-label and BPMNEdge-label carry that, and both are
composite properties — so a BPMNLabel sits one level below the plane's planeElement list.
The DI walk enumerated only that list, so a label was never entered into the remap table, and three things followed from the one omission:
| Before | Now | |
|---|---|---|
| Address | {base}#Label_Start — outside the model namespace |
…/view/{viewId}/node/Label_Start |
| View | absent | archvis:view → the enclosing view |
| Type | archvis:ArchNode |
archvis:LabelNode |
archvis:LabelNode is the class for a node that positions text rather than depicting something.
archvis:ArchNode is the domain of archvis:archElement, so typing a label that way asserted it
depicted a model element — and it never carried the archvis:archElement that would have made the
assertion true.
<…/view/BPMNDiagram_1/node/Shape_Start> a archvis:ArchNode ;
archvis:archElement <…/element/StartEvent_1> ;
archvis:label <…/view/BPMNDiagram_1/node/Label_Start> .
<…/view/BPMNDiagram_1/node/Label_Start> a archvis:LabelNode , bpmndi:BPMNLabel ;
archvis:view <…/view/BPMNDiagram_1> ;
archvis:bounds-x 82.0 ; archvis:bounds-y 140.0 .
The node is kept rather than dropped because its bounds are not derivable from the shape's: the label above sits below and wider than the 36×36 circle it belongs to, and nothing else records that.
An edge's label carries no archvis:label back-link. archvis:label is declared
rdfs:domain arch-vis:Node, and core-vis makes Link a sibling of Node under DiagElement rather
than a subclass — so asserting it on an edge would entail, through that domain axiom, that every
labelled BPMN sequence flow is a Node. An edge label is reachable through the notation-native
bpmndi:label instead, which is emitted for both carriers. An aligned route needs the domain widened
to DiagElement upstream.
Worth noting how this was found: the graph conformed to the published shapes both before and after.
core-vis publishes no SHACL shapes, so nothing validated the diagram layer at all. The only thing that
caught it was the converter's own self-check, reporting 447 incomplete nodes across
20 diagrams in the words it asks you to send upstream.
BPMN Lite (simplified EA-level output)¶
Pass --type-mapping config/type-mapping-bpmn-lite.yml to map types to the
BPMN Lite Ontology (~28 classes vs ~144):
java -jar bpmn2linkedarchi.jar convert process.bpmn \
--type-mapping config/type-mapping-bpmn-lite.yml \
--base-iri https://example.org/la/ --format TRIG -o out.trig
Usage¶
# Full BPMN ontology → TriG
java -jar bpmn2linkedarchi.jar convert \
process.bpmn \
--base-iri https://example.org/la/ \
--model-id demo \
--format TRIG \
--include-di \
-o out.trig
# Full BPMN ontology → Turtle
java -jar bpmn2linkedarchi.jar convert \
process.bpmn \
--base-iri https://example.org/la/ \
--model-id demo \
--format TURTLE \
--include-di \
-o out.ttl
CLI options¶
| Option | Default | Description |
|---|---|---|
<inputs> |
required | BPMN 2.0 XML file(s) |
-o, --output |
required | Output artifact, path[:FORMAT[:PROFILE]]. Repeatable — several artifacts are written from one conversion, which is faster than re-running the converter and is what makes them share one prov:generatedAtTime. PROFILE is full (default), no-geometry, no-views or no-diagrams; see output serialization |
--base-iri |
required | Base IRI for minting all resource IRIs, e.g. https://example.org/la/. An http/https IRI, so the resources it mints dereference |
--model-id |
filename | Model identifier for a single input. An index id wins over it, and giving it with several inputs is refused — see one model per --model-id. Unlike an index id, the value is not validated |
--format |
inferred | Default format for any --output that names none: TRIG / TURTLE / JSONLD / RDFXML / NTRIPLES / NQUADS. A file extension such as ttl is also understood |
--include-di |
true |
Parse BPMNDI geometry |
--emit-raw-di-geometry |
false |
Keep the raw di:bounds / di:waypoint triples and their dc:Bounds / dc:Point nodes. By default those are suppressed and geometry is flattened into archvis:bounds-*. Required if you want to validate with --shapes di |
--type-mapping |
none | YAML type overrides (use for BPMN Lite) |
--[no-]emit-skos-labels |
true |
Mirror bpmn:name to a language-tagged skos:prefLabel, the canonical Linked.Archi label required by bpmnsh:RequiredNameShape. bpmn:name is kept either way — the label is added, never moved. --no-emit-skos-labels leaves the output without labels, which does not conform to the published BPMN shapes |
--label-language |
en |
BCP-47 tag for skos:prefLabel literals |
--emit-skos-notation |
false |
Replace bpmn:id → skos:notation |
--emit-direct-rel-triples |
false |
Emit {src} {pred} {tgt} shortcuts, each bridged back to its flow resource with rdf:reifies. Needs predicates: in a --type-mapping |
--emit-bpmn-source-target |
false |
Also emit bpmn:sourceRef/bpmn:targetRef |
--emit-extension-data |
false |
Map foreign children of bpmn:extensionElements onto the elements they annotate, taking each child's own qualified name as the predicate. See Extension data |
--ns-global-id |
none | Base IRI for extension rdf:resource values written without a prefix, so a bare id resolves into a cross-model graph. Requires --emit-extension-data |
--emit-ontology-imports |
false |
Emit owl:imports for the BPMN ontologies in the output |
--dual-typing |
true |
Emit arch:Element / arch:QualifiedRelationship plus arch:ModelConcept alongside the BPMN types |
--diagrams-index |
none | Diagram index for batch processing. When given, only indexed files are converted — in every lifecycle state unless --exclude-states says otherwise — and each one's id becomes its model ID |
--diagrams-root |
index location | Root directory that index file paths are resolved against |
--exclude-states |
none | Leave index entries in these lifecycle states out of the run. Every state is converted by default, so this is the only option that withholds one. Comma-separated, and it cannot name all five. See Lifecycle states |
--include-states |
none | Deprecated and ignored: every state is converted by default. Use --exclude-states |
--include-drafts |
false |
Deprecated and ignored: drafts are converted by default |
--ns-core |
https://meta.linked.archi/core# |
Override the core namespace |
--ns-core-vis |
https://meta.linked.archi/core-vis# |
Override the core-vis namespace |
Extension data¶
A BPMN file can carry data the BPMN metamodel knows nothing about — a link to an ArchiMate
element, a LeanIX factsheet id, an owning team — as foreign-namespace children of
bpmn:extensionElements. --emit-extension-data maps that data onto the element it annotates.
This is BPMN's in-file route. A diagram index can make the same statements without touching the source file, which suits a team that owns the pipeline rather than the models; both routes apply in the same run. Extension data covers the choice and what the routes share.
The index is also the only route for a statement about the diagram or the model as a whole —
which architecture state it depicts, which viewpoint it conforms to. bpmn:extensionElements nests
inside the element it annotates, so a .bpmn has nowhere to put one:
prefixes:
arch: https://meta.linked.archi/core#
model:
id: order-domain
links:
arch:architectureState: arch:Baseline # about the model
views:
- id: order-fulfillment
file: order-fulfillment.bpmn
links:
arch:architectureState: arch:Target # about this view
See statements about a view or a model.
A view-level block needs the entry to name a view, so it requires the model/views schema rather than
the legacy flat one; in the flat schema an entry is a model, and links: there is model-level.
architectureState: is the checked form of one such statement — baseline, target or transitional,
with a typo failing the run rather than emitting a link to a term nothing defines. It is index-only
for BPMN, for the same reason, and is not gated by --emit-extension-data:
model:
id: order-domain
architectureState: baseline
views:
- id: order-fulfillment
file: order-fulfillment.bpmn
architectureState: target
See Architecture state, including why it is
orthogonal to status:.
It is off by default. A file authored in a modelling tool is usually full of that tool's own extension elements (Camunda form data, execution listeners), and mapping those unasked would add triples to every existing model.
The element name is the predicate¶
Write the term you mean as the element name:
<bpmn:extensionElements>
<am:realizes rdf:resource="kg:CAP-MasterDataManagement"/>
<x:costCentre>CC-4711</x:costCentre>
</bpmn:extensionElements>
There is nothing to configure, because XML already did the work. Prefixes in element names are
resolved by any XML parser — it is the one place they are — so <am:realizes> arrives carrying
https://meta.linked.archi/archimate3/onto# from the document's own xmlns:am. The predicate is
that namespace plus the local name, and no option, attribute or carrier vocabulary is involved.
What still needs resolving is the object, because it lives in an attribute value where XML will
not help. That is the only place this converter does prefix resolution of its own, and the only
reason --ns-global-id exists.
Whether an element becomes a link or a literal follows from its object, not from its name:
| Content | Becomes |
|---|---|
rdf:resource="…" |
a link — the RDF/XML convention, saying "this is a reference" outright |
| text naming a resource anyway — a declared CURIE, an absolute IRI | a link |
| any other text | one literal-valued predicate |
| element children, or attributes | a node carrying its own predicates |
| nothing at all | skipped — no value to record |
Use a published ontology for the name. The examples below use
Linked.Archi core (arch:) and the
ArchiMate 3 ontology (am:) alongside owl: and
skos:. A consumer is free to point at their own ontology and every rule here applies unchanged,
but a term from a published vocabulary is one a downstream consumer already understands.
A predicate namespace has to end in # or /
An XML namespace is under no such obligation, so xmlns:t="http://example.org/schema/tags"
with <t:owner> would mint http://example.org/schema/tagsowner — an IRI that looks plausible
and denotes nothing anyone intended. The element is skipped and reported rather than emitted.
Terms worth knowing before inventing one:
| To say | Use |
|---|---|
| this process realizes a capability | am:realizes |
| this application serves the task | am:serves (with direction="Backward") |
| this task adds detail to a coarser concept | arch:refines |
| this task is part of a larger concept | arch:partOf |
| who owns the concept | arch:conceptOwner (see validation) |
| where its master data lives | arch:masterDataSource |
| this task and that node describe the same system | skos:exactMatch |
| they correspond, but not exactly | skos:closeMatch, skos:relatedMatch |
| they are one individual, and their properties should merge | owl:sameAs — read the caution below first |
skos:exactMatch is the default for correspondence, not owl:sameAs
The two say different things and a reasoner treats them differently.
skos:exactMatch says the BPMN element and the other node describe the same real-world
thing, without claiming they are one RDF resource. Each keeps its own types and properties,
nothing is inferred onto the other, and the link is revocable without touching either source.
This is the normal case: a task in a process model is this model's mention of something that
has its own, separately maintained description elsewhere.
owl:sameAs asserts the two IRIs denote one resource. A reasoner is entitled to merge
everything known about both, in both directions. That is the right tool only when the two IRIs
really are alternate names for one node — reconciling an id after a rename, say — and a wrong
owl:sameAs silently corrupts every query downstream of it.
Choose with the substitutability test: could a reasoner safely replace one IRI with the other
everywhere, forever? If yes, they are one entity → owl:sameAs. Otherwise →
skos:exactMatch. Most cross-model links fail the test, because notations describe the same
system at different granularities and for different concerns. Full rationale in
Extension data → Stating a correspondence.
These relate instances — this task, that component. Relating one language's types to
another's (bpmn:UserTask to am:BusinessProcess) is a different job that does not belong in a
model file at all; it is authored once per language pair in a cross-language mapping document. See
Extension data → Instance-level and type-level.
Links¶
<bpmn:definitions
xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:arch="https://meta.linked.archi/core#"
xmlns:am="https://meta.linked.archi/archimate3/onto#"
xmlns:kg="https://example.org/graph/"
xmlns:lx="https://leanix.example.com/factsheet/">
<bpmn:process id="Process_GeoMapping" name="Geo Mapping">
<bpmn:extensionElements>
<am:realizes rdf:resource="kg:CAP-MasterDataManagement"/>
</bpmn:extensionElements>
<bpmn:serviceTask id="Activity_MapGeo" name="Map source geo to target geo">
<bpmn:extensionElements>
<am:serves rdf:resource="lx:APP-refdata-importer" direction="Backward"/>
<arch:refines>archi:BusinessProcess-GeoMapping</arch:refines>
</bpmn:extensionElements>
</bpmn:serviceTask>
java -jar bpmn2linkedarchi.jar convert geo-mapping-linked.bpmn \
--base-iri https://example.org/la/ --model-id geo-mapping \
--emit-extension-data \
--format TRIG -o out.trig
<…/bpmn/geo-mapping/element/Process_GeoMapping>
am:realizes <https://example.org/graph/CAP-MasterDataManagement> .
<https://leanix.example.com/factsheet/APP-refdata-importer>
am:serves <…/bpmn/geo-mapping/element/Activity_MapGeo> .
The link is flattened into the triple a query would otherwise have to reconstruct. The third
element shows rdf:resource being optional where the text already names a resource: archi: is
declared, so <arch:refines>archi:BusinessProcess-GeoMapping</arch:refines> is read as a reference
rather than a string. Prefer rdf:resource when you want it to be unmistakable.
Note the second triple reads from the application, because am:serves runs that way and the element
declared direction="Backward". Without it an author would have to invent an inverse of a term
that already exists.
The output above is shown with prefixes for readability. arch: is registered by the converter, but
a namespace it does not already know is written out in full — the xmlns declarations in the BPMN
file are used for resolution, not carried into the output. Register the ones you link with under
namespaces: in a type-mapping file to get short names:
namespaces:
am: https://meta.linked.archi/archimate3/onto#
kg: https://example.org/graph/
lx: https://leanix.example.com/factsheet/
Correspondence and identity need no separate mechanism — an element named skos:exactMatch is a
skos:exactMatch triple, and the same goes for owl:sameAs:
skos:exactMatch is the right default here, per the caution above: this says the BPMN process and
the ArchiMate element describe the same process, which is a correspondence rather than a claim that
the two IRIs denote one individual. Write owl:sameAs instead only where the substitutability test
passes and a property merge is what you want.
That the predicate is just an element name matters because most cross-model references are typed
relations (am:realizes, arch:refines) that neither correspondence nor identity can express.
--ns-global-id therefore does something narrower here than in the ArchiMate converter: it supplies
a base for bare targets rather than designating an identity field.
How an object is resolved¶
The predicate needs no help, because it is an element name. The object does: an attribute value
is text, and XML resolves no prefixes there, so lx:APP-x in rdf:resource is a plain string until
the converter expands it against the document's own xmlns declarations — including one on the
annotated element or the extension element itself, following normal XML scoping.
First match wins:
| Form | Example | Result |
|---|---|---|
| CURIE with a prefix declared in the document | lx:APP-x |
expanded against the declaration |
| Absolute IRI | https://leanix.example.com/factsheet/APP-x |
passed through |
| No prefix, with a base configured | CAP-1 + --ns-global-id https://example.org/graph/ |
https://example.org/graph/CAP-1 |
| Anything else | nowhere:CAP-1 |
warning, value kept as a literal |
Write an http/https IRI when you write one in full: it dereferences, so a consumer can follow
it. A few schemes without :// are accepted as absolute too — urn:, mailto:, tag:, doi:,
data: — because a value like urn:example:x is unambiguously an IRI rather than a CURIE. They are
accepted, not recommended.
The document's own declaration wins over reading the value as an absolute IRI, because a CURIE and
an IRI have the same prefix:rest shape and a declared prefix is the better evidence. A prefixed
value with no declaration is reported rather than emitted as a nowhere:CAP-1 IRI that resolves
nowhere:
[WARN] flow.bpmn: <skos:exactMatch> on 'ManualTask_1' points at 'nowhere:CAP-Unknown', which is not
a resolvable IRI — prefix 'nowhere:' in 'nowhere:CAP-Unknown' is not declared in the BPMN document.
Add xmlns:nowhere="…" to bpmn:definitions, or write the IRI in full. Kept as a literal, so it is
not a link in the graph.
The value is kept as a literal so nothing authored is lost, but it is not a link — a query for links will not match it. Treat the warning as an error in a publishing pipeline.
Note the asymmetry with text content: a bare name in rdf:resource resolves against
--ns-global-id because the attribute says a reference was intended, while the same bare name as
text content stays a literal. <x:tier>gold</x:tier> is a value, not an IRI.
Validation and cross-graph links¶
A link points at something in another graph, so nothing in this file says what the target is. Where the term you used constrains its object's class, validating the BPMN output on its own will report a violation that merging the graphs would resolve.
arch:conceptOwner is the one to know about: the core shapes require its object to be an
arch:Stakeholder, and a BusinessRole typed in your ArchiMate model carries no type here.
[Violation] arch:conceptOwner must point to a Stakeholder.
focusNode <…/bpmn/geo-mapping/element/UserTask_Review>
The link itself is correct and the merged graph conforms. Either validate the BPMN output together
with the model that types the target, or pick a term whose range is unconstrained
(arch:masterDataSource, arch:refines, arch:partOf, am:realizes, am:serves, owl:sameAs
and the skos: mapping terms all validate cleanly on their own). There is no way to assert the
target's type from the BPMN side, because the subject of a link is always the annotated element.
direction¶
direction |
Emitted |
|---|---|
absent, Forward, None |
element predicate target |
Backward |
target predicate element |
Both |
both of the above |
| anything else | warning, read as Forward |
None is folded into Forward because a triple has a direction whether or not the author
expressed a preference. Where the target could not be resolved to an IRI, only the forward triple
is written — a literal cannot be the subject of the inverse — and that is reported too.
Data with no published term¶
An element whose content is plain text becomes a literal-valued predicate. This is the extension
point for facts no published ontology has a term for — a cost centre, an internal service level.
Where a standard term does exist, prefer it: arch:masterDataSource says where master data lives
far better than a vocab:source string, because a consumer already knows what it means and the
value is a resource rather than a name.
A leaf is one predicate; a group keeps its parts together on a node of its own, so two service levels on one element stay distinguishable:
<bpmn:serviceTask id="ServiceTask_1">
<bpmn:extensionElements>
<arch:masterDataSource rdf:resource="lx:APP-refdata-importer"/>
<x:costCentre>CC-4711</x:costCentre>
<x:serviceLevel>
<x:tier>gold</x:tier>
<x:responseTime>PT4H</x:responseTime>
</x:serviceLevel>
<x:criticality level="high">tier-1</x:criticality>
</bpmn:extensionElements>
with xmlns:x="https://example.org/vocab#" declared in the file:
<…/element/ServiceTask_1>
arch:masterDataSource <https://leanix.example.com/factsheet/APP-refdata-importer> ;
vocab:costCentre "CC-4711" ;
vocab:serviceLevel [ vocab:tier "gold" ; vocab:responseTime "PT4H" ] ;
vocab:criticality [ vocab:level "high" ] .
The predicate namespace comes from the document, so there is no option to pass and no chance of two
extension vocabularies colliding: <a:tier> and <b:tier> stay distinct predicates.
Attributes become predicates on the group node, which is why criticality is a node rather than a
literal. An unprefixed attribute has no namespace in XML, and level on its own is not a predicate,
so it is read against the namespace of the element it qualifies. direction and rdf:resource are
never emitted as data — they say how to map the element rather than asserting anything about it.
What is not mapped¶
| Why | |
|---|---|
dc: / dcterms: children |
Already read into the provenance graph as the file's title, author and dates. Mapping them again would state one fact under two vocabularies |
| Children in a BPMN namespace | The CMOF parse's business, not this one's |
| An empty element with no attributes and no text | No value to record, and a predicate pointing at an empty string would claim there is one |
Extension data on an element with no id |
Nothing to attach it to, since IRIs are minted from the id. Reported, with the element named |
| Mixed content — text alongside child elements | No predicate to hang the text on |
An element whose namespace does not end in # or / |
The predicate would run into the local name. Reported, with the namespace named |
A type + target wrapper element |
The shape an earlier draft of this feature used. Reported, with the direct form to write instead — see below |
bpmn:relationship |
Out of scope; see below |
If you authored against the type + target wrapper¶
An earlier draft of this feature wrapped the predicate in a carrier element and put it in a type
attribute:
That shape is refused, because mapping it now would take the carrier's own name as the predicate and
emit literals called type and target — junk that looks like data. The run says what to write:
[WARN] flow.bpmn: Skipped <x:link type="am:realizes"> on 'Task_1': the predicate is the element
name, not a type attribute. Write it as <am:realizes rdf:resource="kg:CAP-1"/>, declaring a prefix
for 'am' if it has one.
The wrapper was dropped rather than kept alongside the direct form because it is strictly worse: it needed a carrier vocabulary nobody had published, and it moved the predicate into an attribute where XML will not resolve its prefix, so the converter had to re-implement resolution that the XML parser already does for element names.
bpmn:relationship is out of scope¶
BPMN's own <bpmn:relationship> element looks like the intended mechanism for relating a process
to an external artifact, and it is valid BPMN, but it is not mapped. Its source and target
children are emitted as empty typed nodes and their content does not reach the graph:
Use extensionElements instead. It attaches the link to the element it describes rather than to a
detached definitions-level node, and tools round-trip it.
Worth knowing if you have such a file: bpmn:source and bpmn:target are xsd:QName in the BPMN
schema, so an undeclared prefix — tns: is the common one — makes render fail while convert
succeeds, because the two use different parsers and only Camunda's resolves the QName. The
[WARN] Skipped … line names the prefix and the line:
[WARN] Skipped flow.bpmn: SAXException while parsing input stream — Error: URI=null Line=16:
UndeclaredPrefix: Cannot resolve 'tns:Process_1' as a QName: the prefix 'tns' is not declared.
BPMN ontology packages¶
All packages are published on meta.linked.archi and fetched at runtime; none are bundled
in the JAR.
| Package | Namespace | Asset name | Content |
|---|---|---|---|
| BPMN core | https://meta.linked.archi/bpmn/onto# |
bpmn |
Process, Task, Event, Gateway, Flow |
| BPMN DI | https://meta.linked.archi/bpmn/di# |
bpmn-di |
BPMNDiagram, BPMNShape, BPMNEdge |
| DI core | https://meta.linked.archi/bpmn/di-core# |
bpmn-di-core |
Abstract Diagram, Shape, Edge |
| DC | https://meta.linked.archi/bpmn/dc# |
bpmn-dc |
Bounds, Point, Font |
| Infrastructure | https://meta.linked.archi/bpmn/infra# |
bpmn-infra |
Definitions, Import |
| Suite imports | https://meta.linked.archi/bpmn/suite# |
bpmn-suite |
owl:imports aggregator only — no axioms of its own, so it is not loaded by default. The mapping to arch: core types lives in BPMN core above |
| BPMN Lite | https://meta.linked.archi/bpmn-lite/onto# |
— | EA-level subset (~28 classes), via --type-mapping |
Architecture¶
BPMN 2.0 XML
→ BpmnXmlToRdfConverter (CMOF-driven StAX parser)
→ Raw RDF model (CMOF-typed, fragment IRIs)
→ LinkedArchiEmitter (IRI remapping + named graph emission)
→ TriG / Turtle output
Render¶
Renders BPMN files to SVG using the BPMNDI geometry already in the XML (Camunda model library) — no layout engine, so the result matches the authoring tool: event circles, gateway diamonds, task boxes with type icons, pools and lanes, sequence-flow arrows.
java -jar bpmn2linkedarchi.jar render process.bpmn -o svg/
# Batch, hyperlinked to the graph, with custom shape styling
java -jar bpmn2linkedarchi.jar render models/*.bpmn \
--base-iri https://example.org/la/ \
--diagrams-root models/ \
--diagrams-index models/diagram-index.yaml \
--shape-config config/shape-config-bpmn.yml \
-o out/svg/
| Option | Default | Description |
|---|---|---|
<inputs> |
required | BPMN 2.0 XML file(s) |
-o, --output-dir |
required | Directory the SVG files are written to |
--base-iri |
none | When given, every shape is wrapped in <a href> pointing at its element IRI ({base}bpmn/{modelId}/element/{id}), matching what convert mints |
--shape-config |
built-in defaults | YAML file with per-type style overrides (fill, stroke, strokeWidth, icon) |
--diagrams-index |
none | YAML index selecting which diagrams to render and their model IDs |
--diagrams-root |
index location | Root directory that index file paths are resolved against |
--exclude-states |
none | Leave index entries in these lifecycle states out of the run. Every state is rendered by default. Comma-separated, and it cannot name all five. Resolved exactly as convert resolves it, so a diagram in the graph has a picture |
--include-states |
none | Deprecated and ignored: every state is rendered by default. Use --exclude-states |
--include-drafts |
false |
Deprecated and ignored: drafts are rendered by default |
--[no-]render-archived |
true |
Draw entries with status: archived. --no-render-archived leaves them out; convert is unaffected, so the diagram keeps its IRIs but its image URL stops resolving |
Output naming: with --diagrams-index the file is <id>.svg and any sub-directory in the
index file: path is mirrored under the output directory. Without an index the SVGs are
written flat as <filename>.svg. This and the rest of the shared render behaviour are
described in SVG Rendering.
The same id names the SVG here and the IRI path segment in convert
({base}bpmn/{id}/…), which is what lets a link in the diagram resolve against the graph.
Because it is used unescaped in both, it must be a slug — ^[a-z0-9][a-z0-9._-]*$ — and
id and file: must each appear once in the index. A violation fails the command rather
than being silently rewritten or dropped; see
id rules. Note that changing an id
renames the published SVG and moves every element IRI for that model.
Index metadata (title, author, version, created, modified, description) is rendered into an
info box at the top of the SVG, overlaid on top of any metadata found in the BPMN file's own
documentation and extension elements.
Per-file failures are reported as [WARN] Skipped … and the command exits 1; a failure
before rendering starts (bad shape config, missing index) exits 2.
Shape config¶
shapes:
UserTask:
fill: "#ddeeff"
stroke: "#333333"
icon: |
<circle cx="8" cy="5" r="3.5" fill="none" stroke="#555" stroke-width="1.2"/>
<path d="M1 15 Q8 9 15 15" fill="none" stroke="#555" stroke-width="1.2"/>
ExclusiveGateway:
fill: "#ffffcc"
stroke: "#333333"
Types not listed keep their built-in rendering. icon is a raw SVG snippet drawn in a
16×16 space at the top-left of the shape. A full reference file ships in the distribution
as config/shape-config-bpmn.yml.
Ontology download¶
Downloads the published BPMN ontologies and SHACL shapes from meta.linked.archi into a
directory, so they can be inspected or passed back in via --shapes. Nothing is bundled
in the JAR — the files come from the registry at runtime.
# Everything (ontologies + shapes)
java -jar bpmn2linkedarchi.jar ontology --dir ontologies/
# Just one category
java -jar bpmn2linkedarchi.jar ontology --dir ontologies/ --only SHACL
| Option | Default | Description |
|---|---|---|
--dir |
(required) | Output directory |
--only |
ALL |
Download a subset: ALL, ONTOLOGY or SHACL |
--overwrite |
false |
Overwrite files that already exist |
--asset-dir |
<java.io.tmpdir>/linked-archi-assets |
Where fetched documents are held |
--offline |
false |
Use documents already fetched instead of downloading |
Existing files are skipped unless --overwrite is given.
Validate¶
Runs SHACL validation on converted output. Shapes and ontologies are fetched from
meta.linked.archi at runtime.
Default shapes are bpmn-shapes, bpmn-infra-shapes and core-shapes, which constrain the
normalized output this converter produces: type correctness and single-valuedness of BPMN
attributes, the naming rule (bpmnsh:RequiredNameShape), and — from core — that every
connector has exactly one arch:source and arch:target. The five BPMN ontologies plus
core are loaded for rdfs:subClassOf reasoning, so the alignment to arch:Element /
arch:QualifiedRelationship that bpmn/onto declares resolves.
The diagram-interchange shapes are not in the default set, because they constrain raw DI
structures (di:bounds pointing at a dc:Bounds node, di:waypoint) and convert
flattens geometry into archvis:bounds-* unless told otherwise. The shape set has to
match how you converted:
# Raw DI geometry -> validate with the DI shapes too
java -jar bpmn2linkedarchi.jar convert process.bpmn -o out.trig --emit-raw-di-geometry
java -jar bpmn2linkedarchi.jar validate -i out.trig --shapes di
Selecting di (or all) against normalized output reports missing di:bounds and
di:waypoint for every shape and edge — that is the shape set being wrong for the data,
not a defect in the output. Use the default set for normalized output.
--shapes all selects every published BPMN shape document, and individual names
(bpmn-shapes, bpmn-di-shapes, bpmn-di-core-shapes, bpmn-dc-shapes,
bpmn-infra-shapes) can be combined freely. Run validate --list-assets to see them.
Naming¶
BPMN's naming rule is bpmnsh:RequiredNameShape, published in bpmn-shapes. It requires a
language-tagged skos:prefLabel on the twelve classes BPMN expects to be named — activities
and their task subtypes, processes, collaborations, conversations, pools, lanes, data objects
and stores, messages — and deliberately does not target gateways, events or connectors,
whose name the spec leaves optional. So unnamed gateways, events and sequence flows are
valid and report nothing.
bpmn:name alone does not satisfy it. skos:prefLabel is the canonical Linked.Archi label
that every consumer reads, and it must carry a language tag; bpmn:name is the retained
source attribute, a plain xsd:string. convert emits both by default, which is what
the published converter contract asks for:
<…/element/Task_CheckInventory>
a bpmn:ServiceTask ;
bpmn:name "Check Inventory" ; # source attribute, verbatim
skos:prefLabel "Check Inventory"@en . # canonical label
Convert with --no-emit-skos-labels and every element that must be named fails this rule.
Set --label-language where the source is not English.
There is no core label rule any more: core-shapes#ElementLabelShape and #ViewLabelShape
were withdrawn, because requiring a label on every arch:Element is not sound across
notations. Nothing needs disabling for BPMN, so this converter no longer passes
--without-shape by default. To switch the BPMN rule off for a run:
One exception, with --emit-extension-data: a link whose term constrains its object's class is
reported when the target is typed in another graph rather than in this file. See
Validation and cross-graph links.
Exits 0 when the data conforms, 1 when violations are found, 2 on error.
See Validation for the shared engine, all options, coverage reporting and report format.