Invoke Decisions from Processes and Cases

BPMN & CMMN Integration

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

BPMN Business Rule Task

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

<definitions id="taskAssigneeExample"
  xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
  xmlns:camunda="http://camunda.org/schema/1.0/bpmn"
  targetNamespace="Examples">

  <process id="process">

    <!-- ... -->

    <businessRuleTask id="businessRuleTask"
                      camunda:decisionRef="myDecision"
                      camunda:mapDecisionResult="singleEntry"
                      camunda:resultVariable="result" />

    <!-- ... -->

  </process>
</definitions>

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

DMN Decision Task

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

<definitions id="definitions"
                  xmlns="http://www.omg.org/spec/CMMN/20151109/MODEL"
                  xmlns:camunda="http://camunda.org/schema/1.0/cmmn"
                  targetNamespace="Examples">
  <case id="case">
    <casePlanModel id="CasePlanModel_1">
      <planItem id="PI_DecisionTask_1" definitionRef="DecisionTask_1" />
      <decisionTask id="DecisionTask_1"
                    decisionRef="myDecision"
                    camunda:mapDecisionResult="singleEntry"
                    camunda:resultVariable="result">
      </decisionTask>
    </casePlanModel>
  </case>
</definitions>

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

The Decision Result

The output of the decision, also called decision result, is a complex object of type DmnDecisionTableResult. Generally, it is a list of key-value pairs. Each entry in the list represents one matched rule. The output entries of this rule are represented by the key-value pairs.

The type DmnDecisionTableResult provides methods from the List interface and some convenience methods like getSingleResult() or getFirstResult() to get the result of a matched rule. The rule results provide methods from the Map interface and also convenience methods like getSingleEntry() or getFirstEntry().

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

DmnDecisionTableResult decisionResult = ...;

Object value = decisionResult
  .getSingleResult()
  .getEntry("result");

It also provides methods to get typed output entries like getSingleEntryTyped(). Please refer to the User Guide for details about typed values. A complete list of all methods can be found in the Java Docs .

The decision result is available in the local scope of the executing task as a transient variable named decisionResult. It can be passed into a variable by using 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 use cases. The mapping is similar to an output variable mapping. It extracts a value from the decision result which is saved in a process/case variable. The following mappings are available:

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

Only the singleEntry mapper returns a typed value that wraps the value of the output entry and additional type information. The other mappers return collections which contain the value of the output entries as normal Java objects without additional type information.

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

Limitations of Serialization

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

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

BPMN:

<businessRuleTask id="businessRuleTask"
                  camunda:decisionRef="myDecision"
                  camunda:mapDecisionResult="singleEntry"
                  camunda:resultVariable="result" />

CMMN:

<decisionTask id="DecisionTask_1"
              decisionRef="myDecision"
              camunda:mapDecisionResult="singleEntry"
              camunda:resultVariable="result">

Name of the Result Variable

The result variable should not have the name decisionResult since the decision result itself is saved in a variable with this name. Otherwise an exception 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 used to pass the decision result into variables.

Limitations of Serialization

If you pass a collection or a complex object to a variable then you should consider 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 an output variable mapping.

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

<businessRuleTask id="businessRuleTask" camunda:decisionRef="myDecision">
  <extensionElements>
    <camunda:inputOutput>
      <camunda:outputParameter name="result">
        ${decisionResult.getSingleResult().result}
      </camunda:outputParameter>
      <camunda:outputParameter name="reason">
        ${decisionResult.getSingleResult().reason}
      </camunda:outputParameter>
    </camunda:inputOutput>
  </extensionElements>
</businessRuleTask>

In addition to an output variable mapping, the decision result can also be processed by an execution listener, which is attached to the business rule task.

<businessRuleTask id="businessRuleTask" camunda:decisionRef="myDecision">
  <extensionElements>
    <camunda:executionListener event="end"
      delegateExpression="${myDecisionResultListener}" />
  </extensionElements>
