Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "BPMN2-Modeler/DeveloperTutorials/ModelExtension"

m (save point)
m
 
(4 intermediate revisions by the same user not shown)
Line 1: Line 1:
 
==Versions==
 
==Versions==
 
This Tutorial was developed with Eclipse 4.4 (Luna) and BPMN2-Plugin version 1.1.1.
 
This Tutorial was developed with Eclipse 4.4 (Luna) and BPMN2-Plugin version 1.1.1.
 
== Prerequisites ==
 
This discussion assumes that you are comfortable with XML and XML Schema and have at least a working knowledge of the BPMN2 language.
 
 
In this tutorial, all EMF model types (EClasses) attributes (EAttributes) and references (EReferences) are displayed in bold, for example '''UserTask'''. References to Java or XML code fragments appear in italics, for example ''modelExtension''.
 
  
 
== Model Extension: what is it, and why do I need it? ==
 
== Model Extension: what is it, and why do I need it? ==
Line 17: Line 12:
 
The BPMN2 Modeler extension API offers several different methods of extending the BPMN2 model, and each addresses a particular use-case as discussed next.
 
The BPMN2 Modeler extension API offers several different methods of extending the BPMN2 model, and each addresses a particular use-case as discussed next.
  
=== Use the <modelExtension> extension point ===
+
== The <modelExtension> extension point ==
 
The BPMN2 Modeler core exposes the ''org.eclipse.bpmn2.modeler.core.org.eclipse.bpmn2.modeler.runtime'' plugin extension point. One of these extension elements is called "modelExtension". Please see the [http://download.eclipse.org/bpmn2-modeler/doc/api/core.html Core Extension Point API] documentation for details.
 
The BPMN2 Modeler core exposes the ''org.eclipse.bpmn2.modeler.core.org.eclipse.bpmn2.modeler.runtime'' plugin extension point. One of these extension elements is called "modelExtension". Please see the [http://download.eclipse.org/bpmn2-modeler/doc/api/core.html Core Extension Point API] documentation for details.
  
This extension point is used if you simply need to "decorate" a specific BPMN2 type with your own, business-specific attributes and elements, e.g. a '''UserTask''' or '''SequenceFlow'''. The [https://git.eclipse.org/c/bpmn2-modeler/org.eclipse.bpmn2-modeler.git/tree/plugins/org.eclipse.bpmn2.modeler.runtime.jboss.jbpm5 jBPM extension plugin] demonstrates a perfect example of when to use this extension:
+
This is used if you simply need to "decorate" a specific BPMN2 type with your own, business-specific attributes and elements, e.g. a '''UserTask''' or '''SequenceFlow'''. The [https://git.eclipse.org/c/bpmn2-modeler/org.eclipse.bpmn2-modeler.git/tree/plugins/org.eclipse.bpmn2.modeler.runtime.jboss.jbpm5 jBPM extension plugin] demonstrates a perfect example of when to use this extension:
  
 
<pre>
 
<pre>
Line 35: Line 30:
 
Here the ''type'' identifies a BPMN2 model type to which this extension applies, in this case a '''SequenceFlow'''. Note that we do not use the fully qualified "org.eclipse.bpmn2.SequenceFlow" class name because ''modelExtension'' is limited to only BPMN2 model objects and can not be used with external models.
 
Here the ''type'' identifies a BPMN2 model type to which this extension applies, in this case a '''SequenceFlow'''. Note that we do not use the fully qualified "org.eclipse.bpmn2.SequenceFlow" class name because ''modelExtension'' is limited to only BPMN2 model objects and can not be used with external models.
  
The extension adds an attribute named ''priority'' to '''SequenceFlow'''; this attribute will be qualified with the "tns" prefix which corresponds to the targetNamespace "http://www.jboss.org/drools" defined by the jBPM plugin. The resulting XML looks like this:
+
This extension adds an attribute named ''priority'' to '''SequenceFlow'''; this attribute will be qualified with the "tns" prefix which corresponds to the targetNamespace "http://www.jboss.org/drools" defined by the jBPM plugin. The resulting XML looks like this:
  
 
<pre>
 
<pre>
Line 132: Line 127:
 
# All BPMN2 '''Activity''' objects ('''Activity''' is a super class of '''UserTask''') have an optional '''ioSpecification''' container which defines inputs to, and outputs from the '''Activity'''. The line ''<property name="ioSpecification">'' will cause a new '''IoSpecification''' object to be created and attached to the '''UserTask'''.
 
# All BPMN2 '''Activity''' objects ('''Activity''' is a super class of '''UserTask''') have an optional '''ioSpecification''' container which defines inputs to, and outputs from the '''Activity'''. The line ''<property name="ioSpecification">'' will cause a new '''IoSpecification''' object to be created and attached to the '''UserTask'''.
 
# The lines that follow populate the '''IoSpecification''' object with '''OutputSet''', '''DataInputs''' and '''InputSet''' objects each of which are, in turn, constructed and initialized
 
# The lines that follow populate the '''IoSpecification''' object with '''OutputSet''', '''DataInputs''' and '''InputSet''' objects each of which are, in turn, constructed and initialized
# Within the '''inputSets''' definition, this line:
+
# Within the '''inputSets''' definition, this line: ''<property name="dataInputRefs" ref="ioSpecification/dataInputs#0"/>'' initializes the '''dataInputRefs''' of the '''InputSet''' object with a reference to the newly created '''DataInput''' object that has already been created and inserted into the '''IoSpecification''' object's '''dataInputs''' list at index 0.
<pre>
+
  <property name="dataInputRefs" ref="ioSpecification/dataInputs#0"/>
+
</pre>
+
initializes the '''dataInputRefs''' of the '''InputSet''' object with a reference to the newly created '''DataInput''' object that has already been created and inserted into the '''IoSpecification''' object's '''dataInputs''' list at index 0.
+
 
# Similarly, the next line creates a reference to the '''DataInupt''' object at '''IoSpecification''' '''dataInputs''' list index 1.
 
# Similarly, the next line creates a reference to the '''DataInupt''' object at '''IoSpecification''' '''dataInputs''' list index 1.
 
# The remaining bits of this example function similarly to initialize the '''DataInputAssociations''' of the '''IoSpecification'''.
 
# The remaining bits of this example function similarly to initialize the '''DataInputAssociations''' of the '''IoSpecification'''.
  
=== Use the <customTask> extension point ===
+
== The <customTask> extension point ==
=== BYOM! (bring your own model!) ===
+
This extension is similar to ''modelExtension'' (i.e. it is used to extend and initialize a BPMN2 object) but is used only for objects that have a visual representation on the editing canvas. The name "customTask" is a misnomer because it can be used with not just '''Tasks''', but any BPMN2 type that has a visual associated with it, for example '''SequenceFlow''', '''EventDefinition''', '''Gateway''', etc.
  
== Define a BPMN model extension ==
+
The ''customTask'' definitions also appear in the tool palette as individual tool items in a tool drawer of your choosing. A Java class is required to manage the lifecycle of ''customTask'' objects. This class is involved in the Graphiti framework during creation, update and deletion so that every visual aspect of the element can be controlled.
If you extend the [[BPMN2-Modeler/DeveloperTutorials/ExtendingRuntime|BPMN Runtime]] you also typically define a BPMN model extension where you specify custom data elements inside the BPMN file. You can use the Eclipse Modeling Framework to extend the BPMN model schema. Eclipse will create the necessary java classes out of the model description. To extend the model follow these steps:
+
  
# Create a new EMF Model
+
For a detailed explanation of how to implement the Java code behind a ''customTask'', see the [[BPMN2-Modeler/DeveloperTutorials/CreateCustomTask|Create a Custom Task]] tutorial.
# Generate a ‘genmodel’ file
+
# Generate Java Classes based on the genmodel file
+
# Define the BPMN Extension Points
+
 
+
===Create a new EMF model===
+
So in the beginning you need in to create  a new EMF model file:
+
 
+
* Create a new Folder in your plugin project named ‘model’
+
* add a new Ecore Model file in the new model folder – e.g. ‘mymodel.ecore’
+
 
+
In EMF Model File you need to define the “name” and “namespace” and add a new Child Element of type ‘Class’ into the model with the name ‘DocumentRoot’. (note: the root name should not include special characters!). Below the DocumentRoot add a new Child element “EAnotation” with the source URL ‘http:///org/eclipse/emf/ecore/util/ExtendedMetaData’. So far your model file looks like this:
+
 
+
<pre>
+
<?xml version="1.0" encoding="UTF-8"?>
+
<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+
xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore" name="model" nsURI="http://www.imixs.org/bpmn2"
+
nsPrefix="imixs">
+
<eClassifiers xsi:type="ecore:EClass" name="DocumentRoot">
+
<eAnnotations source="http:///org/eclipse/emf/ecore/util/ExtendedMetaData">
+
<details key="name" value=""/>
+
<details key="kind" value="mixed"/>
+
</eAnnotations>
+
</eClassifiers>
+
</ecore:EPackage>
+
</pre>
+
 
+
'''Note:''' the nsURI and the nsPrefix is from the Imixs-BPMN project. You should change these values.
+
 
+
This model file will now be the container for your extension features.
+
 
+
===Create Extension classes===
+
The next step is to define the additional  properties for your model extension. In the following example I add a new taskConfig element.
+
 
+
The ‘taskConfig’ element will be used by my custom Task created in Part-I. of the tutorial. The ‘taskConfig’ element contains a list of “Parameter” elements, each having a “name” and a “value” attribute. So this is a kind of Map Object:
+
 
+
<pre>
+
<?xml version="1.0" encoding="UTF-8"?>
+
<ecore:EPackage xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI"
+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore"
+
name="model" nsURI="http://www.imixs.org/bpmn2" nsPrefix="imixs">
+
<eClassifiers xsi:type="ecore:EClass" name="DocumentRoot">
+
<eAnnotations source="http:///org/eclipse/emf/ecore/util/ExtendedMetaData">
+
<details key="name" value="" />
+
<details key="kind" value="mixed" />
+
</eAnnotations>
+
<eStructuralFeatures xsi:type="ecore:EReference"
+
name="taskConfig" upperBound="-2" eType="#//TaskConfig" containment="true">
+
<eAnnotations source="http:///org/eclipse/emf/ecore/util/ExtendedMetaData">
+
<details key="name" value="taskConfig" />
+
<details key="kind" value="element" />
+
<details key="namespace" value="##targetNamespace" />
+
</eAnnotations>
+
</eStructuralFeatures>
+
</eClassifiers>
+
<eClassifiers xsi:type="ecore:EClass" name="Parameter">
+
<eStructuralFeatures xsi:type="ecore:EAttribute"
+
name="name"
+
eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString" />
+
<eStructuralFeatures xsi:type="ecore:EAttribute"
+
name="value"
+
eType="ecore:EDataType http://www.eclipse.org/emf/2002/Ecore#//EString" />
+
</eClassifiers>
+
<eClassifiers xsi:type="ecore:EClass" name="TaskConfig">
+
<eStructuralFeatures xsi:type="ecore:EReference"
+
name="parameters" upperBound="-1" eType="#//Parameter" containment="true">
+
<eAnnotations source="http:///org/eclipse/emf/ecore/util/ExtendedMetaData">
+
<details key="name" value="parameter" />
+
<details key="kind" value="element" />
+
<details key="namespace" value="##targetNamespace" />
+
</eAnnotations>
+
</eStructuralFeatures>
+
</eClassifiers>
+
</ecore:EPackage>
+
</pre>
+
 
+
===Create a genmodel file===
+
When the EMF file is defined you can create the genmodel file. Choose from the context menu of the EMF file:
+
 
+
<pre>‘New->Other->EMF Generator Model’</pre>
+
 
+
and create a new file ‘mymodel.genmodel’. Choose the importer ‘ecoremodel’. Click on load will verify the EMF model file created before.
+
 
+
Now you need to change some of the details of the new genfile. Open the genfile and choose the root element to change the property ‘Template & Merge – >Force Overwrite’ to ‘true’. Next select the package node (first child element) and add a package name to the ‘All -> Base Package’ Element in the package node. This will be the package for the generated model classes. Finally set ‘Model -> File Extention’ to ‘bpmn’ and the ‘Model -> Resource Type’ to ‘XML’. Save these changes. Your genmodel file now should looks like this:
+
 
+
<pre>
+
<?xml version="1.0" encoding="UTF-8"?>
+
<genmodel:GenModel xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:ecore="http://www.eclipse.org/emf/2002/Ecore"
+
    xmlns:genmodel="http://www.eclipse.org/emf/2002/GenModel" modelDirectory="/Imixs-BPMN2/src" modelPluginID="Imixs-BPMN2" forceOverwrite="true"
+
    modelName="Imixs" rootExtendsClass="org.eclipse.emf.ecore.impl.MinimalEObjectImpl$Container"
+
    importerID="org.eclipse.emf.importer.ecore" complianceLevel="7.0" copyrightFields="false"
+
    operationReflection="true" importOrganizing="true">
+
  <foreignModel>imixs.ecore</foreignModel>
+
  <genPackages prefix="Model" basePackage="org.imixs.bpmn" resource="XML" disposableProviderFactory="true"
+
      fileExtensions="bpmn" ecorePackage="imixs.ecore#/">
+
    <genClasses ecoreClass="imixs.ecore#//DocumentRoot">
+
      <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference imixs.ecore#//DocumentRoot/taskConfig"/>
+
    </genClasses>
+
    <genClasses ecoreClass="imixs.ecore#//Parameter">
+
      <genFeatures createChild="false" ecoreFeature="ecore:EAttribute imixs.ecore#//Parameter/name"/>
+
      <genFeatures createChild="false" ecoreFeature="ecore:EAttribute imixs.ecore#//Parameter/value"/>
+
    </genClasses>
+
    <genClasses ecoreClass="imixs.ecore#//TaskConfig">
+
      <genFeatures property="None" children="true" createChild="true" ecoreFeature="ecore:EReference imixs.ecore#//TaskConfig/parameters"/>
+
    </genClasses>
+
  </genPackages>
+
</genmodel:GenModel>
+
</pre>
+
 
+
===Generate java classes===
+
Now you can generate the java classes. Click on the root element of the genfile and choose ‘Generate Model Code’. This will create the class files in the predefined package.
+
 
+
Note: to avoid that classes will be overwritten later when you change the model definition, you can modify the @generated marker in the class comments with ‘NOT’. See:
+
 
+
<pre>
+
/**
+
* <!-- begin-user-doc -->
+
* The <b>Resource </b> associated with the package.
+
* <!-- end-user-doc -->
+
* @see org.imixs.bpmn.model.util.ModelResourceFactoryImpl
+
* @generated NOT
+
*/
+
public class ModelResourceImpl extends XMLResourceImpl {
+
.....
+
</pre>
+
 
+
This is necessary for the generated classes ‘MyModelResourceFactoryImpl’ and ‘MyModelResourceImpl’ which need to be changed like in the following example:
+
 
+
<pre>
+
/**
+
* <!-- begin-user-doc -->
+
* The <b>Resource </b> associated with the package.
+
* <!-- end-user-doc -->
+
* @see org.eclipse.bpmn2.modeler.examples.customtask.MyModel.util.MyModelResourceFactoryImpl
+
* @generated NOT
+
*/
+
public class MyModelResourceImpl extends Bpmn2ModelerResourceImpl {
+
/**
+
* Creates an instance of the resource.
+
* <!-- begin-user-doc -->
+
* <!-- end-user-doc -->
+
* @param uri the URI of the new resource.
+
* @generated
+
*/
+
public MyModelResourceImpl(URI uri) {
+
super(uri);
+
}
+
 
+
} //MyModelResourceImpl
+
/**
+
* <!-- begin-user-doc -->
+
* The <b>Resource Factory</b> associated with the package.
+
* <!-- end-user-doc -->
+
* @see org.eclipse.bpmn2.modeler.examples.customtask.MyModel.util.MyModelResourceImpl
+
* @generated NOT
+
*/
+
public class MyModelResourceFactoryImpl extends Bpmn2ModelerResourceFactoryImpl {
+
/**
+
* Creates an instance of the resource factory.
+
* <!-- begin-user-doc -->
+
* <!-- end-user-doc -->
+
* @generated
+
*/
+
public MyModelResourceFactoryImpl() {
+
super();
+
}
+
 
+
/**
+
* Creates an instance of the resource.
+
* <!-- begin-user-doc -->
+
* <!-- end-user-doc -->
+
* @generated
+
*/
+
@Override
+
public Resource createResource(URI uri) {
+
Bpmn2ModelerResourceImpl resource = new Bpmn2ModelerResourceImpl(uri);
+
return resource;
+
}
+
 
+
} //MyModelResourceFactoryImpl
+
</pre>
+
+
 
+
==Define the Extension points==
+
Now you can add the extension points to the runtime extension for the new model and the property section for the customTask element.
+
 
+
===Create a model extension point===
+
First add the model extension into the plugin.xml definition. Open the plugin.xml with the Eclipse Plugin Editor and go to the tab ‘extensions’. On the bpmn runtime extension point add a ‘model’ node and add there the generated ResourceFactory class. The RuntimeID has to match the predefined ID from the runtime.
+
 
+
[[Image:screen_04.png|center|thumb|500px|screen04]]
+
 
+
===Create a new Property extension point===
+
Finally you add a ‘propertyTab’ node.  The “runtime ID” is again the id form the runtime extension. Into the field ‘features’ add the value ‘extensionValues#TaskConfig.parameters’ to refer to your custom model extension. Into the field type add ‘org.eclipse.bpmn2.Task’.
+
 
+
[[Image:screen_05.png|center|thumb|500px|screen05]]
+
 
+
The features property is a space-separated list of features that you want to display in the property tab. In this case I only want one feature. If the feature is a list (or in this case a ‘Feature Map’) a “#” object type selector can be used to only display list items of a specific type. Since this is a list of objects, select only elements from the list that are “TaskConfig” objects; for each of those objects display the “parameters” feature, which is again a list. This notation can be extended as deeply as required by the model, but it is rarely necessary. For example, if the “parameters” list contained different types of objects, we could have specified a class of feature, for example:
+
 
+
<pre>extensionValues#TaskConfig.parameters#SomeOhterClass.someFeature</pre>
+
 
+
Every customTask extension must provide some method of identifying a given object using model one or more model features. This is done by defining a extension property that is added to the customTask object when it is constructed. In my example the feature name will be “type”. This dos not have be defined in my extension model because it will be created dynamically (in our model) at runtime.
+
 
+
The attribute ‘value’ should also have a value in case I want to define more than one customTask with different extensions. This value essentially identifies the FeatureContainerClass that will manager this object; if I have customTasks with different requirements, I may need to define different FeatureContainers for each. Look at the ‘TestTaskElementFeatureContainer1′ class in Part-I. of the tutorial.
+
 
+
This is the property should look like in the plugin.xml now:
+
 
+
<pre>
+
....
+
<property name="extensionValues">
+
<value>
+
<property name="taskConfig">
+
<value>
+
<property name="parameters">
+
<value>
+
<property name="name" value="taskName">
+
</property>
+
<property name="value" value="My custom task">
+
</property>
+
</value>
+
</property>
+
<property name="parameters">
+
<value>
+
<property name="name" value="processing time">
+
</property>
+
<property name="value" value="1 h">
+
</property>
+
</value>
+
</property>
+
</value>
+
</property>
+
</value>
+
</property>
+
....
+
</pre>
+
 
+
===Create a propertySection class===
+
The java class for the porpertyTab can be extended from the ‘org.eclipse.bpmn2.modeler.core.merrimac.clad.DefaultPropertySection’. See the following example:
+
 
+
<pre>
+
public class ImixsTaskPropertySection extends DefaultPropertySection {
+
public ImixsTaskPropertySection() {
+
super();
+
}
+
 
+
@Override
+
protected AbstractDetailComposite createSectionRoot() {
+
// This constructor is used to create the detail composite for use in the Property Viewer.
+
return new MyTaskDetailComposite(this);
+
}
+
 
+
@Override
+
public AbstractDetailComposite createSectionRoot(Composite parent, int style) {
+
// This constructor is used to create the detail composite for use in the popup Property Dialog.
+
return new MyTaskDetailComposite(parent, style);
+
}
+
 
+
public class MyTaskDetailComposite extends DefaultDetailComposite {
+
 
+
public MyTaskDetailComposite(AbstractBpmn2PropertySection section) {
+
super(section);
+
}
+
 
+
public MyTaskDetailComposite(Composite parent, int style) {
+
super(parent, style);
+
}
+
 
+
@Override
+
public void createBindings(EObject be) {
+
// This must be a Task because this Property Tab is only active for Tasks.
+
// The Property Tab will only display the Parameter list in our TaskConfig
+
// model element (see the definition of this element in MyModel.ecore).
+
Task myTask = (Task)be;
+
TaskConfig taskConfig = null;
+
// Fetch all TaskConfig extension objects from the Task
+
List<TaskConfig> allTaskConfigs = ModelDecorator.getAllExtensionAttributeValues(myTask, TaskConfig.class);
+
if (allTaskConfigs.size()==0) {
+
// There are none, so we need to construct a new TaskConfig
+
// which is required by the Property Sheet UI.
+
taskConfig = ModelFactory.eINSTANCE.createTaskConfig();
+
TargetRuntime rt = getTargetRuntime();
+
// We need our CustomTaskDescriptor for this Task. The ID must match
+
// the one defined in the <customTask> extension point in plugin.xml
+
CustomTaskDescriptor ctd = rt.getCustomTask("org.imixs.workflow.bpmn.customTask");
+
// Get the model feature for the "taskConfig" element name.
+
// Again, this must match the <property> element in <customTask>
+
EStructuralFeature feature = ctd.getModelDecorator().getEStructuralFeature(be, "taskConfig");
+
// Add the newly constructed TaskConfig object to the Task's Extension Values list.
+
// Note that we will delay the actual insertion of the new object until some feature
+
// of the object changes (e.g. the Parameter.name)
+
ModelDecorator.addExtensionAttributeValue(myTask, feature, taskConfig, true);
+
}
+
else {
+
// Else reuse the existing TaskConfig object.
+
taskConfig = allTaskConfigs.get(0);
+
}
+
// Display the Parameters list in TaskConfig
+
bindList(taskConfig, ModelPackage.eINSTANCE.getTaskConfig_Parameters());
+
}
+
}
+
}
+
</pre>
+
 
+
===Define a modelEnablement extension===
+
By default extension model features are “disabled”, that is they are not displayed in Property Sheets. You can enable these using a “modelEnablement” extension. The ID is again the runtimeID. Here you can control which BPMN elements in general will be available by your runtime extension. The section in your plugin.xml file can look like this:
+
 
+
<pre>
+
....
+
    <modelEnablement
+
            id="Imixs-BPMN2.modelEnablement2"
+
            profile="Full"
+
            ref="org.eclipse.bpmn2.modeler.runtime.none:Full"
+
            runtimeId="org.imixs.workflow.bpmn.runtime">
+
        <enable object="TaskConfig">
+
        </enable>
+
        <enable object="Parameter">
+
        </enable>
+
      </modelEnablement>
+
....
+
</pre>
+
  
==Test the plugin…==
+
== BYOM! (bring your own model!) ==
Now you can test again the result of your BPMN2 Plugin extention. If add your custom task element into the editor you will see the new property section.
+
Sometimes it becomes necessary to embed objects from an existing EMF model into a BPMN2 document. the BPMN 2.0 specification defines an extension mechanism using a special container element called (surprise!) "extensionElements". Feel free insert your application-specific model objects directly into this container, [http://www.merriam-webster.com/dictionary/willy-nilly willy-nilly], without fear of breaking BPMN 2.0 compliance!
  
[[Image:screen_06.png|thumb|500px|center|screen_06]]
+
The very detailed tutorial [[BPMN2-Modeler/DeveloperTutorials/ModelExtensionBYOM|Extending BPMN2 using an external EMF model]] shows you exactly how to do this, and provides some concrete examples.

Latest revision as of 10:30, 12 March 2015

Versions

This Tutorial was developed with Eclipse 4.4 (Luna) and BPMN2-Plugin version 1.1.1.

Model Extension: what is it, and why do I need it?

There are only two possible reasons for wanting to build a BPMN2 Modeler extension plugin that we can think of:

  1. We need a graphical tool that can build BPMN 2.0 compliant, executable process files for a particular BPM execution engine.
  2. We would like to use a graphical tool for documenting our business processes, but the BPMN 2.0 spec is insufficient for defining some particular piece of information that our business requires.

In both cases, you will likely need to extend the BPMN 2.0 language with your engine, or business-specific XML attributes and elements. In some cases you may even want to limit what is presented by the editor if you only need a small subset of BPMN 2.0, but this is a topic for another tutorial.

The BPMN2 Modeler extension API offers several different methods of extending the BPMN2 model, and each addresses a particular use-case as discussed next.

The <modelExtension> extension point

The BPMN2 Modeler core exposes the org.eclipse.bpmn2.modeler.core.org.eclipse.bpmn2.modeler.runtime plugin extension point. One of these extension elements is called "modelExtension". Please see the Core Extension Point API documentation for details.

This is used if you simply need to "decorate" a specific BPMN2 type with your own, business-specific attributes and elements, e.g. a UserTask or SequenceFlow. The jBPM extension plugin demonstrates a perfect example of when to use this extension:

    <modelExtension
        id="org.eclipse.bpmn2.modeler.runtime.jboss.jbpm5.modelExtension.sequenceFlow"
        runtimeId="org.jboss.runtime.jbpm5"
        name="%modelExtension.name.0"
        type="SequenceFlow">
        <property name="priority" value="1"/>
        <property name="name" value=""/>
    </modelExtension>

Here the type identifies a BPMN2 model type to which this extension applies, in this case a SequenceFlow. Note that we do not use the fully qualified "org.eclipse.bpmn2.SequenceFlow" class name because modelExtension is limited to only BPMN2 model objects and can not be used with external models.

This extension adds an attribute named priority to SequenceFlow; this attribute will be qualified with the "tns" prefix which corresponds to the targetNamespace "http://www.jboss.org/drools" defined by the jBPM plugin. The resulting XML looks like this:

    <bpmn2:sequenceFlow id="SequenceFlow_3" tns:priority="2" sourceRef="ExclusiveGateway_1" targetRef="Task_3"/>

As a bonus, this extension point is also used to override default initial values for existing BPMN2 attributes, and insert fully populated and initialized elements whenever a new BPMN2 object is created. Note that the name attribute in the example above is defined in the BPMN2 FlowElement. The default behavior of the editor is to automatically assign a generated name to all SequenceFlows, but the modelExtension example above initializes the name to an empty string.

A more complex example of this initialization capability is demonstrated by the jBPM UserTask model extension (truncated here for simplicity):

    <modelExtension
        id="org.eclipse.bpmn2.modeler.runtime.jboss.jbpm5.modelExtension.userTask"
        runtimeId="org.jboss.runtime.jbpm5"
        name="%modelExtension.name.12"
        type="UserTask">
        
        <property name="ioSpecification">
            <value>
                <property name="outputSets">
                    <value>
                        <property name="name" value="Output Set" type="EString"/>
                    </value>
                </property>
                <property name="dataInputs">
                    <value>
                        <property name="name" value="TaskName" type="EString"/>
                    </value>
                </property>
                <property name="dataInputs">
                    <value>
                        <property name="name" value="Priority" type="EInt"/>
                    </value>
                </property>
                
                ...

                <property name="inputSets">
                    <value>
                        <property name="dataInputRefs" ref="ioSpecification/dataInputs#0"/>
                        <property name="dataInputRefs" ref="ioSpecification/dataInputs#1"/>
                        
                        ...
                        
                    </value>
                </property>
            </value>
        </property>
        <property name="dataInputAssociations">
            <value>
                <property name="assignment">
                    <value>
                        <property name="from" type="FormalExpression">
                            <value>
                                <property name="body" value="Task Name"/>
                            </value>
                        </property>
                        <property name="to" type="FormalExpression">
                            <value>
                                <property name="body" ref="ioSpecification/dataInputs#0"/>
                            </value>
                        </property>
                    </value>
                </property>
                <property name="targetRef" ref="ioSpecification/dataInputs#0"/>
            </value>
        </property>
        <property name="dataInputAssociations">
            <value>
                <property name="assignment">
                    <value>
                        <property name="from" type="FormalExpression">
                            <value>
                                <property name="body" value="1"/>
                            </value>
                        </property>
                        <property name="to" type="FormalExpression">
                            <value>
                                <property name="body" ref="ioSpecification/dataInputs#1"/>
                            </value>
                        </property>
                    </value>
                </property>
                <property name="targetRef" ref="ioSpecification/dataInputs#1"/>
            </value>
        </property>
        
        ...
        
    </modelExtension>

There's a lot of stuff going on here, let's go through each of these extension point elements:

  1. This modelExtension applies to the BPMN2 UserTask type.
  2. All BPMN2 Activity objects (Activity is a super class of UserTask) have an optional ioSpecification container which defines inputs to, and outputs from the Activity. The line <property name="ioSpecification"> will cause a new IoSpecification object to be created and attached to the UserTask.
  3. The lines that follow populate the IoSpecification object with OutputSet, DataInputs and InputSet objects each of which are, in turn, constructed and initialized
  4. Within the inputSets definition, this line: <property name="dataInputRefs" ref="ioSpecification/dataInputs#0"/> initializes the dataInputRefs of the InputSet object with a reference to the newly created DataInput object that has already been created and inserted into the IoSpecification object's dataInputs list at index 0.
  5. Similarly, the next line creates a reference to the DataInupt object at IoSpecification dataInputs list index 1.
  6. The remaining bits of this example function similarly to initialize the DataInputAssociations of the IoSpecification.

The <customTask> extension point

This extension is similar to modelExtension (i.e. it is used to extend and initialize a BPMN2 object) but is used only for objects that have a visual representation on the editing canvas. The name "customTask" is a misnomer because it can be used with not just Tasks, but any BPMN2 type that has a visual associated with it, for example SequenceFlow, EventDefinition, Gateway, etc.

The customTask definitions also appear in the tool palette as individual tool items in a tool drawer of your choosing. A Java class is required to manage the lifecycle of customTask objects. This class is involved in the Graphiti framework during creation, update and deletion so that every visual aspect of the element can be controlled.

For a detailed explanation of how to implement the Java code behind a customTask, see the Create a Custom Task tutorial.

BYOM! (bring your own model!)

Sometimes it becomes necessary to embed objects from an existing EMF model into a BPMN2 document. the BPMN 2.0 specification defines an extension mechanism using a special container element called (surprise!) "extensionElements". Feel free insert your application-specific model objects directly into this container, willy-nilly, without fear of breaking BPMN 2.0 compliance!

The very detailed tutorial Extending BPMN2 using an external EMF model shows you exactly how to do this, and provides some concrete examples.

Back to the top