BPMN & CMMN Integration

This section explains how to invoke DMN decision from BPMN and CMMN.

BPMN Business Rule Task

The BPMN business rule task can reference a deployed decisiondefinition. The decision definition is evaluated when the task is executed.

  1. <definitions id="taskAssigneeExample"
  2. xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
  3. xmlns:camunda="http://camunda.org/schema/1.0/bpmn"
  4. targetNamespace="Examples">
  5. <process id="process">
  6. <!-- ... -->
  7. <businessRuleTask id="businessRuleTask"
  8. camunda:decisionRef="myDecision"
  9. camunda:mapDecisionResult="singleEntry"
  10. camunda:resultVariable="result" />
  11. <!-- ... -->
  12. </process>
  13. </definitions>

For more information on how to reference a decision definition from a businessrule task, please refer to the BPMN 2.0 reference.

DMN Decision Task

The CMMN decision task references a deployed decision definition.The decision definition is invoked when the task is activated.

  1. <definitions id="definitions"
  2. xmlns="http://www.omg.org/spec/CMMN/20151109/MODEL"
  3. xmlns:camunda="http://camunda.org/schema/1.0/cmmn"
  4. targetNamespace="Examples">
  5. <case id="case">
  6. <casePlanModel id="CasePlanModel_1">
  7. <planItem id="PI_DecisionTask_1" definitionRef="DecisionTask_1" />
  8. <decisionTask id="DecisionTask_1"
  9. decisionRef="myDecision"
  10. camunda:mapDecisionResult="singleEntry"
  11. camunda:resultVariable="result">
  12. </decisionTask>
  13. </casePlanModel>
  14. </case>
  15. </definitions>

For more information on how to reference a decision definition from a decisiontask, please refer to the CMMN 1.1 reference.

The Decision Result

The output of the decision, also called decision result, is a complex object oftype DmnDecisionResult. Generally, it is a list of key-value pairs.

If the decision is implemented as decision table then each entry in the list represents one matched rule. The output entries of thisrule are represented by the key-value pairs. The key of a pair is specified bythe name of the output.

Instead, if the decision is implemented as decision literal expression then the list contains only one entry. This entry represents the expression value and is mapped by the variable name.

The type DmnDecisionResult provides methods from the List interfaceand some convenience methods like getSingleResult() or getFirstResult() toget the result of a matched rule. The rule results provide methods from theMap interface and also convenience methods like getSingleEntry() orgetFirstEntry().

If the decision result contains only a single output value (e.g., evaluating a decision literal expression) thenthe value can be retrieved from the result using the getSingleEntry() methodwhich combines getSingleResult() and getSingleEntry().

For example, the following code returns the output entry with name result ofthe only matched rule.

  1. DmnDecisionResult decisionResult = ...;
  2. Object value = decisionResult
  3. .getSingleResult()
  4. .getEntry("result");

It also provides methods to get typed output entries likegetSingleEntryTyped(). Please refer to the User Guide fordetails about typed values. A complete list of all methods can be found in theJava Docs.

The decision result is available in the local scope of the executing task as atransient variable named decisionResult. It can be passed into a variable byusing a predefined or a custom mapping of the decision result, if necessary.

Predefined Mapping of the Decision Result

The engine includes predefined mappings of the decision result for common usecases. The mapping is similar to an output variable mapping. It extracts avalue from the decision result which is saved in a process/case variable. Thefollowing mappings are available:

Mapper Result Is suitable for
singleEntry TypedValue decision literal expressions and decision tables with no more than one matching rule and only one output
singleResult Map<String, Object> decision tables with no more than one matching rule
collectEntries List<Object> decision tables with multiple matching rules and only one output
resultList List<Map<String, Object>> decision tables with multiple matching rules and multiple outputs

Only the singleEntry mapper returns a typed value thatwraps the value of the output entry and additional type information. Theother mappers return collections which contain the value of the output entriesas normal Java objects without additional type information.

Note that the mapper throws an exception if the decision result is notsuitable. For example, the singleEntry mapper throws an exception if thedecision result contains more than one matched rule.

Limitations of Serialization

If you are using one of the predefined mappers singleResult, collectEntriesor resultList then you should consider the limitations of serialization.

To specify the name of the process/case variable to store the result of themapping, the camunda:resultVariable attribute is used.

BPMN:

  1. <businessRuleTask id="businessRuleTask"
  2. camunda:decisionRef="myDecision"
  3. camunda:mapDecisionResult="singleEntry"
  4. camunda:resultVariable="result" />

CMMN:

  1. <decisionTask id="DecisionTask_1"
  2. decisionRef="myDecision"
  3. camunda:mapDecisionResult="singleEntry"
  4. camunda:resultVariable="result">

Name of the Result Variable

The result variable should not have the name decisionResult since thedecision result itself is saved in a variable with this name. Otherwise anexception is thrown while saving the result variable.

Custom Mapping of the Decision Result

Instead of a predefined mapping, a custom decision result mapping can be usedto pass the decision result into variables.

Limitations of Serialization

If you pass a collection or a complex object to a variable then you shouldconsider the limitations of serialization.

Custom Mapping to Process Variables

If a business rule task is used to invoke a decision inside a BPMN process,then the decision result can be passed into process variables by using anoutput variable mapping.

For example, if the decision result has multiple output values which should besaved in separate process variables this can be done achieved by defining anoutput mapping on the business rule task.

  1. <businessRuleTask id="businessRuleTask" camunda:decisionRef="myDecision">
  2. <extensionElements>
  3. <camunda:inputOutput>
  4. <camunda:outputParameter name="result">
  5. ${decisionResult.getSingleResult().result}
  6. </camunda:outputParameter>
  7. <camunda:outputParameter name="reason">
  8. ${decisionResult.getSingleResult().reason}
  9. </camunda:outputParameter>
  10. </camunda:inputOutput>
  11. </extensionElements>
  12. </businessRuleTask>

In addition to an output variable mapping, the decision result can also beprocessed by an execution listener, which is attached to the business ruletask.

  1. <businessRuleTask id="businessRuleTask" camunda:decisionRef="myDecision">
  2. <extensionElements>
  3. <camunda:executionListener event="end"
  4. delegateExpression="${myDecisionResultListener}" />
  5. </extensionElements>
  6. </businessRuleTask>
  1. public class MyDecisionResultListener implements ExecutionListener {
  2. @Override
  3. public void notify(DelegateExecution execution) throws Exception {
  4. DmnDecisionResult decisionResult = (DmnDecisionResult) execution.getVariable("decisionResult");
  5. String result = decisionResult.getSingleResult().get("result");
  6. String reason = decisionResult.getSingleResult().get("reason");
  7. // ...
  8. }
  9. }

Custom Mapping to Case Variables

If a decision task is used to invoke a decision inside a CMMN case, thedecision result can be passed to a case variable by using a case executionlistener which is attached to the decision task.

  1. <decisionTask id="decisionTask" decisionRef="myDecision">
  2. <extensionElements>
  3. <camunda:caseExecutionListener event="complete"
  4. class="org.camunda.bpm.example.MyDecisionResultListener" />
  5. </extensionElements>
  6. </decisionTask>
  1. public class MyDecisionResultListener implements CaseExecutionListener {
  2. @Override
  3. public void notify(DelegateCaseExecution caseExecution) throws Exception;
  4. DmnDecisionResult decisionResult = (DmnDecisionResult) caseExecution.getVariable("decisionResult");
  5. String result = decisionResult.getSingleResult().get("result");
  6. String reason = decisionResult.getSingleResult().get("reason");
  7. // ...
  8. caseExecution.setVariable("result", result);
  9. // ...
  10. }
  11. }

Limitations of the Serialization of the Mapping Result

The predefined mappings singleResult, collectEntries and resultList mapthe decision result to Java collections. The implementation of the collectionsdepends on the used JDK and contains untyped values as Objects. When a collectionis saved as process/case variable then it is serialized as object value becausethere is no suitable primitive value type. Depending on the used object valueserialization, this can lead to deserialization problems.

In case you are using the default built-in object serialization, the variablecan not be deserialized if the JDK is updated or changed and contains anincompatible version of the collection class. Otherwise, if you are usinganother serialization like JSON then you should ensure that the untyped valueis deserializable. For example, a collection of date values can not bedeserialized using JSON because JSON has no registered mapper for date bydefault.

The same problems can occur by using a custom output variable mapping sinceDmnDecisionResult has methods that return the same collections as thepredefined mappers. Additionally, it is not recommended to save aDmnDecisionResult or a DmnDecisionResultEntries as process/case variable becausethe underlying implementation can change in a new version of Camunda BPM.

To prevent any of these problems, you should use primitive variables only.Alternatively, you can use a custom object for serialization that you controlby yourself.

Accessing Variables from Decisions

DMN Decision tables and Decision Literal Expressions contain multiple expressions which will be evaluated by theDMN engine. For more information about the expressions of a decisionplease see our DMN 1.1 reference. These expressions canaccess all process/case variables which are available in the scope of thecalling task. The variables are provided through a read-only variable context.

As a shorthand, process/case variables can be directly referenced by name inexpressions. For example, if a process variable foo exists, then thisvariable can be used in an input expression, input entry and output entry of a decision tableby its name.

  1. <input id="input">
  2. <!--
  3. this input expression will return the value
  4. of the process/case variable `foo`
  5. -->
  6. <inputExpression>
  7. <text>foo</text>
  8. </inputExpression>
  9. </input>

The returned value of the process/case variable in the expression willbe a normal object and not a typed value. If you wantto use the typed value in your expression, you have to get the variablefrom the variable context. The following snippet does the same as the aboveexample. It gets the variable foo from the variable context and returnsits unwrapped value.

  1. <input id="input">
  2. <!--
  3. this input expression uses the variable context to
  4. get the typed value of the process/case variable `foo`
  5. -->
  6. <inputExpression>
  7. <text>
  8. variableContext.resolve("foo").getValue()
  9. </text>
  10. </inputExpression>
  11. </input>

Expression Language Integration

By default, the DMN engine uses JUEL as expression language for inputexpressions, output entries and literal expressions. It uses FEEL as expression language for inputentries. Please see the DMN engine guide for moreinformation about expression languages.

Accessing Beans

If the DMN engine is invoked by the Camunda BPM platform, it uses the sameJUEL configuration as the Camunda BPM engine. Therefore, it is alsopossible to access Spring and CDI Beans from JUEL expressions in decisions.For more information on this integration, please see the correspondingsection in the Spring and CDI guides.

Extending the Expression Language

Use of Internal API

These APIs are not part of the public API and may change in later releases.

It is possible to add own functions which can be used inside JUEL expressions.Therefore a new FunctionMapper has to be implemented. The function mapper thanhas to be added to the process engine configuration after it wasinitialized.

  1. ProcessEngineConfigurationImpl processEngineConfiguration = (ProcessEngineConfigurationImpl) processEngine
  2. .getProcessEngineConfiguration();
  3. processEngineConfiguration
  4. .getExpressionManager()
  5. .addFunctionMapper(new MyFunctionMapper());

This can be done, for example, by creating a process engine plugin.

Please note that these functions are available in all JUEL expressionsin the platform, not only in DMN decisions.

原文: https://docs.camunda.org/manual/7.9/user-guide/process-engine/decisions/bpmn-cmmn/