</businessRuleTask>
public class MyDecisionResultListener implements ExecutionListener {

  @Override
  public void notify(DelegateExecution execution) throws Exception {
    DmnDecisionTableResult decisionResult = (DmnDecisionTableResult) execution.getVariable("decisionResult");
    String result = decisionResult.getSingleResult().get("result");
    String reason = decisionResult.getSingleResult().get("reason");
    // ...
  }

}

Custom Mapping to Case Variables

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

<decisionTask id="decisionTask" decisionRef="myDecision">
  <extensionElements>
    <camunda:caseExecutionListener event="complete"
      class="org.camunda.bpm.example.MyDecisionResultListener" />
  </extensionElements>
</decisionTask>
public class MyDecisionResultListener implements CaseExecutionListener {

  @Override
  public void notify(DelegateCaseExecution caseExecution) throws Exception;
    DmnDecisionTableResult decisionResult = (DmnDecisionTableResult) caseExecution.getVariable("decisionResult");
    String result = decisionResult.getSingleResult().get("result");
    String reason = decisionResult.getSingleResult().get("reason");
    // ...
    caseExecution.setVariable("result", result);
    // ...
  }

}

Limitations of the Serialization of the Mapping Result

The predefined mappings singleResult, collectEntries and resultList map the decision result to Java collections. The implementation of the collections depend on the used JDK and contain untyped values as Objects. When a collection is saved as process/case variable then it is serialized as object value because there is no suitable primitive value type. Depending on the used object value serialization, this can lead to deserialization problems.

In case you are using the default built-in object serialization, the variable can not be deserialized if the JDK is upgraded or changed and contains an incompatible version of the collection class. Otherwise, if you are using another serialization like JSON then you should ensure that the untyped value is deserializable. For example, a collection of date values can not be deserialized using JSON because JSON has no registered mapper for date by default.

The same problems can occur by using a custom output variable mapping since DmnDecisionTableResult has methods that returns the same collections as the predefined mappers. Additionally, it is not recommended to save a DmnDecisionTableResult or a DmnRuleResult as process/case variable because the 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 control by yourself.

Accessing Variables from Decision Tables

DMN Decision tables contain multiple expressions which will be evaluated by the DMN engine. For more information about the expressions of a decision table please see our DMN 1.1 reference. These expressions can access all process/case variables which are available in the scope of the calling task. The variables are provided through a read-only variable context.

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

<input id="input">
  <!--
    this input expression will return the value
    of the process/case variable `foo`
  -->
  <inputExpression>
    <text>foo</text>
  </inputExpression>
</input>

The returned value of the process/case variable in the expression will be a normal Object and not a typed value. If you want to use the typed value in your expression, you have to get the variable from the variable context. The following snippet does the same as the above example. It gets the variable foo from the variable context and returns its unwrapped value.

<input id="input">
  <!--
    this input expression uses the variable context to
    get the typed value of the process/case variable `foo`
  -->
  <inputExpression>
    <text>
      variableContext.resolve("foo").getValue()
    </text>
  </inputExpression>
</input>

Expression Language Integration

By default, the DMN engine uses JUEL as expression language for input expressions and output entries. It uses FEEL as expression language for input entries. Please see the DMN engine guide for more information about expression languages.

Accessing Beans

If the DMN engine is invoked by the Camunda BPM platform, it uses the same JUEL configuration as the Camunda BPM engine. Therefore, it is also possible to access Spring and CDI Beans from JUEL expressions in decision tables. For more information on this integration, please see the corresponding section 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 than has to be added to the process engine configuration after it was initialized.

ProcessEngineConfigurationImpl processEngineConfiguration = (ProcessEngineConfigurationImpl) processEngine
  .getProcessEngineConfiguration();

processEngineConfiguration
  .getExpressionManager()
  .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 expressions in the platform, not only in DMN decision tables.

On this Page: