kogitoLogo

Welcome

1. Kogito business automation

Kogito is a cloud-native business automation technology for building cloud-ready business applications. The name Kogito derives from the Latin "Cogito", as in "Cogito, ergo sum" ("I think, therefore I am"), and is pronounced [ˈkoː.d͡ʒi.to] (KO-jee-to). The letter K refers to Kubernetes, the base for OpenShift as the target cloud platform for Kogito, and to the Knowledge Is Everything (KIE) open source business automation project from which Kogito originates.

Kogito is optimized for a hybrid cloud environment and adapts to your domain and tooling needs. The core objective of Kogito is to help you mold a set of business processes and decisions into your own domain-specific cloud-native set of services.

Image of business assets moving to cloud services
Figure 1. Business processes and decisions to cloud services

When you use Kogito, you are building a cloud-native application as a set of independent domain-specific services to achieve some business value. The processes and decisions that you use to describe the target behavior are executed as part of the services that you create. The resulting services are highly distributed and scalable with no centralized orchestration service, and the runtime that your service uses is optimized for what your service needs.

Kogito includes components that are based on well-known business automation KIE projects, specifically Drools, jBPM, and OptaPlanner, to offer dependable open source solutions for business rules, business processes, and constraint solving.

1.1. Cloud-first priority

Kogito is designed to run and scale on a cloud infrastructure. You can use Kogito with the latest cloud-based technologies, such as Quarkus, Knative, and Apache Kafka, to increase start times and instant scaling on container application platforms, such as OpenShift.

Image of cloud-based technologies
Figure 2. Technologies used with Kogito

For example, Kogito is compatible with the following technologies:

  • OpenShift, based on Kubernetes, is the target platform for building and managing containerized applications.

  • Quarkus is native Java stack for Kubernetes that you can use to build applications with Kogito services.

  • Spring Boot is also supported with Kogito so that you can use the Spring Framework with Kogito.

  • GraalVM with Quarkus enables you to use native compilation with Kogito, resulting in fast start-up times and minimal footprint. For example, a native Kogito service starts in about 0.003ms, about 100 times faster than a non-native start-up. Fast start-up is almost a necessity in a cloud ecosystem, especially if you need small serverless applications.

  • Knative enables you to build serverless applications with Kogito that you can scale up or down (to zero) as needed.

  • Prometheus and Grafana are compatible with Kogito services for monitoring and analytics with optional extensions.

  • Infinispan and MongoDB are middleware technologies that Kogito supports for persistence.

  • Kafka and Keycloak are middleware technologies that Kogito supports for messaging and security.

1.2. Domain-specific flexibility

Kogito adapts to your business domain instead of forcing you to modify your domain to work with Kogito. You can expose your Kogito services with domain-specific APIs, based on the processes and decisions that you have defined. Domain-specific APIs for Kogito services do not require third-party or internal APIs.

For example, a process for onboarding employees could generate remote REST API endpoints that you can use to onboard new employees or get information on their status, all using domain-specific JSON data.

Image of REST API endpoints in Swagger UI
Figure 3. Example custom API endpoints in Swagger

You can also expose domain-specific data through events or in a data index so that the data can be consumed and queried by other services.

1.3. Developer-centered experience

Another focus of Kogito is optimal developer experience. You can use much or all of your existing tooling and workflow to develop, build, and deploy Kogito services, whether locally for testing or into the cloud. Quarkus offers development-mode features to help with local testing, such as live reload of your processes and decisions in your running applications for advanced debugging.

Kogito tooling is embeddable so that you can continue using the workflow you already use for cloud-native services. For example, the Kogito Bundle VSCode extension enables you to edit your Business Process Model and Notation (BPMN) 2.0 business processes and Decision Model and Notation (DMN) decision models directly in your VSCode IDE, next to your other application code.

Demo of Kogito BPMN2 extension in VSCode
Figure 4. Building a Kogito process service in VSCode

To deploy your services into the cloud, you can use the Kogito Operator, which guides you through every step. The Kogito Operator is based on the Operator SDK and automates many of the deployment steps for you. For example, when you give the operator a link to the Git repository that contains your application, the operator can automatically configure the components required to build your project from source and deploy the resulting services. Kogito also offers a command-line interface (CLI) to simplify some of these deployment tasks.

1.4. Kogito on Quarkus and Spring Boot

The primary Java frameworks that Kogito supports are Quarkus (recommended) and Spring Boot.

Quarkus is a Kubernetes-native Java framework with a container-first approach to building Java applications, especially for Java virtual machines (JVMs) such as GraalVM and HotSpot. Quarkus optimizes Java specifically for Kubernetes by reducing the size of both the Java application and container image footprint, eliminating some of the Java programming workload from previous generations, and reducing the amount of memory required to run those images.

For Kogito services, Quarkus is the preferred framework for optimal Kubernetes compatibility and enhanced developer features, such as live reload in development mode for advanced debugging.

Spring Boot is a Java-based framework for building standalone production-ready Spring applications. Spring Boot enables you to develop Spring applications with minimal configurations and without an entire Spring configuration setup.

For Kogito services, Spring Boot is supported for developers who need to use Kogito in an existing Spring Framework environment.

2. Creating and running your first Kogito services

As a developer of business processes and decisions, you can use Kogito business automation to build cloud-native applications that adapt to your business domain and tooling.

Prerequisites
  • JDK 11 or later is installed.

  • Apache Maven 3.6.2 or later is installed.

2.1. Example applications with Kogito services

Kogito includes example applications in the kogito-examples repository in GitHub. These example applications contain various types of Kogito services on Quarkus or Spring Boot to help you develop your own applications. The services use one or more Business Process Model and Notation (BPMN) process models, Decision Model and Notation (DMN) decision models, Drools Rule Language (DRL) rule units, XLS spreadsheet decision tables, or Java classes to define the service logic.

For information about each example application and instructions for using them, see the README file in the relevant application folder.

In the kogito-examples repository in GitHub, the example applications in the default stable branch use the latest version of Kogito. When you run examples in a local environment, ensure that the environment matches the requirements that are listed in the README file of the relevant application folder. Also, this might require making the necessary network ports available, as configured for Quarkus, Spring Boot, and docker-compose where applicable.

The following list describes some of the examples provided with Kogito:

Decision services
  • dmn-quarkus-example and dmn-springboot-example: A decision service (on Quarkus or Spring Boot) that uses DMN to determine driver penalty and suspension based on traffic violations.

  • rules-quarkus-helloworld: A Hello World decision service on Quarkus with a single DRL rule unit.

  • ruleunit-quarkus-example and ruleunit-springboot-example: A decision service (on Quarkus or Spring Boot) that uses DRL with rule units to validate a loan application and that exposes REST operations to view application status.

  • dmn-event-driven-quarkus and dmn-event-driven-springboot: A DMN decision service on Quarkus and Spring Boot that demonstrates the capability of the Kogito Event-Driven decisions add-on.

Decision services that use the Kogito Trusty Service and Explainability Service
  • dmn-tracing-quarkus: A DMN decision service on Quarkus that uses the kogito-addons-quarkus-tracing-decision add-on to generate tracing events that the Kogito Trusty Service and Explainability Service can consume and expose.

  • trusty-demonstration: A tutorial for deploying the dmn-tracing-quarkus example application on Kubernetes as a demonstration of Kogito Trusty Service and Explainability Service capabilities in a cloud environment.

  • dmn-tracing-quarkus: A DMN service on Quarkus to evaluate a loan approval and generate tracing events that might be consumed by the Trusty service.

Process services
  • process-quarkus-example and process-springboot-example: A process service (on Quarkus or Spring Boot) for ordering items and that exposes REST operations to create new orders or to list and delete active orders.

  • onboarding-example: A combination of a process service and two decision services that use DMN and DRL for onboarding new employees.

  • flexible-process-quarkus and flexible-process-springboot: A process service (on Quarkus or Spring Boot) that uses flexible processes for case management in Kogito.

  • process-usertasks-quarkus-with-console and process-usertasks-springboot-with-console: A process service with user tasks (on Quarkus or Spring Boot), displaying the entire user task lifecycle using the Kogito Management and Task Console.

  • process-springboot-example: A process service on Spring Boot for ordering items using add-ons.

  • process-usertasks-with-security-oidc-quarkus-with-console: A quickstart project using add-ons to show typical user task orchestration.

Process services that use Decision services
  • process-decision-quarkus: A process service integrated with decisions services, using an embedded method, in which business rule tasks are used for communication. The decisions are declared with DMN and DRL.

  • process-decision-rest-quarkus: A process service integrated with decisions services, using REST endpoints for remote communication. The decisions are declared with DMN and DRL.

Process services that use OptaPlanner
  • process-optaplanner-quarkus and process-optaplanner-springboot: A process service (on Quarkus or Spring Boot) that uses OptaPlanner to optimize airline flight schedules and seat assignments. For information about OptaPlanner integration with Kogito, see the OptaPlanner documentation.

Process services for OpenShift deployment
  • kogito-travel-agency: A combination of process services and decision services that use DRL and XLS for travel booking, intended for deployment on OpenShift.

Serverless Workflow services
  • serverless-workflow-greeting-quarkus and serverless-workflow-greeting-springboot: A Serverless Workflow greeting service with both JSON and YAML workflow definitions.

  • serverless-workflow-service-calls-quarkus and serverless-workflow-service-calls-springboot: A Serverless Workflow service for returning country information.

  • serverless-workflow-events-quarkus: A Serverless Workflow service for processing job applicant approvals and that showcases event-driven services.

  • serverless-workflow-github-showcase: A Serverless Workflow service deployed as a GitHub bot application that reacts upon a new PR being opened in a given GitHub project.

Process services that use MongoDB
  • process-mongodb-persistence-quarkus and process-mongodb-persistence-springboot: A quickstart project on (Quarkus and Spring Boot) using add-ons that process deals for travelers using MongoDB.

2.1.1. Running the Kogito example applications

To get started quickly with Kogito, you can run any of the example applications in the kogito-examples repository in GitHub and experiment with the Kogito services.

For this procedure, use the process-quarkus-example or process-springboot-example application. You can follow similar steps with the other Kogito examples on Quarkus or Spring Boot.

In the process-quarkus-example and process-springboot-example applications, the orders.bpmn2 process describes the steps that need to be followed when ordering items. The process includes a script task for writing debug information and a call activity for invoking a subprocess, using a custom Order data object.

Image of `orders.bpmn` example process
Figure 5. Example orders.bpmn2 process

The Add items subprocess invokes the following orderItems.bpmn2 process, which uses a CalculationService.calculateTotal custom Java service and a user task to verify the order.

Image of `orderItems.bpmn` example process
Figure 6. Example orderItems.bpmn2 process invoked as a subprocess

The persons.bpmn2 process invokes a Drools Rule Language (DRL) rule unit in a business rule task to determine customer age, followed by a user task for special handling requirements for children, if applicable.

Image of `persons` process diagram
Figure 7. Example persons.bpmn2 process invoked as a subprocess

Based on these processes and on application configurations, this example service exposes REST operations to create new orders, to list and delete active orders, and to determine the age of a specified person.

Procedure
  1. Download the latest kogito-examples release to a local directory and extract the file.

  2. In a command terminal, navigate to the extracted kogito-examples-RELEASE/process-PLATFORM-example folder, and enter one of the following commands to build and run the example.

    Quarkus and Spring Boot support the following run modes:

    • Development mode: For local testing. On Quarkus, development mode also offers live reload of your processes and decisions in your running applications for advanced debugging.

    • JVM mode: For compatibility with a Java virtual machine (JVM).

    • Native mode: (Quarkus only, requires GraalVM or Mandrel) For direct binary execution as native code.

    The command that you use depends on your preferred run mode and application environment:

    • For development mode:

      On Quarkus
      $ mvn clean compile quarkus:dev
      On Sprint Boot
      $ mvn clean compile spring-boot:run
    • For JVM mode:

      On Quarkus
      $ mvn clean package
      $ java -jar target/quarkus-app/quarkus-run.jar
      On Spring Boot
      $ mvn clean package
      $ java -jar target/sample-kogito-1.0-SNAPSHOT-runner.jar
    • For native mode (requires GraalVM or Mandrel):

      On Quarkus only
      $ mvn clean package -Dnative
      $ ./target/sample-kogito-1.0-SNAPSHOT-runner
  3. After the Kogito service is running, use a REST client, curl utility, or the Swagger UI configured for the application (such as http://localhost:8080/q/swagger-ui or http://localhost:8080/swagger-ui.html) to send API requests with the following components:

    • URL: http://localhost:8080/

    • HTTP headers: For POST and PUT requests only:

      • accept: application/json

      • content-type: application/json

    • HTTP methods: GET, POST, PUT or DELETE

    Example POST request body to create an order (JSON)
    {
      "approver": "john",
      "order": {
        "orderNumber": "12345",
        "shipped": false
      }
    }
    Example curl command to create an order
    curl -X POST http://localhost:8080/orders -H 'content-type: application/json' -H 'accept: application/json' -d '{"approver" : "john", "order" : {"orderNumber" : "12345", "shipped" : false}}'
    Example response (JSON)
    {
      "approver": "john",
      "id": "6b53c227-6d5e-40b7-8c8c-a541a2a47d58",
      "order": {
        "orderNumber": "12345",
        "shipped": false,
        "total": 0.3845152065899532
      }
    }
    Image of Swagger UI for example application
    Figure 8. Swagger UI to interact with all application endpoints (such as http://localhost:8080/q/swagger-ui or http://localhost:8080/swagger-ui.html)
    For the predefined Kogito example applications, the Swagger UI for interacting with service endpoints is available only on Quarkus examples that you run in development mode or in native mode.

    This example procedure uses the following curl commands for convenience:

    • Create an order:

      Example request
      curl -X POST http://localhost:8080/orders -H 'content-type: application/json' -H 'accept: application/json' -d '{"approver" : "john", "order" : {"orderNumber" : "12345", "shipped" : false}}'
      Example response
      {"approver":"john","id":"6b53c227-6d5e-40b7-8c8c-a541a2a47d58","order":{"orderNumber":"12345","shipped":false,"total":0.3845152065899532}}

      The new order has an "id" field with a generated UUID that you can use to retrieve details about this specific order, if needed.

    • View active orders:

      Example request
      curl -X GET http://localhost:8080/orders -H 'content-type: application/json' -H 'accept: application/json'
      Example response
      [{"approver":"john","id":"6b53c227-6d5e-40b7-8c8c-a541a2a47d58","order":{"orderNumber":"12345","shipped":false,"total":0.3845152065899532}}]
    • View order details using the returned UUID:

      Example request
      curl -X GET http://localhost:8080/orders/6b53c227-6d5e-40b7-8c8c-a541a2a47d58 -H 'content-type: application/json' -H 'accept: application/json'
      Example response
      {"approver":"john","id":"6b53c227-6d5e-40b7-8c8c-a541a2a47d58","order":{"orderNumber":"12345","shipped":false,"total":0.3845152065899532}}

      You use the "id" value for the order that was returned when you created the order or when you retrieved active orders.

    • Cancel the order using the returned UUID:

      Example request
      curl -X DELETE http://localhost:8080/orders/6b53c227-6d5e-40b7-8c8c-a541a2a47d58 -H 'content-type: application/json' -H 'accept: application/json'

2.1.2. Interacting with Kogito Management and Task Console using example applications

You can interact with the Kogito Management and Task Console to run the processes using the example applications in the kogito-examples repository in GitHub. The example applications containing the with-consoles suffix, allow you to experiment with the Kogito services and Kogito Management and Task Console.

The Management and Task Console need to interact with the Kogito Data Index service using minimal persistence configuration, which you can perform using docker-compose configuration. The example applications provide docker-compose configuration to configure and start the required services, including Kafka, Infinispan, Kogito Data Index, Kogito Management Console, and Kogito Task Console.

Image of default services started by docker-compose 'with-console' examples
Figure 9. Default services started by docker-compose 'with-console' example applications
In each example application, the docker-compose starts the needed services to run the project. The docker-compose also configures additional services such as Keycloak (process-usertasks-with-security-oidc-PLATFORM-with-console) or Kogito Jobs (process-usertasks-timer-quarkus-with-console).
Procedure
  1. Download the latest kogito-examples release to a local directory and extract the file.

  2. In a command terminal, navigate to the extracted kogito-examples-RELEASE/process-usertask-PLATFORM__-with-console folder

  3. Compile the project to generate the persistence file descriptor, which docker-compose uses.

  4. From the docker-compose folder, execute ./startServices.sh to start the containers with the services needed in each case.

  5. Enter one of the following commands to build and run the example:

    Quarkus and Spring Boot support the following run modes:

    • Development mode: For local testing. On Quarkus, development mode also offers live reload of your processes and decisions in your running applications for advanced debugging.

    • JVM mode: For compatibility with a Java virtual machine (JVM).

    • Native mode: (Quarkus only, requires GraalVM or Mandrel) For direct binary execution as native code.

    The command that you use depends on your preferred run mode and application environment:

    • For development mode:

      On Quarkus
      $ mvn clean compile quarkus:dev
      On Sprint Boot
      $ mvn clean compile spring-boot:run
    • For JVM mode:

      On Quarkus
      $ mvn clean package
      $ java -jar target/quarkus-app/quarkus-run.jar
      On Spring Boot
      $ mvn clean package
      $ java -jar target/sample-kogito-1.0-SNAPSHOT-runner.jar
    • For native mode (requires GraalVM or Mandrel):

      On Quarkus only
      $ mvn clean package -Dnative
      $ ./target/sample-kogito-1.0-SNAPSHOT-runner
  6. When the Kogito service is running, use a REST client, curl utility, or the Swagger UI (if configured for the application) to start a new request.

  7. Use the Kogito Management console to see the created process instance available by default at http://localhost:8280.

  8. Use the Kogito Task console available by default at http://localhost:8380 to execute the user tasks.

In case some ports need to be changed, you can find the configuration of the containers in the docker-compose/docker-compose.yml file.

2.2. Kogito BPMN and DMN modelers

Kogito provides the following extensions or applications that you can use to design Business Process Model and Notation (BPMN) process models and Decision Model and Notation (DMN) decision models for your Kogito services using graphical modelers.

For convenience, all Kogito BPMN and DMN modelers are available in the Business Modeler Hub desktop application.

  • Kogito VSCode extension: (Recommended) Enables you to view and design BPMN models, DMN models, and test scenario files in Visual Studio Code (VSCode). The VSCode extension in the Kogito Business Modeler Hub requires VSCode 1.46.0 or later.

    To install the Kogito VSCode extension directly in VSCode without the Kogito Business Modeler Hub, select the Extensions menu option in VSCode and search for and install the Kogito Bundle extension.

  • Kogito GitHub Chrome extension: Enables you to view and design BPMN and DMN models in GitHub repositories in Google Chrome.

    To install the Kogito GitHub Chrome extension without the Kogito Business Modeler Hub, you can download and extract the chrome_extension_kogito_kie_editors_VERSION.zip file from the kogito-tooling releases page in GitHub, and then in the upper-right corner in Chrome, go to Customize and controlSettingsExtensionsLoad unpacked and open the extracted dist folder.

  • Business Modeler desktop application: Enables you to view and design BPMN and DMN models locally.

    To run the Kogito Business Modeler desktop application without the Kogito Business Modeler Hub, you can download and extract the business_modeler_preview_RELEASE.zip file from the kogito-tooling releases page in GitHub, and then follow the instructions in the application README file to run the application on your specific operating system.

  • Business Modeler online viewer: Enables you to view and design BPMN and DMN models online at https://kiegroup.github.io/kogito-online/. The online modeler supports the .new format, so you can also enter bpmn.new or dmn.new in a web browser to start designing a new BPMN or DMN model in the online modeler.

  • Business Modeler standalone editors: Enable you to view and design BPMN and DMN models embedded in your web applications. To download the necessary files, you can either use the NPM artifacts from the Kogito tooling repository or download the JavaScript files directly for the DMN standalone editor library at https://kiegroup.github.io/kogito-online/standalone/dmn/index.js and for the BPMN standalone editor library at https://kiegroup.github.io/kogito-online/standalone/bpmn/index.js.

2.2.1. Installing and using the Kogito Business Modeler Hub

Kogito provides a Business Modeler Hub desktop application with all Kogito extensions or applications for modeling Business Process Model and Notation (BPMN) process models and Decision Model and Notation (DMN) decision models in Kogito services. Kogito currently provides extensions for VSCode (recommended) and GitHub modelers, a desktop application for offline modeling, and an online modeler.

As you develop Kogito services, you can use the Kogito Business Modeler Hub to install, launch, or uninstall the available modeling extensions or applications as needed.

Procedure
  1. In a web browser, go to the Business Modeler Hub download page, select the relevant Operating System option, and click Download.

  2. Extract the downloaded business_modeler_hub_preview_RELEASE binary file to a local directory.

    • On Linux: In a command terminal, navigate to the directory where you downloaded the business_modeler_hub_preview_RELEASE binary file and enter the following command to extract the contents:

      Extract the Kogito Business Modeler Hub distribution
      $ tar -xzpf business_modeler_hub_preview_RELEASE.tar.gz
    • On Windows or Mac: In your file browser, navigate to the directory where you downloaded the business_modeler_hub_preview_RELEASE binary file and extract the ZIP file.

  3. In a command terminal, navigate to the extracted business_modeler_hub_preview_RELEASE folder and enter the following command to run the application:

    Run the Kogito Business Modeler Hub application
    $ cd PATH_TO_MODELER_HUB
    $ ./Business\ Modeler\ Hub\ Preview-RELEASE/Business\ Modeler\ Hub\ Preview

    The Business Modeler Hub opens in a new window:

    Image of Business Modeler Hub
    Figure 10. Kogito Business Modeler Hub window
    If the Business Modeler Hub window does not appear, review the README documentation in the Business Modeler Hub application folder for any details specific to your operating system.

    Use the Kogito Business Modeler Hub to install, launch, or uninstall the available extensions or applications as needed.

2.2.2. Installing the Kogito VSCode extension bundle without the Kogito Business Modeler Hub

Although you can install and launch the Kogito Visual Studio Code (VSCode) extension from the Business Modeler Hub desktop application, along with all other available Kogito modelers, you can also install Kogito VSCode extensions from Visual Studio Marketplace directly in VSCode.

VSCode is the preferred integrated development environment (IDE) for developing Kogito services. Kogito provides a Kogito Bundle VSCode extension that enables you to design Decision Model and Notation (DMN) decision models, Business Process Model and Notation (BPMN) 2.0 business processes, and test scenarios directly in VSCode. Kogito also provides individual DMN Editor and BPMN Editor VSCode extensions for DMN or BPMN support only, if needed.

Prerequisites
  • VSCode 1.46.0 or later is installed.

Procedure
  1. In your VSCode IDE, select the Extensions menu option and search for Kogito Bundle for DMN, BPMN, and test scenario file support.

    For DMN or BPMN file support only, you can also search for the individual DMN Editor or BPMN Editor extensions.

  2. When the Kogito extension appears in the extension list in VSCode, select it and click Install.

  3. For optimal VSCode editor behavior, after the extension installation is complete, reload or close and re-launch your instance of VSCode.

After you install the VSCode extension bundle, any .dmn or .bpmn2 files that you open in VSCode are automatically displayed as graphical models. Additionally, any .scesim files that you open are automatically displayed as tabular test scenario models for testing the functionality of your business decisions.

If the Kogito DMN, BPMN, or test scenario modelers open only the XML source of a DMN, BPMN, or test scenario file and displays an error message, review the reported errors and the model file to ensure that all elements are correctly defined.

For new DMN or BPMN models, you can also enter dmn.new or bpmn.new in a web browser to design your DMN or BPMN model in the Kogito online modeler. When you finish creating your model, you can click Download in the online modeler page to import your DMN or BPMN file into your Kogito project in VSCode.

2.2.3. Configuring the Kogito standalone editors

The Kogito standalone editors are distributed in a self-contained library that provides an all-in-one JavaScript file for each editor. The JavaScript file uses a comprehensive API to set and control the editor.

You can install the standalone editors in three ways:

  • Use hosted JavaScript files directly

  • Download each JavaScript file manually

  • Use the NPM package

Procedure
  1. Install the Kogito standalone editors using one of the following methods:

    Use hosted JavaScript files directly: For this method, add the following <script> tags to your HTML page:

    Script tags for your HTML page
    <script src="https://kiegroup.github.io/kogito-online/standalone/dmn/index.js"></script>
    <script src="https://kiegroup.github.io/kogito-online/standalone/bpmn/index.js"></script>

    Download each JavaScript file manually: For this method, follow these steps:

    1. Download the JavaScript files:

    2. Add the downloaded Javascript files to your hosted application.

    3. Add the following <script> tag to your HTML page:

      Script tag for your HTML page
      <script src="https://<YOUR_PAGE>/dmn/index.js"></script>

    Use the NPM package: For this method, follow these steps:

    1. Go to the Kogito tooling repository in NPM and download the NPM package.

    2. Add the NPM package to your package.json file:

      Adding the NPM package
      npm install @kogito-tooling/kie-editors-standalone
    3. Import each editor library to your TypeScript file:

      Importing each editor
      import * as DmnEditor from "@kogito-tooling/kie-editors-standalone/dist/dmn"
      import * as BpmnEditor from "@kogito-tooling/kie-editors-standalone/dist/bpmn"
  2. After you install the standalone editors, open the required editor by using the provided editor API, as shown in the following example for opening a DMN editor. The API is the same for each editor.

    Opening the DMN standalone editor
    const editor = DmnEditor.open({
      container: document.getElementById("dmn-editor-container"),
      initialContent: Promise.resolve(""),
      readOnly: false,
      origin: "",
      resources: new Map([
        [
          "MyIncludedModel.dmn",
          {
            contentType: "text",
            content: Promise.resolve("")
          }
        ]
      ])
    });

    Use the following parameters with the editor API:

    Table 1. Example parameters
    Parameter Description

    container

    HTML element in which the editor is appended.

    initialContent

    Promise to a DMN model content. This parameter can be empty, as shown in the following examples:

    • Promise.resolve("")

    • Promise.resolve("<DIAGRAM_CONTENT_DIRECTLY_HERE>")

    • fetch("MyDmnModel.dmn").then(content ⇒ content.text())

    readOnly (Optional)

    Enables you to allow changes in the editor. Set to false (default) to allow content editing and true for read-only mode in editor.

    origin (Optional)

    Origin of the repository. The default value is window.location.origin.

    resources (Optional)

    Map of resources for the editor. For example, this parameter is used to provide included models for the DMN editor or work item definitions for the BPMN editor. Each entry in the map contains a resource name and an object that consists of content-type (text or binary) and content (similar to the initialContent parameter).

    The returned object contains the methods that are required to manipulate the editor.

    Table 2. Returned object methods
    Method Description

    getContent(): Promise<string>

    Returns a promise containing the editor content.

    setContent(path: string, content: string): void

    Sets the content of the editor.

    getPreview(): Promise<string>

    Returns a promise containing an SVG string of the current diagram.

    subscribeToContentChanges(callback: (isDirty: boolean) ⇒ void): (isDirty: boolean) ⇒ void

    Sets a callback to be called when the content changes in the editor and returns the same callback to be used for unsubscription.

    unsubscribeToContentChanges(callback: (isDirty: boolean) ⇒ void): void

    Unsubscribes the passed callback when the content changes in the editor.

    markAsSaved(): void

    Resets the editor state that indicates that the content in the editor is saved. Also, it activates the subscribed callbacks related to content change.

    undo(): void

    Undoes the last change in the editor. Also, it activates the subscribed callbacks related to content change.

    redo(): void

    Redoes the last undone change in the editor. Also, it activates the subscribed callbacks related to content change.

    close(): void

    Closes the editor.

    getElementPosition(selector: string): Promise<Rect>

    Provides an alternative to extend the standard query selector when an element lives inside a canvas or a video component. The selector parameter must follow the <PROVIDER>:::<SELECT> format, such as Canvas:::MySquare or Video:::PresenterHand. This method returns a Rect representing the element position.

    envelopeApi: MessageBusClientApi<KogitoEditorEnvelopeApi>

    This is an advanced editor API. For more information about advanced editor API, see MessageBusClientApi and KogitoEditorEnvelopeApi.

2.3. Creating a Maven project for a Kogito service

Before you can begin developing Kogito services, you need to create a Maven project where you can build your Kogito assets and any other related resources for your application.

Procedure
  1. In a command terminal, navigate to a local folder where you want to store the new Kogito project.

  2. Enter the following command to generate a project within a defined folder:

    On Quarkus
    $ mvn io.quarkus:quarkus-maven-plugin:create \
        -DprojectGroupId=org.acme -DprojectArtifactId=sample-kogito \
        -DprojectVersion=1.0.0-SNAPSHOT -Dextensions=kogito-quarkus

    Alternatively, you can generate a project using the Quarkus website.

    On Spring Boot
    $ mvn archetype:generate \
        -DarchetypeGroupId=org.kie.kogito \
        -DarchetypeArtifactId=kogito-spring-boot-archetype \
        -DgroupId=org.acme -DartifactId=sample-kogito \
        -DarchetypeVersion=1.22.0.Final \
        -Dversion=1.0-SNAPSHOT

    These commands generate a sample-kogito Maven project and import the Kogito extension for all required dependencies and configurations to prepare your application for business automation.

  3. Open or import the project in your VSCode IDE to view the contents.

2.3.1. Creating a custom Kogito Spring Boot project

With Spring Boot, you can create custom Maven projects using Kogito Spring Boot archetype. This archetype enables adding Kogito Spring Boot starters or add-ons in the project creation. The Kogito Spring Boot starters include:

  • decisions

  • processes

  • rules

  • serverless-workflows

  • predictions

Procedure
  1. In a command terminal, navigate to a local folder where you want to store the new Kogito project.

  2. Enter any of the following command to generate a project using starters or addons property:

    • To generate a project based on Kogito process services using the starters property, enter the following command:

      $ mvn archetype:generate \
          -DarchetypeGroupId=org.kie.kogito \
          -DarchetypeArtifactId=kogito-springboot-archetype \
          -DgroupId=org.acme -DartifactId=sample-kogito \
          -DarchetypeVersion=1.22.0.Final \
          -Dversion=1.0-SNAPSHOT
          -Dstarters=processes

      The new project includes the dependencies required to run Kogito process services. You can combine multiple Kogito Spring Boot starters using a comma-separated list. For example starters=processes,rules.

    • To generate a project containing Prometheus monitoring and Infinispan persistence using the Kogito add-ons property, enter the following command:

      $ mvn archetype:generate \
          -DarchetypeGroupId=org.kie.kogito \
          -DarchetypeArtifactId=kogito-springboot-archetype \
          -DgroupId=org.acme -DartifactId=sample-kogito \
          -DarchetypeVersion=1.22.0.Final \
          -Dversion=1.0-SNAPSHOT
          -Dstarters=processes
          -Daddons=monitoring-prometheus,persistence-infinispan
      When you pass an add-on to the property, the add-on name does not require the kogito-addons-springboot prefix. Also, you can combine the add-ons and starters properties to customize the project.
  3. Open or import the project in your IDE to view the contents.

For more information about Kogito Spring Boot starters and Kogito add-ons, see Spring Boot Starters for your Kogito project.

2.3.2. Creating a custom Kogito project using code scaffolding

This feature is experimental and might be substantially modified or removed in a future release. This feature also requires a special project setup. The generated code uses APIs that are not final in Kogito and will not function the same way in future releases.

The Quarkus extension and Maven plug-in within a standard Kogito project generate all the required code and boilerplate for your Kogito services so that you do not have to write the project setup code yourself. By default, a Kogito project generates a REST service from business assets automatically. The generated service usually exposes default REST endpoints using the information that is inferred from the business assets that you include in your project.

If you want to customize the code for your Kogito project, such as adding more REST endpoints, you can use code scaffolding to generate the project code to a specified user directory and then modify the code as needed.

With code scaffolding, you generate your Kogito project with the Kogito Maven archetype as usual, but you disable the project ability to automatically regenerate the project code and explicitly instruct Kogito to generate the code in a custom target directory. The code is generated one time in the specified target directory and is not regenerated at build time unless you explicitly request it. You can customize the project code in the specified target directory as needed.

On Quarkus, disabling code regeneration also disables hot reload of business assets in development mode. This limitation will be improved in a future release.
Procedure
  1. In a command terminal, navigate to a local folder where you want to store the custom Kogito project and enter the following command to generate your Kogito project with the Kogito Maven archetype as usual:

    On Quarkus
    $ mvn io.quarkus:quarkus-maven-plugin:create \
        -DprojectGroupId=org.acme -DprojectArtifactId=sample-kogito \
        -DprojectVersion=1.0.0-SNAPSHOT -Dextensions=kogito-quarkus

    Alternatively, you can generate a project using the Quarkus website.

    On Spring Boot
    $ mvn archetype:generate \
        -DarchetypeGroupId=org.kie.kogito \
        -DarchetypeArtifactId=kogito-spring-boot-archetype \
        -DgroupId=org.acme -DartifactId=sample-kogito \
        -DarchetypeVersion=1.22.0.Final \
        -Dversion=1.0-SNAPSHOT

    This command generates a sample-kogito Maven project and imports the Kogito extension for all required dependencies and configurations to prepare your application for business automation.

  2. In the project pom.xml file, modify the project dependencies and plug-ins to disable code regeneration:

    • On Quarkus: Delete the kogito-quarkus dependency and add the kogito-maven-plugin plug-in, as shown in the following examples:

      Deleting the kogito-quarkus dependency
      <dependencies>
        ...
        <dependency>
          <groupId>org.kie.kogito</groupId>
          <artifactId>kogito-quarkus</artifactId>
        </dependency>
        ...
      </dependencies>
      Adding the kogito-maven-plugin plug-in
      <build>
        ...
        <plugins>
          ...
          <plugin>
            <groupId>org.kie.kogito</groupId>
            <artifactId>kogito-maven-plugin</artifactId>
            <configuration>
              <onDemand>true</onDemand>
            </configuration>
          </plugin>
          ...
        </plugins>
        ...
      </build>
    • On Spring Boot: Modify the existing kogito-maven-plugin plug-in as shown in the following example:

      Modifying the kogito-maven-plugin plug-in
      <build>
        ...
        <plugins>
          ...
          <plugin>
            <groupId>org.kie.kogito</groupId>
            <artifactId>kogito-maven-plugin</artifactId>
            <configuration>
              <onDemand>true</onDemand>
            </configuration>
          </plugin>
          ...
        </plugins>
        ...
      </build>
  3. In a command terminal, navigate to the root of your Kogito project and enter the following command to generate your modified project using code scaffolding:

    Generating the Kogito project using code scaffolding
     $ mvn compile kogito:scaffold

    This command compiles the assets in your project and generates the code in the src/main/java directory. In the automatic Kogito project build, the default directory is target/generated-sources/kogito.

    If you want to override the default location for code generation, use the following command to define a custom target directory:

    Generating the Kogito project using code scaffolding with a custom directory
     $ mvn compile kogito:scaffold -Dkogito.codegen.sources.directory=src/main/generated-java

    The kogito.codegen.sources.directory parameter defines the custom target directory where you want the code to be generated, such as src/main/generated-java in this example.

    If you invoke the generation command again with the same target directory, the files are overwritten. This limitation will be fixed in a future release.

    After you run the scaffolding command, either the default src/main/java directory or the custom target directory that you defined now contains the following resources:

    • Your existing project files

    • Generated data classes corresponding to process variables or Decision Model and Notation (DMN) definitions

    • Generated REST endpoints

    This example project contains a persons Business Process Model and Notation (BPMN) process model, so the generated REST endpoints are modified as shown in the following Java object:

    Modified REST endpoints for a person BPMN process model
    package org.acme.travels;
    
    // Imports
    
    @Path("/persons")
    @ApplicationScoped
    public class PersonsResource {
    
        @Inject
        @Named("persons")
        Process<PersonsModel> process;
    
        @POST
        @Produces(MediaType.APPLICATION_JSON)
        @Consumes(MediaType.APPLICATION_JSON)
        public PersonsModelOutput createResource_persons(
                @Context HttpHeaders httpHeaders,
                @QueryParam("businessKey") String businessKey,
                @Valid @NotNull PersonsModelInput resource) {
            ...
        }
    
        // Other endpoints
    }

    The PersonsModel, PersonsModelInput, and PersonsModelOutput elements are the generated data models. These models are inferred automatically from the process variables in the process. Input and output models are inferred by how you tagged your process variables.

    Similar endpoints are generated for any available rule units. This example project contains a PersonValidationService class for a rule unit model, so the generated REST endpoints are modified as shown in the following Java object:

    Modified REST endpoints for a PersonValidationService rule unit class
    package org.acme.travels;
    
    // Imports
    
    @Path("/persons-validation-service")
    @ApplicationScoped
    public class PersonsValidationServiceResource {
    
        @Inject
        RuleUnit<PersonValidationService> unit;
    
        @POST
        @Produces(MediaType.APPLICATION_JSON)
        @Consumes(MediaType.APPLICATION_JSON)
        public QueryResult fireRules(PersonValidationService resource) {
            ...
        }
    
        // Other endpoints
    }

2.4. Creating custom REST endpoints using Kogito API

Kogito enables you to create custom REST endpoints using Kogito API.

Prerequisites
  • JDK 11 or later is installed.

  • Apache Maven is installed.

The Kogito API is only available for Quarkus and is currently experimental. This feature is available to the adopters to collect feedback and modify in a future release.
Procedure
  1. Create a Kogito project using the Maven plugin or Quarkus CLI.

    Example project using the Quarkus Maven plugin
    mvn io.quarkus:quarkus-maven-plugin:create \
       -DprojectGroupId=com.company \
       -DprojectArtifactId=sample-kogito \
       -Dextensions="kogito"
  2. Add a business asset to the resource folder such as a DMN file. To create the example DMN model, copy the following PersonDecisions.dmn file content:

    Example PersonDecisions.dmn file
    <dmn:definitions xmlns:dmn="http://www.omg.org/spec/DMN/20180521/MODEL/" xmlns="https://kiegroup.org/dmn/rest-example" xmlns:di="http://www.omg.org/spec/DMN/20180521/DI/" xmlns:kie="http://www.drools.org/kie/dmn/1.2" xmlns:dmndi="http://www.omg.org/spec/DMN/20180521/DMNDI/" xmlns:dc="http://www.omg.org/spec/DMN/20180521/DC/" xmlns:feel="http://www.omg.org/spec/DMN/20180521/FEEL/" id="_84B432F5-87E7-43B1-9101-1BAFE3D18FC5" name="PersonDecisions" typeLanguage="http://www.omg.org/spec/DMN/20180521/FEEL/" namespace="https://kiegroup.org/dmn/rest-example">
      <dmn:extensionElements/>
      <dmn:inputData id="_F9685B74-0C69-4982-B3B6-B04A14D79EDB" name="age">
        <dmn:extensionElements/>
        <dmn:variable id="_0E345A3C-BB1F-4FB2-B00F-C5691FD1D36C" name="age" typeRef="number"/>
      </dmn:inputData>
      <dmn:decision id="_0D2BD7A9-ACA1-49BE-97AD-19699E0C9852" name="adult">
        <dmn:extensionElements/>
        <dmn:variable id="_54CD509F-452F-40E5-941C-AFB2667D4D45" name="adult" typeRef="boolean"/>
        <dmn:informationRequirement id="_2F819B03-36B7-4DEB-AED6-2B46AE3ADB75">
          <dmn:requiredInput href="#_F9685B74-0C69-4982-B3B6-B04A14D79EDB"/>
        </dmn:informationRequirement>
        <dmn:decisionTable id="_58370567-05DE-4EC0-AC2D-A23803C1EAAE" hitPolicy="UNIQUE" preferredOrientation="Rule-as-Row">
          <dmn:input id="_ADEF36CD-286A-454A-ABD8-9CF96014021B">
            <dmn:inputExpression id="_4930C2E5-7401-46DD-8329-EAC523BFA492" typeRef="number">
              <dmn:text>age</dmn:text>
            </dmn:inputExpression>
          </dmn:input>
          <dmn:output id="_9867E9A3-CBF6-4D66-9804-D2206F6B4F86"/>
          <dmn:annotation name=""/>
          <dmn:rule id="_59D6BFF0-35B4-4B7E-8D7B-E31CB0DB8242">
            <dmn:inputEntry id="_7DC55D63-234F-497B-A12A-93DA358C0136">
              <dmn:text>&gt; 18</dmn:text>
            </dmn:inputEntry>
            <dmn:outputEntry id="_B3BB5B97-05B9-464A-AB39-58A33A9C7C00">
              <dmn:text>true</dmn:text>
            </dmn:outputEntry>
            <dmn:annotationEntry>
              <dmn:text/>
            </dmn:annotationEntry>
          </dmn:rule>
          <dmn:rule id="_8FCD63FE-8AD8-4F56-AD12-923E87AFD1B1">
            <dmn:inputEntry id="_B4EF7F13-E486-46CB-B14E-1D21647258D9">
              <dmn:text>&lt;= 18</dmn:text>
            </dmn:inputEntry>
            <dmn:outputEntry id="_F3A9EC8E-A96B-42A0-BF87-9FB1F2FDB15A">
              <dmn:text>false</dmn:text>
            </dmn:outputEntry>
            <dmn:annotationEntry>
              <dmn:text/>
            </dmn:annotationEntry>
          </dmn:rule>
        </dmn:decisionTable>
      </dmn:decision>
      <dmndi:DMNDI>
        <dmndi:DMNDiagram id="_8D228582-6DE6-4164-B7E9-52E249EB7F9F" name="DRG">
          <di:extension>
            <kie:ComponentsWidthsExtension>
              <kie:ComponentWidths dmnElementRef="_58370567-05DE-4EC0-AC2D-A23803C1EAAE">
                <kie:width>50</kie:width>
                <kie:width>100</kie:width>
                <kie:width>100</kie:width>
                <kie:width>100</kie:width>
              </kie:ComponentWidths>
            </kie:ComponentsWidthsExtension>
          </di:extension>
          <dmndi:DMNShape id="dmnshape-drg-_F9685B74-0C69-4982-B3B6-B04A14D79EDB" dmnElementRef="_F9685B74-0C69-4982-B3B6-B04A14D79EDB" isCollapsed="false">
            <dmndi:DMNStyle>
              <dmndi:FillColor red="255" green="255" blue="255"/>
              <dmndi:StrokeColor red="0" green="0" blue="0"/>
              <dmndi:FontColor red="0" green="0" blue="0"/>
            </dmndi:DMNStyle>
            <dc:Bounds x="404" y="464" width="100" height="50"/>
            <dmndi:DMNLabel/>
          </dmndi:DMNShape>
          <dmndi:DMNShape id="dmnshape-drg-_0D2BD7A9-ACA1-49BE-97AD-19699E0C9852" dmnElementRef="_0D2BD7A9-ACA1-49BE-97AD-19699E0C9852" isCollapsed="false">
            <dmndi:DMNStyle>
              <dmndi:FillColor red="255" green="255" blue="255"/>
              <dmndi:StrokeColor red="0" green="0" blue="0"/>
              <dmndi:FontColor red="0" green="0" blue="0"/>
            </dmndi:DMNStyle>
            <dc:Bounds x="613" y="324" width="100" height="50"/>
            <dmndi:DMNLabel/>
          </dmndi:DMNShape>
          <dmndi:DMNEdge id="dmnedge-drg-_2F819B03-36B7-4DEB-AED6-2B46AE3ADB75" dmnElementRef="_2F819B03-36B7-4DEB-AED6-2B46AE3ADB75">
            <di:waypoint x="504" y="489"/>
            <di:waypoint x="613" y="349"/>
          </dmndi:DMNEdge>
        </dmndi:DMNDiagram>
      </dmndi:DMNDI>
    </dmn:definitions>

    The PersonDecisions.dmn file defines a DMN model with https://kiegroup.org/dmn/_52CEF9FD-9943-4A89-96D5-6F66810CA4C1 namespace and PersonDecisions name.

  3. Create a source file for the custom REST endpoint:

    Example REST endpoint
    package org.acme;
    
    import java.util.Map;
    
    import javax.inject.Inject;
    import javax.ws.rs.Consumes;
    import javax.ws.rs.POST;
    import javax.ws.rs.Path;
    import javax.ws.rs.Produces;
    import javax.ws.rs.core.MediaType;
    
    import org.kie.kogito.incubation.application.AppRoot;
    import org.kie.kogito.incubation.common.DataContext;
    import org.kie.kogito.incubation.common.MapDataContext;
    import org.kie.kogito.incubation.decisions.DecisionIds;
    import org.kie.kogito.incubation.decisions.services.DecisionService;
    
    @Path("/hello")
    public class GreetingResource {
    
        // Inject the application root
        @Inject AppRoot appRoot;
        // Inject a decision service
        @Inject DecisionService svc;
    
        @POST
        @Consumes(MediaType.APPLICATION_JSON)
        @Produces(MediaType.APPLICATION_JSON)
        public DataContext hello(Map<String, Object> payload) {
            var id = appRoot
                    .get(DecisionIds.class)
                    .get("https://kiegroup.org/dmn/_52CEF9FD-9943-4A89-96D5-6F66810CA4C1",
                            "PersonDecisions");
            var ctx = MapDataContext.from(payload);
            return svc.evaluate(id, ctx);
        }
    }

    The previous example of REST endpoints contains the following aspects:

    • The AppRoot object is injected that is used to create identifiers for resources to be evaluated.

    • The @Inject DecisionService svc injects the DecisionService, which is used to evaluate a DMN model. The following list describes different types of services:

      • StraightThroughProcessService for BPMN processes that terminate immediately and do not suspend into intermediate states

      • DecisionService for DMN decision models

      • PredictionService for PMML predictive models

      • RuleUnitService for Rule Unit evaluation

        Each service can be injected (@inject) and provides an evaluate(Id, DataContext) method. For more information about how to invoke the services, you can see dmn-incubation-api-quarkus, pmml-incubation-api-quarkus, process-incubation-api-quarkus, and rules-incubation-api-quarkus example applications.

    • The REST endpoint expects an arbitrary Map<String, Object> of values.

    • In the body of the REST handler, the Map is converted into a DataContext. Each service expects a DataContext to evaluate together with the identifier.

    • The evaluate(id, ctx) method returns a DataContext. The DataContext be returned directly by the REST handler: the system knows how to handle the response.

    A DataContext is a class that implements the DataContext interface. DataContext provides <T extends DataContext> T as(Class<T>) method to convert a type of data context to another. The root type of data context provides the as() method only, because the root type does not contain additional behavior.

    In the previous example, DataContext is converted into a MapDataContext and Person object. The following is an example of using a DataContext:

    Example of using a DataContext
    DataContext ctx = ...;
    MapDataContext mctx = ctx.as(MapDataContext.class);
    Person person = mctx.as(Person.class);

    In the previous example, MapDataContext is a type of built-in data context (Map<String, Object>) and provides get() and set() methods to access the data. The get() method pulls the entered data by performing an internal conversion if applicable.

    Example of using MapDataContext
    DataContext ctx = ...;
    MapDataContext mctx = ctx.as(MapDataContext.class);
    ctx.set("Paul", new Person("Paul", 79));
    Object o = ctx.get("Paul");
    Person p = ctx.get("Paul", Paul.class);

    Also, you can customize the example REST endpoint further by expecting a DataContext in the method signature:

    Example REST endpoint
        @POST
        @Consumes(MediaType.APPLICATION_JSON)
        @Produces(MediaType.APPLICATION_JSON)
        public DataContext hello(MapDataContext ctx) { // use ctx here
            var id = appRoot
                    .get(DecisionIds.class)
                    .get("https://kiegroup.org/dmn/rest-example",
                            "PersonDecisions");
            return svc.evaluate(id, ctx);
        }

    In addition, you can transform the Map data context into a Java class. This mapping is performed automatically using field naming conventions as shown in the following example:

    Example Java class
    import org.kie.kogito.incubation.common.*;
    public class Person implements DataContext, DefaultCastable {
        String name;
        int age;
        // constructors...
        // getters and setters...
    }

    The DataContext interface marks the Java class as a bindable context, enabling you to convert another DataContext using the as(Class<DataContext>) method:

    Example conversion of a MapDataContext into a Person
        public DataContext hello(MapDataContext ctx) {
            ...
            Person person = ctx.as(Person.class);
            return svc.evaluate(id, person);
        }

    Also, you can use the converted DataContext directly as an argument. In the following example, the Person is used as an argument:

    Example REST endpoint using Person as an argument
        public DataContext hello(Person person) {
            ...
            return svc.evaluate(id, ctx);
        }

    The custom data contexts can also be used in the return type as shown in the following example:

    Example REST endpoint using Person as an argument and a custom return type
        class MyCustomDataContext implements DataContext, DefaultCastable {...}
        public MyCustomDataContext hello(Person person) {
            ...
            DataContext result = svc.evaluate(id, ctx);
            MyCustomDataContext typedResult = result.as(MyCustomDataContext.class)
            return result;
        }
  4. Optionally, you can disable automated generation of the REST endpoint by setting kogito.generate.rest.decisions property to false in the application.properties file:

    application.properties file
    kogito.generate.rest.decisions=false
  5. Test the created custom REST endpoint:

    Example of invoking a REST endpoint
    # starts the application in devmode
    $ mvn quarkus:dev
    # invoke the REST service
    $ curl -X POST \
        -H 'Accept: application/json' \
        -H 'Content-Type: application/json' \
        -d '{"name": "Paul" , "age" : 79}' \
        http://localhost:8080/hello
    Example response
    {
      "name":"Paul",
      "adult":true,
      "age":79
    }

2.5. Spring Boot Starters for your Kogito project

The Spring Boot starters are a set of dependency descriptors that you can include in your Kogito application. Using Spring Boot starters, you can eliminate the need to go through sample code, and copy and pasting dependency descriptors.

When you create a project, the pom.xml file contains all the necessary starters. You can also include a new starter if required. For more information on creating projects using Kogito Spring Boot archetype, see Creating a custom Kogito Spring Boot project.

2.5.1. Kogito Spring Boot Starters

The Kogito Spring Boot starter is an all-in-one descriptor for the Kogito projects that require business automation engines provided by Kogito. The Kogito Spring Boot starter includes decisions, rules, processes, predictions, and the Serverless Workflow implementation.

When your project consists of all the assets and you want a quick way to get started with Kogito, you can use the kogito-spring-boot-starter. For more granular approach, you can use the specific starters or a combination of starters.

You can add a Kogito Spring Boot starter to your project as follows:

<dependencies>
  <dependency>
    <groupId>org.kie.kogito</groupId>
    <artifactId>kogito-spring-boot-starter</artifactId>
  </dependency>
</dependencies>
If you plan to use Serverless Workflow with events, you can add kogito-serverless-workflow-spring-boot-starter to the project. The kogito-serverless-workflow-spring-boot-starter includes CloudEvents support required by Serverless Workflows.

The following list describes the different Spring Boot starters:

Kogito decisions Spring Boot starter

Starter for decisions such as DMN support for your Kogito Spring Boot project. You can add a Kogito decisions Spring Boot starter as follows:

<dependencies>
  <dependency>
    <groupId>org.kie.kogito</groupId>
    <artifactId>kogito-decisions-spring-boot-starter</artifactId>
  </dependency>
</dependencies>
Kogito predictions Spring Boot starter

Starter for predictions such as PMML support for your Kogito Spring Boot project. You can add a Kogito predictions Spring Boot starter as follows:

<dependencies>
  <dependency>
    <groupId>org.kie.kogito</groupId>
    <artifactId>kogito-predictions-spring-boot-starter</artifactId>
  </dependency>
</dependencies>
Kogito processes Spring Boot starter

Starter for processes such as BPMN support for your Kogito Spring Boot project. You can add a Kogito processes Spring Boot starter as follows:

<dependencies>
  <dependency>
    <groupId>org.kie.kogito</groupId>
    <artifactId>kogito-processes-spring-boot-starter</artifactId>
  </dependency>
</dependencies>
Kogito rules Spring Boot starter

Starter for rules such as DRL support for your Kogito Spring Boot project. You can add a Kogito rules Spring Boot starter as follows:

<dependencies>
  <dependency>
    <groupId>org.kie.kogito</groupId>
    <artifactId>kogito-rules-spring-boot-starter</artifactId>
  </dependency>
</dependencies>
Kogito Serverless Workflow Spring Boot starter

Starter for the Serverless Workflow support for your Kogito Spring Boot project. You can add a Kogito Serverless Workflow starter as follows:

<dependencies>
  <dependency>
    <groupId>org.kie.kogito</groupId>
    <artifactId>kogito-serverless-workflow-spring-boot-starter</artifactId>
  </dependency>
</dependencies>

2.6. Kogito add-ons

You can extend Kogito core capabilities by using add-ons modules. The add-on modules extend persistence, monitoring, messaging, and other features of Kogito services.

The following tables list all the supported add-ons:

Table 3. Kogito add-ons for Quarkus
Name Artifact Id Since

Messaging

kogito-addons-quarkus-messaging

1.0.0

Events Decisions

kogito-addons-quarkus-events-decisions

1.2.0

Events Process

kogito-addons-quarkus-events-process

0.3.0

Explainability

kogito-addons-quarkus-explainability

0.15.0

Jobs Management

kogito-addons-quarkus-jobs-management

0.6.0

Knative Eventing

kogito-addons-quarkus-knative-eventing

1.10.0

Mail

kogito-addons-quarkus-mail

1.6.0

Elastic Monitoring

kogito-addons-quarkus-monitoring-elastic

1.2.0

Prometheus Monitoring

kogito-addons-quarkus-monitoring-prometheus

0.1.0

Persistence ISPN Health

kogito-addons-quarkus-persistence-infinispan-health

0.8.0

Persistence Kafka

kogito-addons-quarkus-persistence-kafka

1.4.0

Process Management

kogito-addons-quarkus-process-management

0.3.0

Process SVG

kogito-addons-quarkus-process-svg

1.0.0

Task Management

kogito-addons-quarkus-task-management

1.4.0

Task Notification

kogito-addons-quarkus-task-notification

1.6.0

Tracing Decision

kogito-addons-quarkus-tracing-decision

0.11.0

Events MongoDB

kogito-addons-quarkus-events-mongodb

1.11.0

Source Files

kogito-addons-quarkus-source-files

1.20.0

Table 4. Kogito add-ons for Spring Boot
Name Artifact Id Since

Messaging

kogito-addons-springboot-messaging

1.0.0

Events Decisions

kogito-addons-springboot-events-decisions

1.2.0

Events Kafka

kogito-addons-springboot-events-kafka

0.3.0

Explainability

kogito-addons-springboot-explainability

0.15.0

Jobs Management

kogito-addons-springboot-jobs-management

0.6.0

Mail

kogito-addons-springboot-mail

1.6.0

Elastic Monitoring

kogito-addons-springboot-monitoring-elastic

1.2.0

Prometheus Monitoring

kogito-addons-springboot-monitoring-prometheus

0.1.0

Process Management

kogito-addons-springboot-process-management

0.3.0

Process SVG

kogito-addons-springboot-process-svg

1.0.0

Task Management

kogito-addons-springboot-task-management

1.4.0

Task Notification

kogito-addons-springboot-task-notification

1.6.0

Tracing Decision

kogito-addons-springboot-tracing-decision

0.11.0

Events MongoDB

kogito-addons-springboot-events-mongodb

1.11.0

2.6.1. Kogito messaging add-on

Kogito provides a messaging add-on, enabling a process to register an event listener. The messaging add-on uses events in the Kogito projects. For more information, see the following sections:

To configure messaging capabilities for your Kogito services, you must add the messaging add-on as a dependency in the pom.xml file of your Kogito project:

Project dependency to enable messaging operations in Quarkus projects
<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-addons-quarkus-messaging</artifactId>
  <version>1.22</version>
</dependency>
Project dependency to enable messaging operations in Spring Boot projects
<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-addons-springboot-messaging</artifactId>
  <version>1.22</version>
</dependency>

For Quarkus, the Kogito process event add-on implementation is based on Smallrye Messaging library, which provides a set of connectors for event brokers, such as JMS, AMQP, and Kafka. Therefore, the Kogito process event add-on is not specifically combined with any event broker, but also requires additional configuration to use a suitable Smallrye connector.

For example, to use Kafka as an event broker in Quarkus, you must add the following dependency in the pom.xml file of your Kogito project:

Project dependency to enable event broker in Quarkus
<dependency>
   <groupId>io.quarkus</groupId>
   <artifactId>quarkus-smallrye-reactive-messaging-kafka</artifactId>
</dependency>

Smallrye uses an abstraction named channel. The implementation of channels depends on the underlying event broker. For example, when using Kafka, every channel is mapped to a topic.

Channels are configured by using properties. The pattern for these properties is mp.messaging.[incoming|outgoing].<channel name>.<property_name>.

Kogito allows different channel mapping strategies:

  • Define one default incoming channel to receive all the incoming messages and one default outgoing channel to store all the published messages.

  • Define a channel for each message name so that every message type has a dedicated channel.

  • Define a channel for certain message names and let the not mapped message names to use the default incoming or outgoing channel.

Kogito first searches for channel name equals to message name in the properties. If found, it uses that channel for that message name. If not, it searches for default channel definition. If also not existing, then an error will be reported.

The name for the default incoming channel is kogito_incoming_stream and for the default outgoing channel is kogito_outgoing_stream.

You can change the default incoming and outgoing topic name as follows:

Quarkus
kogito.addon.messaging.incoming.defaultName=<default channel name>
kogito.addon.messaging.outgoing.defaultName=<default channel name>
Spring Boot
kogito.addon.cloudevents.kafka.kogito_incoming_stream=<default channel name>
kogito.addon.cloudevents.kafka.kogito_outgoing_stream=<default channel name>

Optionally, for Kafka connector, you can define the Kafka topic to be used for that channel using the following form:

mp.messaging.[incoming|outgoing].<channel name>.topic = <topic name>

If the topic property is not found, then the topic name is considered to be the same as Kafka name. Note that you can map different channels to the same topic, if you wish to do so.

2.6.1.1. Providing a key for a Kafka record

To specify the key to your Kafka record, you can use a custom MessageDecorator implementation.

For an example, see StringKeyDecorator.java

2.6.1.2. Customizing message format

By default, Kogito uses Cloud event JSON format for messaging. However, you can change the default format by providing your own event marshaler implementation. For an example, see AvroEventMarshaller.java example

2.6.2. Kogito Decision Model and Notation (DMN) event-driven add-on

Quarkus

To use the DMN event-driven add-on, add the following code to the pom.xml file of your project:

<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-addons-quarkus-events-decisions</artifactId>
  <version>1.22</version>
</dependency>
Spring Boot
<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-addons-springboot-events-decisions</artifactId>
  <version>1.22</version>
</dependency>

2.6.3. Kogito explainability add-on

The Explainability add-on in Kogito provides integration with Explainability and Trusty Services. You can also use trusty-demonstration example application.

For more information, see the Kogito Trusty Service and Explainability Service.

To use the explainability add-on, add the following code to the pom.xml file of your project:

Quarkus
<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-addons-quarkus-explainability</artifactId>
  <version>1.22</version>
</dependency>
Spring Boot
<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-addons-springboot-explainability</artifactId>
  <version>1.22</version>
</dependency>

2.6.4. Kogito jobs management add-on

The Jobs Management add-on provides integration and configuration for a project using the supporting services related to Jobs. For more information, see the documentation.

To use the Jobs Management add-on, add the following code to the pom.xml file of your project:

Quarkus
<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-addons-quarkus-jobs-management</artifactId>
  <version>1.22</version>
</dependency>
Spring Boot
<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-addons-springboot-jobs-management</artifactId>
  <version>1.22</version>
</dependency>

2.6.5. Kogito Knative Eventing add-on

The Knative Eventing add-on is used in the Kogito project for messaging. The Kogito Knative Eventing add-on enables your project to connect to a sink or make it a KogitoSource.

The add-on processes the K_SINK and K_CE_OVERRIDES environment variables injected by Knative Eventing controllers (SinkBinding or KogitoSource).

The service requires the Kogito Messaging and Quarkus HTTP connector libraries to wire the Kogito service with a given sink. The Kogito Messaging and Quarkus HTTP connector perform as dependencies for the Knative Eventing add-on.

Spring Boot does not support Knative Eventing currently.

For more information, see Knative Eventing in Kogito services.

To use the Knative Eventing add-on, add the following code to the pom.xml file of your project:

Quarkus
<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-addons-quarkus-knative-eventing</artifactId>
  <version>1.22</version>
</dependency>
2.6.5.1. Auto-generated Knative resources

The Knative Eventing add-on can generate Knative Eventing resources by inspecting your BPMN or Serverless Workflow files for event definitions. For more information about creating BPMN files using message trigger events, see Knative Eventing in Kogito services. You can also see Orchestrating microservices with Serverless Workflow in Kogito to know more about the Serverless Workflow events.

To enable the auto-generation of Knative resources, add the following dependency to your project:

<dependency>
  <groupId>io.quarkus</groupId>
  <artifactId>quarkus-kubernetes</artifactId>
</dependency>

After adding the previous dependency, the add-on generates Knative Eventing resources to handle the events that are produced or consumed by your BPMN or Serverless Workflow resources on Knative Eventing platform.

For each consumed event, the add-on generates a Knative Eventing Trigger as shown in the following example:

Example Knative Trigger custom resource for a Kogito service with Broker binding
apiVersion: eventing.knative.dev/v1
kind: Trigger
metadata:
  name: travellers-trigger-process-knative-quickstart-quarkus
spec:
  broker: default
  filter:
    attributes:
      type: travellers
  subscriber:
    ref:
      apiVersion: serving.knative.dev/v1
      kind: Service
      name: process-knative-quickstart-quarkus

The Trigger resource requires a subscriber to receive the events that are routed by the Broker. In this case, the current Kogito service work as a subscriber, and the Knative Eventing add-on uses the Kogito service deployment, which is configured by Quarkus Kubernetes.

If a Broker is not specified, the Knative Eventing add-on generates a default InMemoryChannel as follows:

Example Broker custom resource for Knative Eventing with default InMemoryChannel
apiVersion: eventing.knative.dev/v1
kind: Broker
metadata:
  name: default

If a Broker is configured in the target namespace, you can specify the Broker using the following property: org.kie.kogito.addons.knative.eventing.broker=myKafkaBroker.

If the Kogito service contains any produced event definitions, the Knative Eventing add-on generates a Knative Eventing SinkBinding, or a Knative Eventing KogitoSource. By default, the SinkBinding is generated, but you can change this behavior by setting org.kie.kogito.addons.knative.eventing.generate-kogito-source property to true.

Example SinkBinding custom resource for Knative Eventing
apiVersion: sources.knative.dev/v1
kind: SinkBinding
metadata:
  name: sb-process-knative-quickstart-quarkus
spec:
  sink:
    ref:
      apiVersion: eventing.knative.dev/v1
      kind: Broker
      name: default
  subject:
    apiVersion: serving.knative.dev/v1
    kind: Service
    name: process-knative-quickstart-quarkus
Example KogitoSource custom resource for Knative Eventing
apiVersion: kogito.knative.dev/v1alpha1
kind: KogitoSource
metadata:
  name: process-knative-quickstart-quarkus
spec:
  sink:
    ref:
      apiVersion: eventing.knative.dev/v1
      kind: Broker
      name: default
      namespace: ""
  subject:
    apiVersion: serving.knative.dev/v1
    kind: Service
    name: process-knative-quickstart-quarkus

The target deployment defined in the Quarkus Kubernetes plugin work as the subject for SinkBinding and KogitoSource. You can configure the sink using the application properties. By default, the generated Broker is defined as the sink.

The following table lists all the available properties for the Knative Eventing add-on:

Table 5. Kogito Knative Eventing Add-on properties
Property Description Example value

org.kie.kogito.addons.knative.eventing.sink.namespace

Property for set sink namespace

default

org.kie.kogito.addons.knative.eventing.sink.name

Property to set sink name

my-sink

org.kie.kogito.addons.knative.eventing.sink.api-version

Property to set Kubernetes API version of the sink

eventing.knative.dev/v1

org.kie.kogito.addons.knative.eventing.sink.kind

Property to set Kubernetes resource kind of the sink

Broker

org.kie.kogito.addons.knative.eventing.broker

Property to set name of the pre-defined Broker

myKafkaBroker

org.kie.kogito.addons.knative.eventing.generate-kogito-source

Property to enable the generation of KogitoSource instead of SinkBinding

false

org.kie.kogito.addons.knative.eventing.auto-generate-broker

Property to enable the generation of default in-memory Broker

true

For more information about the Knative Eventing add-on, see the following example applications:

2.6.6. Kogito mail add-on

The mail add-on is used to send emails in a process project. To use the mail add-on, add the following code to the pom.xml file of your project:

Quarkus
<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-addons-quarkus-mail</artifactId>
  <version>1.22</version>
</dependency>
Spring Boot
<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-addons-springboot-mail</artifactId>
  <version>1.22</version>
</dependency>

2.6.7. Kogito events add-on

The events add-on provides a default implementation in supported target platforms for EventEmitter and EventReceiver interfaces. You can use EventEmitter and EventReceiver interfaces to enable messaging by process, serverless workflow events, and event decision handling. For more information, see Knative Eventing in Kogito services, [con-serverless-workflow-definitions_kogito-orchestrating-serverless], and Enabling Kafka messaging for Kogito services.

2.6.7.1. Implementing message payload decorator

Any dependant add-on can implement the MessagePayloadDecorator.

Prerequisites
  • You have installed the Events add-on in Kogito.

Procedure
  1. Create a file named META-INF/services/org.kie.kogito.add-on.cloudevents.message.MessagePayloadDecorator in your class path.

  2. Open the file.

  3. Enter the full name of your implementation class in the file.

  4. Save the file.

The MessagePayloadDecoratorProvider loads the file upon application startup and adds the file to the decoration chain. When Kogito calls the MessagePayloadDecoratorProvider#decorate, your implementation is part of the decoration algorithm.

To use the events add-on, add the following code to the pom.xml file of your project:

Events smallrye add-on for Quarkus
<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-addons-quarkus-events-smallrye</artifactId>
  <version>1.22</version>
</dependency>
Events decisions add-on for Quarkus
<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-addons-events-decisions</artifactId>
  <version>1.22</version>
</dependency>
Events Kafka add-on for Spring Boot
<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-addons-springboot-events-kafka</artifactId>
  <version>1.22</version>
</dependency>
Events decisions add-on for Spring Boot
<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-addons-springboot-events-decisions</artifactId>
  <version>1.22</version>
</dependency>

2.6.8. Kogito monitoring add-on

The monitoring add-on provides monitoring capabilities in the Kogito project. For more information, see Metrics monitoring in Kogito.

To use the monitoring add-on, add the following code to the pom.xml file of your project:

Quarkus dependency for elastic search
<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-addons-quarkus-monitoring-elastic</artifactId>
  <version>1.22</version>
</dependency>
Quarkus dependency for Prometheus
<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-addons-quarkus-monitoring-prometheus</artifactId>
  <version>1.22</version>
</dependency>
Spring Boot dependency for elastic search
<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-addons-springboot-monitoring-elastic</artifactId>
  <version>1.22</version>
</dependency>
Spring Boot dependency for Prometheus
<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-addons-springboot-monitoring-prometheus</artifactId>
  <version>1.22</version>
</dependency>

2.6.9. Kogito persistence add-on

The persistence add-on provides persistence capability in your Kogito project. For more information, Persistence in Kogito services.

To use the Persistence add-on, add the following code to the pom.xml file of your project:

Quarkus dependency for persistence filesystem
<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-addons-quarkus-persistence-filesystem</artifactId>
  <version>1.22</version>
</dependency>
Spring Boot dependency for persistence filesystem
<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-addons-springboot-persistence-filesystem</artifactId>
  <version>1.22</version>
</dependency>
Quarkus dependency for persistence Infinispan
<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-addons-quarkus-persistence-infinispan</artifactId>
  <version>1.22</version>
</dependency>
Spring Boot dependency for persistence Infinispan
<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-addons-springboot-persistence-infinispan</artifactId>
  <version>1.22</version>
</dependency>
Quarkus dependency for persistence JDBC
<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-addons-quarkus-persistence-jdbc</artifactId>
  <version>1.22</version>
</dependency>
Spring Boot dependency for persistence JDBC
<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-addons-springboot-persistence-jdbc</artifactId>
  <version>1.22</version>
</dependency>
Quarkus dependency for persistence MongoDB
<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-addons-quarkus-persistence-mongodb</artifactId>
  <version>1.22</version>
</dependency>
Spring Boot dependency for persistence MongoDB
<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-addons-springboot-persistence-mongodb</artifactId>
  <version>1.22</version>
</dependency>
Quarkus dependency for persistence PostgreSQL
<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-addons-quarkus-persistence-postgresql</artifactId>
  <version>1.22</version>
</dependency>
Spring Boot dependency for persistence PostgreSQL
<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-addons-springboot-persistence-postgresql</artifactId>
  <version>1.22</version>
</dependency>
Quarkus dependency for persistence Kafka
<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-addons-quarkus-persistence-kafka</artifactId>
  <version>1.22</version>
</dependency>
Spring Boot dependency for persistence Kafka
<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-addons-springboot-persistence-kafka</artifactId>
  <version>1.22</version>
</dependency>

2.6.10. Kogito process management add-on

Kogito provides a kogito-addons-quarkus-process-management add-on that enables basic REST operations that you can use to manage process instances. These REST operations are supplemental to any other specific REST operations that you have configured in your application.

To configure process management REST capabilities for your Kogito services, you can add the process management add-on as a dependency in the pom.xml file of your Kogito project:

Project dependency to enable process management REST operations
<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-addons-quarkus-process-management</artifactId>
</dependency>

The Kogito process management add-on provides REST support for the following basic operations:

  • Process instances: Abort an active process instance

  • Node instances: Cancel or re-trigger a node instance, or trigger a new node instance

  • Error handling: Retrieve error details for a process instance, or skip or re-trigger a failed node instance

In addition to exposed REST operations, the process management add-on also provides the following REST exception mappers to generate more meaningful error messages for typical exception types:

  • ProcessInstanceNotFound

  • NodeInstanceNotFound

  • NodeNotFound

  • ProcessInstanceExecutionError

  • NotAuthorized

  • InvalidTransition (for work items)

  • InvalidLifeCyclePhase (for work items)

These exception mappers produce a valid HTTP error code with JSON payload with the context that caused the exception.

For example, the following is a ProcessInstanceNotFoundException error generated at runtime:

Example error with JSON payload at runtime
HTTP code : 404

{
  "processInstanceId" : "c6862071-0f2e-4f21-9bc8-586245a76c3aa",
  "message" : "Process instance with id c6862071-0f2e-4f21-9bc8-586245a76c3aa not found"
}

2.6.11. Kogito process SVG add-on

Kogito provides an add-on named process-svg-addon, enabling the basic REST operations that you can use to visualize the process diagram and execution path of the related process instances. The REST operations are supplemental to any other specific REST operation that is configured in your application.

The add-on requires the access of process SVG files. The process SVG files can be placed in a file system, which is accessible to the service or available in META-INF/processSVG/ folder in the class path. For example, when the SVG files are generated, rename the files to {processId}.svg. The {processId}.svg file is used by the add-on and placed in a file system folder (accessible by the service) or in META-INF/processSVG folder in the class path. The configuration property kogito.svg.folder.path of the add-on points to the file system folder, otherwise the files are searched in the class path.

The VSCode extension enables you to export the process to SVG format:

Manually export to SVG at VSCode
Figure 11. Export process diagram at VSCode

When you export a process to SVG format, the generated SVG is named to {processFileName}-svg.svg.

The process code generation searches this export and, if this file exists, copies and renames it to `META-INF/processSVG/{processId}.svg allowing the process SVG addon to consume it directly.

To configure process SVG REST capabilities for your Kogito services, you can add the process SVG add-on as a dependency in the pom.xml file of your Kogito project:

Project dependency to enable process SVG REST operations in Quarkus projects
<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-addons-quarkus-process-svg</artifactId>
</dependency>
Project dependency to enable process SVG REST operations in Spring Boot projects
<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-addons-springboot-process-svg</artifactId>
</dependency>

The process SVG add-on provides the following set of configuration properties:

Table 6. Process SVG add-on configuration properties in Kogito

Property

Description

kogito.svg.folder.path

Determines the folder in which the add-on searches the initial process diagrams. If this property is not enabled, then the add-on searches META-INF/processSVG/ folder in the class path.

Default value: empty

Example: kogito.svg.folder.path=/home/user/diagrams

kogito.svg.color.completed

Changes the color to fill the completed nodes when showing the execution path.

Default value: #C0C0C0

Example: kogito.svg.color.completed=#C2C0C1

kogito.svg.color.completed.border

Changes the color of the border of the completed node when showing the execution path.

Default value: #030303

Example: kogito.svg.color.completed.border=#C2C0C1

kogito.svg.color.active.border

Changes the color of the border of the active node when showing the execution path.

Default value: #FF0000

Example: kogito.svg.color.active.border=#C2C0C1

The Kogito process SVG add-on provides REST support for the following basic operations:

  • Process Diagram: Return the process SVG diagram

  • Process instance diagram: Return the process SVG diagram displaying the executed path of a specific process instance

2.6.12. Kogito process event add-on

Kogito provides a process event add-on, which you can use to send processes, tasks, and variable events to an external event listener. In this case, the processes, tasks, and variable events are generated as a result of the execution of an operation, which modifies a process state; such events are known as runtime events.

In Kogito, every modifying operation is executed within an abstraction called a unit of work. Examples of such operations include creating a process instance, transitioning a task, and modifying a variable. A runtime event is published when a unit of work is completed.

You can use the Kogito process event add-on to build a historical representation of all process instance executions. Also, you can use this add-on with process REST API to build a custom graphical user interface to handle the user tasks.

By default, the event format follows CloudEvent specification. The CloudEvent specification allows sending information using the data field, which contains a JSON of one of the following types:

To configure process event capabilities for your Kogito services, you can add the process event add-on as a dependency in the pom.xml file of your Kogito project:

Project dependency to enable process event operations in Quarkus projects
<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-addons-quarkus-events-process</artifactId>
</dependency>
Project dependency to enable process event operations in Spring Boot projects
<dependency>
  	<groupId>org.kie.kogito</groupId>
  	<artifactId>kogito-addons-springboot-events-process-kafka</artifactId>
</dependency>
The Kogito process event add-on for Spring Boot is specific to Kafka. Therefore, you do not need to add additional dependencies to your pom.xml file to use an event broker.

For Quarkus, the Kogito process event add-on implementation is based on Smallrye Messaging library, which provides a set of connectors for event brokers, such as JMS, AMQP, and Kafka. Therefore, the Kogito process event add-on is not specifically combined with any event broker in Quarkus, but requires additional configuration to use a suitable Smallrye connector.

For example, to use Kafka as an event broker in Quarkus, you can add the following dependency in the pom.xml file of your Kogito project:

Project dependency to enable Kafka event broker in Quarkus
 <dependency>
  	<groupId>io.quarkus</groupId>
  	<artifactId>quarkus-smallrye-reactive-messaging-kafka</artifactId>
</dependency>

Smallrye defines an abstraction named channel to enable multi-broker support. For every channel that you define in your Kogito application, you can specify the connector to use for that channel using the following form:

mp.messaging.[incoming|outgoing].<channel name>.connector = <connector name>

Optionally, for Kafka connector, you can define the Kafka topic to be used for that channel using the following form:

mp.messaging.[incoming|outgoing].<channel name>.topic = <topic name>

You can also set up a channel property using the following form:

mp.messaging.[incoming|outgoing].<channel name>.<property name>= <property value>

If the defined property is not found, then the topic name is considered to be the same as Kafka name.

The Kogito process event add-on defines a channel for each event type, such as kogito-processinstances-events, kogito-usertaskinstances-events, and kogito-variables-events. Therefore, when the process event add-on is enabled, you must add the event types to your application.properties file using the following properties:

Properties to define event types in application.properties file
mp.messaging.outgoing.kogito-processinstances-events.connector=smallrye-kafka
mp.messaging.outgoing.kogito-processinstances-events.topic=kogito-processinstances-events
mp.messaging.outgoing.kogito-processinstances-events.value.serializer=org.apache.kafka.common.serialization.StringSerializer

mp.messaging.outgoing.kogito-usertaskinstances-events.connector=smallrye-kafka
mp.messaging.outgoing.kogito-usertaskinstances-events.topic=kogito-usertaskinstances-events
mp.messaging.outgoing.kogito-usertaskinstances-events.value.serializer=org.apache.kafka.common.serialization.StringSerializer

mp.messaging.outgoing.kogito-variables-events.connector=smallrye-kafka
mp.messaging.outgoing.kogito-variables-events.topic=kogito-variables-events
mp.messaging.outgoing.kogito-variables-events.value.serializer=org.apache.kafka.common.serialization.StringSerializer

Additionally, you can disable the publishing on any channel by setting the related property to false as shown in the following example:

Example properties to disable publishing
kogito.events.usertasks.enabled=false
kogito.events.variables.enabled=false

2.6.13. Kogito task management add-on

The Task Management add-on provides integration with the Task Console. For more information, see Kogito Task Console.

To use the Task Management add-on, add the following code to the pom.xml file of your project:

Quarkus
<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-addons-quarkus-task-management</artifactId>
  <version>1.22</version>
</dependency>
Spring Boot
<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-addons-springboot-task-management</artifactId>
  <version>1.22</version>
</dependency>

2.6.14. Kogito tracing add-on

The Tracing add-on provides Decision model and notation (DMN) services tracing capability. For more information, see the https://docs.jboss.org/kogito/release/latest/html_single/#con-trusty-service_kogito-configuring [documentation].

To use the Tracing add-on, add the following code to the pom.xml file of your project:

Quarkus
<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-addons-quarkus-tracing-decision</artifactId>
  <version>1.22</version>
</dependency>
Spring boot
<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-addons-springboot-tracing-decision</artifactId>
  <version>1.22</version>
</dependency>

2.6.15. Kogito source files add-on

The Kogito Source Files Add-on adds the capability of listing and downloading source files to Kogito projects.

⚠️ This add-on isn’t supposed to be used by end-users, if not needed for internal integrations, since it can be a potential security breach for some users' use cases.

To add the endpoints to download your services' source files, you can add the source files add-on as a dependency in your pom.xml file.

Project dependency to enable source files endpoints
<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-addons-quarkus-source-files</artifactId>
</dependency>

Access to all the endpoints is granted to the role source-files-client. To define basic authentication, you can add the following code to your application.properties file:

Example of credentials for the user scott and password jb0ss
quarkus.security.users.embedded.enabled=true
quarkus.security.users.embedded.plain-text=true
quarkus.security.users.embedded.users.scott=jb0ss
quarkus.security.users.embedded.roles.scott=source-files-client

You can also disable plain-text passwords by setting the quarkus.security.users.embedded.plain-text property to false or just removing it from the configuration. In this case, the passwords are encrypted using MD5.

Example of credentials for the user scott and password jb0ss encrypted using MD5
quarkus.security.users.embedded.enabled=true
quarkus.security.users.embedded.users.scott=4960d03c13b5aff4ca6886758a8be019
quarkus.security.users.embedded.roles.scott=source-files-client

To define a more robust authentication mechanism, you can refer to the Quarkus documentation.

The Source Files Add-on provides REST support for the following operation:

2.6.15.1. List all the source files for the specified process
  • GET: /management/process/{id}/sources

    • parameters:

      • name: id

        • in: path

Example: to list the source files for the process with id petstore_http, you can use the following command:
$ curl -X 'GET' \
  'http://localhost:8080/management/process/petstore_http/sources' \
  -H 'accept: application/json' \
  -H 'Authorization: Basic c2NvdHQ6amIwc3M='
Output
[
  {
    "uri": "https://raw.githubusercontent.com/OAI/OpenAPI-Specification/main/examples/v2.0/json/petstore-simple.json"
  },
  {
    "uri": "/sources/org/kie/kogito/examples/petstore_http.sw.json"
  }
]
2.6.15.2. Download the specified source file
Example: to download the source file located at /sources/org/kie/kogito/examples/petstore_http.sw.json, you can use the following command:
$ curl -X 'GET' \
  'http://localhost:8080/sources/org/kie/kogito/examples/petstore_http.sw.json' \
  -H 'accept: */*' \
  -H 'Authorization: Basic c2NvdHQ6amIwc3M='

2.7. Designing the application logic for a Kogito service using DMN and BPMN

After you create your Kogito project, you can create or import Business Process Model and Notation (BPMN) 2.0 business processes, Decision Model and Notation (DMN) decision models, Drools Rule Language (DRL) business rules, XLS or XLSX decision tables, and other assets in the src/main/resources folder of your project. You can also include Java classes in the src/main/java folder of your project that act as Java services or provide implementations that you call from your business processes or decisions.

The example for this procedure is a basic Kogito service that provides a REST endpoint /persons. This endpoint is automatically generated based on an example PersonProcess.bpmn2 business process that employs an example PersonDecisions.dmn DMN model to make decisions based on the data being processed.

The business process contains the business logic of the Kogito service. The process provides the complete set of steps to achieve the business goal. The process is also the entry point to the service that can be consumed by other services.

The business decision contains the decision logic of the Kogito service. In this example, the decision logic is invoked as part of the business process. You can define business rules and decisions in several ways, such as with DMN models, DRL rules, or XLS or XLSX decision tables. The example for this procedure uses a DMN model.

Procedure
  1. In the Maven project that you generated for your Kogito service, navigate to the src/main/java/org/acme folder and add the following Person.java file:

    Example person Java object
    package org.acme;
    
    import java.io.Serializable;
    
    public class Person {
    
    	private String name;
    	private int age;
    	private boolean adult;
    
    	public String getName() {
    		return name;
    	}
    
    	public void setName(String name) {
    		this.name = name;
    	}
    
    	public int getAge() {
    		return age;
    	}
    
    	public void setAge(int age) {
    		this.age = age;
    	}
    
    	public boolean isAdult() {
    		return adult;
    	}
    
    	public void setAdult(boolean adult) {
    		this.adult = adult;
    	}
    
    	@Override
    	public String toString() {
    		return "Person [name=" + name + ", age=" + age + ", adult=" + adult + "]";
    	}
    
    }

    This example Java object sets and retrieves a person’s name, age, and adult status.

  2. Navigate to the src/main/resources folder and add the following PersonDecisions.dmn DMN decision model:

    Image of PersonDecisions decision diagram
    Figure 12. Example PersonDecisions DMN decision requirements diagram (DRD)
    Image of PersonDecisions decision table
    Figure 13. Example DMN boxed expression for isAdult decision
    Image of PersonDecisions data types
    Figure 14. Example DMN data types

    This example DMN model consists of a basic DMN input node and a decision node defined by a DMN decision table with a custom structured data type.

    In VSCode (recommended), you can add the Kogito Bundle VSCode extension to design the decision requirements diagram (DRD), boxed expression, and data types with the Kogito DMN modeler.

    To create this example DMN model quickly, you can copy the following PersonDecisions.dmn file content:

    Example DMN file
    <dmn:definitions xmlns:dmn="http://www.omg.org/spec/DMN/20180521/MODEL/" xmlns="https://kiegroup.org/dmn/_52CEF9FD-9943-4A89-96D5-6F66810CA4C1" xmlns:di="http://www.omg.org/spec/DMN/20180521/DI/" xmlns:kie="http://www.drools.org/kie/dmn/1.2" xmlns:dmndi="http://www.omg.org/spec/DMN/20180521/DMNDI/" xmlns:dc="http://www.omg.org/spec/DMN/20180521/DC/" xmlns:feel="http://www.omg.org/spec/DMN/20180521/FEEL/" id="_84B432F5-87E7-43B1-9101-1BAFE3D18FC5" name="PersonDecisions" typeLanguage="http://www.omg.org/spec/DMN/20180521/FEEL/" namespace="https://kiegroup.org/dmn/_52CEF9FD-9943-4A89-96D5-6F66810CA4C1">
      <dmn:extensionElements/>
      <dmn:itemDefinition id="_DEF2C3A7-F3A9-4ABA-8D0A-C823E4EB43AB" name="tPerson" isCollection="false">
        <dmn:itemComponent id="_DB46DB27-0752-433F-ABE3-FC9E3BDECC97" name="Age" isCollection="false">
          <dmn:typeRef>number</dmn:typeRef>
        </dmn:itemComponent>
        <dmn:itemComponent id="_8C6D865F-E9C8-43B0-AB4D-3F2075A4ECA6" name="Name" isCollection="false">
          <dmn:typeRef>string</dmn:typeRef>
        </dmn:itemComponent>
        <dmn:itemComponent id="_9033704B-4E1C-42D3-AC5E-0D94107303A1" name="Adult" isCollection="false">
          <dmn:typeRef>boolean</dmn:typeRef>
        </dmn:itemComponent>
      </dmn:itemDefinition>
      <dmn:inputData id="_F9685B74-0C69-4982-B3B6-B04A14D79EDB" name="Person">
        <dmn:extensionElements/>
        <dmn:variable id="_0E345A3C-BB1F-4FB2-B00F-C5691FD1D36C" name="Person" typeRef="tPerson"/>
      </dmn:inputData>
      <dmn:decision id="_0D2BD7A9-ACA1-49BE-97AD-19699E0C9852" name="isAdult">
        <dmn:extensionElements/>
        <dmn:variable id="_54CD509F-452F-40E5-941C-AFB2667D4D45" name="isAdult" typeRef="boolean"/>
        <dmn:informationRequirement id="_2F819B03-36B7-4DEB-AED6-2B46AE3ADB75">
          <dmn:requiredInput href="#_F9685B74-0C69-4982-B3B6-B04A14D79EDB"/>
        </dmn:informationRequirement>
        <dmn:decisionTable id="_58370567-05DE-4EC0-AC2D-A23803C1EAAE" hitPolicy="UNIQUE" preferredOrientation="Rule-as-Row">
          <dmn:input id="_ADEF36CD-286A-454A-ABD8-9CF96014021B">
            <dmn:inputExpression id="_4930C2E5-7401-46DD-8329-EAC523BFA492" typeRef="number">
              <dmn:text>Person.Age</dmn:text>
            </dmn:inputExpression>
          </dmn:input>
          <dmn:output id="_9867E9A3-CBF6-4D66-9804-D2206F6B4F86" typeRef="boolean"/>
          <dmn:rule id="_59D6BFF0-35B4-4B7E-8D7B-E31CB0DB8242">
            <dmn:inputEntry id="_7DC55D63-234F-497B-A12A-93DA358C0136">
              <dmn:text>&gt; 18</dmn:text>
            </dmn:inputEntry>
            <dmn:outputEntry id="_B3BB5B97-05B9-464A-AB39-58A33A9C7C00">
              <dmn:text>true</dmn:text>
            </dmn:outputEntry>
          </dmn:rule>
          <dmn:rule id="_8FCD63FE-8AD8-4F56-AD12-923E87AFD1B1">
            <dmn:inputEntry id="_B4EF7F13-E486-46CB-B14E-1D21647258D9">
              <dmn:text>&lt;= 18</dmn:text>
            </dmn:inputEntry>
            <dmn:outputEntry id="_F3A9EC8E-A96B-42A0-BF87-9FB1F2FDB15A">
              <dmn:text>false</dmn:text>
            </dmn:outputEntry>
          </dmn:rule>
        </dmn:decisionTable>
      </dmn:decision>
      <dmndi:DMNDI>
        <dmndi:DMNDiagram>
          <di:extension>
            <kie:ComponentsWidthsExtension>
              <kie:ComponentWidths dmnElementRef="_58370567-05DE-4EC0-AC2D-A23803C1EAAE">
                <kie:width>50</kie:width>
                <kie:width>100</kie:width>
                <kie:width>100</kie:width>
                <kie:width>100</kie:width>
              </kie:ComponentWidths>
            </kie:ComponentsWidthsExtension>
          </di:extension>
          <dmndi:DMNShape id="dmnshape-_F9685B74-0C69-4982-B3B6-B04A14D79EDB" dmnElementRef="_F9685B74-0C69-4982-B3B6-B04A14D79EDB" isCollapsed="false">
            <dmndi:DMNStyle>
              <dmndi:FillColor red="255" green="255" blue="255"/>
              <dmndi:StrokeColor red="0" green="0" blue="0"/>
              <dmndi:FontColor red="0" green="0" blue="0"/>
            </dmndi:DMNStyle>
            <dc:Bounds x="404" y="464" width="100" height="50"/>
            <dmndi:DMNLabel/>
          </dmndi:DMNShape>
          <dmndi:DMNShape id="dmnshape-_0D2BD7A9-ACA1-49BE-97AD-19699E0C9852" dmnElementRef="_0D2BD7A9-ACA1-49BE-97AD-19699E0C9852" isCollapsed="false">
            <dmndi:DMNStyle>
              <dmndi:FillColor red="255" green="255" blue="255"/>
              <dmndi:StrokeColor red="0" green="0" blue="0"/>
              <dmndi:FontColor red="0" green="0" blue="0"/>
            </dmndi:DMNStyle>
            <dc:Bounds x="404" y="311" width="100" height="50"/>
            <dmndi:DMNLabel/>
          </dmndi:DMNShape>
          <dmndi:DMNEdge id="dmnedge-_2F819B03-36B7-4DEB-AED6-2B46AE3ADB75" dmnElementRef="_2F819B03-36B7-4DEB-AED6-2B46AE3ADB75">
            <di:waypoint x="504" y="489"/>
            <di:waypoint x="404" y="336"/>
          </dmndi:DMNEdge>
        </dmndi:DMNDiagram>
      </dmndi:DMNDI>
    </dmn:definitions>

    To create this example DMN model in VSCode using the Kogito DMN modeler, follow these steps:

    1. Open the empty PersonDecisions.dmn file and in the upper-right corner of the DMN modeler, click the Properties icon and confirm that the DMN model Name is set to PersonDecisions.

    2. In the left palette, select DMN Input Data, drag the node to the canvas, and double-click the node to name it Person.

    3. In the left palette, select DMN Decision, drag the node to the canvas, double-click the node to name it isAdult, and link to it from the input node.

    4. Select the decision node to display the node options and click the Edit icon to open the DMN boxed expression editor to define the decision logic for the node.

    5. Click the undefined expression field and select Decision Table.

    6. Click the upper-left corner of the decision table to set the hit policy to Unique.

    7. Set the input and output columns so that the input source Person.Age with type number determines the age limit and the output target isAdult with type boolean determines adult status:

      Image of PersonDecisions decision table
      Figure 15. Example DMN decision table for isAdult decision
    8. In the upper tab options, select the Data Types tab and add the following tPerson structured data type and nested data types:

      Image of PersonDecisions data types
      Figure 16. Example DMN data types
    9. After you define the data types, select the Editor tab to return to the DMN modeler canvas.

    10. Select the Person input node, click the Properties icon, and under Information item, set the Data type to tPerson.

    11. Select the isAdult decision node, click the Properties icon, and under Information item, confirm that the Data type is still set to boolean. You previously set this data type when you created the decision table.

    12. Save the DMN decision file.

  3. In the src/main/resources folder, add the following PersonProcess.bpmn2 BPMN process model:

    Image of person process diagram
    Figure 17. Example PersonProcess BPMN process

    This example process consists of the following basic BPMN components:

    • Start event

    • Business rule task

    • Exclusive gateway

    • User task

    • End events

    In VSCode (recommended), you can add the Kogito Bundle VSCode extension to model the business process with the Kogito BPMN modeler.

    To create this example process quickly, you can copy the following PersonProcess.bpmn2 file content:

    Example BPMN file
    <bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:bpsim="http://www.bpsim.org/schemas/1.0" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:drools="http://www.jboss.org/drools" id="_J4ElsVJgEDiScotxwBQ14Q" exporter="jBPM Process Modeler" exporterVersion="2.0" targetNamespace="http://www.omg.org/bpmn20">
      <bpmn2:itemDefinition id="_personItem" structureRef="org.acme.Person"/>
      <bpmn2:itemDefinition id="_isAdultItem" structureRef="Boolean"/>
      <bpmn2:itemDefinition id="_UserTask_1_SkippableInputXItem" structureRef="Object"/>
      <bpmn2:itemDefinition id="_UserTask_1_PriorityInputXItem" structureRef="Object"/>
      <bpmn2:itemDefinition id="_UserTask_1_CommentInputXItem" structureRef="Object"/>
      <bpmn2:itemDefinition id="_UserTask_1_DescriptionInputXItem" structureRef="Object"/>
      <bpmn2:itemDefinition id="_UserTask_1_CreatedByInputXItem" structureRef="Object"/>
      <bpmn2:itemDefinition id="_UserTask_1_TaskNameInputXItem" structureRef="Object"/>
      <bpmn2:itemDefinition id="_UserTask_1_GroupIdInputXItem" structureRef="Object"/>
      <bpmn2:itemDefinition id="_UserTask_1_ContentInputXItem" structureRef="Object"/>
      <bpmn2:itemDefinition id="_UserTask_1_NotStartedReassignInputXItem" structureRef="Object"/>
      <bpmn2:itemDefinition id="_UserTask_1_NotCompletedReassignInputXItem" structureRef="Object"/>
      <bpmn2:itemDefinition id="_UserTask_1_NotStartedNotifyInputXItem" structureRef="Object"/>
      <bpmn2:itemDefinition id="_UserTask_1_NotCompletedNotifyInputXItem" structureRef="Object"/>
      <bpmn2:itemDefinition id="_UserTask_1_personInputXItem" structureRef="org.acme.Person"/>
      <bpmn2:itemDefinition id="_BusinessRuleTask_1_namespaceInputXItem" structureRef="java.lang.String"/>
      <bpmn2:itemDefinition id="_BusinessRuleTask_1_modelInputXItem" structureRef="java.lang.String"/>
      <bpmn2:itemDefinition id="_BusinessRuleTask_1_decisionInputXItem" structureRef="java.lang.String"/>
      <bpmn2:itemDefinition id="_BusinessRuleTask_1_PersonInputXItem" structureRef="org.acme.Person"/>
      <bpmn2:itemDefinition id="_BusinessRuleTask_1_isAdultOutputXItem" structureRef="Boolean"/>
      <bpmn2:process id="persons" drools:packageName="org.acme" drools:version="1.0" drools:adHoc="false" name="Person Process" isExecutable="true" processType="Public">
        <bpmn2:property id="person" itemSubjectRef="_personItem" name="person"/>
        <bpmn2:property id="isAdult" itemSubjectRef="_isAdultItem" name="isAdult"/>
        <bpmn2:sequenceFlow id="SequenceFlow_1" sourceRef="StartEvent_1" targetRef="BusinessRuleTask_1"/>
        <bpmn2:sequenceFlow id="SequenceFlow_2" sourceRef="BusinessRuleTask_1" targetRef="ExclusiveGateway_1"/>
        <bpmn2:sequenceFlow id="SequenceFlow_3" sourceRef="ExclusiveGateway_1" targetRef="UserTask_1">
          <bpmn2:conditionExpression xsi:type="bpmn2:tFormalExpression" language="http://www.java.com/java">return isAdult == false;</bpmn2:conditionExpression>
        </bpmn2:sequenceFlow>
        <bpmn2:sequenceFlow id="SequenceFlow_4" sourceRef="UserTask_1" targetRef="EndEvent_1"/>
        <bpmn2:sequenceFlow id="SequenceFlow_5" sourceRef="ExclusiveGateway_1" targetRef="EndEvent_2">
          <bpmn2:conditionExpression xsi:type="bpmn2:tFormalExpression" language="http://www.java.com/java">return isAdult == true;</bpmn2:conditionExpression>
        </bpmn2:sequenceFlow>
        <bpmn2:startEvent id="StartEvent_1" name="StartProcess">
          <bpmn2:extensionElements>
            <drools:metaData name="elementname">
              <drools:metaValue>StartProcess</drools:metaValue>
            </drools:metaData>
          </bpmn2:extensionElements>
          <bpmn2:outgoing>SequenceFlow_1</bpmn2:outgoing>
        </bpmn2:startEvent>
        <bpmn2:businessRuleTask id="BusinessRuleTask_1" name="Evaluate person" implementation="http://www.jboss.org/drools/dmn">
          <bpmn2:extensionElements>
            <drools:metaData name="elementname">
              <drools:metaValue>Evaluate person</drools:metaValue>
            </drools:metaData>
          </bpmn2:extensionElements>
          <bpmn2:incoming>SequenceFlow_1</bpmn2:incoming>
          <bpmn2:outgoing>SequenceFlow_2</bpmn2:outgoing>
          <bpmn2:ioSpecification>
            <bpmn2:dataInput id="BusinessRuleTask_1_namespaceInputX" drools:dtype="java.lang.String" itemSubjectRef="_BusinessRuleTask_1_namespaceInputXItem" name="namespace"/>
            <bpmn2:dataInput id="BusinessRuleTask_1_decisionInputX" drools:dtype="java.lang.String" itemSubjectRef="_BusinessRuleTask_1_decisionInputXItem" name="decision"/>
            <bpmn2:dataInput id="BusinessRuleTask_1_modelInputX" drools:dtype="java.lang.String" itemSubjectRef="_BusinessRuleTask_1_modelInputXItem" name="model"/>
            <bpmn2:dataInput id="BusinessRuleTask_1_PersonInputX" drools:dtype="org.acme.Person" itemSubjectRef="_BusinessRuleTask_1_PersonInputXItem" name="Person"/>
            <bpmn2:dataOutput id="BusinessRuleTask_1_isAdultOutputX" drools:dtype="Boolean" itemSubjectRef="_BusinessRuleTask_1_isAdultOutputXItem" name="isAdult"/>
            <bpmn2:inputSet>
              <bpmn2:dataInputRefs>BusinessRuleTask_1_namespaceInputX</bpmn2:dataInputRefs>
              <bpmn2:dataInputRefs>BusinessRuleTask_1_decisionInputX</bpmn2:dataInputRefs>
              <bpmn2:dataInputRefs>BusinessRuleTask_1_modelInputX</bpmn2:dataInputRefs>
              <bpmn2:dataInputRefs>BusinessRuleTask_1_PersonInputX</bpmn2:dataInputRefs>
            </bpmn2:inputSet>
            <bpmn2:outputSet>
              <bpmn2:dataOutputRefs>BusinessRuleTask_1_isAdultOutputX</bpmn2:dataOutputRefs>
            </bpmn2:outputSet>
          </bpmn2:ioSpecification>
          <bpmn2:dataInputAssociation>
            <bpmn2:targetRef>BusinessRuleTask_1_namespaceInputX</bpmn2:targetRef>
            <bpmn2:assignment>
              <bpmn2:from xsi:type="bpmn2:tFormalExpression">https://kiegroup.org/dmn/_52CEF9FD-9943-4A89-96D5-6F66810CA4C1</bpmn2:from>
              <bpmn2:to xsi:type="bpmn2:tFormalExpression">BusinessRuleTask_1_namespaceInputX</bpmn2:to>
            </bpmn2:assignment>
          </bpmn2:dataInputAssociation>
          <bpmn2:dataInputAssociation>
            <bpmn2:targetRef>BusinessRuleTask_1_decisionInputX</bpmn2:targetRef>
            <bpmn2:assignment>
              <bpmn2:from xsi:type="bpmn2:tFormalExpression">isAdult</bpmn2:from>
              <bpmn2:to xsi:type="bpmn2:tFormalExpression">BusinessRuleTask_1_decisionInputX</bpmn2:to>
            </bpmn2:assignment>
          </bpmn2:dataInputAssociation>
          <bpmn2:dataInputAssociation>
            <bpmn2:targetRef>BusinessRuleTask_1_modelInputX</bpmn2:targetRef>
            <bpmn2:assignment>
              <bpmn2:from xsi:type="bpmn2:tFormalExpression">PersonDecisions</bpmn2:from>
              <bpmn2:to xsi:type="bpmn2:tFormalExpression">BusinessRuleTask_1_modelInputX</bpmn2:to>
            </bpmn2:assignment>
          </bpmn2:dataInputAssociation>
          <bpmn2:dataInputAssociation>
            <bpmn2:sourceRef>person</bpmn2:sourceRef>
            <bpmn2:targetRef>BusinessRuleTask_1_PersonInputX</bpmn2:targetRef>
          </bpmn2:dataInputAssociation>
          <bpmn2:dataOutputAssociation>
            <bpmn2:sourceRef>BusinessRuleTask_1_isAdultOutputX</bpmn2:sourceRef>
            <bpmn2:targetRef>isAdult</bpmn2:targetRef>
          </bpmn2:dataOutputAssociation>
        </bpmn2:businessRuleTask>
        <bpmn2:exclusiveGateway id="ExclusiveGateway_1" name="Exclusive Gateway 1" gatewayDirection="Diverging">
          <bpmn2:extensionElements>
            <drools:metaData name="elementname">
              <drools:metaValue>Exclusive Gateway 1</drools:metaValue>
            </drools:metaData>
          </bpmn2:extensionElements>
          <bpmn2:incoming>SequenceFlow_2</bpmn2:incoming>
          <bpmn2:outgoing>SequenceFlow_5</bpmn2:outgoing>
          <bpmn2:outgoing>SequenceFlow_3</bpmn2:outgoing>
        </bpmn2:exclusiveGateway>
        <bpmn2:userTask id="UserTask_1" name="Special handling for children">
          <bpmn2:extensionElements>
            <drools:metaData name="elementname">
              <drools:metaValue>Special handling for children</drools:metaValue>
            </drools:metaData>
          </bpmn2:extensionElements>
          <bpmn2:incoming>SequenceFlow_3</bpmn2:incoming>
          <bpmn2:outgoing>SequenceFlow_4</bpmn2:outgoing>
          <bpmn2:ioSpecification>
            <bpmn2:dataInput id="UserTask_1_TaskNameInputX" drools:dtype="Object" itemSubjectRef="_UserTask_1_TaskNameInputXItem" name="TaskName"/>
            <bpmn2:dataInput id="UserTask_1_personInputX" drools:dtype="org.acme.Person" itemSubjectRef="_UserTask_1_personInputXItem" name="person"/>
            <bpmn2:dataInput id="UserTask_1_SkippableInputX" drools:dtype="Object" itemSubjectRef="_UserTask_1_SkippableInputXItem" name="Skippable"/>
            <bpmn2:dataInput id="UserTask_1_PriorityInputX" drools:dtype="Object" itemSubjectRef="_UserTask_1_PriorityInputXItem" name="Priority"/>
            <bpmn2:inputSet>
              <bpmn2:dataInputRefs>UserTask_1_TaskNameInputX</bpmn2:dataInputRefs>
              <bpmn2:dataInputRefs>UserTask_1_personInputX</bpmn2:dataInputRefs>
              <bpmn2:dataInputRefs>UserTask_1_SkippableInputX</bpmn2:dataInputRefs>
              <bpmn2:dataInputRefs>UserTask_1_PriorityInputX</bpmn2:dataInputRefs>
            </bpmn2:inputSet>
          </bpmn2:ioSpecification>
          <bpmn2:dataInputAssociation>
            <bpmn2:targetRef>UserTask_1_TaskNameInputX</bpmn2:targetRef>
            <bpmn2:assignment>
              <bpmn2:from xsi:type="bpmn2:tFormalExpression">ChildrenHandling</bpmn2:from>
              <bpmn2:to xsi:type="bpmn2:tFormalExpression">UserTask_1_TaskNameInputX</bpmn2:to>
            </bpmn2:assignment>
          </bpmn2:dataInputAssociation>
          <bpmn2:dataInputAssociation>
            <bpmn2:sourceRef>person</bpmn2:sourceRef>
            <bpmn2:targetRef>UserTask_1_personInputX</bpmn2:targetRef>
          </bpmn2:dataInputAssociation>
          <bpmn2:dataInputAssociation>
            <bpmn2:targetRef>UserTask_1_SkippableInputX</bpmn2:targetRef>
            <bpmn2:assignment>
              <bpmn2:from xsi:type="bpmn2:tFormalExpression">true</bpmn2:from>
              <bpmn2:to xsi:type="bpmn2:tFormalExpression">UserTask_1_SkippableInputX</bpmn2:to>
            </bpmn2:assignment>
          </bpmn2:dataInputAssociation>
          <bpmn2:dataInputAssociation>
            <bpmn2:targetRef>UserTask_1_PriorityInputX</bpmn2:targetRef>
            <bpmn2:assignment>
              <bpmn2:from xsi:type="bpmn2:tFormalExpression">1</bpmn2:from>
              <bpmn2:to xsi:type="bpmn2:tFormalExpression">UserTask_1_PriorityInputX</bpmn2:to>
            </bpmn2:assignment>
          </bpmn2:dataInputAssociation>
        </bpmn2:userTask>
        <bpmn2:endEvent id="EndEvent_1" name="End Event 1">
          <bpmn2:extensionElements>
            <drools:metaData name="elementname">
              <drools:metaValue>End Event 1</drools:metaValue>
            </drools:metaData>
          </bpmn2:extensionElements>
          <bpmn2:incoming>SequenceFlow_4</bpmn2:incoming>
        </bpmn2:endEvent>
        <bpmn2:endEvent id="EndEvent_2" name="End Event 2">
          <bpmn2:extensionElements>
            <drools:metaData name="elementname">
              <drools:metaValue>End Event 2</drools:metaValue>
            </drools:metaData>
          </bpmn2:extensionElements>
          <bpmn2:incoming>SequenceFlow_5</bpmn2:incoming>
        </bpmn2:endEvent>
      </bpmn2:process>
      <bpmndi:BPMNDiagram>
        <bpmndi:BPMNPlane bpmnElement="persons">
          <bpmndi:BPMNShape id="shape_EndEvent_2" bpmnElement="EndEvent_2">
            <dc:Bounds height="56" width="56" x="622" y="201"/>
          </bpmndi:BPMNShape>
          <bpmndi:BPMNShape id="shape_EndEvent_1" bpmnElement="EndEvent_1">
            <dc:Bounds height="56" width="56" x="622" y="105"/>
          </bpmndi:BPMNShape>
          <bpmndi:BPMNShape id="shape_UserTask_1" bpmnElement="UserTask_1">
            <dc:Bounds height="78" width="134" x="449" y="94"/>
          </bpmndi:BPMNShape>
          <bpmndi:BPMNShape id="shape_ExclusiveGateway_1" bpmnElement="ExclusiveGateway_1">
            <dc:Bounds height="56" width="56" x="365" y="105"/>
          </bpmndi:BPMNShape>
          <bpmndi:BPMNShape id="shape_BusinessRuleTask_1" bpmnElement="BusinessRuleTask_1">
            <dc:Bounds height="71" width="141" x="180" y="97"/>
          </bpmndi:BPMNShape>
          <bpmndi:BPMNShape id="shape_StartEvent_1" bpmnElement="StartEvent_1">
            <dc:Bounds height="56" width="56" x="80" y="105"/>
          </bpmndi:BPMNShape>
          <bpmndi:BPMNEdge id="edge_shape_ExclusiveGateway_1_to_shape_EndEvent_2" bpmnElement="SequenceFlow_5">
            <di:waypoint x="390" y="155"/>
            <di:waypoint x="393" y="231"/>
            <di:waypoint x="622" y="219"/>
          </bpmndi:BPMNEdge>
          <bpmndi:BPMNEdge id="edge_shape_UserTask_1_to_shape_EndEvent_1" bpmnElement="SequenceFlow_4">
            <di:waypoint x="583" y="133"/>
            <di:waypoint x="622" y="123"/>
          </bpmndi:BPMNEdge>
          <bpmndi:BPMNEdge id="edge_shape_ExclusiveGateway_1_to_shape_UserTask_1" bpmnElement="SequenceFlow_3">
            <di:waypoint x="415" y="130"/>
            <di:waypoint x="449" y="133"/>
          </bpmndi:BPMNEdge>
          <bpmndi:BPMNEdge id="edge_shape_BusinessRuleTask_1_to_shape_ExclusiveGateway_1" bpmnElement="SequenceFlow_2">
            <di:waypoint x="321" y="132.5"/>
            <di:waypoint x="365" y="130"/>
          </bpmndi:BPMNEdge>
          <bpmndi:BPMNEdge id="edge_shape_StartEvent_1_to_shape_BusinessRuleTask_1" bpmnElement="SequenceFlow_1">
            <di:waypoint x="116" y="123"/>
            <di:waypoint x="180" y="132.5"/>
          </bpmndi:BPMNEdge>
        </bpmndi:BPMNPlane>
      </bpmndi:BPMNDiagram>
      <bpmn2:relationship type="BPSimData">
        <bpmn2:extensionElements>
          <bpsim:BPSimData>
            <bpsim:Scenario id="default" name="Simulationscenario">
              <bpsim:ScenarioParameters/>
              <bpsim:ElementParameters elementRef="UserTask_1">
                <bpsim:TimeParameters>
                  <bpsim:ProcessingTime>
                    <bpsim:NormalDistribution mean="0" standardDeviation="0"/>
                  </bpsim:ProcessingTime>
                </bpsim:TimeParameters>
                <bpsim:ResourceParameters>
                  <bpsim:Availability>
                    <bpsim:FloatingParameter value="0"/>
                  </bpsim:Availability>
                  <bpsim:Quantity>
                    <bpsim:FloatingParameter value="0"/>
                  </bpsim:Quantity>
                </bpsim:ResourceParameters>
                <bpsim:CostParameters>
                  <bpsim:UnitCost>
                    <bpsim:FloatingParameter value="0"/>
                  </bpsim:UnitCost>
                </bpsim:CostParameters>
              </bpsim:ElementParameters>
              <bpsim:ElementParameters elementRef="BusinessRuleTask_1">
                <bpsim:TimeParameters>
                  <bpsim:ProcessingTime>
                    <bpsim:NormalDistribution mean="0" standardDeviation="0"/>
                  </bpsim:ProcessingTime>
                </bpsim:TimeParameters>
                <bpsim:ResourceParameters>
                  <bpsim:Availability>
                    <bpsim:FloatingParameter value="0"/>
                  </bpsim:Availability>
                  <bpsim:Quantity>
                    <bpsim:FloatingParameter value="0"/>
                  </bpsim:Quantity>
                </bpsim:ResourceParameters>
                <bpsim:CostParameters>
                  <bpsim:UnitCost>
                    <bpsim:FloatingParameter value="0"/>
                  </bpsim:UnitCost>
                </bpsim:CostParameters>
              </bpsim:ElementParameters>
              <bpsim:ElementParameters elementRef="StartEvent_1">
                <bpsim:TimeParameters>
                  <bpsim:ProcessingTime>
                    <bpsim:NormalDistribution mean="0" standardDeviation="0"/>
                  </bpsim:ProcessingTime>
                </bpsim:TimeParameters>
              </bpsim:ElementParameters>
            </bpsim:Scenario>
          </bpsim:BPSimData>
        </bpmn2:extensionElements>
        <bpmn2:source>_J4ElsVJgEDiScotxwBQ14Q</bpmn2:source>
        <bpmn2:target>_J4ElsVJgEDiScotxwBQ14Q</bpmn2:target>
      </bpmn2:relationship>
    </bpmn2:definitions>

    To create this example BPMN process in VSCode using the Kogito BPMN modeler, follow these steps:

    1. Open the empty PersonProcess.bpmn2 file and in the upper-right corner of the BPMN modeler, click the Properties icon and define the following properties:

      • Process: Set the following values:

        • Name: Person Process

        • ID: persons

        • Package: org.acme

      • Process Data: Add the following process variables:

        • person with the type org.acme.Person (Use the Custom data type option to define the custom type.)

        • isAdult with the type Boolean

    2. In the left palette, select ActivitiesBusiness Rule, drag the task to the canvas, and link to it from the start event.

    3. Select the business rule task and define the following properties:

      • General: Set the rule task Name to Evaluate person.

      • Implementation/Execution: Set the following values:

        • Rule Language: DMN

        • Namespace: The namespace property value from the PersonDecisions.dmn file that you created previously, such as https://kiegroup.org/dmn/_52CEF9FD-9943-4A89-96D5-6F66810CA4C1

        • Decision Name: isAdult

        • DMN Model Name: PersonDecisions

      • Data Assignments: Add the following assignments:

        • Data Input: Add a data input with the name Person, with the type org.acme.Person, and with the source person.

        • Data Output: Add a data output with the name isAdult, with the type Boolean, and with the source isAdult.

    4. In the left palette, select GatewaysExclusive, drag the gateway to the canvas, and link to it from the rule task.

    5. In the left palette, select ActivitiesUser, drag the user task to the canvas, and link to it from the exclusive gateway.

    6. Select the user task and define the following properties:

      • General: Set the user task Name to Special handling for children.

      • Implementation/Execution: Set the Task Name to ChildrenHandling, and add a data input with the name person, the type org.acme.Person, and the source person.

    7. In the left palette, select End EventsEnd, drag two end events to the canvas, and link to one end event from the user task and to the other end event from the exclusive gateway.

    8. Select the connector that connects the exclusive gateway to the end event and for the Implementation/Execution property, set the Condition Expression to Java and enter the condition return isAdult == true;.

    9. Select the connector that connects the exclusive gateway to the user task and for the Implementation/Execution property, set the Condition Expression to Java and enter the condition to return isAdult == false;

    10. Save the BPMN process file.

2.7.1. Using DRL rule units as an alternative decision service

As an alternative to using Decision Model and Notation (DMN) to define this example decision service, you can also use a Drools Rule Language (DRL) file implemented as a rule unit.

A DRL rule unit is a module for rules and a unit of execution. A rule unit collects a set of rules with the declaration of the type of facts that the rules act on. A rule unit also serves as a unique namespace for each group of rules. A single rule base can contain multiple rule units. You typically store all the rules for a unit in the same file as the unit declaration so that the unit is self-contained. For more information about rule units, see Developing decision services with Kogito.

Procedure
  1. In the src/main/resources folder of your example Kogito project, instead of using a DMN file, add the following PersonRules.drl file:

    Example PersonRules DRL file
    package org.acme
    unit PersonRules;
    
    import org.acme.Person;
    
    rule isAdult
    	when
    		$person: /person[ age > 18 ]
    	then
        modify($person) {
        	setAdult(true)
        };
    end

    This example rule determines that any person who is older than 18 is classified as an adult. The rule file also declares that the rule belongs to the rule unit PersonRules. This is the rule unit that you define as part of the business rule task in the example BPMN process. When you build the project, the rule unit is generated and associated with the DRL file.

    The rule also defines the condition using OOPath notation. OOPath is an object-oriented syntax extension to XPath for navigating through related elements while handling collections and filtering constraints.

    You can also rewrite the same rule condition in a more explicit form using the traditional rule pattern syntax, as shown in the following example:

    Example PersonRules DRL file using traditional notation
    package org.acme
    unit PersonRules;
    
    import org.acme.Person;
    
    rule isAdult
    	when
    		$person: Person(age > 18) from person
    	then
        modify($person) {
        	setAdult(true)
        };
    end
  2. In the src/main/resources folder, use the Kogito BPMN modeler in VSCode to open the PersonProcess.bpmn2 process diagram that you created.

  3. Select the Evaluate person business rule task and modify the following properties:

    • Implementation/Execution: Set the following values:

      • Rule Language: DRL (instead of DMN)

      • Rule Flow Group: unit:org.acme.PersonRules

        This rule unit syntax in the Rule Flow Group field specifies that you are using the org.acme.PersonRules rule unit instead of a traditional rule flow group. This is the rule unit that you referenced in the example DRL file. When you build the project, the business process implicitly declares the rule unit as part of the business rule task to execute the DRL file.

    • Data Assignments: Open the assignment settings and change the data input Name to person (instead of Person). This accommodates the input variable syntax required by the DRL file.

  4. Select the connector that connects the exclusive gateway to the end event and for the Implementation/Execution property, verify that the Condition Expression is set to Java and change the condition to return person.isAdult();.

  5. Select the connector that connects the exclusive gateway to the user task and for the Implementation/Execution property, verify that the Condition Expression is set to Java and change the condition return ! person.isAdult();.

  6. Save the process file to update the model.

2.8. Testing the decision logic for a Kogito service using test scenarios

As you develop business decisions in your Kogito services, you can use test scenarios to validate the functionality of your decisions before you begin running and using your Kogito services. With a test scenario, you use data from your project to set given conditions and expected results based on one or more defined business decisions. When you run the scenario, the expected results and actual results of the decision instance are compared. If the expected results match the actual results, the test is successful. If the expected results do not match the actual results, then the test fails.

You define test scenarios in .scesim (scenario simulation) files that you can model in Visual Studio Code (VSCode) using the Kogito Bundle VSCode extension. You can use one or multiple .scesim files in your Kogito project, and each .scesim file can contain one or multiple test scenarios based on the defined decision data.

The example for this procedure uses a basic PersonDecisionsTest.scesim test scenario file that validates the decision logic in the example PersonDecisions.dmn Decision Model and Notation (DMN) model that you created previously.

Test scenarios in Kogito currently support DMN decision services only. Test scenarios will support Drools Rule Language (DRL) decision services in a future release.
Prerequisites
Procedure
  1. In the Maven project that contains your Kogito decision services, add the following dependency to the pom.xml file to enable test scenario execution for your project:

    Dependency to enable test scenario execution
    <dependency>
      <groupId>org.kie.kogito</groupId>
      <artifactId>kogito-scenario-simulation</artifactId>
      <scope>test</scope>
    </dependency>
  2. Navigate to the src/main folder of the project and create a test folder with the following subfolders. If you have an existing test folder structure, you can adapt the steps that follow according to your project layout.

    Test folder structure for test scenarios
    src/main/test/
              └── java/testscenario
              └── resources
  3. Navigate to the test/java/testscenario folder and add the following KogitoScenarioJunitActivatorTest.java class:

    Activator class for test scenarios
    package testscenario;
    
    @org.junit.runner.RunWith(org.kogito.scenariosimulation.runner.KogitoJunitActivator.class)
    public class KogitoScenarioJunitActivatorTest {
    
    }

    This activator class is a custom JUnit 5 runner that enables the execution of test scenario files in your Kogito project. When you run test scenarios, this class loads all .scesim files available in the project and executes them. For each row (scenario) in a test scenario file, the activator class generates a JUnit test result.

  4. Navigate to the test/resources folder and add the following PersonDecisionsTest.scesim test scenario file:

    Image of PersonDecisionsTest test scenario
    Figure 18. Example test scenarios for PersonDecisions DMN decision logic

    The GIVEN columns specify input conditions based on the corresponding decision service. The EXPECT column specifies the expected results of the decision service based on the defined GIVEN conditions. Each row in the table is a defined scenario with example values for the GIVEN and EXPECT definitions to test the decision logic of the corresponding decision service.

    This example test scenario file tests the decision logic for the following PersonDecisions.dmn model that you created previously. This DMN model determines whether a specified person is an adult or is underage.

    Image of PersonDecisions decision diagram
    Figure 19. Example PersonDecisions DMN decision requirements diagram (DRD)
    Image of PersonDecisions decision table
    Figure 20. Example DMN boxed expression for isAdult decision

    In VSCode (recommended), you can add the Kogito Bundle VSCode extension to design the test scenarios with the test scenario modeler.

    To create these example test scenarios quickly, you can copy the following PersonDecisionsTest.scesim file content:

    Example test scenario file
    <ScenarioSimulationModel version="1.8">
      <simulation>
        <scesimModelDescriptor>
          <factMappings>
            <FactMapping>
              <expressionElements/>
              <expressionIdentifier>
                <name>Index</name>
                <type>OTHER</type>
              </expressionIdentifier>
              <factIdentifier>
                <name>#</name>
                <className>java.lang.Integer</className>
              </factIdentifier>
              <className>java.lang.Integer</className>
              <factAlias>#</factAlias>
              <columnWidth>70</columnWidth>
              <factMappingValueType>NOT_EXPRESSION</factMappingValueType>
            </FactMapping>
            <FactMapping>
              <expressionElements/>
              <expressionIdentifier>
                <name>Description</name>
                <type>OTHER</type>
              </expressionIdentifier>
              <factIdentifier>
                <name>Scenario description</name>
                <className>java.lang.String</className>
              </factIdentifier>
              <className>java.lang.String</className>
              <factAlias>Scenario description</factAlias>
              <columnWidth>300</columnWidth>
              <factMappingValueType>NOT_EXPRESSION</factMappingValueType>
            </FactMapping>
            <FactMapping>
              <expressionElements>
                <ExpressionElement>
                  <step>Person</step>
                </ExpressionElement>
                <ExpressionElement>
                  <step>Age</step>
                </ExpressionElement>
              </expressionElements>
              <expressionIdentifier>
                <name>1|1</name>
                <type>GIVEN</type>
              </expressionIdentifier>
              <factIdentifier>
                <name>Person</name>
                <className>Person</className>
              </factIdentifier>
              <className>number</className>
              <factAlias>Person</factAlias>
              <expressionAlias>Age</expressionAlias>
              <genericTypes/>
              <columnWidth>114</columnWidth>
              <factMappingValueType>NOT_EXPRESSION</factMappingValueType>
            </FactMapping>
            <FactMapping>
              <expressionElements>
                <ExpressionElement>
                  <step>Person</step>
                </ExpressionElement>
                <ExpressionElement>
                  <step>Name</step>
                </ExpressionElement>
              </expressionElements>
              <expressionIdentifier>
                <name>1|2</name>
                <type>GIVEN</type>
              </expressionIdentifier>
              <factIdentifier>
                <name>Person</name>
                <className>Person</className>
              </factIdentifier>
              <className>string</className>
              <factAlias>Person</factAlias>
              <expressionAlias>Name</expressionAlias>
              <genericTypes/>
              <columnWidth>114</columnWidth>
              <factMappingValueType>NOT_EXPRESSION</factMappingValueType>
            </FactMapping>
            <FactMapping>
              <expressionElements>
                <ExpressionElement>
                  <step>isAdult</step>
                </ExpressionElement>
              </expressionElements>
              <expressionIdentifier>
                <name>1|4</name>
                <type>EXPECT</type>
              </expressionIdentifier>
              <factIdentifier>
                <name>isAdult</name>
                <className>isAdult</className>
              </factIdentifier>
              <className>boolean</className>
              <factAlias>isAdult</factAlias>
              <expressionAlias>value</expressionAlias>
              <genericTypes/>
              <columnWidth>114</columnWidth>
              <factMappingValueType>NOT_EXPRESSION</factMappingValueType>
            </FactMapping>
          </factMappings>
        </scesimModelDescriptor>
        <scesimData>
          <Scenario>
            <factMappingValues>
              <FactMappingValue>
                <factIdentifier>
                  <name>Scenario description</name>
                  <className>java.lang.String</className>
                </factIdentifier>
                <expressionIdentifier>
                  <name>Description</name>
                  <type>OTHER</type>
                </expressionIdentifier>
                <rawValue class="string">Is an adult</rawValue>
              </FactMappingValue>
              <FactMappingValue>
                <factIdentifier>
                  <name>Person</name>
                  <className>Person</className>
                </factIdentifier>
                <expressionIdentifier>
                  <name>1|1</name>
                  <type>GIVEN</type>
                </expressionIdentifier>
                <rawValue class="string">20</rawValue>
              </FactMappingValue>
              <FactMappingValue>
                <factIdentifier>
                  <name>Person</name>
                  <className>Person</className>
                </factIdentifier>
                <expressionIdentifier>
                  <name>1|2</name>
                  <type>GIVEN</type>
                </expressionIdentifier>
                <rawValue class="string">"John Quark"</rawValue>
              </FactMappingValue>
              <FactMappingValue>
                <factIdentifier>
                  <name>isAdult</name>
                  <className>isAdult</className>
                </factIdentifier>
                <expressionIdentifier>
                  <name>1|4</name>
                  <type>EXPECT</type>
                </expressionIdentifier>
                <rawValue class="string">true</rawValue>
              </FactMappingValue>
              <FactMappingValue>
                <factIdentifier>
                  <name>#</name>
                  <className>java.lang.Integer</className>
                </factIdentifier>
                <expressionIdentifier>
                  <name>Index</name>
                  <type>OTHER</type>
                </expressionIdentifier>
                <rawValue class="string">1</rawValue>
              </FactMappingValue>
            </factMappingValues>
          </Scenario>
          <Scenario>
            <factMappingValues>
              <FactMappingValue>
                <factIdentifier>
                  <name>Scenario description</name>
                  <className>java.lang.String</className>
                </factIdentifier>
                <expressionIdentifier>
                  <name>Description</name>
                  <type>OTHER</type>
                </expressionIdentifier>
                <rawValue class="string">Is underage</rawValue>
              </FactMappingValue>
              <FactMappingValue>
                <factIdentifier>
                  <name>Person</name>
                  <className>Person</className>
                </factIdentifier>
                <expressionIdentifier>
                  <name>1|1</name>
                  <type>GIVEN</type>
                </expressionIdentifier>
                <rawValue class="string">15</rawValue>
              </FactMappingValue>
              <FactMappingValue>
                <factIdentifier>
                  <name>Person</name>
                  <className>Person</className>
                </factIdentifier>
                <expressionIdentifier>
                  <name>1|2</name>
                  <type>GIVEN</type>
                </expressionIdentifier>
                <rawValue class="string">"Jenny Quark"</rawValue>
              </FactMappingValue>
              <FactMappingValue>
                <factIdentifier>
                  <name>isAdult</name>
                  <className>isAdult</className>
                </factIdentifier>
                <expressionIdentifier>
                  <name>1|4</name>
                  <type>EXPECT</type>
                </expressionIdentifier>
                <rawValue class="string">false</rawValue>
              </FactMappingValue>
              <FactMappingValue>
                <factIdentifier>
                  <name>#</name>
                  <className>java.lang.Integer</className>
                </factIdentifier>
                <expressionIdentifier>
                  <name>Index</name>
                  <type>OTHER</type>
                </expressionIdentifier>
                <rawValue class="string">2</rawValue>
              </FactMappingValue>
            </factMappingValues>
          </Scenario>
        </scesimData>
      </simulation>
      <background>
        <scesimModelDescriptor>
          <factMappings>
            <FactMapping>
              <expressionElements/>
              <expressionIdentifier>
                <name>1|1</name>
                <type>GIVEN</type>
              </expressionIdentifier>
              <factIdentifier>
                <name>Empty</name>
                <className>java.lang.Void</className>
              </factIdentifier>
              <className>java.lang.Void</className>
              <factAlias>INSTANCE 1</factAlias>
              <expressionAlias>PROPERTY 1</expressionAlias>
              <columnWidth>114</columnWidth>
              <factMappingValueType>NOT_EXPRESSION</factMappingValueType>
            </FactMapping>
          </factMappings>
        </scesimModelDescriptor>
        <scesimData>
          <BackgroundData>
            <factMappingValues>
              <FactMappingValue>
                <factIdentifier>
                  <name>Empty</name>
                  <className>java.lang.Void</className>
                </factIdentifier>
                <expressionIdentifier>
                  <name>1|1</name>
                  <type>GIVEN</type>
                </expressionIdentifier>
              </FactMappingValue>
            </factMappingValues>
          </BackgroundData>
        </scesimData>
      </background>
      <settings>
        <dmnFilePath>src/main/resources/PersonDecisions.dmn</dmnFilePath>
        <type>DMN</type>
        <dmnNamespace>https://kiegroup.org/dmn/_52CEF9FD-9943-4A89-96D5-6F66810CA4C1</dmnNamespace>
        <dmnName>PersonDecisions</dmnName>
        <skipFromBuild>false</skipFromBuild>
        <stateless>false</stateless>
      </settings>
      <imports>
        <imports/>
      </imports>
    </ScenarioSimulationModel>

    To create this example test scenario file in VSCode using the Kogito test scenario modeler, follow these steps:

    1. Open the empty PersonDecisionsTest.scesim file and in the Create Test Scenario window that appears, set the Source type to DMN, select the PersonDecisions.dmn DMN model from the drop-down options, and click Create.

      Image of Create Test Scenario window
      Figure 21. Create test scenario definition

      The test scenario modeler automatically generates a scenario template based on the available DMN data types and fields that you defined in the DMN model.

      You can right-click the relevant header cells to insert or delete columns as needed to modify the table structure. You can also select the relevant header cells to modify or insert data objects from the Test Tools panel in the right toolbar.

    2. For this example, modify the generated test scenario header cells and specified data objects as needed to create the following test scenario template. Use the Test Tools panel in the right toolbar to select and insert the data objects as needed. Many of the header cell values might already be defined for you.

      • GIVEN: Verify that the first header cell (instance) is set to the Person data object and that the subheader cells (properties) are set to the Age and Name data objects. Delete any other columns under GIVEN that were automatically generated, if applicable.

      • EXPECT: Verify that the first header cell (instance) is set to the isAdult data object and that the subheader cell (property) is set to the value data object. Delete any other columns under EXPECT that were automatically generated, if applicable.

        Image of test scenario template
        Figure 22. Define test scenario header cells
    3. In row 1, create a test scenario with the following values:

      • Scenario description: Is an adult

      • GIVEN: Set the following values:

        • PersonAge: 20

        • PersonName: John Quark

      • EXPECT: Set the following value:

        • isAdultvalue: true

      This example scenario tests whether the person John Quark with 20 years of age is correctly identified by the isAdult DMN decision as an adult (true), based on the decision logic that adults are more than 18 years old.

    4. Right-click any cell in row 1, select Insert row below, and in row 2, create another test scenario with the following values:

      • Scenario description: Is underage

      • GIVEN: Set the following values:

        • PersonAge: 15

        • PersonName: Jenny Quark

      • EXPECT: Set the following value:

        • isAdultvalue: false

      This example scenario tests whether the person Jenny Quark with 15 years of age is correctly identified by the isAdult DMN decision as not an adult (false), based on the decision logic that adults are more than 18 years old.

    5. Save the test scenario file.

  5. After you define and save the test scenarios, in a command terminal, navigate to the project that contains your Kogito decision service and test scenarios and enter the following command to run the test scenarios:

    Run the test scenarios
    mvn clean test

    A summary of the test scenario execution appears in the command terminal, and detailed reports are generated in the target/surefire-reports folder of your Kogito project.

    In the following example output, the test scenarios were executed successfully and encountered no errors:

    Terminal output for successful test scenarios
    [INFO] --- maven-surefire-plugin:2.22.1:test (default-test) @ sample-kogito ---
    [INFO]
    [INFO] -------------------------------------------------------
    [INFO]  T E S T S
    [INFO] -------------------------------------------------------
    [INFO] Running testscenario.KogitoScenarioJunitActivatorTest
    ./target/classes/PersonDecisions.dmn
    ./src/main/resources/PersonDecisions.dmn
    ./target/classes/PersonDecisions.dmn
    ./src/main/resources/PersonDecisions.dmn
    [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.535 s - in testscenario.KogitoScenarioJunitActivatorTest
    [INFO]
    [INFO] Results:
    [INFO]
    [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
    [INFO]
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time:  52.884 s
    [INFO] Finished at: 2020-05-05T15:19:53-04:00
    [INFO] ------------------------------------------------------------------------

    The expected results defined in the test scenarios matched the actual results of the isAdult DMN decision instance in the PersonDecisions.dmn file. This match of expected and actual results for the decision instance means that the decision logic functions as intended.

    In the following example output, the test scenarios were executed and the Is underage scenario encountered an error:

    Terminal output for a test scenario that encountered a decision error
    [INFO] --- maven-surefire-plugin:2.22.1:test (default-test) @ sample-kogito ---
    [INFO]
    [INFO] -------------------------------------------------------
    [INFO]  T E S T S
    [INFO] -------------------------------------------------------
    [INFO] Running testscenario.KogitoScenarioJunitActivatorTest
    ./target/classes/PersonDecisions.dmn
    ./src/main/resources/PersonDecisions.dmn
    ./target/classes/PersonDecisions.dmn
    ./src/main/resources/PersonDecisions.dmn
    [ERROR] Tests run: 2, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.534 s <<< FAILURE! - in testscenario.KogitoScenarioJunitActivatorTest
    [ERROR] #2: Is underage  Time elapsed: 0.06 s  <<< ERROR!
    org.drools.scenariosimulation.backend.runner.IndexedScenarioException: #2: Scenario 'Is underage' failed(/home/jsmith/sample-kogito/target/test-classes/PersonDecisionsTest.scesim)
    Caused by: org.drools.scenariosimulation.backend.runner.ScenarioException: Scenario 'Is underage' failed
    
    [INFO]
    [INFO] Results:
    [INFO]
    [ERROR] Errors:
    [ERROR]   KogitoScenarioJunitActivatorTest » IndexedScenario #2: Scenario 'Is underage' ...
    [INFO]
    [ERROR] Tests run: 2, Failures: 0, Errors: 1, Skipped: 0
    [INFO]
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD FAILURE
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time:  6.521 s
    [INFO] Finished at: 2020-05-05T15:26:10-04:00
    [INFO] ------------------------------------------------------------------------
    [ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.22.1:test (default-test) on project sample-kogito: There are test failures.
    [ERROR]
    [ERROR] Please refer to /home/jsmith/sample-kogito/target/surefire-reports for the individual test results.
    [ERROR] Please refer to dump files (if any exist) [date].dump, [date]-jvmRun[N].dump and [date].dumpstream.

    The expected results defined in the Is underage test scenario did not match the actual results of the isAdult DMN decision instance in the PersonDecisions.dmn file. This mismatch of expected and actual results for the decision instance means that either the test scenario identified a flaw in the decision logic or the test scenario is incorrectly defined. In this case, the Is underage test scenario was intentionally modified incorrectly with an age of 20 instead of an age of 18 or less. Reverting the age to 15 as shown in the previous example resolves the error.

2.9. Running a Kogito service

After you design the business decisions and processes for your Kogito service, you can run your Quarkus or Spring Boot application in one of the following modes:

  • Development mode: For local testing. On Quarkus, development mode also offers live reload of your processes and decisions in your running applications for advanced debugging.

  • JVM mode: For compatibility with a Java virtual machine (JVM).

  • Native mode: (Quarkus only, requires GraalVM or Mandrel) For direct binary execution as native code.

Procedure

In a command terminal, navigate to the project that contains your Kogito service and enter one of the following commands, depending on your preferred run mode and application environment:

  • For development mode:

    On Quarkus
    $ mvn clean compile quarkus:dev
    On Spring Boot
    $ mvn clean compile spring-boot:run
  • For JVM mode:

    On Quarkus
    $ mvn clean package
    $ java -jar target/quarkus-app/quarkus-run.jar
    On Spring Boot
    $ mvn clean package
    $ java -jar target/sample-kogito-1.0-SNAPSHOT-runner.jar
  • For native mode (requires GraalVM):

    On Quarkus only
    $ mvn clean package -Dnative
    $ ./target/sample-kogito-1.0-SNAPSHOT-runner

Alternatively, you can use a container builder image. You do not need to install GraalVM because a container containing GraalVM is pulled automatically. Note that the executable targets a Linux kernel by default. For example:

On Quarkus only (GraalVM native build using container)
$ mvn clean package -Dnative -Dquarkus.native.container-build

To use Mandrel instead of GraalVM, run the following command:

On Quarkus only (Mandrel native build using container)
$ mvn clean package -Dnative -Dquarkus.native.container-build -Dquarkus.native.builder-image=quay.io/quarkus/ubi-quarkus-mandrel:21.1-java11

For further information about building a native image, see Quarkus - Building a Native Executable.

2.10. Interacting with a running Kogito service

After your Kogito service is running, you can send REST API requests to interact with your application and execute your services according to how you set up the application.

This example tests the /persons REST API endpoint that is automatically generated based on the PersonProcess.bpmn2 business process, according to the decisions in the PersonDecisions.dmn file (or the rules in the PersonRules.drl file if you used a DRL rule unit).

For this example, use a REST client, curl utility, or the Swagger UI configured for the application (such as http://localhost:8080/q/swagger-ui or http://localhost:8080/swagger-ui.html) to send API requests with the following components:

  • URL: http://localhost:8080/persons

  • HTTP headers: For POST requests only:

    • accept: application/json

    • content-type: application/json

  • HTTP methods: GET, POST, or DELETE

Example POST request body to add an adult (JSON)
{
  "person": {
    "name": "John Quark",
    "age": 20
  }
}
Example curl command to add an adult
curl -X POST http://localhost:8080/persons -H 'content-type: application/json' -H 'accept: application/json' -d '{"person": {"name":"John Quark", "age": 20}}'
Example response (JSON)
{
  "id": "3af806dd-8819-4734-a934-728f4c819682",
  "person": {
    "name": "John Quark",
    "age": 20,
    "adult": false
  },
  "isAdult": true
}
Image of Swagger UI for example application
Figure 23. Swagger UI to interact with all application endpoints (such as http://localhost:8080/q/swagger-ui or http://localhost:8080/swagger-ui.html)

This example procedure uses curl commands for convenience.

Procedure

In a command terminal window that is separate from your running application, navigate to the project that contains your Kogito service and use any of the following curl commands with JSON requests to interact with your running service:

On Spring Boot, you might need to modify how your application exposes API endpoints in order for these example requests to function. For more information, see the README file included in the example Spring Boot project that you created for this tutorial.
  • Add an adult person:

    Example request
    curl -X POST http://localhost:8080/persons -H 'content-type: application/json' -H 'accept: application/json' -d '{"person": {"name":"John Quark", "age": 20}}'
    Example response
    {"id":"3af806dd-8819-4734-a934-728f4c819682","person":{"name":"John Quark","age":20,"adult":false},"isAdult":true}
  • Add an underage person:

    Example request
    curl -X POST http://localhost:8080/persons -H 'content-type: application/json' -H 'accept: application/json' -d '{"person": {"name":"Jenny Quark", "age": 15}}'
    Example response
    {"id":"8eef502b-012b-4628-acb7-73418a089c08","person":{"name":"Jenny Quark","age":15,"adult":false},"isAdult":false}
  • View active process instances:

    Example request
    curl -X GET http://localhost:8080/persons -H 'content-type: application/json' -H 'accept: application/json'
    Example response
    [{"id":"8eef502b-012b-4628-acb7-73418a089c08","person":{"name":"Jenny Quark","age":15,"adult":false},"isAdult":false}]
  • View process instance details using the returned process UUID:

    Example request
    curl -X GET http://localhost:8080/persons/8eef502b-012b-4628-acb7-73418a089c08/tasks -H 'content-type: application/json' -H 'accept: application/json'
    Example response (JSON)
    {"cdec4241-d676-47de-8c55-4ee4f9598bac":"ChildrenHandling"}
  • View task instance details using the returned process and task UUIDs:

    Example request
    curl -X GET http://localhost:8080/persons/8eef502b-012b-4628-acb7-73418a089c08/ChildrenHandling/cdec4241-d676-47de-8c55-4ee4f9598bac -H 'content-type: application/json' -H 'accept: application/json'
    Example response
    {"person":{"name":"Jenny Quark","age":15,"adult":false},"name":"ChildrenHandling","id":"cdec4241-d676-47de-8c55-4ee4f9598bac"}
  • Complete the evaluation using the returned UUIDs:

    Example request
    curl -X POST http://localhost:8080/persons/8eef502b-012b-4628-acb7-73418a089c08/ChildrenHandling/cdec4241-d676-47de-8c55-4ee4f9598bac -H 'content-type: application/json' -H 'accept: application/json' -d '{}'

3. Deploying Kogito services on OpenShift

As a developer of business processes and decisions, you can deploy Kogito services on OpenShift for cloud implementation. The Kogito Operator automates many of the deployment steps for you or guides you through the deployment process. You can use the Kogito command-line interface (CLI) to interact with the Kogito Operator for deployment tasks.

Prerequisites
  • OpenShift 4.3 or later is installed.

  • The OpenShift project for the deployment is created.

3.1. Kogito on OpenShift

You can deploy Kogito services on OpenShift for cloud implementation. In this architecture, Kogito services are deployed as OpenShift pods that you can scale up and down individually to provide as few or as many containers as required for a particular service. You can use standard OpenShift methods to manage the pods and balance the load.

To help you deploy your services on OpenShift, Kogito provides an operator and a command-line interface (CLI):

  • Kogito Operator: An operator that guides you through the deployment process. The Kogito Operator is based on the Operator SDK and automates many of the deployment steps for you. For example, when you give the operator a link to the Git repository that contains your application, the operator can automatically configure the components required to build your project from source and deploy the resulting services.

  • Kogito command-line interface (CLI): A CLI tool that enables you to interact with the Kogito Operator for deployment tasks. The Kogito CLI also enables you to deploy Kogito services from source instead of relying on custom resources and YAML files. You can use the Kogito CLI as a command-line alternative for deploying Kogito services without the OpenShift web console.

3.2. Probes for Kogito services on OpenShift

The probes in OpenShift and Kubernetes verify that an application is working or it needs to be restarted. For Kogito services on Quarkus and Spring Boot, probes interact with the application using an HTTP request, defaulting to the endpoints that are exposed by an extension. Therefore, to run your Kogito services on OpenShift or Kubernetes, you must import the extensions to enable the liveness, readiness, and startup probes for your application.

3.2.1. Enabling probes for Quarkus-based Kogito services on OpenShift

You can enable the probes for the Quarkus-based Kogito services on OpenShift.

Procedure
  1. In a command terminal, navigate to the pom.xml file of your Kogito project and add the following dependency for the Quarkus smallrye-health extension:

SmallRye Health dependency for Quarkus applications on OpenShift
<dependencies>
  <dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-smallrye-health</artifactId>
  </dependency>
</dependencies>

3.2.2. Enabling probes for Spring Boot-based Kogito services on OpenShift

You can enable the probes for the Spring Boot-based Kogito services on OpenShift.

Procedure
  1. In a command terminal, navigate to the pom.xml file of your Kogito project and add the following Spring Boot actuator dependency:

Spring Boot actuator dependency for Spring Boot applications on OpenShift
<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
  </dependency>
</dependencies>

3.2.3. Setting custom probes for Kogito services on OpenShift

You can also configure the custom endpoints for the liveness, readiness, and startup probes.

Procedure
  1. Define the probes in the KogitoRuntime YAML file of your Kogito project, as shown in the following example:

Example Kogito service custom resource with custom probe endpoints
apiVersion: app.kiegroup.org/v1beta1 # Kogito API for this service
kind: KogitoRuntime
metadata:
  name: process-quarkus-example # Application name
spec:
  replicas: 1
  probes:
    livenessProbe:
      httpGet:
        path: /probes/live # Liveness endpoint
        port: 8080
    readinessProbe:
      httpGet:
        path: /probes/ready # Readiness endpoint
        port: 8080
    startupProbe:
      tcpSocket:
        port: 8080

3.3. Kogito Operator installation

This section describes different ways of installing Kogito Operator on various platforms. You can install the Kogito Operator on OpenShift or on Kubernetes.

3.3.1. Installing the Kogito Operator on OpenShift

You can install the Kogito Operator on OpenShift using different ways, including:

  • Installing Kogito Operator using Operator Hub and OpenShift Web Console

  • Installing Kogito Operator using OC CLI

  • Installing Kogito Operator using OC CLI and Operator Lifecycle Manager (OLM)

This section describes different ways of installing Kogito Operator on OpenShift.

3.3.1.1. Installing Kogito Operator using Operator Hub and OpenShift Web Console

To install the Kogito Operator on OpenShift, you can use the Kogito Operator from Operator Hub and OpenShift Web Console.

Prerequisites
  • OpenShift cluster is started and running.

  • You have access to the OpenShift web console with cluster-admin permissions.

    You can use the Red Hat CodeReady containers for development and testing purposes.
  • kubectl and Red Hat OpenShift Container platform (oc) is installed on your local machine.

  • Operator Lifecycle Manager (OLM) is running on the OpenShift Cluster. For more information about OLM, see Operator Lifecycle Manager concepts and resources.

Procedure
  1. Navigate to your OpenShift console and open the Operator Hub.

  2. Enter kogito in the text box under the All Items label.

  3. Click on the operator containing the Community label.

  4. Click Continue.

    A window appears consisting of the information related to the Kogito Operator.

  5. Click Install.

  6. Keep the default options as-is and click Install.

    The message as Installed operator - ready for use indicates that Kogito Operator is installed and ready for use.

  7. To verify that Kogito Operator is installed, click on View Operator and scroll down.

  8. Review the conditions and search for InstallSucceeded.

3.3.1.2. Installing Kogito Operator using oc CLI

You can install the Kogito Operator using oc CLI.

Prerequisites
  • OpenShift cluster is started and running.

  • kubectl and Red Hat OpenShift Container platform (oc) is installed on your local machine.

Procedure
  1. Create a project or namespace in OpenShift using oc CLI.

  2. Open a terminal and run the following command:

    $ oc new-project kogito-operator-system --display-name="Kogito Operator" --description="Kogito Operator Project"
    Ensure that oc CLI is connected to your OpenShift cluster.
  3. Replace the current_operator_version with the latest Kogito Operator version and run the following command:

    $ export NAMESPACE=kogito-operator-system
    $ export VERSION=<current_operator_version>
    $ oc apply -n "${NAMESPACE}" -f "https://github.com/kiegroup/kogito-operator/releases/download/${VERSION}/kogito-operator.yaml"
  4. To verify the Kogito Operator installation, execute the following command and ensure that kogito-operator-controller-manager is running:

    $ oc get pods -n kogito-operator-system
    NAME                                                  READY   STATUS    RESTARTS   AGE
    kogito-operator-controller-manager-aaaaa-bbbbb   2/2     Running   0          2m2
  5. Review the logs to ensure that there is no issue with the Kogito Operator installation:

    $  oc logs kogito-operator-controller-manager-aaaaa-bbbbb -c manager -n kogito-operator-system
    
    $ oc logs kogito-operator-controller-manager-aaaaa-bbbbb -c kube-rbac-proxy -n kogito-operator-system

    If you do not see any issues, that means the installation is successful and Kogito Operator is ready for use.

3.3.1.3. Installing Kogito Operator using oc CLI and Operator Lifecycle Manager (OLM)

You can also use the Operator Lifecycle Manager (OLM) to install the Kogito Operator on OpenShift.

Prerequisites
  • OpenShift cluster is started and running.

  • kubectl and Red Hat OpenShift Container platform (oc) is installed on your local machine.

  • Operator Lifecycle Manager (OLM) is running on the OpenShift Cluster. For more information about OLM, see Operator Lifecycle Manager concepts and resources.

Procedure
  1. Create a Subscription object and update startingCSV with the Kogito Operator version that you want to install using oc CLI:

    Example of creating a Subscrition object
    kogito-subscription.yml
    ---
    apiVersion: operators.coreos.com/v1alpha1
    kind: Subscription
    metadata:
      name: kogito-operator
      namespace: openshift-operators
    spec:
      channel: 1.x
      installPlanApproval: Automatic
      name: kogito-operator
      source: community-operators
      sourceNamespace: openshift-marketplace
      startingCSV: kogito-operator.v1.13.0
    
    $ oc create -f kogito-subscription.yml

    This makes the Kogito Operator globally available in the OpenShift cluster.

  2. Run the following command to verify the health of installed operator:

    $ oc describe sub kogito-operator -n openshift-operators

    In the command output, check the Conditions section to verify the status of Operator subscription condition types. The CatalogSourcesUnhealthy condition type contains the status as false since all available catalog sources are healthy.

    If you do not see any issues, that means the installation is successful and Kogito Operator is ready for use.

3.3.2. Supported commands and operations in Kogito CLI

The Kogito command-line interface (CLI) supports the following operations on Linux, Mac, and Windows operating systems. The syntax of some operations might vary for Windows users, such as a backslash (\) instead of a forward slash (/).

Table 7. Supported commands and operations in the Kogito CLI
Operation Kogito CLI command Example

Create an OpenShift project for Kogito services.

kogito new-project PROJECT_NAME

kogito new-project kogito-travel-agency

Connect the Kogito Operator to an existing OpenShift project with Kogito services.

kogito use-project PROJECT_NAME

kogito use-project kogito-travel-agency

Install the Infinispan infrastructure for process data persistence in Kogito services.

kogito install infra INFINISPAN_INFRA_NAME --kind Infinispan --apiVersion infinispan.org/v1 --resource-name kogito-infinispan

kogito install infra kogito-infinispan-infra --kind Infinispan --apiVersion infinispan.org/v1 --resource-name kogito-infinispan

Install the Apache Kafka infrastructure for messaging in Kogito services.

kogito install infra KAFKA_INFRA_NAME --kind Kafka --apiVersion kafka.strimzi.io/v1beta2 --resource-name kogito-kafka

kogito install infra kogito-kafka-infra --kind Kafka --apiVersion kafka.strimzi.io/v1beta2 --resource-name kogito-kafka

Install the Kogito Data Index Service for data management in Kogito services and provision the Data Index Service to connect to the specified Infinispan and Kafka infrastructures.

kogito install data-index --infra INFINISPAN_INFRA_NAME --infra KAFKA_INFRA_NAME

kogito install data-index --infra kogito-infinispan-infra --infra kogito-kafka-infra

Install the Kogito Jobs Service for job scheduling in Kogito services and provision the Jobs Service to connect to the specified Infinispan and Kafka infrastructures.

kogito install jobs-service --infra INFINISPAN_INFRA_NAME --infra KAFKA_INFRA_NAME

kogito install jobs-service --infra kogito-infinispan-infra --infra kogito-kafka-infra

Install the Kogito Management Console for managing process instance details in Kogito services.

kogito install mgmt-console

kogito install mgmt-console

Install the Kogito Trusty Service to store tracing events and provide advanced analytical capabilities in Kogito services, and provision the Trusty Service to connect to the specified Infinispan and Kafka infrastructures.

kogito install trusty --infra INFINISPAN_INFRA_NAME --infra KAFKA_INFRA_NAME

kogito install trusty --infra kogito-infinispan-infra --infra kogito-kafka-infra

Install the Kogito Explainability Service to analyze decisions made in Kogito services and provision the Explainability Service to connect to the specified Kafka infrastructure.

kogito install explainability --infra KAFKA_INFRA_NAME

kogito install explainability --infra kogito-kafka-infra

Install the Kogito Task Console for viewing and interacting with user tasks in Kogito process services.

kogito install task-console

kogito install task-console

Install Kogito supporting services or infrastructure components for a specified Kogito project. Use this syntax if you did not use the new-project or use-project commands to connect the Kogito Operator to a specified project.

kogito install KOGITO_SUPPORTING_SERVICE --infra KOGITO_INFRA_NAME -p PROJECT_NAME

kogito install infra KOGITO_INFRA_NAME KOGITO_INFRA_DETAILS -p PROJECT_NAME

kogito install data-index --infra kogito-infinispan-infra --infra kogito-kafka-infra -p kogito-travel-agency

kogito install infra kogito-infinispan-infra --kind Infinispan --apiVersion infinispan.org/v1 --resource-name kogito-infinispan -p kogito-travel-agency

kogito install infra kogito-kafka-infra --kind Kafka --apiVersion kafka.strimzi.io/v1beta2 --resource-name kogito-kafka -p kogito-travel-agency

Create a Kogito service definition from a local source or a Git repository and deploy the service. In a local directory source, if the pom.xml file is present, then the entire directory is zipped and uploaded to OCP and the build is initiated. However, if the pom.xml is not present, then only supported extension files (including .dmn, .drl, .bpmn, .bpmn2, .properties, .sw.json, and .sw.yaml) are uploaded from the directory to initiate the build. In a binary build configuration, this command creates the service definition but does not deploy the service.

kogito deploy-service SERVICE_NAME

kogito deploy-service SERVICE_NAME GIT_REPOSITORY_URL --context-dir PROJECT_DIRECTORY

kogito deploy-service travels

kogito deploy-service travels https://github.com/kiegroup/kogito-examples/tree/stable/kogito-travel-agency/extended --context-dir travels

Enable Infinispan persistence and Apache Kafka messaging for a Kogito service during deployment. Use this command if you installed the relevant infrastructures using the Kogito Operator. In a binary build configuration, this command creates the service definition but does not deploy the service.

kogito deploy-service SERVICE_NAME --infra INFINISPAN_INFRA_NAME --infra KAFKA_INFRA_NAME

kogito deploy-service SERVICE_NAME GIT_REPOSITORY_URL --context-dir PROJECT_DIRECTORY --infra INFINISPAN_INFRA_NAME --infra KAFKA_INFRA_NAME

kogito deploy-service travels --infra kogito-infinispan-infra --infra kogito-kafka-infra

kogito deploy-service travels https://github.com/kiegroup/kogito-examples/tree/stable/kogito-travel-agency/extended --context-dir travels --infra kogito-infinispan-infra --infra kogito-kafka-infra

Create a Kogito service definition from a local or Git source and deploy the service using a native build.

kogito deploy-service SERVICE_NAME --native

kogito deploy-service SERVICE_NAME GIT_REPOSITORY_URL --context-dir PROJECT_DIRECTORY --native

kogito deploy-service travels --native

kogito deploy-service travels https://github.com/kiegroup/kogito-examples/tree/stable/kogito-travel-agency/extended --context-dir travels --native

Upload a Kogito service file, such as a Decision Model and Notation (DMN) or Business Process Model and Notation (BPMN) file, or a file directory with multiple files to an OpenShift Cluster and trigger a new Source-to-Image (S2I) build. For single files, you can specify a local file system path or Git repository URL. For file directories, you can specify a local file system path only.

kogito deploy-service SERVICE_NAME PATH_TO_FILE_OR_DIR

kogito deploy-service SERVICE_NAME GIT_FILE_URL

kogito deploy-service travels /tmp/kogito-examples/kogito-travel-agency/extended/travels/src/main/resources/org/acme/travels/travels.bpmn2

kogito deploy-service travels /tmp/kogito-examples/kogito-travel-agency/extended/travels/src/main/resources/org/acme/travels

kogito deploy-service travels https://github.com/kiegroup/kogito-examples/blob/stable/kogito-travel-agency/extended/travels/src/main/resources/org/acme/travels/travels.bpmn2

Delete a Kogito service.

kogito delete-service SERVICE_NAME

kogito delete-service travels

3.4. OpenShift build options with the Kogito Operator and CLI

The Kogito Operator and command-line interface (CLI) support the following options for building and deploying Kogito services on OpenShift:

  • Git source build and deployment

  • Binary build and deployment

  • File build and deployment

  • Native build and deployment

Kogito provides the relevant Source-to-Image (S2I) build configuration depending the deployment option that you use.

For all deployment options, you must be logged in to the relevant OpenShift cluster using the oc login command.

3.4.1. Git source build and deployment

In most use cases, you can use the standard runtime build and deployment method to deploy Kogito services on OpenShift from a Git repository source, as shown in the following examples. These examples are based on the kogito-travel-agency extended example application.

Example Kogito service deployment from existing namespace
// Uses the provisioned namespace in your OpenShift cluster
$ kogito use-project PROJECT_NAME

// Deploys a new Kogito service from a Git source
$ kogito deploy-service travels https://github.com/kiegroup/kogito-examples/tree/stable/kogito-travel-agency/extended --context-dir travels

The Kogito Operator uses the default branch in the specified Git repository, usually master.

Alternatively, you can generate a new namespace in your cluster during deployment:

Example Kogito service deployment from new namespace
// Creates a new namespace in your cluster
$ kogito new-project NEW_PROJECT_NAME

// Deploys a new Kogito service from a Git source
$ kogito deploy-service travels https://github.com/kiegroup/kogito-examples/tree/stable/kogito-travel-agency/extended --context-dir travels
If you are developing or testing your Kogito service locally, you can use the binary build or file build option to build and deploy from a local source instead of from a Git repository.

3.4.2. Binary build and deployment

OpenShift builds can require extensive amounts of time. As a faster alternative for building and deploying your Kogito services on OpenShift, you can use a binary build. In a binary build, you build the application locally and push the built application to an OpenShift BuildConfig configuration to be packaged into the runtime container image.

The following example creates a Kogito service from a local directory, builds the project binaries, and deploys the binary build to OpenShift. This example is based on the kogito-travel-agency extended example application.

Example Kogito service deployment from binary build
// Creates the Kogito service
$ cd ~/kogito-travel-agency/extended/travels
$ kogito deploy-service travels

// Builds the project and generates binary resources in target folder
$ mvn clean package

// Deploys to OpenShift using binary build
$ oc start-build travels-binary --from-dir=target/

Uploading directory "target/" as binary input for the build ...
....
Uploading finished
build.build.openshift.io/travels-1 started

In this example, the following resources are generated in the target folder to prepare for the binary build:

  • quarkus-app/app/travels.jar: Standard JAR file that contains the classes and resources of the project.

  • quarkus-app/quarkus: Contains the generated resources required by the Quarkus application to run.

  • quarkus-app/lib: Directory that contains the project dependencies.

  • quarkus-app/quarkus-run.jar: Executable JAR file for the project. Note that quarkus-run.jar is not an uber-JAR file, and also requires the folders quarkus-app/app, quarkus-app/quarkus, and quarkus-app/lib.

The Kogito images are handling the binary build directly from the target folder, and the content of the target/quarkus-app folder is copied directly.

After the binary build is complete, the result is pushed to the travels Image Stream that was created by the Kogito Operator and triggers a new deployment.

3.4.3. File build and deployment

You can build and deploy your Kogito services from a single file, such as a Decision Model and Notation (DMN), Business Process Model and Notation (BPMN or BPMN2), Drools Rule Language (DRL), or properties file, or from a directory with multiple files. You can specify a single file from your local file system path or from a Git repository URL, or specify a file directory from a local file system path only. When you upload the file or directory to an OpenShift cluster, a new Source-to-Image (S2I) build is automatically triggered.

You cannot upload a file directory from a Git repository. The file directory must be in your local file system. However, you can upload single files from either a Git repository or your local file system.

The following examples upload a single BPMN file from a local directory or Git repository to an OpenShift cluster for an S2I build. These examples are based on the kogito-travel-agency extended example application.

Example Kogito service deployment from a local file
$ kogito deploy-service travels /tmp/kogito-examples/kogito-travel-agency/extended/travels/src/main/resources/org/acme/travels/travels.bpmn2

File found: /tmp/kogito-examples/kogito-travel-agency/extended/travels/src/main/resources/org/acme/travels/travels.bpmn2.
...
The requested file(s) was successfully uploaded to OpenShift, a build with this file(s) should now be running. To see the logs, run 'oc logs -f bc/kogito-travel-agency-builder -n kogito'
Example Kogito service deployment from a Git repository file
$ kogito deploy-service travels https://github.com/kiegroup/kogito-examples/blob/stable/kogito-travel-agency/extended/travels/src/main/resources/org/acme/travels/travels.bpmn2

Asset found: travels.bpmn2.
...
The requested file(s) was successfully uploaded to OpenShift, a build with this file(s) should now be running. To see the logs, run 'oc logs -f bc/kogito-travel-agency-builder -n kogito'

As instructed in the terminal output, you can run the following command to see the build logs:

Example build log for BPMN file build
$ oc logs -f bc/kogito-travel-agency-builder -n kogito

Receiving source from STDIN as file travels.bpmn2
Using docker-registry.default.svc:5000/openshift/kogito-builder as the s2i builder image

The following examples upload multiple files within a local directory to an OpenShift cluster for an S2I build:

Example Kogito service deployment from multiple files in a local directory
$ kogito deploy-service travels /tmp/kogito-examples/kogito-travel-agency/extended/travels/src/main/resources/org/acme/travels

The provided source is a dir, packing files.
File(s) found: [/tmp/kogito-examples/kogito-travel-agency/extended/travels/src/main/resources/org/acme/travels/flightBooking.bpmn2 /tmp/kogito-examples/kogito-travel-agency/extended/travels/src/main/resources/org/acme/travels/hotelBooking.bpmn2 /tmp/kogito-examples/kogito-travel-agency/extended/travels/src/main/resources/org/acme/travels/travels.bpmn2].
...
The requested file(s) was successfully uploaded to OpenShift, a build with this file(s) should now be running. To see the logs, run 'oc logs -f bc/travels-builder -n kogito'

For every valid file type (DMN, BPMN, BPMN2, DRL) in the specified directory, the Kogito CLI compresses the files and uploads them to the OpenShift cluster. Any other unsupported file types are not uploaded. To ensure that other file types are uploaded, consider using a source build or a binary build.

If you need to update an uploaded file or directory after you create the build, you can use the oc start-build command to re-upload the file or directory, as shown in the following example. An S2I build cannot identify only changed files in a directory, so you must re-upload the entire directory of files to update the build.

Example command to re-upload a single local file to update the S2I build
$ oc start-build kogito-travel-agency-builder --from-file tmp/kogito-examples/kogito-travel-agency/extended/travels/src/main/resources/org/acme/travels/travels.bpmn2
Example command to re-upload multiple files from a local directory to update the S2I build
$ oc start-build kogito-travel-agency-builder --from-dir tmp/kogito-examples/kogito-travel-agency/extended/travels/src/main/resources/org/acme/travels

If a build fails, use the OpenShift environment variable BUILD_LOGLEVEL with the desired level as part of your deployment command, as shown in the following example:

Example command to troubleshoot failed build from directory
$ kogito --verbose deploy-service travels /tmp/kogito-examples/kogito-travel-agency/extended/travels/src/main/resources/org/acme/travels --build-env BUILD_LOGLEVEL=5

3.4.4. Native build and deployment

Native build and deployment is for Quarkus only and requires GraalVM or Mandrel.

By default, the Kogito Operator and CLI build services with traditional java compilers to save time and resources. The final generated artifact is a JAR file for the relevant runtime (defaults to Quarkus) with dependencies in the image user’s home directory, such as /home/kogito/bin/lib.

However, for Kogito services on Quarkus with GraalVM or Mandrel (required), the Kogito Operator and CLI can build the services in native mode for direct binary execution as native code. Building in native mode uses a very low footprint on the runtime, although many resources are consumed during build time.

For more information about native build performance, see the GraalVM Performance examples for Java.

For more information about ahead-of-time (AOT) compilation, see the GraalVM Native Image documentation.

The following examples build a Kogito service on Quarkus in native mode using the --native parameter. These examples are based on the kogito-travel-agency extended example application.

Example Kogito service native build from a local source directory
$ cd ~/kogito-travel-agency/extended/travels
$ kogito deploy-service travels --native
Example Kogito service native build from a Git repository source directory
$ kogito deploy-service travels https://github.com/kiegroup/kogito-examples/tree/stable/kogito-travel-agency/extended --context-dir travels --native

In Kogito Operator tests, native builds take approximately 10 minutes and the build pod can consume up to 10GB of RAM and 1.5 CPU cores.

By default, a Kogito project does not contain resource requests or limits. As a result, a native build might be terminated due to insufficient memory. To prevent this behavior, you can create a minimum memory request configuration for the Kogito application build, ensuring that the build pod is allocated on an OpenShift node with enough free memory. The side effect of this configuration is that OpenShift prioritizes the build pod.

For more information about OpenShift pod prioritization based on pod requests and limits, see Quality of Service Tiers in the OpenShift documentation.

The following example is a memory request configuration for the kogito-travel-agency extended example application:

Example memory request configuration for kogito-travel-agency
apiVersion: app.kiegroup.org/v1beta1
kind: KogitoBuild
metadata:
  name: travels
  namespace: kogito
spec:
  type: RemoteSource
  gitSource:
    contextDir: kogito-travel-agency/extended/travels
    uri: "https://github.com/kiegroup/kogito-examples/"
  resources:
    requests:
        memory: "4Gi"
Ensure that you have these resources available on your OpenShift nodes when you run native builds. If the resources are not available, the S2I build fails. You can verify currently allocated and total resources of your nodes by using the command oc describe nodes invoked with admin permission.

You can limit the maximum heap space for the JVM used for a native build. You can apply the limitation by setting the quarkus.native.native-image-xmx property in the application.properties file of your Kogito project. In this case, the build pod requires roughly -Xmx plus 2 GB of memory. The -Xmx value depends on the complexity of the application. For example, for the process-quarkus-example example application, the -Xmx value 2g is sufficient, resulting in the builder pod consuming up to 4.2 GB of memory.

You can also set resource limits for a native build pod. In the process-quarkus-example application, 80 percent of the memory limit is used for heap space in the JVM responsible for the native build. If the computed heap space limit for the JVM is less than 1024 MB, then all the memory from resource limits is used.

The following example is a memory limit configuration for the process-quarkus-example example application:

Example memory request configuration for process-quarkus-example
apiVersion: app.kiegroup.org/v1beta1
kind: KogitoBuild
metadata:
  name: process-quarkus-example
  namespace: kogito
spec:
  type: RemoteSource
  gitSource:
    contextDir: process-quarkus-example
    uri: "https://github.com/kiegroup/kogito-examples/"
  resources:
    limits:
        memory: "4Gi"
3.4.4.1. Building a native image with Kogito

You can build an application as a native executable in Kogito using GraalVM or Mandrel.

Prerequisites
  • JDK 11 or later is installed.

  • Apache Maven 3.6.2 or later is installed.

  • Optional: Quarkus CLI is installed.

    Native build and deployment is Quarkus-only and requires GraalVM or Mandrel.
Procedure
  1. Create a Kogito project using the Maven plugin or Quarkus CLI.

    Example project creation using Quarkus Maven plugin
    mvn io.quarkus:quarkus-maven-plugin:create \
       -DprojectGroupId=com.company \
       -DprojectArtifactId=sample-kogito \
       -Dextensions="kogito"
    Example project creation using Quarkus CLI
    quarkus create -x kogito sample-kogito

    In the previous example, an application named sample-kogito is created. The sample-kogito application is pre-configured to support the native executables.

    For more information about creating a Kogito project, see Creating and running your first Kogito services.

  2. Build a native image using Maven or Quarkus CLI.

    Example of building a native image using Maven plugin
    $ mvn verify -Dnative
    # running
    $ ./target/sample-kogito-1.0-SNAPSHOT-runner
    Example of building a native image using the Quarkus CLI
    $ quarkus build --native
    # running
    $ ./target/sample-kogito-1.0-SNAPSHOT-runner
  3. Build a native executable using a container image or Mandrel as follows:

    • You can build a native executable using a container builder image, in which you do not need to insrall GraalVM. However, a container that contains GraalVM is pulled automatically. Note that the native executable targets a Linux kernel by default. For example:

    Example of building a native image using a container image
    $ mvn verify -Dnative -Dquarkus.native.container-build
    # alternatively
    $ quarkus build --native -Dquarkus.native.container-build=true
    • Alternatively, run the following command to use Mandrel instead of GraalVM to build a native image using container image:

    Example of building a native image using a container image (Mandrel)
    $ mvn verify -Dnative -Dquarkus.native.container-build -Dquarkus.native.builder-image=quay.io/quarkus/ubi-quarkus-mandrel:21.1-java11
    # alternatively
    $ quarkus build --native -Dquarkus.native.container-build -Dquarkus.native.builder-image=quay.io/quarkus/ubi-quarkus-mandrel:21.1-java11

    For more information about building a native image, see Quarkus - Building a Native Executable.

  4. Create a container image using Quarkus.

    For more information about creating a container image using Quarkus, see extensions for building (and pushing) container images.

  5. Deploy the container image using kubctl or oc.

3.5. Kogito service properties configuration

When a Kogito service is deployed, a configMap resource is created for the application.properties configuration of the Kogito service.

The name of the configMap resource consists of the name of the Kogito service and the suffix -properties, as shown in the following example:

Example configMap resource generated during Kogito service deployment
kind: ConfigMap
apiVersion: v1
metadata:
  name: kogito-travel-agency-properties
data:
  application.properties : |-
    property1=value1
    property2=value2

The application.properties data of the configMap resource is mounted in a volume to the container of the Kogito service. Any runtime properties that you add to the application.properties section override the default application configuration properties of the Kogito service.

When the application.properties data of the configMap is changed, a rolling update modifies the deployment and configuration of the Kogito service.

3.5.1. Deploying a Kogito service using OpenShift web console

After you create your Kogito services as part of a business application, you can use the OpenShift web console to deploy your services. The Kogito Operator page in the OpenShift web console guides you through the deployment process. The Kogito Operator is based on the Operator SDK and automates many of the deployment steps for you. For example, when you give the operator a link to the Git repository that contains your application, the operator can automatically configure the components required to build your project from source and deploy the resulting services.

Prerequisites
  • The application with your Kogito services is in a Git repository that is reachable from your OpenShift environment.

  • You have access to the OpenShift web console with cluster-admin permissions.

  • (Quarkus only) The pom.xml file of your Kogito project contains the following dependency for the Quarkus smallrye-health extension. This extension enables the liveness and readiness probes that are required for Quarkus-based projects on OpenShift or Kubernetes.

    SmallRye Heath dependency for Quarkus applications on OpenShift
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-smallrye-health</artifactId>
    </dependency>
Procedure
  1. In the OpenShift web console, go to OperatorsOperatorHub in the left menu, search for and select Kogito, and follow the on-screen instructions to install the latest operator version.

  2. After you install the Kogito Operator, in the OpenShift web console, go to OperatorsInstalled Operators and select Kogito.

  3. In the operator page, select the Kogito Build tab and click Create KogitoBuild to create the Kogito build definition.

    Image of Kogito build page in web console
    Figure 24. Create a Kogito build definition
  4. In the application window, use the Form View or YAML View to configure the build definition.

    At a minimum, define the application configurations shown in the following example YAML file:

    Example YAML definition for an application with Kogito build
    apiVersion: app.kiegroup.org/v1beta1 # Kogito API for this service
    kind: KogitoBuild  # Application type
    metadata:
      name: example-quarkus  # Application name
      namespace: kogito  # OpenShift project namespace
    spec:
      type: RemoteSource
      gitSource:
        uri: 'https://github.com/kiegroup/kogito-examples'  # Git repository containing application (uses default branch)
        contextDir: process-quarkus-example  # Git folder location of application

    If you have configured an internal Maven repository, you can use it as a Maven mirror service and specify the Maven mirror URL in your Kogito build definition to substantially shorten build time:

    spec:
      mavenMirrorURL: http://nexus3-nexus.apps-crc.testing/repository/maven-public/

    For more information about internal Maven repositories, see the Apache Maven documentation.

  5. After you define your application data, click Create to generate the Kogito build.

    Your application is listed in the KogitoBuilds page:

    Image of Kogito build listed in web console
    Figure 25. New Kogito build instance

    You can select the application name to view or modify application settings and YAML details:

    Image of Kogito service details page in web console
    Figure 26. View Kogito build details
  6. In the operator page, select the Kogito Service tab and click Create KogitoRuntime to create the Kogito service definition.

    Image of Kogito service page in web console
    Figure 27. Create a Kogito service definition
  7. In the application window, use the Form View or YAML View to configure the service definition.

    At a minimum, define the application configurations shown in the following example YAML file:

    Example YAML definition for an application with Kogito services
    apiVersion: app.kiegroup.org/v1beta1 # Kogito API for this service
    kind: KogitoRuntime  # Application type
    metadata:
      name: example-quarkus  # Application name
      namespace: kogito  # OpenShift project namespace
  8. After you define your application data, click Create to generate the Kogito service.

    Your application is listed in the Kogito service page:

    Image of Kogito service listed in web console
    Figure 28. New Kogito service instance

    You can select the application name to view or modify application settings and YAML details:

    Image of Kogito service details page in web console
    Figure 29. View Kogito service details
  9. In the left menu of the web console, go to BuildsBuilds to view the status of your application build.

    You can select a specific build to view build details:

    Image of Kogito service build details page in web console
    Figure 30. View Kogito service build details

    For every Kogito service that you create for OpenShift deployment, two builds are generated and listed in the Builds page in the web console: a traditional runtime build and a Source-to-Image (S2I) build with the suffix -builder. The S2I mechanism builds the application in an OpenShift build and then passes the built application to the next OpenShift build to be packaged into the runtime container image. The Kogito S2I build configuration also enables you to build the project directly from a Git repository on the OpenShift platform.

  10. After the application build is complete, go to WorkloadsDeployments to view the application deployments, pod status, and other details.

    You can select the application name to increase or decrease the pod count or modify deployment settings:

    Image of Kogito service deployment details page in web console
    Figure 31. View Kogito service deployment details
  11. After your Kogito service is deployed, in the left menu of the web console, go to NetworkingRoutes to view the access link to the deployed application.

    You can select the application name to view or modify route settings:

    Image of Kogito service route details page in web console
    Figure 32. View Kogito service route details

    With the application route, you can integrate your Kogito services with your business automation solutions as needed.

3.5.2. Deploying Kogito services on OpenShift or Kubernetes using the Kogito CLI

The Kogito command-line interface (CLI) enables you to interact with the Kogito Operator for deployment tasks. The Kogito CLI also enables you to deploy Kogito services from source instead of relying on custom resources and YAML files. You can use the Kogito CLI as a command-line alternative for deploying Kogito services without the OpenShift web console.

Prerequisites
  • The oc OpenShift CLI is installed and you are logged in to the relevant OpenShift cluster. For oc installation and login instructions, see the OpenShift documentation.

  • You have OpenShift permissions to create resources in a specified namespace.

  • (Quarkus only) The pom.xml file of your Kogito project contains the following dependency for the Quarkus smallrye-health extension. This extension enables the liveness and readiness probes that are required for Quarkus-based projects on OpenShift or Kubernetes.

    SmallRye Heath dependency for Quarkus applications on OpenShift
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-smallrye-health</artifactId>
    </dependency>
Procedure
  1. Go to the kogito-cloud-operator releases page in GitHub and download the latest version of the kogito-cli-RELEASE binary file that is specific to your operating system.

  2. Extract the kogito-cli-RELEASE binary file to a local directory:

    • On Linux or Mac: In a command terminal, navigate to the directory where you downloaded the kogito-cli-RELEASE binary file and enter the following command to extract the contents:

      Extract the Kogito CLI distribution
      $ tar -xvf kogito-cli-RELEASE.tar.gz
    • On Windows: In your file browser, navigate to the directory where you downloaded the kogito-cli-RELEASE binary file and extract the ZIP file.

    The kogito executable file appears.

  3. Move the extracted kogito file to an existing directory in your PATH variable:

    • On Linux or Mac: In a command terminal, enter the following command:

      Move the kogito file
      $ cp /PATH_TO_KOGITO /usr/local/bin
    • On Windows: Update the relevant PATH environment variables in your system settings to include the path to the Kogito CLI folder. For example, on Windows 10, go to SettingsSystem InfoAdvanced System SettingsAdvancedEnvironment Variables and in the User or System variables, add the path for the Kogito CLI folder to the PATH variable. Close and reopen your Windows command prompt to apply the changes.

  4. With the Kogito CLI now installed, enter the following commands to deploy your Kogito services on OpenShift from source:

    You must be logged in to the relevant OpenShift cluster using the oc login command.
    Example Kogito service deployment from existing namespace
    // Uses the provisioned namespace in your OpenShift cluster
    $ kogito use-project PROJECT_NAME
    
    // Deploys a new Kogito service from a Git source
    $ kogito deploy-service example-quarkus https://github.com/kiegroup/kogito-examples --context-dir process-quarkus-example

    The Kogito Operator uses the default branch in the specified Git repository, usually master.

    The first time that you use the Kogito CLI to interact with a project or service, the Kogito Operator is automatically installed and used to execute the relevant tasks.

    Alternatively, you can generate a new namespace in your cluster during deployment:

    Example Kogito service deployment from new namespace
    // Creates a new namespace in your cluster
    $ kogito new-project NEW_PROJECT_NAME
    
    // Deploys a new Kogito service from a Git source
    $ kogito deploy-service example-quarkus https://github.com/kiegroup/kogito-examples --context-dir process-quarkus-example

    You can also combine the commands to create the namespace and deploy the service using the following abbreviated syntax:

    Abbreviated command for Kogito service deployment
    $ kogito deploy-service example-quarkus https://github.com/kiegroup/kogito-examples --context-dir process-quarkus-example --project PROJECT_NAME

3.5.3. Integrating a custom Kogito service with Infinispan

You can integrate a custom Kogito service with the Infinispan cluster using the following configuration modes:

  • Automatic configuration

  • Manual configuration

3.5.3.1. Integrating a custom Kogito service with Infinispan using automatic configuration mode

In automatic configuration mode the Kogito operator automatically fetches the relevant Infinispan connection details from the Infinispan custom resource and provides it to the custom Kogito service.

The Infinispan operator provides operational intelligence and reduces management complexity for deploying Infinispan on Kubernetes and OpenShift.

For more information about installing the Infinispan operator and setting up the Infinispan cluster, see the Infinispan operator 2.2 Guide.

KogitoInfra defines the relevant infrastructure component or the third-party operator for the Kogito service, such as Infinispan and MongoDB for persistence or Apache Kafka for messaging.

Prerequisites
  • Infinispan cluster is configured by using the Infinispan operator.

Procedure
  1. Install a KogitoInfra custom resource instance with spec.resource.name and spec.resource.namespace configurations.

    spec.resource.name refers to the Infinispan custom resource name and spec.resource.namespace refers to the Infinispan custom resource namespace.

    Example Kogito infrastructure resource for Infinispan persistence
    apiVersion: app.kiegroup.org/v1beta1
    kind: KogitoInfra
    metadata:
      name: kogito-infinispan-infra
    spec:
      resource:
        apiVersion: infinispan.org/v1
        kind: Infinispan
        name: kogito-infinispan
        namespace: infinispan-cluster-namespace
  2. To enable Infinispan persistence, bind KogitoInfra with the Kogito service by performing one of the following tasks:

    • Use the --infra INFINISPAN_INFRA_NAME flag during deployment using the Kogito CLI, for example:

      $ kogito deploy-service travels --infra kogito-infinispan-infra
    • Edit the spec.infra configuration in the Kogito Runtime custom resource, for example:

      apiVersion: app.kiegroup.org/v1beta1
      kind: KogitoRuntime
      metadata:
        name: travels
      spec:
        infra:
          - kogito-infinispan-infra
3.5.3.2. Integrating a custom Kogito service with Infinispan using manual configurations

If you set up a custom Infinispan cluster, you can refer to it in the Kogito runtime custom resource by configuring the following environment variables and application properties:

Example Kogito service deployment with Infinispan persistence enabled using the Kogito CLI
$ kogito deploy-service
Required environment variables for a custom Infinispan cluster
ENABLE_PERSISTENCE=true

# On Quarkus
`QUARKUS_INFINISPAN_CLIENT_AUTH_USERNAME`
`QUARKUS_INFINISPAN_CLIENT_AUTH_PASSWORD`

# On Spring Boot
`INFINISPAN_REMOTE_AUTH_USERNAME`
`INFINISPAN_REMOTE_AUTH_PASSWORD`
Required application properties for a custom Infinispan cluster
# On Quarkus
quarkus.infinispan-client.server-list=
quarkus.infinispan-client.use-auth=
quarkus.infinispan-client.sasl-mechanism=
quarkus.infinispan-client.auth-realm=

# On Spring Boot
infinispan.remote.server-list=
infinispan.remote.use-auth=
infinispan.remote.sasl-mechanism=
infinispan.remote.auth-realm=
3.5.3.3. Infinispan persistence in Kogito services

After you install the KogitoInfra custom resource to connect with the Infinispan infrastructure, to enable Infinispan persistence for a Kogito service using the Kogito Operator, use the --infra INFINISPAN_INFRA_NAME flag during deployment in the Kogito CLI or edit the spec.infra configuration in the KogitoRuntime custom resource:

Example Kogito service deployment with Infinispan persistence enabled using the Kogito CLI
$ kogito deploy-service travels --infra INFINISPAN_INFRA_NAME
Example Kogito service custom resource with Infinispan persistence enabled
apiVersion: app.kiegroup.org/v1beta1
kind: KogitoRuntime
metadata:
  name: travels
spec:
  infra:
    - INFINISPAN_INFRA_NAME

If you set up a custom Infinispan cluster, you can refer to it in the KogitoRuntime custom resource by configuring the following environment variables and application properties:

Required environment variables for a custom Infinispan cluster
ENABLE_PERSISTENCE=true

# On Quarkus
QUARKUS_INFINISPAN_CLIENT_AUTH_USERNAME
QUARKUS_INFINISPAN_CLIENT_AUTH_PASSWORD

# On Spring Boot
INFINISPAN_REMOTE_AUTH_USERNAME
INFINISPAN_REMOTE_AUTH_PASSWORD
Required application properties for a custom Infinispan cluster
# On Quarkus
quarkus.infinispan-client.server-list=
quarkus.infinispan-client.use-auth=
quarkus.infinispan-client.sasl-mechanism=
quarkus.infinispan-client.auth-realm=

# On Spring Boot
infinispan.remote.server-list=
infinispan.remote.use-auth=
infinispan.remote.sasl-mechanism=
infinispan.remote.auth-realm=
3.5.3.4. Apache Kafka messaging in Kogito services

After you install the KogitoInfra custom resource to connect with the Apache Kafka infrastructure, to enable Kafka messaging for a Kogito service using the Kogito Operator, use the --infra KAFKA_INFRA_NAME flag during deployment in the Kogito CLI or edit the spec.infra configuration in the KogitoRuntime custom resource:

Example Kogito service deployment with Kafka messaging enabled using the Kogito CLI
$ kogito deploy-service travels --infra KAFKA_INFRA_NAME
Example Kogito service custom resource with Kafka messaging enabled
apiVersion: app.kiegroup.org/v1beta1
kind: KogitoRuntime
metadata:
  name: travels
spec:
  infra:
    - KAFKA_INFRA_NAME

When you enable Kafka messaging, a variable named KAFKA_BOOTSTRAP_SERVERS is injected into the service container. On Quarkus, this is the default behavior when you use Kafka Client 1.x or later. On Spring Boot, you might need to use a property substitution in the application.properties file, such as the following example:

Example application property substitution for Kafka on Spring Boot
spring.kafka.bootstrap.servers=${KAFKA_BOOTSTRAP_SERVERS}

If the service container has any environment variables with the suffix _BOOTSTRAP_SERVERS, the variables are also injected by the value of the KAFKA_BOOTSTRAP_SERVERS variable.

For example, when you deploy the following Kogito service, the variables MP_MESSAGING_INCOMING_TRAVELLERS_BOOTSTRAP_SERVERS and MP_MESSAGING_OUTGOING_PROCESSEDTRAVELLERS_BOOTSTRAP_SERVERS are injected with the deployed Kafka service URL:

Example Kogito service deployment with injected Kafka variable values
$ kogito deploy-service travels https://github.com/kiegroup/kogito-examples/tree/stable/kogito-travel-agency/extended --context-dir travels --infra kogito-kafka-infra \
--build-env MAVEN_ARGS_APPEND="-Pevents" \
-e MP_MESSAGING_INCOMING_TRAVELLERS_BOOTSTRAP_SERVERS
-e MP_MESSAGING_OUTGOING_PROCESSEDTRAVELLERS_BOOTSTRAP_SERVERS

If you set up a custom Kafka cluster, you can refer to it in the KogitoRuntime custom resource by configuring the following environment variable and application property:

Required environment variable for a custom Kafka cluster
ENABLE_EVENTS=true
Required application property for a custom Kafka cluster
# On Quarkus
kafka.bootstrap.servers=

# On Spring Boot
spring.kafka.bootstrap-servers=
3.5.3.5. MongoDB persistence in Kogito services

After you install the KogitoInfra custom resource to connect with the MongoDB infrastructure, to enable MongoDB persistence for a Kogito service using the Kogito Operator, use the --infra MONGODB_INFRA_NAME flag during deployment in the Kogito CLI or edit the spec.infra configuration in the KogitoRuntime custom resource:

Example Kogito service deployment with MongoDB persistence enabled using the Kogito CLI
$ kogito deploy-service travels --infra MONGODB_INFRA_NAME
Example Kogito service custom resource with MongoDB persistence enabled
apiVersion: app.kiegroup.org/v1beta1
kind: KogitoRuntime
metadata:
  name: travels
spec:
  infra:
    - MONGODB_INFRA_NAME

If you set up a custom MongoDB cluster and do not use a KogitoInfra resource, you can refer to it in the KogitoRuntime custom resource by configuring the following environment variables and application properties:

Required environment variables for a custom MongoDB cluster
ENABLE_PERSISTENCE=true

# On Quarkus
QUARKUS_MONGODB_CREDENTIALS_AUTH_SOURCE
QUARKUS_MONGODB_CREDENTIALS_USERNAME
QUARKUS_MONGODB_CREDENTIALS_PASSWORD
QUARKUS_MONGODB_DATABASE

# On Spring Boot
SPRING_DATA_MONGODB_AUTHENTICATION_DATABASE
SPRING_DATA_MONGODB_USERNAME
SPRING_DATA_MONGODB_PASSWORD
SPRING_DATA_MONGODB_DATABASE
Required application properties for a custom MongoDB cluster
# On Quarkus
quarkus.mongodb.connection-string=

# On Spring Boot
spring.data.mongodb.host=
spring.data.mongodb.port=

On Spring Boot, spring.data.mongodb.uri does not work simultaneously with host, port, and credentials properties (Spring Boot). Also, it is recommended to set the password in Kubernetes.

3.5.4. Integrating a custom Kogito service with Knative Eventing

Knative Eventing is a serverless platform you can use to create event producers and consumers for your applications. For more information about Knative Eventing, see Knative Eventing and the Knative docs repository on GitHub.

When you integrate a custom Kogito service with Knative Eventing you can create a Knative broker, bind that Knative broker with the Kogito Operator, and include a cross-reference link for Knative Eventing.

Knative events conform to the CloudEvents specification. For more information, see CloudEvents.

You can use the Knative Eventing add-on to include your Kogito Services in the Knative Eventing platform. For more information about the Knative Eventing add-on, see Knative Eventing add-on on GitHub.

To use all features, in addition to those available as part of the Kogito Knative Eventing Source Operator, you must integrate the custom Kogito service with Knative Eventing.

3.5.4.1. Creating a Knative Broker

Knative brokers and triggers provide an "event mesh" model that enables an event producer to deliver events to a broker. The broker then uniformly distributes the events to consumers by using triggers. For more information about brokers, see Broker - Knative. For more information about triggers, see Triggers - Knative.

Consumers can register different types of events without needing to negotiate directly with event producers. You can use specific filter conditions to optimize event routing on the underlying platform.

To integrate your custom Kogito Service with Knative Eventing you need at least one broker that collects the events produced by your custom Kogito Service. You can create a broker by applying a YAML file by using kubectl.

Prerequisites
  • Administrative privileges are available in the target cluster.

  • Knative Eventing is installed or the OpenShift Serverless Platform is available.

    For more information about installing Knative Eventing, see About installing Knative. For more information about OpenShift Serverless, see OpenShift Serverless

Procedure
  1. Create a <filename.yaml> YAML file containing the following template:

    apiVersion: eventing.knative.dev/v1
    kind: Broker
    metadata:
      name: <broker-name>

    The YAML file creates a broker with the desired name in the current namespace. For more information about configuring broker options using YAML, see Broker configuration example - Knative.

  2. To apply the YAML file, enter the following command:

    kubectl apply -f <filename>.yaml>

    Where <filename> is the name of the file you created in the previous step.

  3. Optional: To verify that the broker is working correctly, enter the following command:

    kubectl -n <namespace> get broker <broker-name>

    The information about your broker is displayed:

    If the broker is working correctly, it shows a READY status of True, for example:

    NAME READY REASON URL AGE

    <broker-name>

    True

    http://broker-ingress.knative-eventing.svc.cluster.local/event-example/<broker-name>;

    1m

    If the READY status is False, wait a few moments and then run the command again.

3.5.4.2. Binding Knative Broker with Kogito operator
  1. Create your Kogito project locally. For more information about Serverless Workflow, see Orchestrating microservices with Serverless Workflow in Kogito.

  2. Build the image with your project. For an example project, see Process with Knative Eventing on GitHub.

  3. Deploy the project as either a Knative Service or a regular deployment.

  4. Create the Kogito source CR, for example:

    apiVersion: kogito.knative.dev/v1alpha1
    kind: KogitoSource
    metadata:
      name: my-kogito-source
    spec:
      subject:
        apiVesion: serving.knative.dev/v1
        kind: Service
        # Put the name of your custom Kogito Service [3]
        name: my-kogito-service
      sink:
        ref:
          apiVersion: eventing.knative.dev/v1
          kind: Broker
          name: <broker-name>
  5. To deploy your source, enter the following command:

    kubectl apply -f <path to the source>.yaml

The custom {KOGITO} Service is now configured to produce cloud events that will be collected by the broker.

If you want the custom {KOGITO} Service to consume Knative Events you can use Knative triggers. For more information, see Knative Eventing in Kogito Services.

3.5.4.3. Using the {KOGITO} Knative add-on

You can use the Kogito add-on instead of manually configuring definitions of the YAML file descriptors for your custom Kogito service.

To use the Kogito Knative add-on to generate the needed YAML files for your custom Kogito service, you add the YAML file to your pom.xml configuration file and compile your project using the Knative profile.

Procedure
  1. Add the the following example to your pom.xml file:

    <profile>
      <id>knative</id>
      <properties>
        <!-- Shortcuts -->
        <namespace>default</namespace>
        <deploy>false</deploy>
        <!-- Quarkus Knative integration to build and deploy -->
        <quarkus.kubernetes.namespace>${namespace}</quarkus.kubernetes.namespace>
        <quarkus.kubernetes.deploy>${deploy}</quarkus.kubernetes.deploy>
        <quarkus.container-image.build>true</quarkus.container-image.build>
        <quarkus.profile>knative</quarkus.profile>
      </properties>
      <dependencies>
        <dependency>
          <groupId>io.quarkus</groupId>
          <artifactId>quarkus-kubernetes</artifactId>
        </dependency>
        <dependency>
          <groupId>io.quarkus</groupId>
          <artifactId>quarkus-container-image-jib</artifactId>
        </dependency>
      </dependencies>
    </profile>

    For more information, see pom.xml example in the Kogito examples on GitHub.

  2. To create the knative.yml and kogito.yml YAML files, enter the following:

    mvn clean install -Pknative -Dnamespace=<your namespace>
  3. To deploy the objects from the knative.yml and kogito.yml YAML files, enter the following command:

    kubectl apply -f target/kubernetes/knative.yml,target/kubernetes/kogito.yml
3.5.4.4. Postgresql persistence with Kogito service

The Kogito Operator does not manage Postgres instances. If Kogito service persistence type is reactive(kogito.persistence.type=postgresql), then you can refer to it in the KogitoRuntime custom resource by configuring the following environment variables. Reactive persistence with kogito.persistence.type=postgresql only worked with Springboot.

Required environment variables for a custom Postgresql persistence
# for Spring Boot
PGUSER
PGHOST
PGPASSWORD
PGDATABASE
PGPORT
Example Kogito service custom resource with reactive Postgresql persistence
apiVersion: app.kiegroup.org/v1beta1
kind: KogitoRuntime
metadata:
  name: process-postgresql-persistence-quarkus
spec:
  image: quay.io/kiegroup/process-postgresql-persistence-quarkus:persistence
  runtime: springboot
  env:
    - name: PGUSER
      value: postgresql
    - name: PGDATABASE
      value: runtimeapp
    - name: PGPASSWORD
      valueFrom:
        secretKeyRef:
          name: kogito-postgresql
          key: postgresql-password
    - name: PGHOST
      value: kogito-postgresql.default.svc.cluster.local
    - name: PGPORT
      value: '5432'

or with the connection string

kogito.persistence.postgresql.connection.uri

whereas if the persistence type is jdbc(kogito.persistence.type=jdbc), then you can refer to it in the KogitoRuntime custom resource by configuring the following environment variables:

Required environment variables for a custom Postgresql persistence
# On Quarkus
QUARKUS_DATASOURCE_DB-KIND
QUARKUS_DATASOURCE_USERNAME
QUARKUS_DATASOURCE_PASSWORD
QUARKUS_DATASOURCE_JDBC_URL

# On Spring Boot
SPRING_DATASOURCE_USERNAME
SPRING_DATASOURCE_PASSWORD
SPRING_DATASOURCE_URL
Example Kogito service custom resource with JDBC Postgresql persistence
apiVersion: app.kiegroup.org/v1beta1
kind: KogitoRuntime
metadata:
  name: process-postgresql-persistence-quarkus
spec:
  image: quay.io/kiegroup/process-postgresql-persistence-quarkus:jdbc
  env:
    - name: QUARKUS_DATASOURCE_DB-KIND
      value: postgresql
    - name: QUARKUS_DATASOURCE_USERNAME
      value: postgres
    - name: QUARKUS_DATASOURCE_PASSWORD
      valueFrom:
        secretKeyRef:
          name: kogito-postgresql
          key: postgresql-password
    - name: QUARKUS_DATASOURCE_JDBC_URL
      value: jdbc:postgresql://kogito-postgresql.default.svc.cluster.local:5432/runtimeapp

3.5.5. Kogito Data Index Service properties configuration

When the Kogito Data Index Service is deployed, a configMap resource is created for the application.properties configuration of the Data Index Service.

The name of the configMap resource consists of the name of the Data Index Service and the suffix -properties, as shown in the following example:

Example configMap resource generated during Kogito Data Index Service deployment
kind: ConfigMap
apiVersion: v1
metadata:
  name: data-index-properties
data:
  application.properties : |-
    property1=value1
    property2=value2

The application.properties data of the configMap resource is mounted in a volume to the container of the Data Index Service. Any runtime properties that you add to the application.properties section override the default application configuration properties of the Data Index Service.

When the application.properties data of the configMap is changed, a rolling update modifies the deployment and configuration of the Data Index Service.

If your Kogito project uses the Infinispan or MongoDB Persistence Kogito add-on to enable Infinispan or MongoDB persistence explicitly, the Kogito Operator mounts a volume based on a configMap resource created for you during the deployment of the service. This configMap resource has the -protobuf-files suffix and lists the protobuf (protocol buffers) files that your service generated during build time.

Example configMap resource with protobuf files listed
kind: ConfigMap
apiVersion: v1
metadata:
  name: example-quarkus-protobuf-files
  labels:
    kogito-protobuf: true
data:
  visaApplications.proto: |-
    syntax = "proto2";
    package org.acme.travels.visaApplications;
    import "kogito-index.proto";
    import "kogito-types.proto";
    option kogito_model = "VisaApplications";
    option kogito_id = "visaApplications";
    ...

When a new persistent Kogito service is deployed, a new set of protobuf files is generated and the Data Index Service creates a new pod instance that refers to the new volume.

Updated protobuf files are automatically refreshed by Kubernetes volumes, so if you add a new property in your domain data, this data is reflected automatically in the Data Index Service without restarts.

If a Kogito service is removed, the associated protobuf files are also removed and are no longer visible in the Data Index Service, but the data is still persisted in Infinispan or MongoDB.

3.5.6. PostgreSQL persistence with the Kogito Data Index Service

Data Index Service does not initialize its database schema automatically. To initialize the database schema, you need to either enable automatic schema initialization using Hibernate or create the schema using the DDL script.

Automatic schema initialization using Hibernate can be enabled using the quarkus.hibernate-orm.database.generation property. For possible values of quarkus.hibernate-orm.database.generation property, see Quarkus docs for database generation options. The value can also be set as an environment variable QUARKUS_HIBERNATE_ORM_DATABASE_GENERATION.

Example DataIndex resource with PostgreSQL persistence enabled (requires Kafka KogitoInfra to be available):
apiVersion: app.kiegroup.org/v1beta1
kind: KogitoSupportingService
metadata:
  name: data-index
spec:
  serviceType: DataIndex
  image: quay.io/kiegroup/kogito-data-index-postgresql:latest
  infra:
    - kogito-kafka-infra
  env:
    - name: QUARKUS_DATASOURCE_DB-KIND
      value: postgresql
    - name: QUARKUS_DATASOURCE_USERNAME
      value: postgres
    - name: QUARKUS_DATASOURCE_PASSWORD
      valueFrom:
        secretKeyRef:
          name: kogito-postgresql
          key: postgresql-password
    - name: QUARKUS_DATASOURCE_JDBC_URL
      value: jdbc:postgresql://kogito-postgresql.default.svc.cluster.local:5432/kogito
    - name: QUARKUS_HIBERNATE_ORM_DATABASE_GENERATION
      value: update

3.5.7. Infinispan persistence with the Kogito Jobs Service

To enable Infinispan persistence for the Kogito Jobs Service, you install the Infinispan infrastructure and then you can use the --infra INFINISPAN_INFRA_NAME flag during deployment in the Kogito CLI or edit the spec.infra configuration in the KogitoSupportingService custom resource that contains the JobsService service type:

Example Jobs Service deployment with Infinispan persistence enabled
$ kogito install infra INFINISPAN_INFRA_NAME --kind Infinispan --apiVersion infinispan.org/v1 --resource-name kogito-infinispan -p PROJECT_NAME
$ kogito install jobs-service --infra INFINISPAN_INFRA_NAME
Example JobsService resource with Infinispan persistence enabled
apiVersion: app.kiegroup.org/v1beta1
kind: KogitoSupportingService
metadata:
  name: jobs-service
spec:
  serviceType: JobsService
  infra:
    - kogito-infinispan-infra

You can refine the Infinispan integration by setting the spec.infra property in the KogitoSupportingService custom resource that contains the JobsService service type. With this property set, the Kogito Operator ensures that the Jobs Service has access to the persistence infrastructure configurations.

3.5.8. Kogito Jobs Service properties configuration

When the Kogito Jobs Service is deployed, a configMap resource is created for the application.properties configuration of the Jobs Service.

The name of the configMap resource consists of the name of the Jobs Service and the suffix -properties, as shown in the following example:

Example configMap resource generated during Kogito Jobs Service deployment
kind: ConfigMap
apiVersion: v1
metadata:
  name: jobs-service-properties
data:
  application.properties : |-
    property1=value1
    property2=value2

The application.properties data of the configMap resource is mounted in a volume to the container of the Jobs Service. Any runtime properties that you add to the application.properties section override the default application configuration properties of the Jobs Service.

When the application.properties data of the configMap is changed, a rolling update modifies the deployment and configuration of the Jobs Service.

3.6. Kogito Operator interaction with Prometheus and Grafana

Kogito provides a monitoring-prometheus-addon add-on that enables Prometheus metrics monitoring for Kogito services and generates Grafana dashboards that consume the default metrics exported by the add-on. The Kogito Operator uses the Prometheus Operator to expose the metrics from your Kogito project for Prometheus to scrape. Due to this dependency, the Prometheus Operator must be installed in the same namespace as your Kogito project.

When you deploy a Kogito service that uses the monitoring-prometheus-addon add-on and the Prometheus Operator is installed, the Kogito Operator creates a ServiceMonitor custom resource to expose the metrics for Prometheus, as shown in the following example:

Example ServiceMonitor resource for Prometheus
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  labels:
    app: onboarding-service
  name: onboarding-service
  namespace: kogito
spec:
  endpoints:
  - path: /metrics
    targetPort: 8080
    scheme: http
  namespaceSelector:
    matchNames:
    - kogito
  selector:
    matchLabels:
      app: onboarding-service

You must manually configure your Prometheus custom resource that is managed by the Prometheus Operator to select the ServiceMonitor resource:

Example Prometheus resource
apiVersion: monitoring.coreos.com/v1
kind: Prometheus
metadata:
  name: prometheus
spec:
  serviceAccountName: prometheus
  serviceMonitorSelector:
    matchLabels:
      app: onboarding-service

After you configure your Prometheus resource with the ServiceMonitor resource, you can see the endpoint being scraped by Prometheus in the Targets page in the Prometheus web console:

Image of Kogito service targets view in Prometheus
Figure 33. Targets page in Prometheus web console

The metrics exposed by the Kogito service appear in the Graph view:

Image of Kogito service graph view in Prometheus
Figure 34. Graph view in Prometheus web console

The Kogito Operator also creates a GrafanaDashboard custom resource defined by the Grafana Operator for each of the Grafana dashboards generated by the add-on. The app label for the dashboards is the name of the deployed Kogito service. You must set the dashboardLabelSelector property of the Grafana custom resource according to the relevant Kogito service.

Example Grafana resource
apiVersion: integreatly.org/v1alpha1
kind: Grafana
metadata:
  name: example-grafana
spec:
  ingress:
    enabled: true
  config:
    auth:
      disable_signout_menu: true
    auth.anonymous:
      enabled: true
    log:
      level: warn
      mode: console
    security:
      admin_password: secret
      admin_user: root
  dashboardLabelSelector:
    - matchExpressions:
        - key: app
          operator: In
          values:
            - my-kogito-application

3.6.1. Replacing Kogito services TrustStores

You can replace the default Java TrustStore that comes in Kogito images. Replacement of Java TrustStore is required when a given Kogito service makes HTTPS calls to services that require encrypted connections using a private Certificate Authority (CA).

Prerequisites
  • The Java TrustStore replaces the default one. You must add the trusted certificates to the default JKS. As an alternative, you can create a new TrustStore that consists of the required Kogito service certificates.

Any Kogito service that is deployed in a given namespace can access the Java TrustStore if required.

To manipulate the Java TrustStore, you can use KeyExplorer or keytool that consists of JDK.

Procedure
  1. Create a Kubernetes Secret based on the customized Java TrustStore:

    Example Kuberbetes Secret
    $ oc create secret generic kogito-truststore --from-file=cacerts=<path to your JKS> --from-literal=keyStorePassword=<TrustStore Password>

    You must define the secret keys as shown in the previous command. cacerts is used for the TrustStore file and keyStorePassword is used for the TrustStore password.

  2. Deploy the Kogito service that uses the TrustStore. For example:

    Example Kogito service deployment
    piVersion: app.kiegroup.org/v1beta1
    kind: KogitoRuntime
    metadata:
      name: my-service
    spec:
      replicas: 1
      image: quay.io/my-namespace/my-service:latest
      trustStoreSecret: kogito-truststore
  3. Alternatively, you can use Kogito CLI to deploy the Kogito service using truststore-secret as follows:

    Example Kogito service deployment using Kogito CLI
    $ kogito deploy my-service https://mygitrepository/my-service.git --truststore-secret kogito-truststore

3.7. Examples provided with Kogito Operator

Several examples are shipped with {KOGITO}. You can review the code for examples and modify it as necessary to suit your needs.

3.7.1. Travel agency tutorial for Kogito services on OpenShift

The kogito-travel-agency extended example application in GitHub contains Kogito services related to travel booking. The purpose of this example application is to help you get started with deploying Kogito services on OpenShift.

The example application illustrates many of the configuration options you can use whether you are deploying services locally or on OpenShift, such as process persistence with Infinispan, messaging with Apache Kafka, and application data indexing with the Kogito Data Index Service.

For more information about this example application, see the README file in the application folder.

This tutorial demonstrates the following two related services in the kogito-travel-agency extended example application:

  • travels: Service for booking a trip to a specified destination, including flight and hotel

  • visas: Service for managing travel visas, if required

The following Business Model and Notation (BPMN) 2.0 process models are the core processes in these services:

Image of travel agency example process
Figure 35. Travels core process
Image of visas example process
Figure 36. Visas core process

These two services communicate with each other through events. The travel agency service schedules specified travel plans and sends visa applications for travelers that require visas to visit a specified country. The visa service then evaluates any visa applications and responds with the visa approval or rejection.

The services expose REST API endpoints that are generated from the BPMN business process definitions in the services. Internally, the services communicate using Apache Kafka messaging. The logic to interact with Kafka to produce and consume messages is also generated from the BPMN process definitions.

Tutorial objectives
  • Deploy an application with advanced Kogito services, including supporting services and infrastructure.

  • Deploy Kogito infrastructures (Infinispan, Kafka, and Data Index Service) using the Kogito Operator and Kogito CLI.

  • Deploy Kogito service definitions using the Kogito CLI.

  • Use binary builds to deploy Kogito services on OpenShift.

Prerequisites
  • VSCode 1.46.0 or later is installed.

  • The Kogito Bundle VSCode extension is installed and enabled in your VSCode IDE.

  • OpenShift 4.3 or later is installed.

  • The oc OpenShift CLI is installed. For oc installation instructions, see the OpenShift documentation.

  • You have access to the OpenShift web console with cluster-admin permissions.

  • The Kogito command-line interface (CLI) is installed from the latest Kogito CLI distribution.

  • Git is installed.

  • JDK 11 or later is installed. (GraalVM is recommended.)

  • Apache Maven 3.6.2 or later is installed.

  • (Quarkus only) The pom.xml file of your Kogito project contains the following dependency for the Quarkus smallrye-health extension. This extension enables the liveness and readiness probes that are required for Quarkus-based projects on OpenShift or Kubernetes.

    SmallRye Heath dependency for Quarkus applications on OpenShift
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-smallrye-health</artifactId>
    </dependency>
3.7.1.1. Cloning the Kogito examples Git repository

For this travel agency tutorial, you need local access to the example services, so you must first clone the kogito-examples Git repository to your local system.

Procedure

In a command terminal, navigate to a directory where you want to store the Kogito example applications and enter the following command to clone the repository:

Cloning the Kogito examples repository
$ git clone https://github.com/kiegroup/kogito-examples.git

The cloned kogito-examples repository contains various types of Kogito services on Quarkus or Spring Boot to help you develop your own applications.

For this travel agency tutorial, you need the kogito-travel-agency extended example application, which contains the following services:

  • travels: Service for booking a trip to a specified destination, including flight and hotel

  • visas: Service for managing travel visas, if required

3.7.1.2. Configuring access to your OpenShift environment

To complete the travel agency tutorial, you must ensure that you have proper access to both the OpenShift web console and to the oc CLI.

You can use different types of OpenShift 4.x environments, such as a full OpenShift cluster or a small CodeReady Containers environment. However, the OpenShift environment must have access to the public Internet in order to be able to pull in the required container images and build artifacts.
Procedure
  1. Log in to the OpenShift web console and in the upper-right corner of the screen, select your profile and click Copy Login Command.

  2. In the new window that appears, log in again to re-authenticate your user and then click Display Token.

  3. Copy the oc login command and enter it in a command terminal:

    Example oc CLI login token
    $ oc login --token=OPENSHIFT_TOKEN --server=https://WEB_CONSOLE_SERVER

    If your authentication fails or you do not have cluster-admin permissions, contact your OpenShift administrator.

3.7.1.3. Creating an OpenShift project and installing the Kogito Operator using the Kogito CLI

To set up an example application with Kogito services for deployment on OpenShift, you must create a project (namespace) in OpenShift in which you can install the application and the Kogito Operator. The Kogito Operator is based on the Operator SDK and automates many of the deployment steps for you. The first time that you use the Kogito CLI to interact with a project or service, the Kogito Operator is automatically installed and used to execute the relevant tasks.

You can create the project and install the Kogito Operator using the OpenShift web console or using the Kogito CLI. This example uses the Kogito CLI.

Procedure

In a command terminal, enter the following command to create an OpenShift project for the kogito-travel-agency extended example application using the Kogito CLI:

Creating the OpenShift project
$ kogito new-project kogito-travel-agency
Project `kogito-travel-agency` created successfully

If you do not have cluster-admin permissions and another user created the kogito-travel-agency project for you, you can alternatively use the following command to connect the Kogito CLI tooling to the existing project:

Connecting to an existing OpenShift project
$ kogito use-project kogito-travel-agency
Project set to 'kogito-travel-agency'

The kogito new-project and kogito use-project commands automatically install the Kogito Operator if it is not installed already.

If your Kogito project requires persistence and messaging infrastructures, you can use the left menu of the OpenShift web console to navigate to OperatorsOperatorHub and install the Infinispan operator for persistence and the Strimzi Operator for Apache Kafka clusters and messaging. You can also install these operators manually using the Infinispan operator Guide or the Strimzi Quick Start guide. The Kogito Operator uses these operators to create the needed persistence and messaging infrastructures.

After you create the OpenShift project using the Kogito CLI and install the Kogito Operator, the operator is listed with any other installed operators in the OpenShift web console in OperatorsInstalled Operators:

Image of installed operators in web console
Figure 37. Installed operators in web console
3.7.1.4. Installing the Infinispan persistence infrastructure for Kogito services on OpenShift

Kogito supports runtime persistence for process data in your services. Kogito persistence is based on Infinispan and enables you to configure key-value storage definitions to persist data, such as active nodes and process instance variables, so that the data is preserved across application restarts.

The Kogito Operator uses the Infinispan operator to deploy the Infinispan infrastructure in a Kogito project. For optimal Kogito deployment on OpenShift, install the Infinispan operator and enable Infinispan persistence for your Kogito services. You can install the Infinispan infrastructure using the Kogito Operator page in the OpenShift web console or using the Kogito CLI.

This example uses the Kogito CLI to install the Infinispan infrastructure and the Kogito Operator page in the web console to verify that the infrastructure is enabled.

Prerequisites
  • The Infinispan operator is installed in the same OpenShift namespace as your Kogito project. You can install the Infinispan operator using the OperatorsOperatorHub page in the OpenShift web console or manually as described in the Infinispan operator Guide.

  • An Infinispan Server resource is deployed in the same OpenShift namespace as your Kogito project. You can try one of the examples as described in the Infinispan operator Guide. For this procedure, set kogito-infinispan as the name of the Infinispan Server resource.

Procedure
  1. In a command terminal, enter the following command to install the Infinispan infrastructure for the Kogito services:

    Installing Infinispan infrastructure
    $ kogito install infra kogito-infinispan-infra --kind Infinispan --apiVersion infinispan.org/v1 --resource-name kogito-infinispan
  2. In the OpenShift web console, use the left menu to navigate to the following windows to verify the installed Infinispan infrastructure:

    • OperatorsInstalled OperatorsKogitoKogito Infra: A new kogito-infinispan-infra custom resource is listed.

      Image of Kogito Infra page in web console
      Figure 38. Kogito infrastructure resource for Infinispan
    • OperatorsInstalled OperatorsInfinispanInfinispan Cluster: A kogito-infinispan custom resource is listed.

      Image of Infinispan Cluster page in web console
      Figure 39. Infinispan cluster resource
    • WorkloadsStateful Sets: A kogito-infinispan stateful set is deployed.

      Image of Stateful Sets page in web console
      Figure 40. Stateful set for Infinispan
3.7.1.5. Installing the Kafka messaging infrastructure for Kogito services on OpenShift

Kogito supports the MicroProfile Reactive Messaging specification for messaging in your services. Kogito messaging is based on Apache Kafka and enables you to configure messages as either input or output of business process execution.

The Kogito Operator uses the Strimzi Operator to deploy and manage the Kafka infrastructure in a Kogito project. For optimal Kogito deployment on OpenShift, install the Strimzi Operator and enable Kafka messaging for your Kogito services. You can install the Kafka infrastructure using the Kogito Operator page in the OpenShift web console or using the Kogito CLI.

This example uses the Kogito CLI to auto-configure an existing Kafka infrastructure and the Kogito Operator page in the web console to verify that the infrastructure is enabled.

Prerequisites
  • The Strimzi Operator is installed in the same OpenShift namespace as your Kogito project. You can install the Strimzi Operator using the OperatorsOperatorHub page in the OpenShift web console or manually as described in the Strimzi Quick Start guide.

  • A Kafka Cluster resource is deployed in the same OpenShift cluster as your Kogito project. You can try one of the examples as described in the Strimzi Guide. For this procedure, set kogito-kafka as the name of the Kafka Cluster resource.

Procedure
  1. In a command terminal, enter the following command to install the Kafka infrastructure for the Kogito services:

    Installing Kafka infrastructure
    $ kogito install infra kogito-kafka-infra --kind Kafka --apiVersion kafka.strimzi.io/v1beta2 --resource-name kogito-kafka
  2. In the OpenShift web console, use the left menu to navigate to the following windows to verify the installed Kafka infrastructure:

    • OperatorsInstalled OperatorsKogitoKogito Infra: A new kogito-kafka-infra custom resource is listed.

      Image of Kogito infra details in web console
      Figure 41. Kafka enabled
    • OperatorsInstalled OperatorsStrimziKafka: A kogito-kafka custom resource is listed.

      Image of Kafkas page in web console
      Figure 42. Kafka custom resource
    • WorkloadsStateful Sets: kogito-kafka-kafka and kogito-kafka-zookeeper stateful sets are deployed.

      Image of Stateful Sets page in web console
      Figure 43. Stateful sets for Kafka
3.7.1.6. Installing the Kogito Data Index Service for Kogito services on OpenShift

Kogito provides a Data Index Service that stores all Kogito events related to processes, tasks, and domain data. The Data Index Service uses Kafka messaging to consume CloudEvents messages from Kogito services, and then indexes the returned data for future GraphQL queries and stores the data in the Infinispan persistence store. The Data Index Service is at the core of all Kogito search, insight, and management capabilities.

The Kogito Operator uses the Data Index Service for data management in a Kogito project. For optimal Kogito deployment on OpenShift, enable the Data Index Service for your Kogito services. You can install the Data Index Service using the Kogito Operator page in the OpenShift web console (under Kogito Supporting Service with serviceType set to DataIndex) or using the Kogito CLI.

This example uses the Kogito CLI to install the Data Index Service and the Kogito Operator page in the web console to verify that the service is enabled.

Prerequisites
  • You have installed the following Infinispan persistence and Kafka messaging infrastructure components for your Kogito services, as shown in the following example commands:

    Installing Infinispan and Kafka infrastructure components
    $ kogito install infra kogito-infinispan-infra --kind Infinispan --apiVersion infinispan.org/v1 --resource-name kogito-infinispan
    $ kogito install infra kogito-kafka-infra --kind Kafka --apiVersion kafka.strimzi.io/v1beta2 --resource-name kogito-kafka
Procedure
  1. In a command terminal, enter the following command to install the Kogito Data Index Service for the Kogito services:

    Installing Data Index Service
    $ kogito install data-index --infra kogito-infinispan-infra --infra kogito-kafka-infra

    When you enter this command, the Kogito Operator provisions the Data Index Service to connect to the specified infrastructures.

  2. In the OpenShift web console, use the left menu to navigate to the following windows to verify the installed Data Index Service:

    • OperatorsInstalled OperatorsKogitoKogito Supporting Service: A new kogito-data-index custom resource is listed.

      Image of Kogito Data Index page in web console
      Figure 44. Data Index Service resource
    • WorkloadsDeployments: A new kogito-data-index deployment is listed.

      Image of Data Index deployment page in web console
      Figure 45. Data Index Service deployment
    • NetworkingRoutes: A new kogito-data-index route is listed.

      Image of Data Index route page in web console
      Figure 46. Data Index Service route

      You can click the Location URL to view the Kogito Data Index Service GraphQL interface (GraphiQL) and enter GraphQL queries for stored data.

      Image of Data Index GraphiQL interface
      Figure 47. Example GraphQL query in GraphiQL interface for Data Index Service
3.7.1.7. Creating Kogito service definitions on OpenShift using the Kogito CLI

After you set up the required infrastructures for your application, you can create the Kogito service definitions and provision the OpenShift resources required for deployment with a binary build. You can create the service definitions using the OpenShift web console or using the Kogito CLI.

This example uses the Kogito CLI to create the service definitions for the kogito-travel-agency extended example application and uses the Kogito Operator page in the web console to verify that the services are created.

The travel agency example application includes the following key OpenShift resources:

  • BuildConfig: Configures the application to support a binary build in addition to a traditional OpenShift build for deployment. In a binary build, you build the application locally and push the built application to the OpenShift build to be packaged into the runtime container image. A binary build enables services to be deployed faster than a traditional OpenShift build and deployment.

  • ImageStream: Defines the set of container images identified by tags.

  • Deployments: Describes the desired state of the application as a pod template.

  • Service: Functions as a Kubernetes-internal load balancer to serve the application pods.

  • Route: Exposes the Service at a host name.

Prerequisites
  • You have installed the following Infinispan persistence and Kafka messaging infrastructure components for your Kogito services, and you have installed the Kogito Data Index Service accordingly, as shown in the following example commands:

    Installing Infinispan, Kafka, and Data Index Service components
    $ kogito install infra kogito-infinispan-infra --kind Infinispan --apiVersion infinispan.org/v1 --resource-name kogito-infinispan
    $ kogito install infra kogito-kafka-infra --kind Kafka --apiVersion kafka.strimzi.io/v1beta2 --resource-name kogito-kafka
    $ kogito install data-index --infra kogito-infinispan-infra --infra kogito-kafka-infra
Procedure
  1. In a command terminal, navigate to the kogito-travel-agency extended example application and enter the following commands to create Kogito service definitions for the travels and visas services with Infinispan persistence and Kafka messaging enabled:

    Creating the travels service with persistence and messaging enabled
    $ kogito deploy-service travels --infra kogito-infinispan-infra --infra kogito-kafka-infra
    Creating the visas service with persistence and messaging enabled
    $ kogito deploy-service visas --infra kogito-infinispan-infra --infra kogito-kafka-infra

    When the deployment configuration of this service is generated, the Kogito Operator automatically configures the environment variables to point to the location of the Kafka and Infinispan environments that you deployed previously. For Kafka messaging, the operator sets the incoming and outgoing messaging channels and properties as needed. For Infinispan persistence, the operator sets the authorization configuration based on the credentials generated by the Infinispan operator.

    You can also provide a Git repository location to create your services remotely instead of creating your services from a local source. However, this example uses local applications to demonstrate how to prepare the Kogito project on a development machine for a direct push to the cloud.
  2. In the OpenShift web console, use the left menu to navigate to OperatorsInstalled OperatorsKogitoKogito Service and verify the new travels and visas services:

    Image of travels and visas services listed in web console
    Figure 48. New travel agency and visas services listed

    The new services are available but not yet deployed on OpenShift until you build and deploy the service projects from source using a binary build.

3.7.1.8. Deploying Kogito services on OpenShift using a binary build

OpenShift builds can require extensive amounts of time. As a faster alternative for building and deploying your Kogito services on OpenShift, you can use a binary build. In a binary build, you build the application locally and push the built application to an OpenShift BuildConfig configuration to be packaged into the runtime container image.

The kogito-travel-agency extended example application includes a BuildConfig configuration to support a binary build in addition to traditional building for deployment.

Kogito also supports Source-to-Image (S2I) builds, which build the application in an OpenShift build and then pass the built application to the next OpenShift build to be packaged into the runtime container image. The Kogito S2I build configuration also enables you to build the project directly from a Git repository on the OpenShift platform.

However, this example uses the local applications to demonstrate how to prepare the Kogito project on a development machine for a direct push to the cloud.

Prerequisites
Procedure
  1. In a command terminal, navigate to the kogito-travel-agency/extended/travels example service and build the project using Maven:

    Building the local travels project
    $ cd kogito-travel-agency/extended/travels
    $ mvn clean package

    This command builds the project in standard JDK mode to package the application as a runner JAR file and include any dependencies in a lib folder.

    Alternatively, you can also build the project in native mode (requires GraalVM and SubstrateVM) to build and compile the application into a native executable for your system.

    The following resources are generated in the target/quarkus-app folder in preparation for deployment from binary build:

    • quarkus-app/app/travels.jar: Standard JAR file that contains the classes and resources of the project.

    • quarkus-app/quarkus: Contains the generated resources required by the Quarkus application to run.

    • quarkus-app/lib: Directory that contains the project dependencies.

    • quarkus-app/quarkus-run.jar: Executable JAR file for the project. Note that quarkus-run.jar is not an uber-JAR file, and also requires the folders quarkus-app/app, quarkus-app/quarkus, and quarkus-app/lib.

  2. From the same kogito-travel-agency/extended/travels directory location where you built the project, enter the following command to deploy the travels service to OpenShift using a binary build:

    Deploying to OpenShift using binary build
    $ oc start-build travels-binary --from-dir=target/
    
    Uploading directory "target/" as binary input for the build ...
    ....
    Uploading finished
    build.build.openshift.io/travels-1 started
    The Kogito images are handling the binary build directly from the target folder, and the content of the target/quarkus-app folder is copied directly.

    You can use the following command to check the logs of the builder pod if needed:

    Checking logs of builder pod
    $ oc logs -f build/travels-binary-1

    After the binary build is complete, the result is pushed to the travels Image Stream that was created by the Kogito Operator and triggers a new deployment.

  3. In the OpenShift web console, use the left menu to navigate to the following windows to verify the deployed service:

    • WorkloadsDeployments: Select the travels deployment to view the application deployment details, pod status, and other details.

      Image of travels service deployment details page in web console
      Figure 49. Travels deployment details
    • NetworkingRoutes: Select the Location URL for the travels route to view the main page of the Kogito travel agency application.

      Image of Travel Agency application main page
      Figure 50. Travel agency application interface

    After you verify that the travel agency application is deployed, repeat the same steps to deploy the visas application.

  4. In a command terminal, navigate to the kogito-travel-agency/extended/visas example service and build the project using Maven:

    Building the local visas project
    $ cd kogito-travel-agency/extended/visas
    $ mvn clean package
  5. Deploy the visas service to OpenShift using a binary build:

    Deploying to OpenShift using binary build
    $ oc start-build visas-binary --from-dir=target/
    
    Uploading directory "target/" as binary input for the build ...
    ....
    Uploading finished
    build.build.openshift.io/visas-1 started

    You can use the following command to check the logs of the builder pod if needed:

    Checking logs of builder pod
    $ oc logs -f build/visas-binary-1

    After the binary build is complete, the result is pushed to the visas Image Stream that was created by the Kogito Operator and triggers a new deployment.

  6. In the OpenShift web console, use the left menu to navigate to the following windows to verify the deployed service:

    • WorkloadsDeployment Configs: Select the visas deployment to view the application deployment configurations, pod status, and other details.

      Image of visas service deployment details page in web console
      Figure 51. Visas deployment details
    • NetworkingRoutes: Select the Location URL for the visas route to view the main page of the Kogito visas application.

      Image of Visas application main page
      Figure 52. Visas application interface
3.7.1.9. Interacting with the deployed travel agency services on OpenShift

After you deploy the example travel agency services on OpenShift, you can interact with the application interfaces to create a new travel plan.

You can also use a REST client or curl utility to send a REST request, such as the following example request body:

Example REST request body to add a traveler and trip
{
  "traveller": {
    "firstName": "Jan",
    "lastName": "Kowalski",
    "email": "jan@email.com",
    "nationality": "Polish",
    "address": {
      "street": "Polna",
      "city": "Krakow",
      "zipCode": "32-000",
      "country": "Poland"
    }
  },
  "trip": {
    "country": "US",
    "city": "New York",
    "begin": "2019-11-04T00:00:00.000+02:00",
    "end": "2019-11-07T00:00:00.000+02:00"
  }
}

The travels service enables users to book a trip to a certain destination, including flight and hotel. A rule set determines whether a visa is required for the specified destination. The visa approval logic is then implemented as needed by the visas service.

For this tutorial, use the application interfaces for the travels and visas services to book a trip from one country to another and approve the required visa.

Prerequisites
Procedure
  1. In the OpenShift web console, use the left menu to navigate to NetworkingRoutes and select the Location URL for the travels route to view the main page of the Kogito travel agency application:

    Image of Routes page in web console
    Figure 53. Routes for available services in web console
    Image of Travel Agency application main page
    Figure 54. Travel agency application interface
  2. In the travel agency application interface, click Plan new trip, enter details for a trip from one country to another, and click Book your trip to finish.

    Ensure that the Nationality is different from the destination country so that a visa is required.

    This example uses a traveler from Poland who is traveling to the United States:

    Image of new trip details
    Figure 55. Book a new trip

    The new trip is displayed in the main page of the application interface:

    Image of new trip in application main page
    Figure 56. New trip listed in main page of application interface
  3. Next to the new trip, click Tasks to view the pending tasks for that trip.

    Image of new trip tasks
    Figure 57. Tasks for the new trip
  4. Next to the VisaApplication task, click Apply, enter random passport details in the application window, and click Submit application.

    Image of new trip tasks
    Figure 58. Submit visa application
  5. In the OpenShift web console, use the left menu to navigate to NetworkingRoutes and select the Location URL for the visas route to view the main page of the Kogito visas application.

    The traveler visa application is displayed in the visas service interface.

    Image of new visa application in visas service interface
    Figure 59. New visa application listed in visas service interface
  6. Next to the listed visa application, click ApprovalApprove to approve the visa application.

    Image of visa application approval
    Figure 60. Approve visa application
  7. Return to the travel agency application interface, and next to the approved trip, click Tasks and then click Complete for the pending ConfirmTravel task to complete the trip:

    Image of approved trip
    Figure 61. Trip listed and visa approved
    Image of pending completion task
    Figure 62. Complete trip confirmation task
    Image of completed trip
    Figure 63. Trip confirmed and complete

    Now that the application activity is complete, you can inspect the data that was created and stored by the Kogito Data Index Service that you configured for your application. In this case, you can search for data from booked trips and visa applications (Travels and VisaApplications) or for data from the underlying processes and tasks (ProcessInstances and UserTaskInstances).

  8. In the OpenShift web console, use the left menu to navigate to NetworkingRoutes and select the Location URL for the kogito-data-index route to view the Kogito Data Index Service GraphQL interface (GraphiQL):

    Image of Data Index GraphiQL interface
    Figure 64. GraphiQL interface for Data Index Service
  9. In the GraphiQL interface, enter any of the following GraphQL queries to retrieve stored application data:

    • Retrieve data from booked trips (Travels):

      Example query
      { Travels {
        id, traveller {
          email
          firstName
          lastName
          nationality
        }, trip {
          begin
          city
          country
          end
          visaRequired
        }
      } }
      Image of GraphQL query and response for travels
      Figure 65. Example response
    • Retrieve data from booked trips by traveler last name (like: "Chop*"):

      Example query
      { Travels (where: {traveller: {lastName: {like: "Chop*"}}}) {
        id, traveller {
          email
          firstName
          lastName
          nationality
        }, trip {
          begin
          city
          country
          end
          visaRequired
        }
      } }
    • Retrieve data from visa applications (VisaApplications):

      Example query
      { VisaApplications {
        visaApplication {
          approved
          city
          country
          duration
          firstName
          lastName
          nationality
          passportNumber
        }
      } }
      Image of GraphQL query and response for visa applications
      Figure 66. Example response
    • Retrieve data from process instances (ProcessInstances):

      Example query
      { ProcessInstances {
        id,
        processId,
        processName,
        state,
        nodes {
          name,
          type,
          enter,
          exit
        }
      } }
      Image of GraphQL query and response for process instances
      Figure 67. Example response
    • Retrieve data from user task instances (UserTaskInstances):

      Example query
      { UserTaskInstances {
        name,
        priority,
        processId,
        processInstanceId
      } }
      Image of GraphQL query and response for user task instances
      Figure 68. Example response

3.8. Kogito Operator architecture

The Kogito Operator is written in Go and is built with the Operator SDK. The Kogito Operator uses the Kubernetes API for most of the deployment tasks that the operator facilitates and for other internal operations.

The Kogito Operator uses the following custom resources to deploy Kogito domain-specific services (the services that you develop), Kogito supporting services, and middleware infrastructure components:

  • KogitoBuild: Defines the build configurations for Kogito domain-specific services deployed on OpenShift

  • KogitoRuntime: Defines the deployment configurations for Kogito domain-specific services deployed on Kubernetes or OpenShift from a container image in an image registry

  • KogitoSupportingService: Defines the deployment configurations for Kogito supporting services, such as the Kogito Data Index Service, Jobs Service, and Management Console

  • KogitoInfra: Defines the relevant middleware infrastructure component or third-party operator for the Kogito service, such as Infinispan and MongoDB for persistence or Apache Kafka for messaging

3.8.1. Kogito Operator dependencies on third-party operators

The Kogito Operator uses the following third-party operators to auto-configure the Kogito service infrastructure components:

  • Infinispan operator: Used to interact with Infinispan Server instances for process data persistence in the Kogito services

  • Strimzi Operator: Used to interact with Apache Kafka clusters with Zookeeper for messaging in Kogito services

  • Keycloak Operator: Used to interact with Keycloak server instances for security and single sign-on capabilities in Kogito services

  • MongoDB Operator: Used to interact with MongoDB instances for process data persistence in Kogito services

You can find a list of third-party operators tested using the Kogito Operator on Kogito Operator README

When you enable an infrastructure mechanism through KogitoInfra deployment, the Kogito Operator uses the relevant third-party operator to configure the infrastructure.

You must define your custom infrastructure resource and link it in the KogitoInfra. You can specify your custom infrastructure resource in the spec.resource.name and spec.resource.namespace configurations:

Example Kogito infrastructure resource for custom messaging
apiVersion: app.kiegroup.org/v1beta1
kind: KogitoInfra
metadata:
  name: my-kafka-infra
spec:
  resource:
    apiVersion: kafka.strimzi.io/v1beta2
    kind: Kafka
    name: my-kafka-instance
    namespace: my-namespace

In this example, the KogitoInfra custom resource connects to the Kafka cluster named my-kafka-instance from the my-namespace for event messaging.

Similarly, you can define a KogitoInfra resource for MongoDB to allow your processes to connect to it. In this case, you also need to define extra properties into the KogitoInfra resource for the Kogito Operator to interact with the MongoDB instance:

Example Kogito infrastructure resource for MongoDB
apiVersion: app.kiegroup.org/v1beta1
kind: KogitoInfra
metadata:
  name: my-mongodb-infra
spec:
  resource:
    apiVersion: mongodb.com/v1
    kind: MongoDB
    name: custom-mongodb
    namespace: my-namespace
  infraProperties:
    username: kogitouser
    database: kogitodatabase

The Kogito Data Index Service similarly depends on Infinispan and Kafka infrastructure components. Without Infinispan or MongoDB persistence and Kafka messaging, the Data Index Service cannot function properly. However, you can specify whether the Data Index Service uses the general infrastructure components that the Kogito Operator generates or a custom alternative for that component.

For example, by default, the KogitoSupportingService custom resource that contains the DataIndex service type specifies the infra configuration for both Infinispan and Kafka in order to use the corresponding infrastructure components:

Default DataIndex resource configuration with Infinispan persistence and messaging
apiVersion: app.kiegroup.org/v1beta1
kind: KogitoSupportingService
metadata:
  name: data-index
spec:
  serviceType: DataIndex
  replicas: 1
  infra:
     - kogito-infinispan-infra
     - kogito-kafka-infra

You typically create kogito-infinispan-infra and kogito-kafka-infra custom resources before configuring the KogitoSupportingService custom resource that contains the DataIndex service type.

You can also connect the Kogito Data Index Service to a MongoDB instance instead:

Default DataIndex resource configuration with MongoDB persistence and messaging
apiVersion: app.kiegroup.org/v1beta1
kind: KogitoSupportingService
metadata:
  name: data-index
spec:
  serviceType: DataIndex
  replicas: 1
  infra:
     - my-mongodb-infra
     - kogito-kafka-infra

3.8.2. Kogito Operator core package structure

The Kogito Operator uses the following core packages. Your understanding of the Kogito Operator package structure can help you use the operator more effectively or contribute to the development of the operator.

Image of Kogito Operator package layout
Figure 69. Kogito Operator core package structure

The following list describes the function and interaction of these core packages:

  • cmd: Contains the operator entry point and CLI implementation

    • manager: Serves as the entry point for the Kogito Operator image

    • kogito: Provides the implementation for the Kogito CLI

  • test: Contains the implementation for Behavior Driven Development (BDD) tests based on Godog (by Cucumber for Go)

    • config: Provides the configuration for BDD tests

    • features: Defines the features for BDD tests

    • framework: Provides the support API framework to interact with other operator components

    • steps: Defines BDD test steps

  • pkg: Contains the implementation for the Kogito Operator

    • apis: Defines the custom resource definition types for the resources that are managed by the Kogito Operator

    • client: Serves as a wrapper for the Kubernetes and OpenShift clients

    • controller: Defines the business logic for how the Kogito Operator responds to changes to the resources that are managed by the operator

    • framework: Provides the common code related to any Kubernetes operator for all controllers

    • infrastructure: Provides the common code related to the Kogito Operator infrastructure for all controllers, such as external endpoints among the services that are managed by the operator

    • logger: Provides the implementation for the common logger for all other packages, based on Zap (by Uber Go)

    • util: Provides the common Go utilities used across the project

To explore Kogito Operator packages or contribute to the operator development, see the Kogito Operator source repository in GitHub.

4. Developing decision services with Kogito

As a developer of business decisions, you can use Kogito business automation to develop decision services using Decision Model and Notation (DMN) models, Drools Rule Language (DRL) rules, XLS or XLSX spreadsheet decision tables, or a combination of all three methods.

4.1. Using DMN models in Kogito services

As a developer of business decisions, you can use Decision Model and Notation (DMN) to model a decision service graphically. The decision requirements of a DMN decision model are determined by a decision requirements graph (DRG) that is depicted in one or more decision requirements diagrams (DRDs). A DRD can represent part or all of the overall DRG for the DMN model. DRDs trace business decisions from start to finish, with each decision node using logic defined in DMN boxed expressions such as decision tables.

Kogito provides runtime support for DMN 1.1, 1.2, 1.3, and 1.4 models at conformance level 3 and design support for DMN 1.2 models at conformance level 3. You can design your DMN models with the Kogito DMN modeler in VSCode or import existing DMN models into your Kogito projects for deployment and execution. Any DMN 1.1 and 1.3 models (do not contain DMN 1.3 features) that you import, open in the DMN designer, and save are converted to DMN 1.2 models.

For more information about DMN, see the Object Management Group (OMG) Decision Model and Notation specification.

4.1.1. Decision-authoring assets in Kogito

Kogito supports several assets that you can use to define business decisions for your decision service. Each decision-authoring asset has different advantages, and you might prefer to use one or a combination of multiple assets depending on your goals and needs.

The following table highlights the main decision-authoring assets supported in Kogito projects to help you decide or confirm the best method for defining decisions in your decision service.

Table 8. Decision-authoring assets supported in Kogito
Asset Highlights Authoring tools Documentation

Decision Model and Notation (DMN) models

  • Are decision models based on a notation standard defined by the Object Management Group (OMG)

  • Use graphical decision requirements diagrams (DRDs) that represent part or all of the overall decision requirements graph (DRG) to trace business decision flows

  • Use an XML schema that allows the DMN models to be shared between DMN-compliant platforms

  • Support Friendly Enough Expression Language (FEEL) to define decision logic in DMN decision tables and other DMN boxed expressions

  • Can be integrated efficiently with Business Process Model and Notation (BPMN) process models

  • Are optimal for creating comprehensive, illustrative, and stable decision flows

Kogito DMN modeler in VSCode or other DMN-compliant editor

Decision Model and Notation (DMN)

DRL rules

  • Are individual rules that you define directly in .drl text files

  • Provide the most flexibility for defining rules and other technicalities of rule behavior

  • Are optimal for creating rules that require advanced DRL options, such as rule units

  • Have strict syntax requirements for rules to be compiled properly

Any integrated development environment (IDE)

Drools Rule Language (DRL)

Spreadsheet decision tables

  • Are tabular sets of rules that you define in .xls or .xlsx spreadsheet files

  • Support template keys and values for creating rule templates

  • Are optimal for business environments that already rely on decision tables for rules

  • Have strict syntax requirements for rules to be compiled properly when used in Kogito

Spreadsheet editor

Spreadsheet decision tables

4.1.2. Decision Model and Notation (DMN)

Decision Model and Notation (DMN) is a standard established by the Object Management Group (OMG) for describing and modeling operational decisions. DMN defines an XML schema that enables DMN models to be shared between DMN-compliant platforms and across organizations so that business analysts and business rules developers can collaborate in designing and implementing DMN decision services. The DMN standard is similar to and can be used together with the Business Process Model and Notation (BPMN) standard for designing and modeling business processes.

For more information about the background and applications of DMN, see the OMG Decision Model and Notation specification.

4.1.2.1. DMN conformance levels

The DMN specification defines three incremental levels of conformance in a software implementation. A product that claims compliance at one level must also be compliant with any preceding levels. For example, a conformance level 3 implementation must also include the supported components in conformance levels 1 and 2. For the formal definitions of each conformance level, see the OMG Decision Model and Notation specification.

The following list summarizes the three DMN conformance levels:

Conformance level 1

A DMN conformance level 1 implementation supports decision requirement diagrams (DRDs), decision logic, and decision tables, but decision models are not executable. Any language can be used to define the expressions, including natural, unstructured languages.

Conformance level 2

A DMN conformance level 2 implementation includes the requirements in conformance level 1, and supports Simplified Friendly Enough Expression Language (S-FEEL) expressions and fully executable decision models.

Conformance level 3

A DMN conformance level 3 implementation includes the requirements in conformance levels 1 and 2, and supports Friendly Enough Expression Language (FEEL) expressions, the full set of boxed expressions, and fully executable decision models.

Kogito provides runtime support for DMN 1.1, 1.2, 1.3, and 1.4 models at conformance level 3 and design support for DMN 1.2 models at conformance level 3. You can design your DMN models with the Kogito DMN modeler in VSCode or import existing DMN models into your Kogito projects for deployment and execution. Any DMN 1.1 and 1.3 models (do not contain DMN 1.3 features) that you import, open in the DMN designer, and save are converted to DMN 1.2 models.

4.1.2.2. DMN decision requirements diagram (DRD) components

A decision requirements diagram (DRD) is a visual representation of your DMN model. A DRD can represent part or all of the overall decision requirements graph (DRG) for the DMN model. DRDs trace business decisions using decision nodes, business knowledge models, sources of business knowledge, input data, and decision services.

The following table summarizes the components in a DRD:

Table 9. DRD components
Component Description Notation

Elements

Decision

Node where one or more input elements determine an output based on defined decision logic.

dmn decision node

Business knowledge model

Reusable function with one or more decision elements. Decisions that have the same logic but depend on different sub-input data or sub-decisions use business knowledge models to determine which procedure to follow.

dmn bkm node

Knowledge source

External authorities, documents, committees, or policies that regulate a decision or business knowledge model. Knowledge sources are references to real-world factors rather than executable business rules.

dmn knowledge source node

Input data

Information used in a decision node or a business knowledge model. Input data usually includes business-level concepts or objects relevant to the business, such as loan applicant data used in a lending strategy.

dmn input data node

Decision service

Top-level decision containing a set of reusable decisions published as a service for invocation. A decision service can be invoked from an external application or a BPMN business process.

dmn decision service node

Requirement connectors

Information requirement

Connection from an input data node or decision node to another decision node that requires the information.

dmn info connector

Knowledge requirement

Connection from a business knowledge model to a decision node or to another business knowledge model that invokes the decision logic.

dmn knowledge connector

Authority requirement

Connection from an input data node or a decision node to a dependent knowledge source or from a knowledge source to a decision node, business knowledge model, or another knowledge source.

dmn authority connector

Artifacts

Text annotation

Explanatory note associated with an input data node, decision node, business knowledge model, or knowledge source.

dmn annotation node

Association

Connection from an input data node, decision node, business knowledge model, or knowledge source to a text annotation.

dmn association connector

The following table summarizes the permitted connectors between DRD elements:

Table 10. DRD connector rules
Starts from Connects to Connection type Example

Decision

Decision

Information requirement

dmn decision to decision

Business knowledge model

Decision

Knowledge requirement

dmn bkm to decision

Business knowledge model

dmn bkm to bkm

Decision service

Decision

Knowledge requirement

dmn decision service to decision

Business knowledge model

dmn decision service to bkm

Input data

Decision

Information requirement

dmn input to decision

Knowledge source

Authority requirement

dmn input to knowledge source

Knowledge source

Decision

Authority requirement

dmn knowledge source to decision

Business knowledge model

dmn knowledge source to bkm

Knowledge source

dmn knowledge source to knowledge source

Decision

Text annotation

Association

dmn decision to annotation

Business knowledge model

dmn bkm to annotation

Knowledge source

dmn knowledge source to annotation

Input data

dmn input to annotation

The following example DRD illustrates some of these DMN components in practice:

dmn example drd
Figure 70. Example DRD: Loan prequalification

The following example DRD illustrates DMN components that are part of a reusable decision service:

dmn example drd3
Figure 71. Example DRD: Phone call handling as a decision service

In a DMN decision service node, the decision nodes in the bottom segment incorporate input data from outside of the decision service to arrive at a final decision in the top segment of the decision service node. The resulting top-level decisions from the decision service are then implemented in any subsequent decisions or business knowledge requirements of the DMN model. You can reuse DMN decision services in other DMN models to apply the same decision logic with different input data and different outgoing connections.

4.1.2.3. Rule expressions in FEEL

Friendly Enough Expression Language (FEEL) is an expression language defined by the Object Management Group (OMG) DMN specification. FEEL expressions define the logic of a decision in a DMN model. FEEL is designed to facilitate both decision modeling and execution by assigning semantics to the decision model constructs. FEEL expressions in decision requirements diagrams (DRDs) occupy table cells in boxed expressions for decision nodes and business knowledge models.

For more information about FEEL in DMN, see the OMG Decision Model and Notation specification.

Data types in FEEL

Friendly Enough Expression Language (FEEL) supports the following data types:

  • Numbers

  • Strings

  • Boolean values

  • Dates

  • Time

  • Date and time

  • Days and time duration

  • Years and months duration

  • Functions

  • Contexts

  • Ranges (or intervals)

  • Lists

The DMN specification currently does not provide an explicit way of declaring a variable as a function, context, range, or list, but Kogito extends the DMN built-in types to support variables of these types.

The following list describes each data type:

Numbers

Numbers in FEEL are based on the IEEE 754-2008 Decimal 128 format, with 34 digits of precision. Internally, numbers are represented in Java as BigDecimals with MathContext DECIMAL128. FEEL supports only one number data type, so the same type is used to represent both integers and floating point numbers.

FEEL numbers use a dot (.) as a decimal separator. FEEL does not support -INF, +INF, or NaN. FEEL uses null to represent invalid numbers.

Kogito extends the DMN specification and supports additional number notations:

  • Scientific: You can use scientific notation with the suffix e<exp> or E<exp>. For example, 1.2e3 is the same as writing the expression 1.2*10**3, but is a literal instead of an expression.

  • Hexadecimal: You can use hexadecimal numbers with the prefix 0x. For example, 0xff is the same as the decimal number 255. Both uppercase and lowercase letters are supported. For example, 0XFF is the same as 0xff.

  • Type suffixes: You can use the type suffixes f, F, d, D, l, and L. These suffixes are ignored.

Strings

Strings in FEEL are any sequence of characters delimited by double quotation marks.

Example
"John Doe"
Boolean values

FEEL uses three-valued boolean logic, so a boolean logic expression may have values true, false, or null.

Dates

Date literals are not supported in FEEL, but you can use the built-in date() function to construct date values. Date strings in FEEL follow the format defined in the XML Schema Part 2: Datatypes document. The format is "YYYY-MM-DD" where YYYY is the year with four digits, MM is the number of the month with two digits, and DD is the number of the day.

Example:

date( "2017-06-23" )

Date objects have time equal to "00:00:00", which is midnight. The dates are considered to be local, without a timezone.

Time

Time literals are not supported in FEEL, but you can use the built-in time() function to construct time values. Time strings in FEEL follow the format defined in the XML Schema Part 2: Datatypes document. The format is "hh:mm:ss[.uuu][(+-)hh:mm]" where hh is the hour of the day (from 00 to 23), mm is the minutes in the hour, and ss is the number of seconds in the minute. Optionally, the string may define the number of milliseconds (uuu) within the second and contain a positive (+) or negative (-) offset from UTC time to define its timezone. Instead of using an offset, you can use the letter z to represent the UTC time, which is the same as an offset of -00:00. If no offset is defined, the time is considered to be local.

Examples:

time( "04:25:12" )
time( "14:10:00+02:00" )
time( "22:35:40.345-05:00" )
time( "15:00:30z" )

Time values that define an offset or a timezone cannot be compared to local times that do not define an offset or a timezone.

Date and time

Date and time literals are not supported in FEEL, but you can use the built-in date and time() function to construct date and time values. Date and time strings in FEEL follow the format defined in the XML Schema Part 2: Datatypes document. The format is "<date>T<time>", where <date> and <time> follow the prescribed XML schema formatting, conjoined by T.

Examples:

date and time( "2017-10-22T23:59:00" )
date and time( "2017-06-13T14:10:00+02:00" )
date and time( "2017-02-05T22:35:40.345-05:00" )
date and time( "2017-06-13T15:00:30z" )

Date and time values that define an offset or a timezone cannot be compared to local date and time values that do not define an offset or a timezone.

If your implementation of the DMN specification does not support spaces in the XML schema, use the keyword dateTime as a synonym of date and time.
Days and time duration

Days and time duration literals are not supported in FEEL, but you can use the built-in duration() function to construct days and time duration values. Days and time duration strings in FEEL follow the format defined in the XML Schema Part 2: Datatypes document, but are restricted to only days, hours, minutes and seconds. Months and years are not supported.

Examples:

duration( "P1DT23H12M30S" )
duration( "P23D" )
duration( "PT12H" )
duration( "PT35M" )
If your implementation of the DMN specification does not support spaces in the XML schema, use the keyword dayTimeDuration as a synonym of days and time duration.
Years and months duration

Years and months duration literals are not supported in FEEL, but you can use the built-in duration() function to construct days and time duration values. Years and months duration strings in FEEL follow the format defined in the XML Schema Part 2: Datatypes document, but are restricted to only years and months. Days, hours, minutes, or seconds are not supported.

Examples:

duration( "P3Y5M" )
duration( "P2Y" )
duration( "P10M" )
duration( "P25M" )
If your implementation of the DMN specification does not support spaces in the XML schema, use the keyword yearMonthDuration as a synonym of years and months duration.
Functions

FEEL has function literals (or anonymous functions) that you can use to create functions. The DMN specification currently does not provide an explicit way of declaring a variable as a function, but Kogito extends the DMN built-in types to support variables of functions.

Example:

function(a, b) a + b

In this example, the FEEL expression creates a function that adds the parameters a and b and returns the result.

Contexts

FEEL has context literals that you can use to create contexts. A context in FEEL is a list of key and value pairs, similar to maps in languages like Java. The DMN specification currently does not provide an explicit way of declaring a variable as a context, but Kogito extends the DMN built-in types to support variables of contexts.

Example:

{ x : 5, y : 3 }

In this example, the expression creates a context with two entries, x and y, representing a coordinate in a chart.

In DMN 1.2, another way to create contexts is to create an item definition that contains the list of keys as attributes, and then declare the variable as having that item definition type.

The Kogito DMN API supports DMN ItemDefinition structural types in a DMNContext represented in two ways:

  • User-defined Java type: Must be a valid JavaBeans object defining properties and getters for each of the components in the DMN ItemDefinition. If necessary, you can also use the @FEELProperty annotation for those getters representing a component name which would result in an invalid Java identifier.

  • java.util.Map interface: The map needs to define the appropriate entries, with the keys corresponding to the component name in the DMN ItemDefinition.

Ranges (or intervals)

FEEL has range literals that you can use to create ranges or intervals. A range in FEEL is a value that defines a lower and an upper bound, where either can be open or closed. The DMN specification currently does not provide an explicit way of declaring a variable as a range, but Kogito extends the DMN built-in types to support variables of ranges.

The syntax of a range is defined in the following formats:

range          := interval_start endpoint '..' endpoint interval_end
interval_start := open_start | closed_start
open_start     := '(' | ']'
closed_start   := '['
interval_end   := open_end | closed_end
open_end       := ')' | '['
closed_end     := ']'
endpoint       := expression

The expression for the endpoint must return a comparable value, and the lower bound endpoint must be lower than the upper bound endpoint.

For example, the following literal expression defines an interval between 1 and 10, including the boundaries (a closed interval on both endpoints):

[ 1 .. 10 ]

The following literal expression defines an interval between 1 hour and 12 hours, including the lower boundary (a closed interval), but excluding the upper boundary (an open interval):

[ duration("PT1H") .. duration("PT12H") )

You can use ranges in decision tables to test for ranges of values, or use ranges in simple literal expressions. For example, the following literal expression returns true if the value of a variable x is between 0 and 100:

x in [ 1 .. 100 ]
Lists

FEEL has list literals that you can use to create lists of items. A list in FEEL is represented by a comma-separated list of values enclosed in square brackets. The DMN specification currently does not provide an explicit way of declaring a variable as a list, but Kogito extends the DMN built-in types to support variables of lists.

Example:

[ 2, 3, 4, 5 ]

All lists in FEEL contain elements of the same type and are immutable. Elements in a list can be accessed by index, where the first element is 1. Negative indexes can access elements starting from the end of the list so that -1 is the last element.

For example, the following expression returns the second element of a list x:

x[2]

The following expression returns the second-to-last element of a list x:

x[-2]

Elements in a list can also be counted by the function count, which uses the list of elements as the parameter.

For example, the following expression returns 4:

count([ 2, 3, 4, 5 ])
Built-in functions in FEEL

To promote interoperability with other platforms and systems, Friendly Enough Expression Language (FEEL) includes a library of built-in functions. The built-in FEEL functions are implemented in the Drools Decision Model and Notation (DMN) engine so that you can use the functions in your DMN decision services.

The following sections describe each built-in FEEL function, listed in the format NAME( PARAMETERS ). For more information about FEEL functions in DMN, see the OMG Decision Model and Notation specification.

Conversion functions

The following functions support conversion between values of different types. Some of these functions use specific string formats, such as the following examples:

  • date string: Follows the format defined in the XML Schema Part 2: Datatypes document, such as 2020-06-01

  • time string: Follows one of the following formats:

    • Format defined in the XML Schema Part 2: Datatypes document, such as 23:59:00z

    • Format for a local time defined by ISO 8601 followed by @ and an IANA Timezone, such as 00:01:00@Etc/UTC

  • date time string: Follows the format of a date string followed by T and a time string, such as 2012-12-25T11:00:00Z

  • duration string: Follows the format of days and time duration and years and months duration defined in the XQuery 1.0 and XPath 2.0 Data Model, such as P1Y2M

date( from ) - using date

Converts from to a date value.

Table 11. Parameters
Parameter Type Format

from

string

date string

Example
date( "2012-12-25" ) - date( "2012-12-24" ) = duration( "P1D" )
date( from ) - using date and time

Converts from to a date value and sets time components to null.

Table 12. Parameters
Parameter Type

from

date and time

Example
date(date and time( "2012-12-25T11:00:00Z" )) = date( "2012-12-25" )
date( year, month, day )

Produces a date from the specified year, month, and day values.

Table 13. Parameters
Parameter Type

year

number

month

number

day

number

Example
date( 2012, 12, 25 ) = date( "2012-12-25" )
date and time( date, time )

Produces a date and time from the specified date and ignores any time components and the specified time.

Table 14. Parameters
Parameter Type

date

date or date and time

time

time

Example
date and time ( "2012-12-24T23:59:00" ) = date and time(date( "2012-12-24" ), time( "23:59:00" ))
date and time( from )

Produces a date and time from the specified string.

Table 15. Parameters
Parameter Type Format

from

string

date time string

Example
date and time( "2012-12-24T23:59:00" ) + duration( "PT1M" ) = date and time( "2012-12-25T00:00:00" )
time( from )

Produces a time from the specified string.

Table 16. Parameters
Parameter Type Format

from

string

time string

Example
time( "23:59:00z" ) + duration( "PT2M" ) = time( "00:01:00@Etc/UTC" )
time( from )

Produces a time from the specified parameter and ignores any date components.

Table 17. Parameters
Parameter Type

from

time or date and time

Example
time(date and time( "2012-12-25T11:00:00Z" )) = time( "11:00:00Z" )
time( hour, minute, second, offset? )

Produces a time from the specified hour, minute, and second component values.

Table 18. Parameters
Parameter Type

hour

number

minute

number

second

number

offset (Optional)

days and time duration or null

Example
time( "23:59:00z" ) = time(23, 59, 0, duration( "PT0H" ))
number( from, grouping separator, decimal separator )

Converts from to a number using the specified separators.

Table 19. Parameters
Parameter Type

from

string representing a valid number

grouping separator

Space ( ), comma (,), period (.), or null

decimal separator

Same types as grouping separator, but the values cannot match

Example
number( "1 000,0", " ", "," ) = number( "1,000.0", ",", "." )
string( from )

Provides a string representation of the specified parameter.

Table 20. Parameters
Parameter Type

from

Non-null value

Examples
string( 1.1 ) = "1.1"
string( null ) = null
duration( from )

Converts from to a days and time duration value or years and months duration value.

Table 21. Parameters
Parameter Type Format

from

string

duration string

Examples
date and time( "2012-12-24T23:59:00" ) - date and time( "2012-12-22T03:45:00" ) = duration( "P2DT20H14M" )
duration( "P2Y2M" ) = duration( "P26M" )
years and months duration( from, to )

Calculates the years and months duration between the two specified parameters.

Table 22. Parameters
Parameter Type

from

date or date and time

to

date or date and time

Example
years and months duration( date( "2011-12-22" ), date( "2013-08-24" ) ) = duration( "P1Y8M" )
Boolean functions

The following functions support Boolean operations.

not( negand )

Performs the logical negation of the negand operand.

Table 23. Parameters
Parameter Type

negand

boolean

Examples
not( true ) = false
not( null ) = null
String functions

The following functions support string operations.

In FEEL, Unicode characters are counted based on their code points.
substring( string, start position, length? )

Returns the substring from the start position for the specified length. The first character is at position value 1.

Table 24. Parameters
Parameter Type

string

string

start position

number

length (Optional)

number

Examples
substring( "testing",3 ) = "sting"
substring( "testing",3,3 ) = "sti"
substring( "testing", -2, 1 ) = "n"
substring( "\U01F40Eab", 2 ) = "ab"
In FEEL, the string literal "\U01F40Eab" is the 🐎ab string (horse symbol followed by a and b).
string length( string )

Calculates the length of the specified string.

Table 25. Parameters
Parameter Type

string

string

Examples
string length( "tes" ) = 3
string length( "\U01F40Eab" ) = 3
upper case( string )

Produces an uppercase version of the specified string.

Table 26. Parameters
Parameter Type

string

string

Example
upper case( "aBc4" ) = "ABC4"
lower case( string )

Produces a lowercase version of the specified string.

Table 27. Parameters
Parameter Type

string

string

Example
lower case( "aBc4" ) = "abc4"
substring before( string, match )

Calculates the substring before the match.

Table 28. Parameters
Parameter Type

string

string

match

string

Examples
substring before( "testing", "ing" ) = "test"
substring before( "testing", "xyz" ) = ""
substring after( string, match )

Calculates the substring after the match.

Table 29. Parameters
Parameter Type

string

string

match

string

Examples
substring after( "testing", "test" ) = "ing"
substring after( "", "a" ) = ""
replace( input, pattern, replacement, flags? )

Calculates the regular expression replacement.

Table 30. Parameters
Parameter Type

input

string

pattern

string

replacement

string

flags (Optional)

string

This function uses regular expression parameters as defined in XQuery 1.0 and XPath 2.0 Functions and Operators.
Example
replace( "banana", "a", "o" ) = "bonono"
replace( "abcd", "(ab)|(a)", "[1=$1][2=$2]" ) = "[1=ab][2=]cd"
contains( string, match )

Returns true if the string contains the match.

Table 31. Parameters
Parameter Type

string

string

match

string

Example
contains( "testing", "to" ) = false
starts with( string, match )

Returns true if the string starts with the match

Table 32. Parameters
Parameter Type

string

string

match

string

Example
starts with( "testing", "te" ) = true
ends with( string, match )

Returns true if the string ends with the match.

Table 33. Parameters
Parameter Type

string

string

match

string

Example
ends with( "testing", "g" ) = true
matches( input, pattern, flags? )

Returns true if the input matches the regular expression.

Table 34. Parameters
Parameter Type

input

string

pattern

string

flags (Optional)

string

This function uses regular expression parameters as defined in XQuery 1.0 and XPath 2.0 Functions and Operators.
Example
matches( "teeesting", "^te*sting" ) = true
split( string, delimiter )

Returns a list of the original string and splits it at the delimiter regular expression pattern.

Table 35. Parameters
Parameter Type

string

string

delimiter

string for a regular expression pattern

This function uses regular expression parameters as defined in XQuery 1.0 and XPath 2.0 Functions and Operators.
Examples
split( "John Doe", "\\s" ) = ["John", "Doe"]
split( "a;b;c;;", ";" ) = ["a","b","c","",""]
string join( list, delimiter )

Returns a string which is composed by joining all the string elements from the list parameter, separated by the delimiter. The delimiter can be an empty string. Null elements in the list parameter are ignored. If list is empty, the result is the empty string. If delimiter is null, the string elements are joined without a separator.

Table 36. Parameters
Parameter Type

list

list of string

delimiter

string

Examples
string join(["a","b","c"], "_and_") = "a_and_b_and_c"
string join(["a","b","c"], "") = "abc"
string join(["a","b","c"], null) = "abc"
string join(["a"], "X") = "a"
string join(["a",null,"c"], "X") = "aXc"
string join([], "X") = ""
string join( list )

Returns a string which is composed by joining all the string elements from the list parameter. Null elements in the list parameter are ignored. If list is empty, the result is the empty string.

Table 37. Parameters
Parameter Type

list

list of string

Examples
string join(["a","b","c"]) = "abc"
string join(["a",null,"c"]) = "ac"
string join([]) = ""
List functions

The following functions support list operations.

In FEEL, the index of the first element in a list is 1. The index of the last element in a list can be identified as -1.
list contains( list, element )

Returns true if the list contains the element.

Table 38. Parameters
Parameter Type

list

list

element

Any type, including null

Example
list contains( [1,2,3], 2 ) = true
count( list )

Counts the elements in the list.

Table 39. Parameters
Parameter Type

list

list

Examples
count( [1,2,3] ) = 3
count( [] ) = 0
count( [1,[2,3]] ) = 2
min( list )

Returns the minimum comparable element in the list.

Table 40. Parameters
Parameter Type

list

list

Alternative signature
min( e1, e2, ..., eN )
Examples
min( [1,2,3] ) = 1
min( 1 ) = 1
min( [1] ) = 1
max( list )

Returns the maximum comparable element in the list.

Table 41. Parameters
Parameter Type

list

list

Alternative signature
max( e1, e2, ..., eN )
Examples
max( 1,2,3 ) = 3
max( [] ) = null
sum( list )

Returns the sum of the numbers in the list.

Table 42. Parameters
Parameter Type

list

list of number elements

Alternative signature
sum( n1, n2, ..., nN )
Examples
sum( [1,2,3] ) = 6
sum( 1,2,3 ) = 6
sum( 1 ) = 1
sum( [] ) = null
mean( list )

Calculates the average (arithmetic mean) of the elements in the list.

Table 43. Parameters
Parameter Type

list

list of number elements

Alternative signature
mean( n1, n2, ..., nN )
Examples
mean( [1,2,3] ) = 2
mean( 1,2,3 ) = 2
mean( 1 ) = 1
mean( [] ) = null
all( list )

Returns true if all elements in the list are true.

Table 44. Parameters
Parameter Type

list

list of boolean elements

Alternative signature
all( b1, b2, ..., bN )
Examples
all( [false,null,true] ) = false
all( true ) = true
all( [true] ) = true
all( [] ) = true
all( 0 ) = null
any( list )

Returns true if any element in the list is true.

Table 45. Parameters
Parameter Type

list

list of boolean elements

Alternative signature
any( b1, b2, ..., bN )
Examples
any( [false,null,true] ) = true
any( false ) = false
any( [] ) = false
any( 0 ) = null
sublist( list, start position, length? )

Returns the sublist from the start position, limited to the length elements.

Table 46. Parameters
Parameter Type

list

list

start position

number

length (Optional)

number

Example
sublist( [4,5,6], 1, 2 ) = [4,5]
append( list, item )

Creates a list that is appended to the item or items.

Table 47. Parameters
Parameter Type

list

list

item

Any type

Example
append( [1], 2, 3 ) = [1,2,3]
concatenate( list )

Creates a list that is the result of the concatenated lists.

Table 48. Parameters
Parameter Type

list

list

Example
concatenate( [1,2],[3] ) = [1,2,3]
insert before( list, position, newItem )

Creates a list with the newItem inserted at the specified position.

Table 49. Parameters
Parameter Type

list

list

position

number

newItem

Any type

Example
insert before( [1,3],1,2 ) = [2,1,3]
remove( list, position )

Creates a list with the removed element excluded from the specified position.

Table 50. Parameters
Parameter Type

list

list

position

number

Example
remove( [1,2,3], 2 ) = [1,3]
reverse( list )

Returns a reversed list.

Table 51. Parameters
Parameter Type

list

list

Example
reverse( [1,2,3] ) = [3,2,1]
index of( list, match )

Returns indexes matching the element.

Parameters
  • list of type list

  • match of any type

Table 52. Parameters
Parameter Type

list

list

match

Any type

Example
index of( [1,2,3,2],2 ) = [2,4]
union( list )

Returns a list of all the elements from multiple lists and excludes duplicates.

Table 53. Parameters
Parameter Type

list

list

Example
union( [1,2],[2,3] ) = [1,2,3]
distinct values( list )

Returns a list of elements from a single list and excludes duplicates.

Table 54. Parameters
Parameter Type

list

list

Example
distinct values( [1,2,3,2,1] ) = [1,2,3]
flatten( list )

Returns a flattened list.

Table 55. Parameters
Parameter Type

list

list

Example
flatten( [[1,2],[[3]], 4] ) = [1,2,3,4]
product( list )

Returns the product of the numbers in the list.

Table 56. Parameters
Parameter Type

list

list of number elements

Alternative signature
product( n1, n2, ..., nN )
Examples
product( [2, 3, 4] ) = 24
product( [] ) = null
product( 2, 3, 4 ) = 24
median( list )

Returns the median of the numbers in the list. If the number of elements is odd, the result is the middle element. If the number of elements is even, the result is the average of the two middle elements.

Table 57. Parameters
Parameter Type

list

list of number elements

Alternative signature
median( n1, n2, ..., nN )
Examples
median( 8, 2, 5, 3, 4 ) = 4
median( [6, 1, 2, 3] ) = 2.5
median( [ ] ) = null
stddev( list )

Returns the standard deviation of the numbers in the list.

Table 58. Parameters
Parameter Type

list

list of number elements

Alternative signature
stddev( n1, n2, ..., nN )
Examples
stddev( 2, 4, 7, 5 ) = 2.081665999466132735282297706979931
stddev( [47] ) = null
stddev( 47 ) = null
stddev( [ ] ) = null
mode( list )

Returns the mode of the numbers in the list. If multiple elements are returned, the numbers are sorted in ascending order.

Table 59. Parameters
Parameter Type

list

list of number elements

Alternative signature
mode( n1, n2, ..., nN )
Examples
mode( 6, 3, 9, 6, 6 ) = [6]
mode( [6, 1, 9, 6, 1] ) = [1, 6]
mode( [ ] ) = [ ]
Numeric functions

The following functions support number operations.

decimal( n, scale )

Returns a number with the specified scale.

Table 60. Parameters
Parameter Type

n

number

scale

number in the range [−6111..6176]

This function is implemented to be consistent with the FEEL:number definition for rounding decimal numbers to the nearest even decimal number.
Examples
decimal( 1/3, 2 ) = .33
decimal( 1.5, 0 ) = 2
decimal( 2.5, 0 ) = 2
decimal( 1.035, 2 ) = 1.04
decimal( 1.045, 2 ) = 1.04
decimal( 1.055, 2 ) = 1.06
decimal( 1.065, 2 ) = 1.06
floor( n )

Returns n with given scale and rounding mode flooring. If at least one of n or scale is null, the result is null.

Table 61. Parameters
Parameter Type

n

number

Alternative signature
floor( n, scale )
Examples
floor( 1.5 ) = 1
floor( -1.56, 1 ) = -1.6
floor( -1.5 ) = -2
ceiling( n )

Returns n with given scale and rounding mode ceiling. If at least one of n or scale is null, the result is null.

Table 62. Parameters
Parameter Type

n

number

Alternative signature
ceiling( n, scale )
Examples
ceiling( 1.5 ) = 2
ceiling( -1.56, 1 ) = -1.5
ceiling( -1.5 ) = -1
round up( n, scale )

Returns n with given scale and rounding mode round up. If at least one of n or scale is null, the result is null.

Table 63. Parameters
Parameter Type

n

number

scale

number

Examples
round up( 5.5, 0 ) = 6
round up( -5.5, 0 ) = -6
round up( 1.121, 2 ) = 1.13
round up( -1.126, 2 ) = -1.13
round down( n, scale )

Returns n with given scale and rounding mode round down. If at least one of n or scale is null, the result is null.

Table 64. Parameters
Parameter Type

n

number

scale

number

Examples
round down( 5.5, 0 ) = 5
round down( -5.5, 0 ) = -5
round down( 1.121, 2 ) = 1.12
round down( -1.126, 2 ) = -1.12
round half up( n, scale )

Returns n with given scale and rounding mode round half up. If at least one of n or scale is null, the result is null.

Table 65. Parameters
Parameter Type

n

number

scale

number

Examples
round half up( 5.5, 0 ) = 6
round half up( -5.5, 0 ) = -6
round half up( 1.121, 2 ) = 1.12
round half up( -1.126, 2 ) = -1.13
round half down( n, scale )

Returns n with given scale and rounding mode round half down. If at least one of n or scale is null, the result is null.

Table 66. Parameters
Parameter Type

n

number

scale

number

Examples
round half down( 5.5, 0 ) = 5
round half down( -5.5, 0 ) = -5
round half down( 1.121, 2 ) = 1.12
round half down( -1.126, 2 ) = -1.13
abs( n )

Returns the absolute value.

Table 67. Parameters
Parameter Type

n

number, days and time duration, or years and months duration

Examples
abs( 10 ) = 10
abs( -10 ) = 10
abs( @"PT5H" ) = @"PT5H"
abs( @"-PT5H" ) = @"PT5H"
modulo( dividend, divisor )

Returns the remainder of the division of the dividend by the divisor. If either the dividend or divisor is negative, the result is of the same sign as the divisor.

This function is also expressed as modulo(dividend, divisor) = dividend - divisor*floor(dividen d/divisor).
Table 68. Parameters
Parameter Type

dividend

number

divisor

number

Examples
modulo( 12, 5 ) = 2
modulo( -12,5 )= 3
modulo( 12,-5 )= -3
modulo( -12,-5 )= -2
modulo( 10.1, 4.5 )= 1.1
modulo( -10.1, 4.5 )= 3.4
modulo( 10.1, -4.5 )= -3.4
modulo( -10.1, -4.5 )= -1.1
sqrt( number )

Returns the square root of the specified number.

Table 69. Parameters
Parameter Type

n

number

Example
sqrt( 16 ) = 4
log( number )

Returns the logarithm of the specified number.

Table 70. Parameters
Parameter Type

n

number

Example
decimal( log( 10 ), 2 ) = 2.30
exp( number )

Returns Euler’s number e raised to the power of the specified number.

Table 71. Parameters
Parameter Type

n

number

Example
decimal( exp( 5 ), 2 ) = 148.41
odd( number )

Returns true if the specified number is odd.

Table 72. Parameters
Parameter Type

n

number

Examples
odd( 5 ) = true
odd( 2 ) = false
even( number )

Returns true if the specified number is even.

Table 73. Parameters
Parameter Type

n

number

Examples
even( 5 ) = false
even ( 2 ) = true
Date and time functions

The following functions support date and time operations.

is( value1, value2 )

Returns true if both values are the same element in the FEEL semantic domain.

Table 74. Parameters
Parameter Type

value1

Any type

value2

Any type

Examples
is( date( "2012-12-25" ), time( "23:00:50" ) ) = false
is( date( "2012-12-25" ), date( "2012-12-25" ) ) = true
is( time( "23:00:50z" ), time( "23:00:50" ) ) = false
is( time( "23:00:50z" ), time( "23:00:50+00:00" ) ) = true
Range functions

The following functions support temporal ordering operations to establish relationships between single scalar values and ranges of such values. These functions are similar to the components in the Health Level Seven (HL7) International Clinical Quality Language (CQL) 1.4 syntax.

before( )

Returns true when an element A is before an element B and when the relevant requirements for evaluating to true are also met.

Signatures
  1. before( point1 point2 )

  2. before( point range )

  3. before( range point )

  4. before( range1,range2 )

Requirements for evaluating to true
  1. point1 < point2

  2. point < range.start or ( point = range.start and not(range.start included) )

  3. range.end < point or ( range.end = point and not(range.end included) )

  4. range1.end < range2.start or (( not(range1.end included) or not(range2.start included) ) and range1.end = range2.start )

Examples
before( 1, 10 ) = true
before( 10, 1 ) = false
before( 1, [1..10] ) = false
before( 1, (1..10] ) = true
before( 1, [5..10] ) = true
before( [1..10], 10 ) = false
before( [1..10), 10 ) = true
before( [1..10], 15 ) = true
before( [1..10], [15..20] ) = true
before( [1..10], [10..20] ) = false
before( [1..10), [10..20] ) = true
before( [1..10], (10..20] ) = true
after( )

Returns true when an element A is after an element B and when the relevant requirements for evaluating to true are also met.

Signatures
  1. after( point1 point2 )

  2. after( point range )

  3. after( range, point )

  4. after( range1 range2 )

Requirements for evaluating to true
  1. point1 > point2

  2. point > range.end or ( point = range.end and not(range.end included) )

  3. range.start > point or ( range.start = point and not(range.start included) )

  4. range1.start > range2.end or (( not(range1.start included) or not(range2.end included) ) and range1.start = range2.end )

Examples
after( 10, 5 ) = true
after( 5, 10 ) = false
after( 12, [1..10] ) = true
after( 10, [1..10) ) = true
after( 10, [1..10] ) = false
after( [11..20], 12 ) = false
after( [11..20], 10 ) = true
after( (11..20], 11 ) = true
after( [11..20], 11 ) = false
after( [11..20], [1..10] ) = true
after( [1..10], [11..20] ) = false
after( [11..20], [1..11) ) = true
after( (11..20], [1..11] ) = true
meets( )

Returns true when an element A meets an element B and when the relevant requirements for evaluating to true are also met.

Signatures
  1. meets( range1, range2 )

Requirements for evaluating to true
  1. range1.end included and range2.start included and range1.end = range2.start

Examples
meets( [1..5], [5..10] ) = true
meets( [1..5), [5..10] ) = false
meets( [1..5], (5..10] ) = false
meets( [1..5], [6..10] ) = false
met by( )

Returns true when an element A is met by an element B and when the relevant requirements for evaluating to true are also met.

Signatures
  1. met by( range1, range2 )

Requirements for evaluating to true
  1. range1.start included and range2.end included and range1.start = range2.end

Examples
met by( [5..10], [1..5] ) = true
met by( [5..10], [1..5) ) = false
met by( (5..10], [1..5] ) = false
met by( [6..10], [1..5] ) = false
overlaps( )

Returns true when an element A overlaps an element B and when the relevant requirements for evaluating to true are also met.

Signatures
  1. overlaps( range1, range2 )

Requirements for evaluating to true
  1. ( range1.end > range2.start or (range1.end = range2.start and range1.end included and range2.end included) ) and ( range1.start < range2.end or (range1.start = range2.end and range1.start included and range2.end included) )

Examples
overlaps( [1..5], [3..8] ) = true
overlaps( [3..8], [1..5] ) = true
overlaps( [1..8], [3..5] ) = true
overlaps( [3..5], [1..8] ) = true
overlaps( [1..5], [6..8] ) = false
overlaps( [6..8], [1..5] ) = false
overlaps( [1..5], [5..8] ) = true
overlaps( [1..5], (5..8] ) = false
overlaps( [1..5), [5..8] ) = false
overlaps( [1..5), (5..8] ) = false
overlaps( [5..8], [1..5] ) = true
overlaps( (5..8], [1..5] ) = false
overlaps( [5..8], [1..5) ) = false
overlaps( (5..8], [1..5) ) = false
overlaps before( )

Returns true when an element A overlaps before an element B and when the relevant requirements for evaluating to true are also met.

Signatures
  1. overlaps before( range1 range2 )

Requirements for evaluating to true
  1. ( range1.start < range2.start or (range1.start = range2.start and range1.start included and not(range2.start included)) ) and ( range1.end > range2.start or (range1.end = range2.start and range1.end included and range2.start included) ) and ( range1.end < range2.end or (range1.end = range2.end and (not(range1.end included) or range2.end included )) )

Examples
overlaps before( [1..5], [3..8] ) = true
overlaps before( [1..5], [6..8] ) = false
overlaps before( [1..5], [5..8] ) = true
overlaps before( [1..5], (5..8] ) = false
overlaps before( [1..5), [5..8] ) = false
overlaps before( [1..5), (1..5] ) = true
overlaps before( [1..5], (1..5] ) = true
overlaps before( [1..5), [1..5] ) = false
overlaps before( [1..5], [1..5] ) = false
overlaps after( )

Returns true when an element A overlaps after an element B and when the relevant requirements for evaluating to true are also met.

Signatures
  1. overlaps after( range1 range2 )

Requirements for evaluating to true
  1. ( range2.start < range1.start or (range2.start = range1.start and range2.start included and not( range1.start included)) ) and ( range2.end > range1.start or (range2.end = range1.start and range2.end included and range1.start included) ) and ( range2.end < range1.end or (range2.end = range1.end and (not(range2.end included) or range1.end included)) )

Examples
overlaps after( [3..8], [1..5] )= true
overlaps after( [6..8], [1..5] )= false
overlaps after( [5..8], [1..5] )= true
overlaps after( (5..8], [1..5] )= false
overlaps after( [5..8], [1..5) )= false
overlaps after( (1..5], [1..5) )= true
overlaps after( (1..5], [1..5] )= true
overlaps after( [1..5], [1..5) )= false
overlaps after( [1..5], [1..5] )= false
overlaps after( (1..5), [1..5] )= false
overlaps after( (1..5], [1..6] )= false
overlaps after( (1..5], (1..5] )= false
overlaps after( (1..5], [2..5] )= false
finishes( )

Returns true when an element A finishes an element B and when the relevant requirements for evaluating to true are also met.

Signatures
  1. finishes( point, range )

  2. finishes( range1, range2 )

Requirements for evaluating to true
  1. range.end included and range.end = point

  2. range1.end included = range2.end included and range1.end = range2.end and ( range1.start > range2.start or (range1.start = range2.start and (not(range1.start included) or range2.start included)) )

Examples
finishes( 10, [1..10] ) = true
finishes( 10, [1..10) ) = false
finishes( [5..10], [1..10] ) = true
finishes( [5..10), [1..10] ) = false
finishes( [5..10), [1..10) ) = true
finishes( [1..10], [1..10] ) = true
finishes( (1..10], [1..10] ) = true
finished by( )

Returns true when an element A is finished by an element B and when the relevant requirements for evaluating to true are also met.

Signatures
  1. finished by( range, point )

  2. finished by( range1 range2 )

Requirements for evaluating to true
  1. range.end included and range.end = point

  2. range1.end included = range2.end included and range1.end = range2.end and ( range1.start < range2.start or (range1.start = range2.start and (range1.start included or not(range2.start included))) )

Examples
finished by( [1..10], 10 ) = true
finished by( [1..10), 10 ) = false
finished by( [1..10], [5..10] ) = true
finished by( [1..10], [5..10) ) = false
finished by( [1..10), [5..10) ) = true
finished by( [1..10], [1..10] ) = true
finished by( [1..10], (1..10] ) = true
includes( )

Returns true when an element A includes an element B and when the relevant requirements for evaluating to true are also met.

Signatures
  1. includes( range, point )

  2. includes( range1, range2 )

Requirements for evaluating to true
  1. (range.start < point and range.end > point) or (range.start = point and range.start included) or (range.end = point and range.end included)

  2. ( range1.start < range2.start or (range1.start = range2.start and (range1.start included or not(range2.start included))) ) and ( range1.end > range2.end or (range1.end = range2.end and (range1.end included or not(range2.end included))) )

Examples
includes( [1..10], 5 ) = true
includes( [1..10], 12 ) = false
includes( [1..10], 1 ) = true
includes( [1..10], 10 ) = true
includes( (1..10], 1 ) = false
includes( [1..10), 10 ) = false
includes( [1..10], [4..6] ) = true
includes( [1..10], [1..5] ) = true
includes( (1..10], (1..5] ) = true
includes( [1..10], (1..10) ) = true
includes( [1..10), [5..10) ) = true
includes( [1..10], [1..10) ) = true
includes( [1..10], (1..10] ) = true
includes( [1..10], [1..10] ) = true
during( )

Returns true when an element A is during an element B and when the relevant requirements for evaluating to true are also met.

Signatures
  1. during( point, range )

  2. during( range1 range2 )

Requirements for evaluating to true
  1. (range.start < point and range.end > point) or (range.start = point and range.start included) or (range.end = point and range.end included)

  2. ( range2.start < range1.start or (range2.start = range1.start and (range2.start included or not(range1.start included))) ) and ( range2.end > range1.end or (range2.end = range1.end and (range2.end included or not(range1.end included))) )

Examples
during( 5, [1..10] ) = true
during( 12, [1..10] ) = false
during( 1, [1..10] ) = true
during( 10, [1..10] ) = true
during( 1, (1..10] ) = false
during( 10, [1..10) ) = false
during( [4..6], [1..10] ) = true
during( [1..5], [1..10] ) = true
during( (1..5], (1..10] ) = true
during( (1..10), [1..10] ) = true
during( [5..10), [1..10) ) = true
during( [1..10), [1..10] ) = true
during( (1..10], [1..10] ) = true
during( [1..10], [1..10] ) = true
starts( )

Returns true when an element A starts an element B and when the relevant requirements for evaluating to true are also met.

Signatures
  1. starts( point, range )

  2. starts( range1, range2 )

Requirements for evaluating to true
  1. range.start = point and range.start included

  2. range1.start = range2.start and range1.start included = range2.start included and ( range1.end < range2.end or (range1.end = range2.end and (not(range1.end included) or range2.end included)) )

Examples
starts( 1, [1..10] ) = true
starts( 1, (1..10] ) = false
starts( 2, [1..10] ) = false
starts( [1..5], [1..10] ) = true
starts( (1..5], (1..10] ) = true
starts( (1..5], [1..10] ) = false
starts( [1..5], (1..10] ) = false
starts( [1..10], [1..10] ) = true
starts( [1..10), [1..10] ) = true
starts( (1..10), (1..10) ) = true
started by( )

Returns true when an element A is started by an element B and when the relevant requirements for evaluating to true are also met.

Signatures
  1. started by( range, point )

  2. started by( range1, range2 )

Requirements for evaluating to true
  1. range.start = point and range.start included

  2. range1.start = range2.start and range1.start included = range2.start included and ( range2.end < range1.end or (range2.end = range1.end and (not(range2.end included) or range1.end included)) )

Examples
started by( [1..10], 1 ) = true
started by( (1..10], 1 ) = false
started by( [1..10], 2 ) = false
started by( [1..10], [1..5] ) = true
started by( (1..10], (1..5] ) = true
started by( [1..10], (1..5] ) = false
started by( (1..10], [1..5] ) = false
started by( [1..10], [1..10] ) = true
started by( [1..10], [1..10) ) = true
started by( (1..10), (1..10) ) = true
coincides( )

Returns true when an element A coincides with an element B and when the relevant requirements for evaluating to true are also met.

Signatures
  1. coincides( point1, point2 )

  2. coincides( range1, range2 )

Requirements for evaluating to true
  1. point1 = point2

  2. range1.start = range2.start and range1.start included = range2.start included and range1.end = range2.end and range1.end included = range2.end included

Examples
coincides( 5, 5 ) = true
coincides( 3, 4 ) = false
coincides( [1..5], [1..5] ) = true
coincides( (1..5), [1..5] ) = false
coincides( [1..5], [2..6] ) = false
Temporal functions

The following functions support general temporal operations.

day of year( date )

Returns the Gregorian number of the day of the year.

Table 75. Parameters
Parameter Type

date

date or date and time

Example
day of year( date(2019, 9, 17) ) = 260
day of week( date )

Returns the Gregorian day of the week: "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", or "Sunday".

Table 76. Parameters
Parameter Type

date

date or date and time

Example
day of week( date(2019, 9, 17) ) = "Tuesday"
month of year( date )

Returns the Gregorian month of the year: "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", or "December".

Table 77. Parameters
Parameter Type

date

date or date and time

Example
month of year( date(2019, 9, 17) ) = "September"
month of year( date )

Returns the Gregorian week of the year as defined by ISO 8601.

Table 78. Parameters
Parameter Type

date

date or date and time

Examples
week of year( date(2019, 9, 17) ) = 38
week of year( date(2003, 12, 29) ) = 1
week of year( date(2004, 1, 4) ) = 1
week of year( date(2005, 1, 1) ) = 53
week of year( date(2005, 1, 3) ) = 1
week of year( date(2005, 1, 9) ) = 1
Sort functions

The following functions support sorting operations.

sort( list, precedes )

Returns a list of the same elements but ordered according to the sorting function.

Table 79. Parameters
Parameter Type

list

list

precedes

function

Example
sort( list: [3,1,4,5,2], precedes: function(x,y) x < y ) = [1,2,3,4,5]
Context functions

The following functions support context operations.

get value( m, key )

Returns the value from the context for the specified entry key.

Table 80. Parameters
Parameter Type

m

context

key

string

Examples
get value( {key1 : "value1"}, "key1" ) = "value1"
get value( {key1 : "value1"}, "unexistent-key" ) = null
get entries( m )

Returns a list of key-value pairs for the specified context.

Table 81. Parameters
Parameter Type

m

context

Example
get entries( {key1 : "value1", key2 : "value2"} ) = [ { key : "key1", value : "value1" }, {key : "key2", value : "value2"} ]
context( entries )

Returns a new context that includes all specified entries. If a context item contains additional entries beyond the required "key" and "value" entries, the additional entries are ignored. If a context item is missing the required "key" and "value" entries, the final result is null. See also: get entries() built-in function.

Table 82. Parameters
Parameter Type

entries

list of context , each item SHALL have two entries having keys named "key" and "value" respectively

Examples
context([{key:"a", value:1}, {key:"b", value:2}]) = {a:1, b:2}
context([{key:"a", value:1}, {key:"b", value:2, something: "else"}]) = {a:1, b:2}
context([{key:"a", value:1}, {key:"b"}]) = null
context put( context, key, value )

Returns a new context that includes the new entry, or overrides the existing value if an entry for the same key already exists in the supplied context parameter. A new entry is added as the last entry of the new context. If overriding an existing entry, the order of the keys maintains the same order as in the original context.

Table 83. Parameters
Parameter Type

context

context

key

string

value

Any type

Examples
context put({x:1}, "y", 2) = {x:1, y:2}
context put({x:1, y:0}, "y", 2) = {x:1, y:2}
context put({x:1, y:0, z:0}, "y", 2) = {x:1, y:2, z:0}
context put( context, keys, value )

Returns the composite of nested invocations to context put() for each item in keys hierarchy in context.

If keys is a list of 1 element, this is equivalent to context put(context, key', value), where key' is the only element in the list keys.

If keys is a list of 2 or more elements, this is equivalent of calling context put(context, key', value'), with:
key' is the head element in the list keys,
value' is the result of invocation of context put(context', keys', value), where:
context' is the result of context.key',
keys' is the remainder of the list keys without the head element key'.

If keys is an empty list or null, the result is null.

Table 84. Parameters
Parameter Type

context

context

keys

list of string

value

Any type

Examples
context put({x:1}, ["y"], 2) = context put({x:1}, "y", 2)
context put({x:1}, ["y"], 2) = {x:1, y:2}
context put({x:1, y: {a: 0} }, ["y", "a"], 2) = context put({x:1, y: {a: 0} }, "y", context put({a: 0}, ["a"], 2))
context put({x:1, y: {a: 0} }, ["y", "a"], 2) = {x:1, y: {a: 2} }
context put({x:1, y: {a: 0} }, [], 2) = null
context merge( contexts )

Returns a new context that includes all entries from the given contexts; if some of the keys are equal, the entries are overridden. The entries are overridden in the same order as specified by the supplied parameter, with new entries added as the last entry in the new context.

Table 85. Parameters
Parameter Type

contexts

list of context

Examples
context merge([{x:1}, {y:2}]) = {x:1, y:2}
context merge([{x:1, y:0}, {y:2}]) = {x:1, y:2}
Miscellaneous functions

These functions provide support utilities for several miscellaneous use-cases. For example, when a decision depends on the current date, like deciding the support SLA over the weekends, additional charges for weekend delivery, etc.

It is important to note that the functions in this section are intended to be side-effect-free, but they are not deterministic and not idempotent from the perspective of an external observer.

As a user, you are encouraged to isolate the decision logic that uses these functions in specific DRG elements, such as Decisions. The encapsulation enables them to be overridden with synthetic values during scenario testing, that remain constant across executions of the DMN model’s test cases.

now()

Returns the current date and time.

Examples
now()
today()

Returns the current date.

Examples
today()
KIE Extended functions

These functions are provided as an extension to the DMN Standard to enable support for various use-cases on top of the Drools DMN Engine.

invoke( namespace, modelName, decisionName, parameters )

Returns the result of the decision evaluation in the specified DMN model available to the DMNRuntime environment in the current DMN model is executed.

This function is deprecated in favor of encouraging the usage of DMN Standard capabilities wherever possible; since DMNv1.2 it shall be possible to use the DMN standard’s Import functionality to import Business Knowledge Model (BKM) nodes and Decision Service nodes, to be invoked from another model.

Table 86. Parameters
Parameter Type

namespace

string

modelName

string

decisionName

string

parameter

context

Examples
invoke(
    "http://namespace_of_model",
    "my model name",
    "my decision name",
    { a:1, b:2 }
)
4.1.2.4. Variable and function names in FEEL

Unlike many traditional expression languages, Friendly Enough Expression Language (FEEL) supports spaces and a few special characters as part of variable and function names. A FEEL name must start with a letter, ?, or _ element. The unicode letter characters are also allowed. Variable names cannot start with a language keyword, such as and, true, or every. The remaining characters in a variable name can be any of the starting characters, as well as digits, white spaces, and special characters such as +, -, /, *, ', and ..

For example, the following names are all valid FEEL names:

  • Age

  • Birth Date

  • Flight 234 pre-check procedure

Several limitations apply to variable and function names in FEEL:

Ambiguity

The use of spaces, keywords, and other special characters as part of names can make FEEL ambiguous. The ambiguities are resolved in the context of the expression, matching names from left to right. The parser resolves the variable name as the longest name matched in scope. You can use ( ) to disambiguate names if necessary.

Spaces in names

The DMN specification limits the use of spaces in FEEL names. According to the DMN specification, names can contain multiple spaces but not two consecutive spaces.

In order to make the language easier to use and avoid common errors due to spaces, Kogito removes the limitation on the use of consecutive spaces. Kogito supports variable names with any number of consecutive spaces, but normalizes them into a single space. For example, the variable references First Name with one space and First Name with two spaces are both acceptable in Kogito.

Kogito also normalizes the use of other white spaces, like the non-breakable white space that is common in web pages, tabs, and line breaks. From a Kogito FEEL engine perspective, all of these characters are normalized into a single white space before processing.

The keyword in

The keyword in is the only keyword in the language that cannot be used as part of a variable name. Although the specifications allow the use of keywords in the middle of variable names, the use of in in variable names conflicts with the grammar definition of for, every and some expression constructs.

4.1.2.5. DMN decision logic in boxed expressions

Boxed expressions in DMN are tables that you use to define the underlying logic of decision nodes and business knowledge models in a decision requirements diagram (DRD). Some boxed expressions can contain other boxed expressions, but the top-level boxed expression corresponds to the decision logic of a single DRD artifact. While DRDs represent the flow of a DMN decision model, boxed expressions define the actual decision logic of individual nodes. DRDs and boxed expressions together form a complete and functional DMN decision model.

The following are the types of DMN boxed expressions:

  • Decision tables

  • Literal expressions

  • Contexts

  • Relations

  • Functions

  • Invocations

  • Lists

All Friendly Enough Expression Language (FEEL) expressions that you use in your boxed expressions must conform to the FEEL syntax requirements in the OMG Decision Model and Notation specification.

DMN decision tables

A decision table in DMN is a visual representation of one or more business rules in a tabular format. You use decision tables to define rules for a decision node that applies those rules at a given point in the decision model. Each rule consists of a single row in the table, and includes columns that define the conditions (input) and outcome (output) for that particular row. The definition of each row is precise enough to derive the outcome using the values of the conditions. Input and output values can be FEEL expressions or defined data type values.

For example, the following decision table determines credit score ratings based on a defined range of a loan applicant’s credit score:

dmn decision table example
Figure 72. Decision table for credit score rating

The following decision table determines the next step in a lending strategy for applicants depending on applicant loan eligibility and the bureau call type:

dmn decision table example2
Figure 73. Decision table for lending strategy

The following decision table determines applicant qualification for a loan as the concluding decision node in a loan prequalification decision model:

dmn decision table example3
Figure 74. Decision table for loan prequalification

Decision tables are a popular way of modeling rules and decision logic, and are used in many methodologies (such as DMN) and implementation frameworks (such as Drools).

Kogito supports both DMN decision tables and Drools-native decision tables, but they are different types of assets with different syntax requirements and are not interchangeable. For more information about Drools-native decision tables in Kogito, see Spreadsheet decision tables.
Hit policies in DMN decision tables

Hit policies determine how to reach an outcome when multiple rules in a decision table match the provided input values. For example, if one rule in a decision table applies a sales discount to military personnel and another rule applies a discount to students, then when a customer is both a student and in the military, the decision table hit policy must indicate whether to apply one discount or the other (Unique, First) or both discounts (Collect Sum). You specify the single character of the hit policy (U, F, C+) in the upper-left corner of the decision table.

The following decision table hit policies are supported in DMN:

  • Unique (U): Permits only one rule to match. Any overlap raises an error.

  • Any (A): Permits multiple rules to match, but they must all have the same output. If multiple matching rules do not have the same output, an error is raised.

  • Priority (P): Permits multiple rules to match, with different outputs. The output that comes first in the output values list is selected.

  • First (F): Uses the first match in rule order.

  • Collect (C+, C>, C<, C#): Aggregates output from multiple rules based on an aggregation function.

    • Collect ( C ): Aggregates values in an arbitrary list.

    • Collect Sum (C+): Outputs the sum of all collected values. Values must be numeric.

    • Collect Min (C<): Outputs the minimum value among the matches. The resulting values must be comparable, such as numbers, dates, or text (lexicographic order).

    • Collect Max (C>): Outputs the maximum value among the matches. The resulting values must be comparable, such as numbers, dates or text (lexicographic order).

    • Collect Count (C#): Outputs the number of matching rules.

Boxed literal expressions

A boxed literal expression in DMN is a literal FEEL expression as text in a table cell, typically with a labeled column and an assigned data type. You use boxed literal expressions to define simple or complex node logic or decision data directly in FEEL for a particular node in a decision. Literal FEEL expressions must conform to FEEL syntax requirements in the OMG Decision Model and Notation specification.

For example, the following boxed literal expression defines the minimum acceptable PITI calculation (principal, interest, taxes, and insurance) in a lending decision, where acceptable rate is a variable defined in the DMN model:

dmn literal expression example2
Figure 75. Boxed literal expression for minimum PITI value

The following boxed literal expression sorts a list of possible dating candidates (soul mates) in an online dating application based on their score on criteria such as age, location, and interests:

dmn literal expression example3b
Figure 76. Boxed literal expression for matching online dating candidates
Boxed context expressions

A boxed context expression in DMN is a set of variable names and values with a result value. Each name-value pair is a context entry. You use context expressions to represent data definitions in decision logic and set a value for a desired decision element within the DMN decision model. A value in a boxed context expression can be a data type value or FEEL expression, or can contain a nested sub-expression of any type, such as a decision table, a literal expression, or another context expression.

For example, the following boxed context expression defines the factors for sorting delayed passengers in a flight-rebooking decision model, based on defined data types (tPassengerTable, tFlightNumberList):

dmn context expression example
Figure 77. Boxed context expression for flight passenger waiting list

The following boxed context expression defines the factors that determine whether a loan applicant can meet minimum mortgage payments based on principal, interest, taxes, and insurance (PITI), represented as a front-end ratio calculation with a sub-context expression:

dmn context expression example2
Figure 78. Boxed context expression for front-end client PITI ratio
Boxed relation expressions

A boxed relation expression in DMN is a traditional data table with information about given entities, listed as rows. You use boxed relation tables to define decision data for relevant entities in a decision at a particular node. Boxed relation expressions are similar to context expressions in that they set variable names and values, but relation expressions contain no result value and list all variable values based on a single defined variable in each column.

For example, the following boxed relation expression provides information about employees in an employee rostering decision:

dmn relation expression example
Figure 79. Boxed relation expression with employee information
Boxed function expressions

A boxed function expression in DMN is a parameterized boxed expression containing a literal FEEL expression, a nested context expression of an external JAVA or PMML function, or a nested boxed expression of any type. By default, all business knowledge models are defined as boxed function expressions. You use boxed function expressions to call functions on your decision logic and to define all business knowledge models.

For example, the following boxed function expression determines airline flight capacity in a flight-rebooking decision model:

dmn function expression example
Figure 80. Boxed function expression for flight capacity

The following boxed function expression contains a basic Java function as a context expression for determining absolute value in a decision model calculation:

dmn function expression example2
Figure 81. Boxed function expression for absolute value

The following boxed function expression determines a monthly mortgage installment as a business knowledge model in a lending decision, with the function value defined as a nested context expression:

dmn function expression example3
Figure 82. Boxed function expression for installment calculation in business knowledge model

The following boxed function expression uses a PMML model included in the DMN file to define the minimum acceptable PITI calculation (principal, interest, taxes, and insurance) in a lending decision:

Boxed invocation expressions

A boxed invocation expression in DMN is a boxed expression that invokes a business knowledge model. A boxed invocation expression contains the name of the business knowledge model to be invoked and a list of parameter bindings. Each binding is represented by two boxed expressions on a row: The box on the left contains the name of a parameter and the box on the right contains the binding expression whose value is assigned to the parameter to evaluate the invoked business knowledge model. You use boxed invocations to invoke at a particular decision node a business knowledge model defined in the decision model.

For example, the following boxed invocation expression invokes a Reassign Next Passenger business knowledge model as the concluding decision node in a flight-rebooking decision model:

dmn invocation example
Figure 83. Boxed invocation expression to reassign flight passengers

The following boxed invocation expression invokes an InstallmentCalculation business knowledge model to calculate a monthly installment amount for a loan before proceeding to affordability decisions:

dmn invocation example2
Figure 84. Boxed invocation expression for required monthly installment
Boxed list expressions

A boxed list expression in DMN represents a FEEL list of items. You use boxed lists to define lists of relevant items for a particular node in a decision. You can also use literal FEEL expressions for list items in cells to create more complex lists.

For example, the following boxed list expression identifies approved credit score agencies in a loan application decision service:

dmn list expression example
Figure 85. Boxed list expression for approved credit score agencies

The following boxed list expression also identifies approved credit score agencies but uses FEEL logic to define the agency status (Inc., LLC, SA, GA) based on a DMN input node:

dmn list expression example2
Figure 86. Boxed list expression using FEEL logic for approved credit score agency status
dmn list expression example2a
4.1.2.6. DMN model example

The following is a real-world DMN model example that demonstrates how you can use decision modeling to reach a decision based on input data, circumstances, and company guidelines. In this scenario, a flight from San Diego to New York is canceled, requiring the affected airline to find alternate arrangements for its inconvenienced passengers.

First, the airline collects the information necessary to determine how best to get the travelers to their destinations:

Input data
  • List of flights

  • List of passengers

Decisions
  • Prioritize the passengers who will get seats on a new flight

  • Determine which flights those passengers will be offered

Business knowledge models
  • The company process for determining passenger priority

  • Any flights that have space available

  • Company rules for determining how best to reassign inconvenienced passengers

The airline then uses the DMN standard to model its decision process in the following decision requirements diagram (DRD) for determining the best rebooking solution:

dmn passenger rebooking drd
Figure 87. DRD for flight rebooking

Similar to flowcharts, DRDs use shapes to represent the different elements in a process. Ovals contain the two necessary input data, rectangles contain the decision points in the model, and rectangles with clipped corners (business knowledge models) contain reusable logic that can be repeatedly invoked.

The DRD draws logic for each element from boxed expressions that provide variable definitions using FEEL expressions or data type values.

Some boxed expressions are basic, such as the following decision for establishing a prioritized waiting list:

dmn context expression example
Figure 88. Boxed context expression example for prioritized wait list

Some boxed expressions are more complex with greater detail and calculation, such as the following business knowledge model for reassigning the next delayed passenger:

dmn reassign passenger
Figure 89. Boxed function expression for passenger reassignment

The following is the DMN source file for this decision model:

<dmn:definitions xmlns="https://www.drools.org/kie-dmn/Flight-rebooking" xmlns:dmn="http://www.omg.org/spec/DMN/20151101/dmn.xsd" xmlns:feel="http://www.omg.org/spec/FEEL/20140401" id="_0019_flight_rebooking" name="0019-flight-rebooking" namespace="https://www.drools.org/kie-dmn/Flight-rebooking">
  <dmn:itemDefinition id="_tFlight" name="tFlight">
    <dmn:itemComponent id="_tFlight_Flight" name="Flight Number">
      <dmn:typeRef>feel:string</dmn:typeRef>
    </dmn:itemComponent>
    <dmn:itemComponent id="_tFlight_From" name="From">
      <dmn:typeRef>feel:string</dmn:typeRef>
    </dmn:itemComponent>
    <dmn:itemComponent id="_tFlight_To" name="To">
      <dmn:typeRef>feel:string</dmn:typeRef>
    </dmn:itemComponent>
    <dmn:itemComponent id="_tFlight_Dep" name="Departure">
      <dmn:typeRef>feel:dateTime</dmn:typeRef>
    </dmn:itemComponent>
    <dmn:itemComponent id="_tFlight_Arr" name="Arrival">
      <dmn:typeRef>feel:dateTime</dmn:typeRef>
    </dmn:itemComponent>
    <dmn:itemComponent id="_tFlight_Capacity" name="Capacity">
      <dmn:typeRef>feel:number</dmn:typeRef>
    </dmn:itemComponent>
    <dmn:itemComponent id="_tFlight_Status" name="Status">
      <dmn:typeRef>feel:string</dmn:typeRef>
    </dmn:itemComponent>
  </dmn:itemDefinition>
  <dmn:itemDefinition id="_tFlightTable" isCollection="true" name="tFlightTable">
    <dmn:typeRef>tFlight</dmn:typeRef>
  </dmn:itemDefinition>
  <dmn:itemDefinition id="_tPassenger" name="tPassenger">
    <dmn:itemComponent id="_tPassenger_Name" name="Name">
      <dmn:typeRef>feel:string</dmn:typeRef>
    </dmn:itemComponent>
    <dmn:itemComponent id="_tPassenger_Status" name="Status">
      <dmn:typeRef>feel:string</dmn:typeRef>
    </dmn:itemComponent>
    <dmn:itemComponent id="_tPassenger_Miles" name="Miles">
      <dmn:typeRef>feel:number</dmn:typeRef>
    </dmn:itemComponent>
    <dmn:itemComponent id="_tPassenger_Flight" name="Flight Number">
      <dmn:typeRef>feel:string</dmn:typeRef>
    </dmn:itemComponent>
  </dmn:itemDefinition>
  <dmn:itemDefinition id="_tPassengerTable" isCollection="true" name="tPassengerTable">
    <dmn:typeRef>tPassenger</dmn:typeRef>
  </dmn:itemDefinition>
  <dmn:itemDefinition id="_tFlightNumberList" isCollection="true" name="tFlightNumberList">
    <dmn:typeRef>feel:string</dmn:typeRef>
  </dmn:itemDefinition>
  <dmn:inputData id="i_Flight_List" name="Flight List">
    <dmn:variable name="Flight List" typeRef="tFlightTable"/>
  </dmn:inputData>
  <dmn:inputData id="i_Passenger_List" name="Passenger List">
    <dmn:variable name="Passenger List" typeRef="tPassengerTable"/>
  </dmn:inputData>
  <dmn:decision name="Prioritized Waiting List" id="d_PrioritizedWaitingList">
    <dmn:variable name="Prioritized Waiting List" typeRef="tPassengerTable"/>
    <dmn:informationRequirement>
      <dmn:requiredInput href="#i_Passenger_List"/>
    </dmn:informationRequirement>
    <dmn:informationRequirement>
      <dmn:requiredInput href="#i_Flight_List"/>
    </dmn:informationRequirement>
    <dmn:knowledgeRequirement>
      <dmn:requiredKnowledge href="#b_PassengerPriority"/>
    </dmn:knowledgeRequirement>
    <dmn:context>
      <dmn:contextEntry>
        <dmn:variable name="Cancelled Flights" typeRef="tFlightNumberList"/>
        <dmn:literalExpression>
          <dmn:text>Flight List[ Status = "cancelled" ].Flight Number</dmn:text>
        </dmn:literalExpression>
      </dmn:contextEntry>
      <dmn:contextEntry>
        <dmn:variable name="Waiting List" typeRef="tPassengerTable"/>
        <dmn:literalExpression>
          <dmn:text>Passenger List[ list contains( Cancelled Flights, Flight Number ) ]</dmn:text>
        </dmn:literalExpression>
      </dmn:contextEntry>
      <dmn:contextEntry>
        <dmn:literalExpression>
          <dmn:text>sort( Waiting List, passenger priority )</dmn:text>
        </dmn:literalExpression>
      </dmn:contextEntry>
    </dmn:context>
  </dmn:decision>
  <dmn:decision name="Rebooked Passengers" id="d_RebookedPassengers">
    <dmn:variable name="Rebooked Passengers" typeRef="tPassengerTable"/>
    <dmn:informationRequirement>
      <dmn:requiredDecision href="#d_PrioritizedWaitingList"/>
    </dmn:informationRequirement>
    <dmn:informationRequirement>
      <dmn:requiredInput href="#i_Flight_List"/>
    </dmn:informationRequirement>
    <dmn:knowledgeRequirement>
      <dmn:requiredKnowledge href="#b_ReassignNextPassenger"/>
    </dmn:knowledgeRequirement>
    <dmn:invocation>
      <dmn:literalExpression>
        <dmn:text>reassign next passenger</dmn:text>
      </dmn:literalExpression>
      <dmn:binding>
        <dmn:parameter name="Waiting List"/>
        <dmn:literalExpression>
          <dmn:text>Prioritized Waiting List</dmn:text>
        </dmn:literalExpression>
      </dmn:binding>
      <dmn:binding>
        <dmn:parameter name="Reassigned Passengers List"/>
        <dmn:literalExpression>
          <dmn:text>[]</dmn:text>
        </dmn:literalExpression>
      </dmn:binding>
      <dmn:binding>
        <dmn:parameter name="Flights"/>
        <dmn:literalExpression>
          <dmn:text>Flight List</dmn:text>
        </dmn:literalExpression>
      </dmn:binding>
    </dmn:invocation>
  </dmn:decision>
  <dmn:businessKnowledgeModel id="b_PassengerPriority" name="passenger priority">
    <dmn:encapsulatedLogic>
      <dmn:formalParameter name="Passenger1" typeRef="tPassenger"/>
      <dmn:formalParameter name="Passenger2" typeRef="tPassenger"/>
      <dmn:decisionTable hitPolicy="UNIQUE">
        <dmn:input id="b_Passenger_Priority_dt_i_P1_Status" label="Passenger1.Status">
          <dmn:inputExpression typeRef="feel:string">
            <dmn:text>Passenger1.Status</dmn:text>
          </dmn:inputExpression>
          <dmn:inputValues>
            <dmn:text>"gold", "silver", "bronze"</dmn:text>
          </dmn:inputValues>
        </dmn:input>
        <dmn:input id="b_Passenger_Priority_dt_i_P2_Status" label="Passenger2.Status">
          <dmn:inputExpression typeRef="feel:string">
            <dmn:text>Passenger2.Status</dmn:text>
          </dmn:inputExpression>
          <dmn:inputValues>
            <dmn:text>"gold", "silver", "bronze"</dmn:text>
          </dmn:inputValues>
        </dmn:input>
        <dmn:input id="b_Passenger_Priority_dt_i_P1_Miles" label="Passenger1.Miles">
          <dmn:inputExpression typeRef="feel:string">
            <dmn:text>Passenger1.Miles</dmn:text>
          </dmn:inputExpression>
        </dmn:input>
        <dmn:output id="b_Status_Priority_dt_o" label="Passenger1 has priority">
          <dmn:outputValues>
            <dmn:text>true, false</dmn:text>
          </dmn:outputValues>
          <dmn:defaultOutputEntry>
            <dmn:text>false</dmn:text>
          </dmn:defaultOutputEntry>
        </dmn:output>
        <dmn:rule id="b_Passenger_Priority_dt_r1">
          <dmn:inputEntry id="b_Passenger_Priority_dt_r1_i1">
            <dmn:text>"gold"</dmn:text>
          </dmn:inputEntry>
          <dmn:inputEntry id="b_Passenger_Priority_dt_r1_i2">
            <dmn:text>"gold"</dmn:text>
          </dmn:inputEntry>
          <dmn:inputEntry id="b_Passenger_Priority_dt_r1_i3">
            <dmn:text>>= Passenger2.Miles</dmn:text>
          </dmn:inputEntry>
          <dmn:outputEntry id="b_Passenger_Priority_dt_r1_o1">
            <dmn:text>true</dmn:text>
          </dmn:outputEntry>
        </dmn:rule>
        <dmn:rule id="b_Passenger_Priority_dt_r2">
          <dmn:inputEntry id="b_Passenger_Priority_dt_r2_i1">
            <dmn:text>"gold"</dmn:text>
          </dmn:inputEntry>
          <dmn:inputEntry id="b_Passenger_Priority_dt_r2_i2">
            <dmn:text>"silver","bronze"</dmn:text>
          </dmn:inputEntry>
          <dmn:inputEntry id="b_Passenger_Priority_dt_r2_i3">
            <dmn:text>-</dmn:text>
          </dmn:inputEntry>
          <dmn:outputEntry id="b_Passenger_Priority_dt_r2_o1">
            <dmn:text>true</dmn:text>
          </dmn:outputEntry>
        </dmn:rule>
        <dmn:rule id="b_Passenger_Priority_dt_r3">
          <dmn:inputEntry id="b_Passenger_Priority_dt_r3_i1">
            <dmn:text>"silver"</dmn:text>
          </dmn:inputEntry>
          <dmn:inputEntry id="b_Passenger_Priority_dt_r3_i2">
            <dmn:text>"silver"</dmn:text>
          </dmn:inputEntry>
          <dmn:inputEntry id="b_Passenger_Priority_dt_r3_i3">
            <dmn:text>>= Passenger2.Miles</dmn:text>
          </dmn:inputEntry>
          <dmn:outputEntry id="b_Passenger_Priority_dt_r3_o1">
            <dmn:text>true</dmn:text>
          </dmn:outputEntry>
        </dmn:rule>
        <dmn:rule id="b_Passenger_Priority_dt_r4">
          <dmn:inputEntry id="b_Passenger_Priority_dt_r4_i1">
            <dmn:text>"silver"</dmn:text>
          </dmn:inputEntry>
          <dmn:inputEntry id="b_Passenger_Priority_dt_r4_i2">
            <dmn:text>"bronze"</dmn:text>
          </dmn:inputEntry>
          <dmn:inputEntry id="b_Passenger_Priority_dt_r4_i3">
            <dmn:text>-</dmn:text>
          </dmn:inputEntry>
          <dmn:outputEntry id="b_Passenger_Priority_dt_r4_o1">
            <dmn:text>true</dmn:text>
          </dmn:outputEntry>
        </dmn:rule>
        <dmn:rule id="b_Passenger_Priority_dt_r5">
          <dmn:inputEntry id="b_Passenger_Priority_dt_r5_i1">
            <dmn:text>"bronze"</dmn:text>
          </dmn:inputEntry>
          <dmn:inputEntry id="b_Passenger_Priority_dt_r5_i2">
            <dmn:text>"bronze"</dmn:text>
          </dmn:inputEntry>
          <dmn:inputEntry id="b_Passenger_Priority_dt_r5_i3">
            <dmn:text>>= Passenger2.Miles</dmn:text>
          </dmn:inputEntry>
          <dmn:outputEntry id="b_Passenger_Priority_dt_r5_o1">
            <dmn:text>true</dmn:text>
          </dmn:outputEntry>
        </dmn:rule>
      </dmn:decisionTable>
    </dmn:encapsulatedLogic>
    <dmn:variable name="passenger priority" typeRef="feel:boolean"/>
  </dmn:businessKnowledgeModel>
  <dmn:businessKnowledgeModel id="b_ReassignNextPassenger" name="reassign next passenger">
    <dmn:encapsulatedLogic>
      <dmn:formalParameter name="Waiting List" typeRef="tPassengerTable"/>
      <dmn:formalParameter name="Reassigned Passengers List" typeRef="tPassengerTable"/>
      <dmn:formalParameter name="Flights" typeRef="tFlightTable"/>
      <dmn:context>
        <dmn:contextEntry>
          <dmn:variable name="Next Passenger" typeRef="tPassenger"/>
          <dmn:literalExpression>
            <dmn:text>Waiting List[1]</dmn:text>
          </dmn:literalExpression>
        </dmn:contextEntry>
        <dmn:contextEntry>
          <dmn:variable name="Original Flight" typeRef="tFlight"/>
          <dmn:literalExpression>
            <dmn:text>Flights[ Flight Number = Next Passenger.Flight Number ][1]</dmn:text>
          </dmn:literalExpression>
        </dmn:contextEntry>
        <dmn:contextEntry>
          <dmn:variable name="Best Alternate Flight" typeRef="tFlight"/>
          <dmn:literalExpression>
            <dmn:text>Flights[ From = Original Flight.From and To = Original Flight.To and Departure > Original Flight.Departure and Status = "scheduled" and has capacity( item, Reassigned Passengers List ) ][1]</dmn:text>
          </dmn:literalExpression>
        </dmn:contextEntry>
        <dmn:contextEntry>
          <dmn:variable name="Reassigned Passenger" typeRef="tPassenger"/>
          <dmn:context>
            <dmn:contextEntry>
              <dmn:variable name="Name" typeRef="feel:string"/>
              <dmn:literalExpression>
                <dmn:text>Next Passenger.Name</dmn:text>
              </dmn:literalExpression>
            </dmn:contextEntry>
            <dmn:contextEntry>
              <dmn:variable name="Status" typeRef="feel:string"/>
              <dmn:literalExpression>
                <dmn:text>Next Passenger.Status</dmn:text>
              </dmn:literalExpression>
            </dmn:contextEntry>
            <dmn:contextEntry>
              <dmn:variable name="Miles" typeRef="feel:number"/>
              <dmn:literalExpression>
                <dmn:text>Next Passenger.Miles</dmn:text>
              </dmn:literalExpression>
            </dmn:contextEntry>
            <dmn:contextEntry>
              <dmn:variable name="Flight Number" typeRef="feel:string"/>
              <dmn:literalExpression>
                <dmn:text>Best Alternate Flight.Flight Number</dmn:text>
              </dmn:literalExpression>
            </dmn:contextEntry>
          </dmn:context>
        </dmn:contextEntry>
        <dmn:contextEntry>
          <dmn:variable name="Remaining Waiting List" typeRef="tPassengerTable"/>
          <dmn:literalExpression>
            <dmn:text>remove( Waiting List, 1 )</dmn:text>
          </dmn:literalExpression>
        </dmn:contextEntry>
        <dmn:contextEntry>
          <dmn:variable name="Updated Reassigned Passengers List" typeRef="tPassengerTable"/>
          <dmn:literalExpression>
            <dmn:text>append( Reassigned Passengers List, Reassigned Passenger )</dmn:text>
          </dmn:literalExpression>
        </dmn:contextEntry>
        <dmn:contextEntry>
          <dmn:literalExpression>
            <dmn:text>if count( Remaining Waiting List ) > 0 then reassign next passenger( Remaining Waiting List, Updated Reassigned Passengers List, Flights ) else Updated Reassigned Passengers List</dmn:text>
          </dmn:literalExpression>
        </dmn:contextEntry>
      </dmn:context>
    </dmn:encapsulatedLogic>
    <dmn:variable name="reassign next passenger" typeRef="tPassengerTable"/>
    <dmn:knowledgeRequirement>
      <dmn:requiredKnowledge href="#b_HasCapacity"/>
    </dmn:knowledgeRequirement>
  </dmn:businessKnowledgeModel>
  <dmn:businessKnowledgeModel id="b_HasCapacity" name="has capacity">
    <dmn:encapsulatedLogic>
      <dmn:formalParameter name="flight" typeRef="tFlight"/>
      <dmn:formalParameter name="rebooked list" typeRef="tPassengerTable"/>
      <dmn:literalExpression>
        <dmn:text>flight.Capacity > count( rebooked list[ Flight Number = flight.Flight Number ] )</dmn:text>
      </dmn:literalExpression>
    </dmn:encapsulatedLogic>
    <dmn:variable name="has capacity" typeRef="feel:boolean"/>
  </dmn:businessKnowledgeModel>
</dmn:definitions>

4.1.3. DMN support in Kogito

Kogito provides runtime support for DMN 1.1, 1.2, 1.3, and 1.4 models at conformance level 3 and design support for DMN 1.2 models at conformance level 3. You can design your DMN models with the Kogito DMN modeler in VSCode or import existing DMN models into your Kogito projects for deployment and execution. Any DMN 1.1 and 1.3 models (do not contain DMN 1.3 features) that you import, open in the DMN designer, and save are converted to DMN 1.2 models.

The following table summarizes the design and runtime support for each DMN version in Kogito:

Table 87. DMN support in Kogito

DMN version

DMN engine support

DMN modeler support

Execution

Open

Save

DMN 1.1

grn check

grn check

bk x

DMN 1.2

grn check

grn check

grn check

DMN 1.3

grn check

grn check

bk x

DMN 1.4

grn check

bk x

bk x

In addition to all DMN conformance level 3 requirements, Kogito also includes enhancements and fixes to FEEL and DMN model components to optimize the experience of implementing DMN decision services with Kogito. From a platform perspective, DMN models are like any other business asset in Kogito, such as DRL files or spreadsheet decision tables, that you can include in your Kogito project and execute to start your DMN decision services.

4.1.3.1. Configurable DMN validation in Kogito

By default, the Kogito CodeGen component uses the kogito.decisions.validation=ENABLED configuration to perform pre-compilation validation of DMN model assets and to perform DMN decision table static analysis.

When this configuration is enabled, Kogito performs the following DMN validations:

  • DMN model files are verified against the DMN specification XSD schema to ensure that the files are valid XML and compliant with the specification.

  • The pre-compilation analysis is performed for the DMN model to ensure that the basic semantic is aligned with the DMN specification.

  • DMN decision tables are statically analyzed for gaps or overlaps and to ensure that the semantic of the decision table follows best practices.

Any DMN validation error causes the Kogito CodeGen build to fail and errors are reported accordingly, as shown in the following example:

Image of DMN validation errors
Figure 90. DMN validation errors

You can modify the default DMN validation and DMN decision table analysis behavior to ignore errors during the project build, or you can disable this default behavior completely, as shown in the following examples:

Default configuration for DMN validation and decision table analysis
kogito.decisions.validation=ENABLED
Configuration to validate DMN models and DMN decision tables but ignore errors
kogito.decisions.validation=IGNORE
Configuration to disable all DMN validation
kogito.decisions.validation=DISABLED
If you enter an unrecognized kogito.decisions.validation configuration flag, all pre-compilation validation features are disabled and the Kogito CodeGen emits related log messages.
4.1.3.2. FEEL enhancements in Kogito

Kogito includes the following enhancements and other changes to FEEL in the current DMN implementation:

  • Space Sensitivity: This DMN implementation of the FEEL language is space insensitive. The goal is to avoid non-deterministic behavior based on the context and differences in behavior based on invisible characters, such as white spaces. This means that for this implementation, a variable named first name with one space is exactly the same as first name with two spaces in it.

  • List functions or() and and() : The specification defines two list functions named or() and and(). However, according to the FEEL grammar, these are not valid function names, as and and or are reserved keywords. This implementation renames these functions to any() and all() respectively, in anticipation for DMN 1.2.

  • Keyword in cannot be used in variable names: The specification defines that any keyword can be reused as part of a variable name, but the ambiguities caused with the for …​ in …​ return loop prevent the reuse of the in keyword. All other keywords are supported as part of variable names.

  • Keywords are not supported in attributes of anonymous types: FEEL is not a strongly typed language and the parser must resolve ambiguity in name parts of an attribute of an anonymous type. The parser supports reusable keywords as part of a variable name defined in the scope, but the parser does not support keywords in attributes of an anonymous type. For example, for item in Order.items return Federal Tax for Item( item ) is a valid and supported FEEL expression, where a function named Federal Tax for Item(…​) can be defined and invoked correctly in the scope. However, the expression for i in [ {x and y : true, n : 1}, {x and y : false, n: 2} ] return i.x and y is not supported because anonymous types are defined in the iteration context of the for expression and the parser cannot resolve the ambiguity.

  • Support for date and time literals on ranges: According to the grammar rules #8, #18, #19, #34 and #62, date and time literals are supported in ranges (pages 110-111). Chapter 10.3.2.7 on page 114, on the other hand, contradicts the grammar and says they are not supported. This implementation chose to follow the grammar and support date and time literals on ranges, as well as extend the specification to support any arbitrary expression (see extensions below).

  • Invalid time syntax: Chapter 10.3.2.3.4 on page 112 and bullet point about time on page 131 both state that the time string lexical representation follows the XML Schema Datatypes specification as well as ISO 8601. According to the XML Schema specification (https://www.w3.org/TR/xmlschema-2/#time), the lexical representation of a time follows the pattern hh:mm:ss.sss without any leading character. The DMN specification uses a leading "T" in several examples, that we understand is a typo and not in accordance with the standard.

  • Support for scientific and hexadecimal notations: This implementation supports scientific and hexadecimal notation for numbers. For example, 1.2e5 (scientific notation), 0xD5 (hexadecimal notation).

  • Support for expressions as end points in ranges: This implementation supports expressions as endpoints for ranges. For example, [date("2016-11-24")..date("2016-11-27")]

  • Support for additional types: The specification only defines the following as basic types of the language:

    • number

    • string

    • boolean

    • days and time duration

    • years and month duration

    • time

    • date and time

      For completeness and orthogonality, this implementation also supports the following types:

    • context

    • list

    • range

    • function

    • unary test

  • Support for unary tests: For completeness and orthogonality, unary tests are supported as first class citizens in the language. They are functions with an implicit single parameter and can be invoked in the same way as functions. For example,

    UnaryTestAsFunction.feel
      {
          is minor : < 18,
          Bob is minor : is minor( bob.age )
      }
  • Support for additional built-in functions: The following additional functions are supported:

    • now() : Returns the current local date and time.

    • today() : Returns the current local date.

    • decision table() : Returns a decision table function, although the specification mentions a decision table. The function on page 114 is not implementable as defined.

    • string( mask, p…​ ) : Returns a string formatted as per the mask. See Java String.format() for details on the mask syntax. For example, string( "%4.2f", 7.1298 ) returns the string "7.12".

  • Support for additional date and time arithmetics: Subtracting two dates returns a day and time duration with the number of days between the two dates, ignoring daylight savings. For example,

    DateArithmetic.feel
    date( "2017-05-12" ) - date( "2017-04-25" ) = duration( "P17D" )
4.1.3.3. DMN model enhancements in Kogito

Kogito includes the following enhancements to DMN model support in the current DMN implementation:

  • Support for types with spaces on names: The DMN XML schema defines type refs such as QNames. The QNames do not allow spaces. Therefore, it is not possible to use types like FEEL date and time, days and time duration or years and months duration. This implementation does parse such typerefs as strings and allows type names with spaces. However, in order to comply with the XML schema, it also adds the following aliases to such types that can be used instead:

    • date and time = dateTime

    • days and time duration = duration or dayTimeDuration

    • years and months duration = duration or yearMonthDuration

      Note that, for the "duration" types, the user can simply use duration and the decision engine will infer the proper duration, either days and time duration or years and months duration.

  • Lists support heterogeneous element types: Currently this implementation supports lists with heterogeneous element types. This is an experimental extension and does limit the functionality of some functions and filters. This decision will be re-evaluated in the future.

  • TypeRef link between Decision Tables and Item Definitions: On decision tables/input clause, if no values list is defined, the decision engine automatically checks the type reference and applies the allowed values check if it is defined.

4.1.4. Creating and editing DMN models in the Kogito DMN modeler

You can use the Kogito DMN modeler in VSCode to design DMN decision requirements diagrams (DRDs) and define decision logic for a complete and functional DMN decision model. Kogito provides design support for DMN 1.2 models at conformance level 3, and includes enhancements and fixes to FEEL and DMN model components to optimize the experience of implementing DMN decision services with Kogito. Kogito also provides runtime support for DMN 1.1, 1.2, 1.3, and 1.4 models at conformance level 3. Any DMN 1.1 and 1.3 models (do not contain DMN 1.3 features) that you import, open in the DMN designer, and save are converted to DMN 1.2 models.

Prerequisites
Procedure
  1. In your VSCode IDE, create or import a DMN file in the relevant folder of your Kogito project, typically in src/main/resources.

    For a new DMN file, you can also enter dmn.new in a web browser to design your decision model in the Kogito online DMN modeler. When you finish creating your decision model, you can click Download in the online modeler page to import your DMN file into your Kogito project.
  2. Open the new or imported DMN file to view the decision requirements diagram (DRD) in the Kogito DMN modeler.

    If the DRD does not open in the Kogito DMN modeler, ensure that you have installed and enabled the Kogito Bundle VSCode extension.

    If the Kogito DMN modeler opens only the XML source of the DMN file and displays an error message, review the reported errors and the DMN model file to ensure that all DMN elements are correctly defined.

  3. Begin adding components to your new or imported DRD by clicking and dragging one of the DMN nodes from the left toolbar:

    dmn drag decision node
    Figure 91. Adding DRD components

    The following DRD components are available:

    • Decision: Use this node for a DMN decision, where one or more input elements determine an output based on defined decision logic.

    • Business knowledge model: Use this node for reusable functions with one or more decision elements. Decisions that have the same logic but depend on different sub-input data or sub-decisions use business knowledge models to determine which procedure to follow.

    • Knowledge source: Use this node for external authorities, documents, committees, or policies that regulate a decision or business knowledge model. Knowledge sources are references to real-world factors rather than executable business rules.

    • Input data: Use this node for information used in a decision node or a business knowledge model. Input data usually includes business-level concepts or objects relevant to the business, such as loan applicant data used in a lending strategy.

    • Text annotation: Use this node for explanatory notes associated with an input data node, decision node, business knowledge model, or knowledge source.

    • Decision service: Use this node to enclose a set of reusable decisions implemented as a decision service for invocation. A decision service can be used in other DMN models and can be invoked from an external application or a BPMN business process.

  4. In the DMN modeler canvas, double-click the new DRD node to enter an informative node name.

  5. If the node is a decision or business knowledge model, select the node to display the node options and click the Edit icon to open the DMN boxed expression editor to define the decision logic for the node:

    dmn decision edit
    Figure 92. Opening a new decision node boxed expression
    dmn bkm edit
    Figure 93. Opening a new business knowledge model boxed expression

    By default, all business knowledge models are defined as boxed function expressions containing a literal FEEL expression, a nested context expression of an external JAVA or PMML function, or a nested boxed expression of any type.

    For decision nodes, you click the undefined table to select the type of boxed expression you want to use, such as a boxed literal expression, boxed context expression, decision table, or other DMN boxed expression.

    dmn decision boxed expression options
    Figure 94. Selecting the logic type for a decision node

    For business knowledge models, you click the upper-left function cell to select the function type, or right-click the function value cell, select Clear, and select a boxed expression of another type.

    dmn bkm define
    Figure 95. Selecting the function or other logic type for a business knowledge model
  6. In the selected boxed expression editor for either a decision node (any expression type) or business knowledge model (function expression), click the applicable table cells to define the table name, variable data types, variable names and values, function parameters and bindings, or FEEL expressions to include in the decision logic.

    You can right-click cells for additional actions where applicable, such as inserting or removing table rows and columns or clearing table contents.

    The following is an example decision table for a decision node that determines credit score ratings based on a defined range of a loan applicant’s credit score:

    dmn decision table example1a
    Figure 96. Decision node decision table for credit score rating

    The following is an example boxed function expression for a business knowledge model that calculates mortgage payments based on principal, interest, taxes, and insurance (PITI) as a literal expression:

    dmn function expression example4
    Figure 97. Business knowledge model function for PITI calculation
  7. After you define the decision logic for the selected node, click Back to MODEL_NAME to return to the DRD view.

  8. For the selected DRD node, use the available connection options to create and connect to the next node in the DRD, or click and drag a new node onto the DRD canvas from the left toolbar.

    The node type determines which connection options are supported. For example, an Input data node can connect to a decision node, knowledge source, or text annotation using the applicable connection type, whereas a Knowledge source node can connect to any DRD element. A Decision node can connect only to another decision or a text annotation.

    The following connection types are available, depending on the node type:

    • Information requirement: Use this connection from an input data node or decision node to another decision node that requires the information.

    • Knowledge requirement: Use this connection from a business knowledge model to a decision node or to another business knowledge model that invokes the decision logic.

    • Authority requirement: Use this connection from an input data node or a decision node to a dependent knowledge source or from a knowledge source to a decision node, business knowledge model, or another knowledge source.

    • Association: Use this connection from an input data node, decision node, business knowledge model, or knowledge source to a text annotation.

    dmn input connection example
    Figure 98. Connecting credit score input to the credit score rating decision
    dmn input connection example2
  9. Continue adding and defining the remaining DRD components of your decision model and save the completed DRD.

    The following is an example DRD for a loan prequalification decision model:

    dmn example drd
    Figure 99. Completed DRD for loan prequalification

    The following is an example DRD for a phone call handling decision model using a reusable decision service:

    dmn example drd3
    Figure 100. Completed DRD for phone call handling with a decision service

    In a DMN decision service node, the decision nodes in the bottom segment incorporate input data from outside of the decision service to arrive at a final decision in the top segment of the decision service node. The resulting top-level decisions from the decision service are then implemented in any subsequent decisions or business knowledge requirements of the DMN model. You can reuse DMN decision services in other DMN models to apply the same decision logic with different input data and different outgoing connections.

4.1.4.1. Defining DMN decision logic in boxed expressions in the Kogito DMN modeler

Boxed expressions in DMN are tables that you use to define the underlying logic of decision nodes and business knowledge models in a decision requirements diagram (DRD). Some boxed expressions can contain other boxed expressions, but the top-level boxed expression corresponds to the decision logic of a single DRD artifact. While DRDs represent the flow of a DMN decision model, boxed expressions define the actual decision logic of individual nodes. DRDs and boxed expressions together form a complete and functional DMN decision model.

You can use the Kogito DMN modeler in VSCode to define decision logic for your DRD components using built-in boxed expressions.

Prerequisites
  • A DMN file is created or imported in your Kogito project in VSCode.

Procedure
  1. In your VSCode IDE, open the DMN file to view the decision requirements diagram (DRD) in the Kogito DMN modeler.

    If the DRD does not open in the Kogito DMN modeler, ensure that you have installed and enabled the Kogito Bundle VSCode extension.

    If the Kogito DMN modeler opens only the XML source of the DMN file and displays an error message, review the reported errors and the DMN model file to ensure that all DMN elements are correctly defined.

  2. In the DMN modeler canvas, select a decision node or business knowledge model node that you want to define and click the Edit icon to open the DMN boxed expression editor:

    dmn decision edit
    Figure 101. Opening a new decision node boxed expression
    dmn bkm edit
    Figure 102. Opening a new business knowledge model boxed expression

    By default, all business knowledge models are defined as boxed function expressions containing a literal FEEL expression, a nested context expression of an external JAVA or PMML function, or a nested boxed expression of any type.

    For decision nodes, you click the undefined table to select the type of boxed expression you want to use, such as a boxed literal expression, boxed context expression, decision table, or other DMN boxed expression.

    dmn decision boxed expression options
    Figure 103. Selecting the logic type for a decision node

    For business knowledge model nodes, you click the upper-left function cell to select the function type, or right-click the function value cell, select Clear, and select a boxed expression of another type.

    dmn bkm define
    Figure 104. Selecting the function or other logic type for a business knowledge model
  3. For this example, use a decision node and select Decision Table as the boxed expression type.

    A decision table in DMN is a visual representation of one or more rules in a tabular format. Each rule consists of a single row in the table, and includes columns that define the conditions (input) and outcome (output) for that particular row.

  4. Click the input column header to define the name and data type for the input condition. For example, name the input column Credit Score.FICO with a number data type. This column specifies numeric credit score values or ranges of loan applicants.

  5. Click the output column header to define the name and data type for the output values. For example, name the output column Credit Score Rating and next to the Data Type option, click Manage to go to the Data Types page where you can create a custom data type with score ratings as constraints.

    dmn manage data types
    Figure 105. Managing data types for a column header value
  6. On the Data Types page, click New Data Type to add a new data type.

    For this example, click New Data Type and create a Credit_Score_Rating data type as a string:

    dmn custom data type add
    Figure 106. Adding a new data type
  7. Click Add Constraints, select Enumeration from the drop-down options, and add the following constraints:

    • "Excellent"

    • "Good"

    • "Fair"

    • "Poor"

    • "Bad"

    dmn custom data type constraints
    Figure 107. Adding constraints to the new data type

    To change the order of data type constraints, you can click the left end of the constraint row and drag the row as needed:

    dmn custom data type constraints drag
    Figure 108. Dragging constraints to change constraint order

    For information about constraint types and syntax requirements for the specified data type, see the Decision Model and Notation specification.

  8. Click OK to save the constraints and click the check mark to the right of the data type to save the data type.

  9. Return to the Credit Score Rating decision table, click the Credit Score Rating column header, and set the data type to this new custom data type.

  10. Use the Credit Score.FICO input column to define credit score values or ranges of values, and use the Credit Score Rating column to specify one of the corresponding ratings you defined in the Credit_Score_Rating data type.

    Right-click any value cell to insert or delete rows (rules) or columns (clauses).

    dmn decision table example1a
    Figure 109. Decision node decision table for credit score rating
  11. After you define all rules, click the upper-left corner of the decision table to define the rule Hit Policy and Builtin Aggregator (for COLLECT hit policy only).

    The hit policy determines how to reach an outcome when multiple rules in a decision table match the provided input values. The built-in aggregator determines how to aggregate rule values when you use the COLLECT hit policy.

    dmn hit policies
    Figure 110. Defining the decision table hit policy

    The following example is a more complex decision table that determines applicant qualification for a loan as the concluding decision node in the same loan prequalification decision model:

    dmn decision table example3
    Figure 111. Decision table for loan prequalification

For boxed expression types other than decision tables, you follow these guidelines similarly to navigate the boxed expression tables and define variables and parameters for decision logic, but according to the requirements of the boxed expression type. Some boxed expressions, such as boxed literal expressions, can be single-column tables, while other boxed expressions, such as function, context, and invocation expressions, can be multi-column tables with nested boxed expressions of other types.

For example, the following boxed context expression defines the parameters that determine whether a loan applicant can meet minimum mortgage payments based on principal, interest, taxes, and insurance (PITI), represented as a front-end ratio calculation with a sub-context expression:

dmn context expression example2
Figure 112. Boxed context expression for front-end client PITI ratio

The following boxed function expression determines a monthly mortgage installment as a business knowledge model in a lending decision, with the function value defined as a nested context expression:

dmn function expression example3
Figure 113. Boxed function expression for installment calculation in business knowledge model

For more information and examples of each boxed expression type, see DMN decision logic in boxed expressions.

4.1.4.2. Creating custom data types for DMN boxed expressions in the Kogito DMN modeler

In DMN boxed expressions in the Kogito DMN modeler, data types determine the structure of the data that you use within an associated table, column, or field in the boxed expression. You can use default DMN data types (such as String, Number, Boolean) or you can create custom data types to specify additional fields and constraints that you want to implement for the boxed expression values.

Custom data types that you create for a boxed expression can be simple or structured:

  • Simple data types have only a name and a type assignment. Example: Age (number).

  • Structured data types contain multiple fields associated with a parent data type. Example: A single type Person containing the fields Name (string), Age (number), Email (string).

Prerequisites
  • A DMN file is created or imported in your Kogito project in VSCode.

Procedure
  1. In your VSCode IDE, open the DMN file to view the decision requirements diagram (DRD) in the Kogito DMN modeler.

    If the DRD does not open in the Kogito DMN modeler, ensure that you have installed and enabled the Kogito Bundle VSCode extension.

    If the Kogito DMN modeler opens only the XML source of the DMN file and displays an error message, review the reported errors and the DMN model file to ensure that all DMN elements are correctly defined.

  2. In the DMN modeler canvas, select a decision node or business knowledge model for which you want to define the data types and click the Edit icon to open the DMN boxed expression editor.

  3. If the boxed expression is for a decision node that is not yet defined, click the undefined table to select the type of boxed expression you want to use, such as a boxed literal expression, boxed context expression, decision table, or other DMN boxed expression.

    dmn decision boxed expression options
    Figure 114. Selecting the logic type for a decision node
  4. Click the cell for the table header, column header, or parameter field (depending on the boxed expression type) for which you want to define the data type and click Manage to go to the Data Types page where you can create a custom data type.

    dmn manage data types
    Figure 115. Managing data types for a column header value

    You can also set and manage custom data types for a specified decision node or business knowledge model node by selecting the Properties icon in the upper-right corner of the DMN modeler:

    dmn manage data types1a
    Figure 116. Managing data types in decision requirements diagram (DRD) properties

    The data type that you define for a specified cell in a boxed expression determines the structure of the data that you use within that associated table, column, or field in the boxed expression.

    In this example, an output column Credit Score Rating for a DMN decision table defines a set of custom credit score ratings based on an applicant’s credit score.

  5. On the Data Types page, click New Data Type to add a new data type.

    For this example, click New Data Type and create a Credit_Score_Rating data type as a string:

    dmn custom data type add
    Figure 117. Adding a new data type

    If the data type requires a list of items, enable the List setting.

  6. Click Add Constraints, select Enumeration from the drop-down options, and add the following constraints:

    • "Excellent"

    • "Good"

    • "Fair"

    • "Poor"

    • "Bad"

    dmn custom data type constraints
    Figure 118. Adding constraints to the new data type

    To change the order of data type constraints, you can click the left end of the constraint row and drag the row as needed:

    dmn custom data type constraints drag
    Figure 119. Dragging constraints to change constraint order

    For information about constraint types and syntax requirements for the specified data type, see the Decision Model and Notation specification.

  7. Click OK to save the constraints and click the check mark to the right of the data type to save the data type.

  8. Return to the Credit Score Rating decision table, click the Credit Score Rating column header, set the data type to this new custom data type, and define the rule values for that column with the rating constraints that you specified.

    dmn decision table example1a
    Figure 120. Decision table for credit score rating

    In the DMN decision model for this scenario, the Credit Score Rating decision flows into the following Loan Prequalification decision that also requires custom data types:

    dmn manage data types blank
    Figure 121. Decision table for loan prequalification
  9. Continuing with this example, return to the Data Types window, click New Data Type, and create a Loan_Qualification data type as a Structure with no constraints.

    When you save the new structured data type, the first sub-field appears so that you can begin defining nested data fields in this parent data type. You can use these sub-fields in association with the parent structured data type in boxed expressions, such as nested column headers in decision tables or nested table parameters in context or function expressions.

    For additional sub-fields, select the addition icon next to the Loan_Qualification data type:

    dmn manage data types structured
    Figure 122. Adding a new structured data type with nested fields
  10. For this example, under the structured Loan_Qualification data type, add a Qualification field with "Qualified" and "Not Qualified" enumeration constraints, and a Reason field with no constraints. Add also a simple Back_End_Ratio and a Front_End_Ratio data type, both with "Sufficient" and "Insufficient" enumeration constraints.

    Click the check mark to the right of each data type that you create to save your changes.

    dmn manage data types structured2
    Figure 123. Adding nested data types with constraints

    To change the order or nesting of data types, you can click the left end of the data type row and drag the row as needed:

    dmn manage data types structured2 drag
    Figure 124. Dragging data types to change data type order or nesting
  11. Return to the decision table and, for each column, click the column header cell, set the data type to the new corresponding custom data type, and define the rule values as needed for the column with the constraints that you specified, if applicable.

    dmn decision table example3
    Figure 125. Decision table for loan prequalification

For boxed expression types other than decision tables, you follow these guidelines similarly to navigate the boxed expression tables and define custom data types as needed.

For example, the following boxed function expression uses custom tCandidate and tProfile structured data types to associate data for online dating compatibility:

dmn manage data types structured3
Figure 126. Boxed function expression for online dating compatibility
dmn manage data types structured3a
Figure 127. Custom data type definitions for online dating compatibility
dmn manage data types structured3b
Figure 128. Parameter definitions with custom data types for online dating compatibility
4.1.4.3. Included models in DMN files in the Kogito VSCode DMN modeler

In the Kogito DMN modeler in VSCode, you can use the Included Models tab to include other DMN models and Predictive Model Markup Language (PMML) models from your project in a specified DMN file. When you include a DMN model within another DMN file, you can use all of the nodes and logic from both models in the same decision requirements diagram (DRD). When you include a PMML model within a DMN file, you can invoke that PMML model as a boxed function expression for a DMN decision node or business knowledge model node.

You cannot include DMN or PMML models from different directories within your Kogito project or from other Kogito projects.

Included models are supported in the Kogito DMN modeler in VSCode only. In other Kogito modeling tools, such as the Business Modeler desktop application or Business Modeler online viewer, the Kogito DMN modeler does not support DMN or PMML included models.
Including other DMN models within a DMN file in the Kogito VSCode DMN modeler

In the Kogito DMN modeler in VSCode, you can include other DMN models from the same directory of your Kogito project in a specified DMN file. When you include a DMN model within another DMN file, you can use all of the nodes and logic from both models in the same decision requirements diagram (DRD), but you cannot edit the nodes from the included model. To edit nodes from included models, you must update the source file for the included model directly. If you update the source file for an included DMN model, open the DMN file where the DMN model is included (or close and re-open) to verify the changes.

You cannot include DMN models from different directories within your Kogito project or from other Kogito projects.

Prerequisites
  • The DMN models are created or imported (as .dmn files) in the same directory of your Kogito project as the DMN file in which you want to include the models, such as in src/main/resources.

Procedure
  1. In your VSCode IDE, open the DMN file to view the decision requirements diagram (DRD) in the Kogito DMN modeler.

    If the DRD does not open in the Kogito DMN modeler, ensure that you have installed and enabled the Kogito Bundle VSCode extension.

    If the Kogito DMN modeler opens only the XML source of the DMN file and displays an error message, review the reported errors and the DMN model file to ensure that all DMN elements are correctly defined.

  2. In the DMN modeler, click the Included Models tab.

  3. Click Include Model, select a DMN model from your directory in the Models list, enter a unique name for the included model, and click Include:

    dmn include model
    Figure 129. Including a DMN model

    The DMN model is added to this DMN file, and all DRD nodes from the included model are listed under Decision Components in the Decision Navigator view:

    dmn include model list
    Figure 130. DMN file with decision components from the included DMN model

    All data types from the included model are also listed in read-only mode in the Data Types tab for the DMN file:

    dmn include model data types
    Figure 131. DMN file with data types from the included DMN model
  4. In the Editor tab of the DMN modeler, click and drag the included DRD components onto the canvas to begin implementing them in your DRD:

    dmn include model drd
    Figure 132. Adding DRD components from the included DMN model

    To edit DRD nodes or data types from included models, you must update the source file for the included model directly. If you update the source file for an included DMN model, open the DMN file where the DMN model is included (or close and re-open) to verify the changes.

    To edit the included model name or to remove the included model from the DMN file, use the Included Models tab in the DMN modeler.

    When you remove an included model, any nodes from that included model that are currently used in the DRD are also removed.
Including PMML models within a DMN file in the Kogito VSCode DMN modeler

In the Kogito DMN modeler in VSCode, you can include Predictive Model Markup Language (PMML) models from your project in a specified DMN file. When you include a PMML model within a DMN file, you can invoke that PMML model as a boxed function expression for a DMN decision node or business knowledge model node. If you update the source file for an included PMML model, you must remove and re-include the PMML model in the DMN file to apply the source changes.

You cannot include PMML models from different directories within your Kogito project or from other Kogito projects.

Prerequisites
  • The PMML models are created or imported (as .pmml files) in the same directory of your Kogito project as the DMN file in which you want to include the models, such as in src/main/resources.

Procedure
  1. In your VSCode IDE, open the DMN file to view the decision requirements diagram (DRD) in the Kogito DMN modeler.

    If the DRD does not open in the Kogito DMN modeler, ensure that you have installed and enabled the Kogito Bundle VSCode extension.

    If the Kogito DMN modeler opens only the XML source of the DMN file and displays an error message, review the reported errors and the DMN model file to ensure that all DMN elements are correctly defined.

  2. In the DMN modeler, click the Included Models tab.

  3. Click Include Model, select a PMML model from your directory in the Models list, enter a unique name for the included model, and click Include:

    dmn include model pmml
    Figure 133. Including a PMML model

    The PMML model is added to this DMN file:

    dmn include model list pmml
    Figure 134. DMN file with included PMML model
  4. In the Editor tab of the DMN modeler, select or create the decision node or business knowledge model node in which you want to invoke the PMML model and click the Edit icon to open the DMN boxed expression editor:

    dmn decision edit
    Figure 135. Opening a new decision node boxed expression
    dmn bkm edit
    Figure 136. Opening a new business knowledge model boxed expression
  5. Set the expression type to Function (default for business knowledge model nodes), click the upper-left function cell, and select PMML.

  6. In the document and model rows in the table, double-click the undefined cells to specify the included PMML document and the relevant PMML model within that document:

    dmn include model expression pmml
    Figure 137. Adding a PMML model in a DMN business knowledge model
    dmn function expression example5
    Figure 138. Example PMML definition in a DMN business knowledge model

    If you update the source file for an included PMML model, you must remove and re-include the PMML model in the DMN file to apply the source changes.

    To edit the included model name or to remove the included model from the DMN file, use the Included Models tab in the DMN modeler.

4.1.4.4. Creating DMN models with multiple diagrams in the Kogito DMN modeler

For complex DMN models, you can use the Kogito DMN modeler in VSCode to design multiple DMN decision requirements diagrams (DRDs) that represent parts of the overall decision requirements graph (DRG) for the DMN decision model. In simple cases, you can use a single DRD to represent all of the overall DRG for the decision model, but in complex cases, a single DRD can become large and difficult to follow. Therefore, to better organize DMN decision models with many decision requirements, you can divide the model into smaller nested DRDs that constitute the larger central DRD representation of the overall DRG.

Prerequisites
Procedure
  1. In your VSCode IDE, create or import a DMN file in the relevant folder of your Kogito project, typically in src/main/resources.

    For a new DMN file, you can also enter dmn.new in a web browser to design your decision model in the Kogito online DMN modeler. When you finish creating your decision model, you can click Download in the online modeler page to import your DMN file into your Kogito project.
  2. Open the new or imported DMN file to view the DRD in the Kogito DMN modeler, and begin designing or modifying the DRD using the DMN nodes in the left toolbar.

    If the DRD does not open in the Kogito DMN modeler, ensure that you have installed and enabled the Kogito Bundle VSCode extension.

    If the Kogito DMN modeler opens only the XML source of the DMN file and displays an error message, review the reported errors and the DMN model file to ensure that all DMN elements are correctly defined.

  3. For any DMN nodes that you want to define in a separate nested DRD, select the node, click the DRD Actions icon, and select from the available options.

    dmn drd actions
    Figure 139. DRD actions icon for subdividing a DRD

    The following options are available:

    • Create: Use this option to create a nested DRD where you can separately define the DMN components and diagram for the selected node.

    • Add to: If you already created a nested DRD, use this option to add the selected node to an existing DRD.

    • Remove: If the node that you selected is already within a nested DRD, use this option to remove the node from that nested DRD.

    After you create a nested DRD within your DMN decision model, the new DRD opens in a separate DRD canvas and the available DRD and components are listed in the Decision Navigator right menu. You can use the Decision Navigator menu to rename or remove a nested DRD.

    dmn drd actions rename
    Figure 140. Rename new nested DRD in the Decision Navigator menu
  4. In the separate canvas for the new nested DRD, design the flow and logic for all required components in this portion of the DMN model, as usual.

  5. Continue adding and defining any other nested DRDs for your decision model and save the completed DMN file.

    For example, the following DRD for a loan prequalification decision model contains all DMN components for the model without any nested DRDs. This example relies on the single DRD for all components and logic, resulting in a large and complex diagram.

    dmn example drd
    Figure 141. Single DRD for loan prequalification

    Alternatively, by following the steps in this procedure, you can divide this example DRD into multiple nested DRDs to better organize the decision requirements, as shown in the following example:

    dmn drd multiple
    Figure 142. Multiple nested DRDs for loan prequalification
    dmn drd multiple front end
    Figure 143. Overview of front end ratio DRD
    dmn drd multiple front end details
    Figure 144. DRD for front end ratio
    dmn drd multiple credit score
    Figure 145. Overview of credit score rating DRD
    dmn drd multiple credit score details
    Figure 146. DRD for credit score rating
    dmn drd multiple back end
    Figure 147. Overview of back end ratio DRD
    dmn drd multiple back end details
    Figure 148. DRD for back end ratio
4.1.4.5. DMN model documentation in the Kogito DMN modeler

In the Kogito DMN modeler, you can use the Documentation tab to generate a report of your DMN model. The DMN model report contains all decision requirements diagrams (DRDs), data types, and boxed expressions in your DMN model. You can use this report to share your DMN model details or as part of your internal reporting workflow.

dmn documentation
Figure 149. Example DMN model report
4.1.4.6. Kogito DMN modeler navigation and properties

The Kogito DMN modeler provides the following additional features to help you navigate through the components and properties of decision requirements diagrams (DRDs).

DMN decision and diagram views

In the upper-right corner of the DMN modeler, select the Decision Navigator view to navigate between the decision components, graphs, and boxed expressions of a selected DRD:

dmn designer nav view
Figure 150. Decision Navigator view
dmn designer nav view2
The DRD components from any DMN models included in the DMN file (in the Included Models tab) are also listed in the Decision Components panel for the DMN file.

In the upper-right corner of the DMN modeler, select the Preview icon to view an elevated preview of the DRD:

dmn designer preview
Figure 151. Diagram preview
DRD properties and design

In the upper-right corner of the DMN modeler, select the Properties icon to modify the identifying information, data types, and appearance of a selected DRD, DRD node, or boxed expression cell:

dmn designer properties
Figure 152. DRD node properties

To view the properties of the entire DRD, click the DRD canvas background instead of a specific node.

DRD search

In the upper-right corner of the DMN modeler, use the search bar to search for text that appears in your DRD. The search feature is especially helpful in complex DRDs with many nodes:

dmn designer search
Figure 153. DRD search
DMN decision service details

Select a decision service node in the DMN designer to view additional properties, including Input Data, Encapsulated Decisions, and Output Decisions in the Properties panel.

dmn decision service details
Figure 154. Decision Service details

4.1.5. Kogito service execution

After you design your Kogito service, you can build and run your application and then send REST API requests to the application to execute your services. The exact REST API requests that you can use depend on how you set up the application.

For example, consider a Kogito service that is set up to generate a /persons REST API endpoint and determines whether a specified customer is an adult or is underage. In this example, you can send the following POST request using a REST client or curl utility to add an adult and execute the service:

Example POST request body to add an adult (JSON)
{
  "person": {
    "name": "John Quark",
    "age": 20
  }
}
Example curl command to add an adult
curl -X POST http://localhost:8080/persons -H 'content-type: application/json' -H 'accept: application/json' -d '{"person": {"name":"John Quark", "age": 20}}'
Example response (JSON)
{
  "id": "3af806dd-8819-4734-a934-728f4c819682",
  "person": {
    "name": "John Quark",
    "age": 20,
    "adult": false
  },
  "isAdult": true
}

Note that for JSON marshaling, Kogito is relying on the default mappers of Quarkus and SpringBoot, which can be configured according to the specific requirements.

For information about creating, running, and testing an example application with Kogito services, see Creating and running your first Kogito services.

For information about deploying your Kogito service to OpenShift, see Deploying Kogito services on OpenShift.

4.1.5.1. REST endpoints for DMN models in Kogito

For each DMN model in a Kogito application, the following REST endpoints are automatically generated based on the content of the model:

  • [POST] /{modelName}: A business-domain endpoint for evaluating the entire DMN model

  • [POST] /{modelName}/{decisionServiceName}: A business-domain endpoint for evaluating a specified decision service component in the DMN model

  • [POST] /{modelName}/dmnresult: An endpoint for evaluating the entire DMN model and returning a DMNResult response, including the business-domain context, helper messages, and helper decision pointers for the DMN model

  • [POST] /{modelName}/{decisionServiceName}/dmnresult: An endpoint for evaluating a specified decision service component in the DMN model and returning a DMNResult response, including the business-domain context, helper messages, and helper decision pointers for the decision service

  • [GET] /{modelName}: An endpoint for returning the DMN XML without decision logic, typically for DMN model introspection

You can use these endpoints to interact with a DMN model or a specific decision service within a model. As you decide between using business-domain and dmnresult variants of these REST endpoints, review the following considerations:

  • REST business-domain endpoints: Use this endpoint type if a client application is only concerned with a positive evaluation outcome, is not interested in parsing Info or Warn messages, and only needs an HTTP 5xx response for any errors. This type of endpoint is also helpful for single-page application-like clients, due to singleton coercion of decision service results that resemble the DMN modeling behavior.

  • REST dmnresult endpoints: Use this endpoint type if a client needs to parse Info, Warn, or Error messages in all cases.

For each endpoint, use a REST client, curl utility, or Swagger UI (if configured for the application) to send requests with the following components:

  • Base URL: http://HOST:PORT/{modelName}

  • Path parameters:

    • {modelName}: The string identifier of the DMN model, such as Traffic Violation

    • {decisionServiceName}: The string identifier of the decision service component in the DMN DRG, such as TrafficViolationDecisionService

    • dmnresult: The string identifier that enables the endpoint to return a full DMNResult response with more detailed Info, Warn, and Error messaging

  • HTTP headers: For POST requests only:

    • accept: application/json

    • content-type: application/json

  • HTTP methods: GET or POST

The examples in the following endpoints are based on a Traffic Violation DMN model that contains a TrafficViolationDecisionService decision service component.

For all of these endpoints, if a DMN evaluation Error message occurs, a DMNResult response is returned along with an HTTP 5xx error. If a DMN Info or Warn message occurs, the relevant response is returned along with the business-domain REST body, in the X-Kogito-decision-messages extended HTTP header, to be used for client-side business logic. When more refined client-side business logic is required, the client can use the dmnresult variant of the endpoints.

Return the DMN XML without decision logic

[GET] /{modelName}

Example REST endpoint

http://localhost:8080/Traffic Violation

Example curl request
curl -X GET http://localhost:8080/Traffic Violation
Example response (XML)
<?xml version="1.0" encoding="UTF-8"?>
<dmn:definitions xmlns:dmn="http://www.omg.org/spec/DMN/20180521/MODEL/" xmlns="https://github.com/kiegroup/drools/kie-dmn/_A4BCA8B8-CF08-433F-93B2-A2598F19ECFF" xmlns:di="http://www.omg.org/spec/DMN/20180521/DI/" xmlns:kie="http://www.drools.org/kie/dmn/1.2" xmlns:feel="http://www.omg.org/spec/DMN/20180521/FEEL/" xmlns:dmndi="http://www.omg.org/spec/DMN/20180521/DMNDI/" xmlns:dc="http://www.omg.org/spec/DMN/20180521/DC/" id="_1C792953-80DB-4B32-99EB-25FBE32BAF9E" name="Traffic Violation" expressionLanguage="http://www.omg.org/spec/DMN/20180521/FEEL/" typeLanguage="http://www.omg.org/spec/DMN/20180521/FEEL/" namespace="https://github.com/kiegroup/drools/kie-dmn/_A4BCA8B8-CF08-433F-93B2-A2598F19ECFF">
  <dmn:itemDefinition id="_63824D3F-9173-446D-A940-6A7F0FA056BB" name="tDriver" isCollection="false">
    ...
  </dmn:itemDefinition>
  <dmn:itemDefinition id="_2D4F30EE-21A6-4A78-A524-A5C238D433AE" name="tFine" isCollection="false">
    ...
  </dmn:itemDefinition>
  <dmn:decision id="_4055D956-1C47-479C-B3F4-BAEB61F1C929" name="Fine">
    <dmn:variable id="_8C1EAC83-F251-4D94-8A9E-B03ACF6849CD" name="Fine" typeRef="tFine">
    </dmn:variable>
    <dmn:informationRequirement id="_800A3BBB-90A3-4D9D-BA5E-A311DED0134F">
      <dmn:requiredInput href="#_1929CBD5-40E0-442D-B909-49CEDE0101DC">
      </dmn:requiredInput>
    </dmn:informationRequirement>
  </dmn:decision>
  <dmn:decision id="_8A408366-D8E9-4626-ABF3-5F69AA01F880" name="Should the driver be suspended?">
    <dmn:question>Should the driver be suspended due to points on his license?</dmn:question>
    <dmn:allowedAnswers>"Yes", "No"</dmn:allowedAnswers>
    <dmn:variable id="_40387B66-5D00-48C8-BB90-E83EE3332C72" name="Should the driver be suspended?" typeRef="string">
    </dmn:variable>
    <dmn:informationRequirement id="_982211B1-5246-49CD-BE85-3211F71253CF">
      <dmn:requiredInput href="#_1F9350D7-146D-46F1-85D8-15B5B68AF22A">
      </dmn:requiredInput>
    </dmn:informationRequirement>
    <dmn:informationRequirement id="_AEC4AA5F-50C3-4FED-A0C2-261F90290731">
      <dmn:requiredDecision href="#_4055D956-1C47-479C-B3F4-BAEB61F1C929">
      </dmn:requiredDecision>
    </dmn:informationRequirement>
  </dmn:decision>
  <dmn:inputData id="_1929CBD5-40E0-442D-B909-49CEDE0101DC" name="Violation">
    <dmn:variable id="_C16CF9B1-5FAB-48A0-95E0-5FCD661E0406" name="Violation" typeRef="tViolation">
    </dmn:variable>
  </dmn:inputData>
  <dmn:inputData id="_1F9350D7-146D-46F1-85D8-15B5B68AF22A" name="Driver">
    <dmn:variable id="_A80F16DF-0DB4-43A2-B041-32900B1A3F3D" name="Driver" typeRef="tDriver">
    </dmn:variable>
  </dmn:inputData>
</dmn:definitions>
Evaluate a specified DMN model

[POST] /{modelName}

For this endpoint, the request body must contain all input data of the DMN model. The response is the resulting DMN context of the model, including the decision values, the original input values, and all other parametric DRG components in serialized form. For example, a business knowledge model is available in string-serialized form in its signature.

Image of Traffic Violation DMN model
Figure 155. Example Traffic Violation DMN model
Example REST endpoint

http://localhost:8080/Traffic Violation

Example POST request body with input data
{
  "Driver": {
    "Points": 2
  },
  "Violation": {
    "Type": "speed",
    "Actual Speed": 120,
    "Speed Limit": 100
  }
}
Example curl request
curl -X POST http://localhost:8080/Traffic Violation -H 'content-type: application/json' -H 'accept: application/json' -d '{"Driver": {"Points": 2}, "Violation": {"Type": "speed", "Actual Speed": 120, "Speed Limit": 100}}'
Example response (JSON)
{
  "Violation": {
    "Type": "speed",
    "Speed Limit": 100,
    "Actual Speed": 120
  },
  "Driver": {
    "Points": 2
  },
  "Fine": {
    "Points": 3,
    "Amount": 500
  },
  "Should the driver be suspended?": "No"
}
Evaluate a specified decision service within a DMN model

[POST] /{modelName}/{decisionServiceName}

For this endpoint, the request body must contain all the requirements of the decision service. The response is the resulting DMN context of the decision service, including the decision values, the original input values, and all other parametric DRG components in serialized form. For example, a business knowledge model is available in string-serialized form in its signature.

If the decision service is composed of a single-output decision, the response is the resulting value of that specific decision. This behavior provides an equivalent value at the API level of a specification feature when invoking the decision service in the model itself. As a result, you can, for example, interact with a DMN decision service from single-page web applications.

Image of decision service in Traffic Violation DMN model
Figure 156. Example TrafficViolationDecisionService decision service with single-output decision
Image of decision service in Traffic Violation DMN model
Figure 157. Example TrafficViolationDecisionService decision service with multiple-output decision
Example REST endpoint

http://localhost:8080/Traffic Violation/TrafficViolationDecisionService

Example POST request body with input data
{
  "Driver": {
    "Points": 2
  },
  "Violation": {
    "Type": "speed",
    "Actual Speed": 120,
    "Speed Limit": 100
  }
}
Example curl request
curl -X POST http://localhost:8080/Traffic Violation/TrafficViolationDecisionService -H 'content-type: application/json' -H 'accept: application/json' -d '{"Driver": {"Points": 2}, "Violation": {"Type": "speed", "Actual Speed": 120, "Speed Limit": 100}}'
Example response for single-output decision (JSON)
"No"
Example response for multiple-output decision (JSON)
{
  "Violation": {
    "Type": "speed",
    "Speed Limit": 100,
    "Actual Speed": 120
  },
  "Driver": {
    "Points": 2
  },
  "Fine": {
    "Points": 3,
    "Amount": 500
  },
  "Should the driver be suspended?": "No"
}
Evaluate a specified DMN model and return a DMNResult response

[POST] /{modelName}/dmnresult

Example REST endpoint

http://localhost:8080/Traffic Violation

Example POST request body with input data
{
  "Driver": {
    "Points": 2
  },
  "Violation": {
    "Type": "speed",
    "Actual Speed": 120,
    "Speed Limit": 100
  }
}
Example curl request
curl -X POST http://localhost:8080/Traffic Violation -H 'content-type: application/json' -H 'accept: application/json' -d '{"Driver": {"Points": 2}, "Violation": {"Type": "speed", "Actual Speed": 120, "Speed Limit": 100}}'
Example response (JSON)
{
  "namespace": "https://github.com/kiegroup/drools/kie-dmn/_A4BCA8B8-CF08-433F-93B2-A2598F19ECFF",
  "modelName": "Traffic Violation",
  "dmnContext": {
    "Violation": {
      "Type": "speed",
      "Speed Limit": 100,
      "Actual Speed": 120,
      "Code": null,
      "Date": null
    },
    "Driver": {
      "Points": 2,
      "State": null,
      "City": null,
      "Age": null,
      "Name": null
    },
    "Fine": {
      "Points": 3,
      "Amount": 500
    },
    "Should the driver be suspended?": "No"
  },
  "messages": [],
  "decisionResults": [
    {
      "decisionId": "_4055D956-1C47-479C-B3F4-BAEB61F1C929",
      "decisionName": "Fine",
      "result": {
        "Points": 3,
        "Amount": 500
      },
      "messages": [],
      "evaluationStatus": "SUCCEEDED"
    },
    {
      "decisionId": "_8A408366-D8E9-4626-ABF3-5F69AA01F880",
      "decisionName": "Should the driver be suspended?",
      "result": "No",
      "messages": [],
      "evaluationStatus": "SUCCEEDED"
    }
  ]
}
Evaluate a specified decision service within a DMN model and return a DMNResult response

[POST] /{modelName}/{decisionServiceName}/dmnresult

Example REST endpoint

http://localhost:8080/Traffic Violation/TrafficViolationDecisionService

Example POST request body with input data
{
  "Driver": {
    "Points": 2
  },
  "Violation": {
    "Type": "speed",
    "Actual Speed": 120,
    "Speed Limit": 100
  }
}
Example curl request
curl -X POST http://localhost:8080/Traffic Violation/TrafficViolationDecisionService -H 'content-type: application/json' -H 'accept: application/json' -d '{"Driver": {"Points": 2}, "Violation": {"Type": "speed", "Actual Speed": 120, "Speed Limit": 100}}'
Example response (JSON)
{
  "namespace": "https://github.com/kiegroup/drools/kie-dmn/_A4BCA8B8-CF08-433F-93B2-A2598F19ECFF",
  "modelName": "Traffic Violation",
  "dmnContext": {
    "Violation": {
      "Type": "speed",
      "Speed Limit": 100,
      "Actual Speed": 120,
      "Code": null,
      "Date": null
    },
    "Driver": {
      "Points": 2,
      "State": null,
      "City": null,
      "Age": null,
      "Name": null
    },
    "Should the driver be suspended?": "No"
  },
  "messages": [],
  "decisionResults": [
    {
      "decisionId": "_8A408366-D8E9-4626-ABF3-5F69AA01F880",
      "decisionName": "Should the driver be suspended?",
      "result": "No",
      "messages": [],
      "evaluationStatus": "SUCCEEDED"
    }
  ]
}

4.1.6. Kogito Audit Investigation Console

The Kogito Audit Investigation Console is a user interface for monitoring and investigating Decision Model and Notation (DMN) model executions in Kogito.

Image of Kogito Audit Investigation Console
Figure 158. Kogito Audit Investigation Console

You can use the Audit Investigation Console to browse DMN model executions, verify execution status, retrieve execution details, such as decision outcomes, input data, and model preview, and obtain explanation information about how the processed decisions were made.

The Audit Investigation Console requires your Kogito services to use the following Kogito components:

  • Kogito Trusty Service: Enables the Audit Investigation Console to access stored events related to decision tracing from your Kogito services. The Kogito Trusty Service requires Infinispan persistence and Apache Kafka messaging for your Kogito service. For more information about the Trusty Service, see Configuring Kogito supporting services and runtime capabilities.

  • Kogito Explainability Service: Generates explainability data that is stored in the Kogito Trusty Service. The Audit Investigation Console accesses the explainbility information from tracing-event data for decisions in your Kogito services. The Kogito Explainbility Service requires Apache Kafka messaging for your Kogito service.

  • Kogito Tracing decision add-on: Enables the Kogito service to produce tracing events and publish the events to Apache Kafka. The add-on also exposes the REST endpoint /predict for the Kogito Explainbility Service.

4.1.6.1. Supported configurations for the tracing add-on

The Kogito Audit Investigation Console requires the following project dependency for the tracing add-on:

Project dependency to enable tracing capability on Quarkus
<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-addons-quarkus-tracing-decision</artifactId>
</dependency>
Project dependency to enable tracing capability on SpringBoot
<dependency>
  <groupId>org.kie.kogito</groupId>
  <artifactId>kogito-addons-springboot-tracing-decision</artifactId>
</dependency>

The tracing add-on enables the Trusty Service to interact with the decision-tracing data through the CloudEvents messages. The default configuration for the add-on pushes the decision-tracing events to the kogito-tracing-decision Apache Kafka topic and pushes the DMN models used by the Kogito service to the kogito-tracing-model topic under the group ID kogito-runtimes.

To specify the Kafka bootstrap server for your Kogito service, set the following property in the application.properties file of your Kogito project:

Application property for Kafka bootstrap server
kafka.bootstrap.servers=http://HOST:PORT
In a Kubernetes or OpenShift environment, if you specified the KAFKA_BOOTSTRAP_SERVERS environment variable in the relevant container, this application property in your Kogito project is not required.

On Quarkus, to customize the tracing add-on configurations, use the format mp.messaging.outgoing.kogito-tracing-decision.PROPERTY_NAME for the decisions, and the format mp.messaging.outgoing.kogito-tracing-model.PROPERTY_NAME for the DMN models.

For example, to change the topic name for the decision-tracing events, add the following line to the application.properties file:

Example property to change the topic name for decision-tracing events
mp.messaging.outgoing.kogito-tracing-decision.topic=my-kogito-tracing-decision

On SpringBoot, to customize the tracing add-on configurations, set the following properties in the application.properties file of your Kogito project as needed:

Table 88. Application properties for the tracing add-on on SpringBoot
Property Description

kogito.addon.tracing.decision.kafka.bootstrapAddress

Sets the address used in the initial connection to find a bootstrap server on the cluster of n brokers

Default value: None (must be set by user)

kogito.addon.tracing.decision.kafka.topic.name

Sets the topic name for the decision-tracing events

Default value: kogito-tracing-decision

kogito.addon.tracing.decision.kafka.topic.partitions

Sets the number of partitions to use for the decision-tracing events topic

Default value: 1

kogito.addon.tracing.decision.kafka.topic.replicationFactor

Sets the factor of the data for the decision-tracing events topic

Default value: 1

kogito.addon.tracing.decision.asyncEnabled

Enables asynchronous callback with the results of the send (success or failure) instead of waiting to complete

Default value: true

kogito.addon.tracing.model.kafka.topic.name

Sets the topic name for the DMN models used by the Kogito service

Default value: kogito-tracing-model

Additional resources
4.1.6.2. Using the Kogito Audit Investigation Console to view DMN model execution details

You can use the Kogito Audit Investigation Console to view DMN model execution details for your Kogito services. You can run the Audit Investigation Console for local Kogito services or add it to your Kogito infrastructure on OpenShift.

Prerequisites
  • A Kogito Trusty Service instance and Explainbility Service instance are configured and running for your Kogito service. The Trusty Service enables the Audit Investigation Console to access stored DMN model execution data. The Trusty Service requires Infinispan persistence and Apache Kafka messaging for your Kogito service. For information about the Trusty Service, see Configuring Kogito supporting services and runtime capabilities.

  • The pom.xml file of your Kogito project contains the following dependency for the tracing add-on:

    Project dependency to enable tracing capability on Quarkus
    <dependency>
      <groupId>org.kie.kogito</groupId>
      <artifactId>kogito-addons-quarkus-tracing-decision</artifactId>
    </dependency>
    Project dependency to enable tracing capability SpringBoot
    <dependency>
      <groupId>org.kie.kogito</groupId>
      <artifactId>kogito-addons-springboot-tracing-decision</artifactId>
    </dependency>
  • The application.properties file of your Kogito project contains the following system properties:

    Application property for REST URLs
    kogito.service.url=http://HOST:PORT
    In a Kubernetes or OpenShift environment, if you specified the KOGITO_SERVICE_URL environment variable in the relevant container, this application property in your Kogito project is not required for the Explainability Service.
    Application property for Kafka bootstrap server
    kafka.bootstrap.servers=http://HOST:PORT
    In a Kubernetes or OpenShift environment, if you specified the KAFKA_BOOTSTRAP_SERVERS environment variable in the relevant container, this application property in your Kogito project is not required.

    The service URL property defines the location where the Kogito service is deployed, such as http://localhost:8080. This property enables the Explainability Service to generate the URLs to interact with Kogito and enrich tracing data with explainbility. The bootstrap property defines the Kafka bootstrap server for your Kogito service.

Procedure
  1. Go to the trusty-ui artifacts page, select the latest release of the Kogito Audit Investigation Console, and download the trusty-ui-VERSION-runner.jar file to a local directory.

  2. In a command terminal, navigate to the directory location of the downloaded trusty-ui-VERSION-runner.jar file and enter the following command to run the Audit Investigation Console:

    Running the Audit Investigation Console
    $ java -Dquarkus.http.port=9000 -Dkogito.trusty.http.url=http://HOST:PORT -jar trusty-ui-VERSION-runner.jar

    The default port for the Audit Investigation Console is 8080, but this example specifies port 9000 to avoid conflicts with the example Kogito service running at port 8080.

    In an OpenShift environment, you can use the Kogito command-line interface (CLI) or the OpenShift web console to add the Audit Investigation Console to your Kogito infrastructure.

    In the trusty-ui CLI command, replace the Trusty Service host and port values with the host and port of your running Trusty Service instance. This command includes a KOGITO_TRUSTY_ENDPOINT environment variable that injects the specified Trusty Service endpoint into the Audit Investigation Console service container.

    Adding the Audit Investigation Console to your OpenShift infrastructure using the Kogito CLI
    $ kogito install trusty-ui --env KOGITO_TRUSTY_ENDPOINT=http://trusty:8080
    Image of Kogito Audit Investigation Console instance on OpenShift
    Figure 159. Kogito Audit Investigation Console instance on OpenShift web console

    If you did not already install the required Trusty Service and Explainability Service, you can likewise use the OpenShift web console to add these instances under the Kogito Supporting Service tab and try again to add the Audit Investigation Console instance.

    Image of Kogito Trusty Service and Explainability Service instances on OpenShift
    Figure 160. Kogito Trusty Service and Explainability Service instances on OpenShift web console
  3. In a web browser, navigate to http://localhost:9000 to open the Audit Investigation Console. If you modified the configured Trusty UI port, use the modified location.

    On OpenShift, navigate to the route URL for the Audit Investigation Console instance.

    Image of Kogito Audit Investigation Console
    Figure 161. Kogito Audit Investigation Console

    The Audit Investigation Console displays data for all available DMN model executions. By default, executions are filtered to display results from the last month. To modify the list view, you can use the search or sort fields at the top of the screen.

  4. In the DMN model executions list, select an execution ID to view and navigate through the details for the selected execution.

    Image of execution details page
    Figure 162. Execution details page

    At the top of the execution details page, you can hover your cursor over the execution status to view information about execution date, time, and executor.

    Image of Execution Status Tooltip
    Figure 163. Execution status tooltip
  5. In the execution details page, use the following tabs in the top menu to view different aspects of the DMN model execution:

    • Outcomes: Use this page to view a list of the decision outcomes in the current execution. Each outcome is a card containing the output of the decision. You can click View Details for each card to open the Outcome Details section for that specific outcome.

    • Outcome Details: Use this page to view information about decision outcomes of the execution.

      Image of Outcome Details page
      Figure 164. Outcome Details page

      You can change which outcome to view by selecting from the drop-down options in the top menu.

      Image of Outcomes selection
      Figure 165. Outcomes selection

      The Explanation section provides insight about how the inputs of the model influenced the decision outcome. A score from -1 to 1 is assigned to each input, representing its positive or negative effect on the decision result. Score values are rounded to the second decimal digit. If you hover your cursor over the graph bars, you can also see the full score value. Next to the Features Score Chart panel, the Features Weight panel contains the same scores in a tabular view, grouped by positive and negative weight.

      The Outcome Influencing Inputs section provides a list of the inputs that were considered during the elaboration of the current decision outcome. The inputs typically consist of a sub-set of the inputs provided to the DMN model, or may consist of all provided inputs. For complex input structures, you can select from the Browse Sections options to view different input sections.

    • Input Data: Use this page to view all inputs submitted to the DMN model for the current execution, including all inputs that were not incorporated in the outcomes processing. For complex input structures, you can select from the Browse Sections options to view different input sections.

      Image of Input Data page
      Figure 166. Input Data page
    • Model Lookup: Use this page to view the decision requirements diagram (DRD) of the executed DMN model.

      Image of Model Lookup page
      Figure 167. Model Lookup page

      The Model Lookup viewer currently does not support DMN models that include other DMN models or PMML models.

For example Kogito services that use the Trusty Service and Explainability Service, see the following example applications in GitHub:

  • dmn-tracing-quarkus: A DMN decision service on Quarkus that uses the kogito-addons-quarkus-tracing-decision add-on to generate tracing events that the Kogito Trusty Service and Explainability Service can consume and expose.

  • trusty-demonstration: A tutorial for deploying the dmn-tracing-quarkus example application on Kubernetes as a demonstration of Kogito Trusty Service and Explainability Service capabilities in a cloud environment.

4.1.6.3. Enabling Kogito Audit Console security with OpenID Connect

For Quarkus-based Kogito services, you can use the Quarkus OpenID Connect adapter with the Kogito Audit Console to enable the console to interact with the Kogito Trusty Service using bearer token authorization. These tokens are issued by OpenID Connect and OAuth 2.0 compliant authorization servers such as Keycloak.

This procedure applies only when you are using a locally cloned copy of the Kogito Audit Console repository in GitHub.
Prerequisites
Procedure
  1. In a command terminal, navigate to the local clone of the Kogito Audit Console repository and enter the following command to run the application with security enabled:

    Run the Audit Console with security enabled
    mvn clean compile quarkus:dev -Dquarkus.profile=keycloak
    Ensure that the service is not started at the same port as the security server. You can change the port by adding -Dquarkus.http.port=PORT_NUMBER to the start-up properties.

    The Kogito Audit Console contains a Quarkus profile to encapsulate the security configuration, so if the service requires security, you can specify the quarkus.profile=keycloak property at build time to enable the needed security. If the keycloak Quarkus profile is not added, the OpenID Connect extension is disabled.

  2. Navigate to the src/main/resources/application.properties file of the Audit Console project and add the following properties:

    Required security properties in applications.properties file
    # OpenID Connect configurations
    %keycloak.quarkus.oidc.enabled=true
    %keycloak.quarkus.oidc.tenant-enabled=true
    %keycloak.quarkus.oidc.auth-server-url=http://localhost:8280/auth/realms/kogito
    %keycloak.quarkus.oidc.client-id=kogito-console-quarkus
    %keycloak.quarkus.oidc.credentials.secret=secret
    %keycloak.quarkus.oidc.application-type=web-app
    %keycloak.quarkus.oidc.logout.path=/logout
    %keycloak.quarkus.oidc.logout.post-logout-path=/
    
    # HTTP security configurations
    %keycloak.quarkus.http.auth.permission.authenticated.paths=/*
    %keycloak.quarkus.http.auth.permission.authenticated.policy=authenticated
    The quarkus.oidc.enabled property enables or disables security at build time, while the quarkus.oidc.tenant-enabled property enables or disables security at runtime.
  3. Replace any property definitions with those of your specific environment, especially the following properties:

    • quarkus.oidc.auth-server-url: The base URL of the OpenID Connect (OIDC) server, such as https://localhost:8280/auth. All other OIDC server page and service URLs are derived from this URL. If you work with Keycloak OIDC server, ensure that the base URL is in the following format: https://HOST:PORT/auth/realms/KEYCLOAK_REALM.

    • quarkus.oidc.client-id: The client ID of the application. Each application has a client ID that is used to identify the application.

    • quarkus.oidc.credentials.secret: The client secret for the application.

  4. In the same application.properties file, also configure the resources to be exposed and the required permissions for accessing the resources.

    If you are enabling security at runtime using the quarkus.oidc.tenant-enabled property, the quarkus.http.auth.permission path and policy must specify how authentication is applied. By default, if security is enabled, the user must be authenticated to access any path.
  5. Stop and restart the Kogito Audit Console to ensure that the security changes are applied.

4.2. Using DRL rules in Kogito services

As a developer of business decisions, you can define business rules using Drools Rule Language (DRL) directly in free-form .drl text files. A DRL file can contain one or more rules that define at a minimum the rule conditions (when) and actions (then).

4.2.1. Decision-authoring assets in Kogito

Kogito supports several assets that you can use to define business decisions for your decision service. Each decision-authoring asset has different advantages, and you might prefer to use one or a combination of multiple assets depending on your goals and needs.

The following table highlights the main decision-authoring assets supported in Kogito projects to help you decide or confirm the best method for defining decisions in your decision service.

Table 89. Decision-authoring assets supported in Kogito
Asset Highlights Authoring tools Documentation

Decision Model and Notation (DMN) models

  • Are decision models based on a notation standard defined by the Object Management Group (OMG)

  • Use graphical decision requirements diagrams (DRDs) that represent part or all of the overall decision requirements graph (DRG) to trace business decision flows

  • Use an XML schema that allows the DMN models to be shared between DMN-compliant platforms

  • Support Friendly Enough Expression Language (FEEL) to define decision logic in DMN decision tables and other DMN boxed expressions

  • Can be integrated efficiently with Business Process Model and Notation (BPMN) process models

  • Are optimal for creating comprehensive, illustrative, and stable decision flows

Kogito DMN modeler in VSCode or other DMN-compliant editor

Decision Model and Notation (DMN)

DRL rules

  • Are individual rules that you define directly in .drl text files

  • Provide the most flexibility for defining rules and other technicalities of rule behavior

  • Are optimal for creating rules that require advanced DRL options, such as rule units

  • Have strict syntax requirements for rules to be compiled properly

Any integrated development environment (IDE)

Drools Rule Language (DRL)

Spreadsheet decision tables

  • Are tabular sets of rules that you define in .xls or .xlsx spreadsheet files

  • Support template keys and values for creating rule templates

  • Are optimal for business environments that already rely on decision tables for rules

  • Have strict syntax requirements for rules to be compiled properly when used in Kogito

Spreadsheet editor

Spreadsheet decision tables

4.2.2. Drools Rule Language (DRL)

Drools Rule Language (DRL) is a notation established by the Drools open source business automation project for defining and describing business rules. You define DRL rules in .drl text files. A DRL file can contain one or more rules that define at a minimum the rule conditions (when) and actions (then).

DRL files consist of the following components:

Components in a DRL file
package
unit

import

declare   // Optional

query  // Optional

rule "rule name"
    // Attributes
    when
        // Conditions
    then
        // Actions
end

rule "rule2 name"

...

The following example DRL rule determines the age limit in a loan application decision service:

Example rule for loan application age limit
rule "Underage"
  when
    /applicant[ applicantName : name, age < 21 ]
    $application : /loanApplication[ applicant == applicantName ]
  then
    $application.setApproved( false );
    $application.setExplanation( "Underage" );
end

A DRL file can contain single or multiple rules and queries, and can define resource declarations and attributes that are assigned and used by your rules and queries. The components in a DRL file are grouped in a defined rule unit that serves as a unique namespace for each group of rules. The DRL package followed by the rule unit definition must be listed at the top of a DRL file, and the rules are typically listed last. All other DRL components can follow any order.

Each rule must have a unique name within the rule unit. If you use the same rule name more than once in any DRL file in the unit, the rules fail to compile. Rule names generally must follow standard Java identifier conventions. However, you can enclose rule names with double quotation marks (rule "rule name") to prevent possible compilation errors, especially if you use spaces in rule names.

4.2.2.1. Packages in DRL

A package is a folder of related assets in Kogito, such as data objects, DRL files, decision tables, and other asset types. A package also serves as a unique namespace for each group of rules. A single rule base can contain multiple packages. You typically store all the rules for a package in the same file as the package declaration so that the package is self-contained. However, you can import objects from other packages that you want to use in the rules.

The following example is a package name and namespace for a DRL file in a mortgage application decision service:

Example package definition in a DRL file
package org.mortgages;

The following railroad diagram shows all the components that may make up a package:

package
Figure 168. Package

Note that a package must have a namespace and be declared using standard Java conventions for package names; i.e., no spaces, unlike rule names which allow spaces. In terms of the order of elements, they can appear in any order in the rule file, with the exception of the package and unit statements, which must be at the top of the file. In all cases, the semicolons are optional.

Notice that any rule attribute (as described in the section [rules-attributes-ref-drl-rules]) may also be written at package level, superseding the attribute’s default value. The modified default may still be replaced by an attribute setting within a rule.

4.2.2.2. Rule units in DRL

A DRL rule unit is a module for rules and a unit of execution. A rule unit collects a set of rules with the declaration of the type of facts that the rules act on. A rule unit also serves as a unique namespace for each group of rules. A single rule base can contain multiple rule units. You typically store all the rules for a unit in the same file as the unit declaration so that the unit is self-contained.

The following example is a rule unit designated in a DRL file in a mortgage application decision service:

Example package definition and rule unit designation in a DRL file
package org.mortgages;
unit MortgageRules;

To define a rule unit, you declare the relevant fact types and declare the data sources for the types by implementing the RuleUnitData interface, and then define the rules in the unit:

Example DRL rule unit file
package org.mortgages;
unit MortgageRules;

import org.kie.kogito.rules.DataSource;
import org.kie.kogito.rules.DataStream;

declare Person
    name : String
    dateOfBirth : Date
    address : Address
end

declare MortgageRules extends RuleUnitData
  person: DataStream<Person> = DataSource.createStream()
end

rule "Using a rule unit with a declared type"
  when
    $p : /person[ name == "James" ]
  then   // Insert Mark, who is a customer of James.
    Person mark = new Person();
    mark.setName( "Mark" );
    person.append( mark );
end

To separate the fact types from the rule unit for use with other DRL rules, you can declare the types in a separate DRL file and then use the DRL rule file to declare the data sources by using the RuleUnitData interface implementation:

Example DRL type declaration as a separate file
package org.mortgages;

declare Person
    name : String
    dateOfBirth : Date
    address : Address
end
Example DRL rule unit file without explicitly defined types
package org.mortgages;
unit MortgageRules;

import org.kie.kogito.rules.DataSource;
import org.kie.kogito.rules.DataStream;

declare MortgageRules extends RuleUnitData
  person: DataStream<Person> = DataSource.createStream()
end

rule "Using a rule unit with a declared type"
  when
    $p : /person[ name == "James" ]
  then   // Insert Mark, who is a customer of James.
    Person mark = new Person();
    mark.setName( "Mark" );
    person.append( mark );
end

In this example, persons is a DataStream data source for facts of type Person. Data sources are typed sources of data that rule units can subscribe to for updates. You interact with the rule unit through the data sources it exposes. A data source can be a DataStream source for append-only storage, a DataStore source for writable storage to add or remove data, or a SingletonStore source for writable storage to set and clear a single element.

As part of your data source declaration, you also import org.kie.kogito.rules.DataSource and the relevant data source support, such as import org.kie.kogito.rules.DataStream in this example.

You can add several rules to the same DRL file, or further break down the rule set and type declarations by creating more files. However you construct your rule sets, ensure that all DRL rule files exist in the same directory and start with the correct package and unit declarations.

Rule unit use case

As an additional rule unit use case, consider the following example decision service that evaluates incoming data from a heat sensor for temperature measurements and produces alerts when the temperature is above a specified threshold.

This example service uses the following types.drl file in the src/main/resources/org/acme folder of the Kogito project to declare the Temperature and the Alert fact types:

Example DRL type declarations
package com.acme;

declare Temperature
  value: double
end

declare Alert
    severity: String
    message: String
end

To define DRL rules that pattern-match against Temperature values, the example service must expose an entry point for the incoming data to the decision engine and publish alerts on a separate channel. To establish this data source for decision data, the example service uses a rule unit with DataStream data sources for Temperature objects and for Alert objects.

The DataStream data source is an append-only store for incoming data, similar to a queue. This type of data source is logical for both sources in this example because the temperature data is coming from an external source (the sensor) and the service publishes the alerts externally as they are produced.

The example service uses the following MonitoringService.drl file in the same src/main/resources/com/acme folder of the Kogito project to declare the data sources for the fact types and defines the rules for the rule unit:

Example DRL rule unit file
package com.acme;
unit MonitoringService;

import org.kie.kogito.rules.DataSource;
import org.kie.kogito.rules.DataStream;

declare MonitoringService extends RuleUnitData
  temperature: DataStream<Temperature> = DataSource.createStream()
  alertData: DataStream<Alert> = DataSource.createStream()
end

rule "tooHot"
when
    $temp : /temperature[value >= 80]
then
    alertData.append(new Alert("HIGH", "Temperature exceeds threshold: " + temp.value));
end

The rule unit implements the required RuleUnitData interface and declares the data sources for the previously defined types. The sample rule raises an alert when the temperature reaches or exceeds 80 degrees.

Data sources for DRL rule units

Data sources are typed sources of data that rule units can subscribe to for updates. You interact with the rule unit through the data sources it exposes.

Kogito supports the following types of data sources. When you declare data sources in DRL rule files, the sources are internally rendered as shown in these examples.

  • DataStream: An append-only storage option. Use this storage option when you want to publish or share data values. You can use the notation DataSource.createStream() to return a DataStream<T> object and use the method append(T) to add more data.

    Example DataStream data source definition
    DataStream<Temperature> temperature = DataSource.createStream();
    // Append value and notify all subscribers
    temperature.append(new Temperature(100));
  • DataStore: A writable storage option for adding or removing data and then notifying all subscribers that mutable data has been modified. Rules can pattern-match against incoming values and update or remove available values. For users familiar with Drools, this option is equivalent to a typed version of an entry point. In fact, a DataStore<Object> is equivalent to an old-style entry point.

    Example DataStore data source definition
    DataStore<Temperature> temperature = DataSource.createStore();
    Temperature temp = new Temperature(100);
    // Add value `t` and notify all subscribers
    DataHandle t = temperature.add(temp);
    temp.setValue(50);
    // Notify all subscribers that the value referenced by `t` has changed
    temperature.update(t, temp);
    // Remove value referenced by `t` and notify all subscribers
    temperature.remove(t);
  • SingletonStore: A writable storage option for setting or clearing a single element and then notifying all subscribers that the element has been modified. Rules can pattern-match against the value and update or clear available values. For users familiar with Drools, this option is equivalent to a global. In fact, a Singleton<Object> is similar to an old-style global, except that when used in conjuction with rules, you can pattern-match against it.

    Example SingletonStore data source definition
    SingletonStore<Temperature> temperature = DataSource.createSingleton();
    Temperature temp = new Temperature(100);
    // Add value `temp` and notify all subscribers
    temperature.set(temp);
    temp.setValue(50);
    // Notify all subscribers that the value has changed
    temperature.update();
    
    Temperature temp2 = new Temperature(200);
    // Overwrite contained value with `temp2` and notify all subscribers
    temperature.set(temp2);
    temp2.setValue(150);
    // Notify all subscribers that the value has changed
    temperature.update();
    
    // Clear store and notify all subscribers
    temperature.clear();

Subscribers to a data source are known as data processors. A data processor implements the DataProcessor<T> interface. This interface contains callbacks to all the events that a subscribed data source can trigger.

Example DataStream data processor
public interface DataProcessor<T> {
    void insert(DataHandle handle, T object);
    void update(DataHandle handle, T object);
    void delete(DataHandle handle);
}

The DataHandle method is an internal reference to an object of a data source. Each callaback method might or might not be invoked, depending on whether the corresponding data source implements the capability. For example, a DataStream source invokes only the insert callback, whereas a SingletonStore source invokes the insert callback on set and the delete callback on clear or before an overwriting set.

DRL rule unit declaration using Java

As an alternative to declaring fact types and rule units in DRL files, you can also declare types and units using Java classes. In this case, you add the source code to the src/main/java folder of your Kogito project instead of src/main/resources.

For example, the following Java classes define the type and rule unit declarations for the example temperature monitoring service:

Example Temperature class
package com.acme;

public class Temperature {
    private final double value;
    public Temperature(double value) { this.value = value; }
    public double getValue() { return value; }
}
Example Alert class
package com.acme;

public class Alert {
    private final String severity
    private final String message;
    public Temperature(String severity, String message) {
        this.severity = severity;
        this.message = message;
    }
    public String getSeverity() { return severity; }
    public String getMessage() { return message; }
}
Example rule unit class
package com.acme;

import org.kie.kogito.rules.DataSource;
import org.kie.kogito.rules.DataStream;

public class MonitoringService implements RuleUnitData {
    private DataStream<Temperature> temperature = DataSource.createStream();
    private DataStream<Alert> alertData = DataSource.createStream();
    public DataStream<Temperature> getTemperature() { return temperature; }
    public DataStream<Alert> getAlertData() { return alertData; }
}

In this scenario, the DRL rule files then stand alone in the src/main/resources folder and consist of the unit and the rules, with no direct declarations, as shown in the following example:

Example DRL rule unit file without declarations
package com.acme;
unit MonitoringService;

rule "tooHot"
  when
    $temp : /temperature[value >= 80]
  then
    alertData.append(new Alert("HIGH", "Temperature exceeds threshold: " + temp.value));
end
DRL rule units with BPMN processes

If you use a DRL rule unit as part of a business rule task in a Business Process Model and Notation (BPMN) process in your Kogito project, you do not need to create an explicit data type declaration or a rule unit class that implements the RuleUnitData interface. Instead, you designate the rule unit in the DRL file as usual and specify the rule unit in the format unit:PACKAGE_NAME.UNIT_NAME in the implementation details for the business rule task in the BPMN process. When you build the project, the business process implicitly declares the rule unit as part of the business rule task to execute the DRL file.

For example, the following is a DRL file with a rule unit designation:

Example DRL rule unit file
package com.acme;
unit MonitoringService;

rule "tooHot"
  when
    $temp : Temperature( value >= 80 ) from temperature
  then
    alertData.add(new Alert("HIGH", "Temperature exceeds threshold: " + temp.value));
end

In the relevant business process in a BPMN 2.0 process modeler, you select the business rule task and for the Implementation/Execution property, you set the rule language to DRL and the rule flow group to unit:com.acme.MonitoringService.

This rule unit syntax specifies that you are using the com.acme.MonitoringService rule unit instead of a traditional rule flow group. This is the rule unit that you referenced in the example DRL file. When you build the project, the business process implicitly declares the rule unit as part of the business rule task to execute the DRL file.

4.2.2.3. Import statements in DRL
import
Figure 169. Import

Similar to import statements in Java, imports in DRL files identify the fully qualified paths and type names for any objects that you want to use in the rules. You specify the package and data object in the format packageName.objectName, with multiple imports on separate lines. The decision engine automatically imports classes from the Java package with the same name as the DRL package and from the package java.lang.

The following example is an import statement for a loan application object in a mortgage application decision service:

Example import statement in a DRL file
import org.mortgages.LoanApplication;
4.2.2.4. Type declarations and metadata in DRL
type declaration
Figure 170. Type declaration
meta data
Figure 171. Metadata

Declarations in DRL files define new fact types or metadata for fact types to be used by rules in the DRL file:

  • New fact types: The default fact type in the java.lang package of Kogito is Object, but you can declare other types in DRL files as needed. Declaring fact types in DRL files enables you to define a new fact model directly in the decision engine, without creating models in a lower-level language like Java. You can also declare a new type when a domain model is already built and you want to complement this model with additional entities that are used mainly during the reasoning process.

  • Metadata for fact types: You can associate metadata in the format @KEY( VALUE ) with new or existing facts. Metadata can be any kind of data that is not represented by the fact attributes and is consistent among all instances of that fact type. The metadata can be queried at run time by the decision engine and used in the reasoning process.

Type declarations without metadata in DRL

A declaration of a new fact does not require any metadata, but must include a list of attributes or fields. If a type declaration does not include identifying attributes, the decision engine searches for an existing fact class in the classpath and raises an error if the class is missing.

For example, the following DRL file contains a declaration of a new fact type Person from a person data source and uses no metadata:

Example declaration of a new fact type with a rule
declare Person
  name : String
  dateOfBirth : java.util.Date
  address : Address
end

rule "Using a declared type"
  when
    $p : /person[ name == "James" ]
  then   // Insert Mark, who is a customer of James.
    Person mark = new Person();
    mark.setName( "Mark" );
    person.append( mark );
end

In this example, the new fact type Person has the three attributes name, dateOfBirth, and address. Each attribute has a type that can be any valid Java type, including another class that you create or a fact type that you previously declared. The dateOfBirth attribute has the type java.util.Date, from the Java API, and the address attribute has the previously defined fact type Address.

To avoid writing the fully qualified name of a class every time you declare it, you can define the full class name as part of the import clause:

Example type declaration with the fully qualified class name in the import
import java.util.Date

declare Person
    name : String
    dateOfBirth : Date
    address : Address
end

When you declare a new fact type, the decision engine generates at compile time a Java class representing the fact type. The generated Java class is a one-to-one JavaBeans mapping of the type definition.

For example, the following Java class is generated from the example Person type declaration:

Generated Java class for the Person fact type declaration
public class Person implements Serializable {
    private String name;
    private java.util.Date dateOfBirth;
    private Address address;

    // Empty constructor
    public Person() {...}

    // Constructor with all fields
    public Person( String name, Date dateOfBirth, Address address ) {...}

    // If keys are defined, constructor with keys
    public Person( ...keys... ) {...}

    // Getters and setters
    // `equals` and `hashCode`
    // `toString`
}

You can then use the generated class in your rules like any other fact, as illustrated in the previous rule example with the Person type declaration from a person data source:

Example rule that uses the declared Person fact type
rule "Using a declared type"
  when
    $p : /person[ name == "James" ]
  then   // Insert Mark, who is a customer of James.
    Person mark = new Person();
    mark.setName( "Mark" );
    person.append( mark );
end
Enumerative type declarations in DRL

DRL supports the declaration of enumerative types in the format declare enum FACT_TYPE, followed by a comma-separated list of values ending with a semicolon. You can then use the enumerative list in the rules in the DRL file.

For example, the following enumerative type declaration defines days of the week for an employee scheduling rule:

Example enumerative type declaration with a scheduling rule
declare enum DaysOfWeek
   SUN("Sunday"),MON("Monday"),TUE("Tuesday"),WED("Wednesday"),THU("Thursday"),FRI("Friday"),SAT("Saturday");

   fullName : String
end

rule "Using a declared Enum"
  when
    $emp : /employee[ dayOff == DaysOfWeek.MONDAY ]
  then
    ...
end
Extended type declarations in DRL

DRL supports type declaration inheritance in the format declare FACT_TYPE_1 extends FACT_TYPE_2. To extend a type declared in Java by a subtype declared in DRL, you repeat the parent type in a declaration statement without any fields.

For example, the following type declarations extend a Student type from a top-level Person type, and a LongTermStudent type from the Student subtype:

Example extended type declarations
import org.people.Person

declare Person end

declare Student extends Person
    school : String
end

declare LongTermStudent extends Student
    years : int
    course : String
end
Type declarations with metadata in DRL

You can associate metadata in the format @KEY( VALUE ) (the value is optional) with fact types or fact attributes. Metadata can be any kind of data that is not represented by the fact attributes and is consistent among all instances of that fact type. The metadata can be queried at run time by the decision engine and used in the reasoning process. Any metadata that you declare before the attributes of a fact type are assigned to the fact type, while metadata that you declare after an attribute are assigned to that particular attribute.

In the following example, the two metadata attributes @author and @dateOfCreation are declared for the Person fact type, and the two metadata items @key (literal) and @maxLength are declared for the name attribute. The @key literal metadata attribute has no required value, so the parentheses and the value are omitted.

Example metadata declaration for fact types and attributes
import java.util.Date

declare Person
    @author( Bob )
    @dateOfCreation( 01-Feb-2009 )

    name : String @key @maxLength( 30 )
    dateOfBirth : Date
    address : Address
end

For declarations of metadata attributes for existing types, you can identify the fully qualified class name as part of the import clause for all declarations or as part of the individual declare clause:

Example metadata declaration for an imported type
import org.drools.examples.Person

declare Person
    @author( Bob )
    @dateOfCreation( 01-Feb-2009 )
end
Example metadata declaration for a declared type
declare org.drools.examples.Person
    @author( Bob )
    @dateOfCreation( 01-Feb-2009 )
end
Metadata tags for fact type and attribute declarations in DRL

Although you can define custom metadata attributes in DRL declarations, the decision engine also supports the following predefined metadata tags for declarations of fact types or fact type attributes.

The examples in this section that refer to the VoiceCall class assume that the sample application domain model includes the following class details:

VoiceCall fact class in an example Telecom domain model
public class VoiceCall {
  private String  originNumber;
  private String  destinationNumber;
  private Date    callDateTime;
  private long    callDuration;  // in milliseconds

  // Constructors, getters, and setters
}
@role

This tag determines whether a given fact type is handled as a regular fact or an event in the decision engine during complex event processing.

Default parameter: fact

Supported parameters: fact, event

@role( fact | event )
Example: Declare VoiceCall as event type
declare VoiceCall
  @role( event )
end
@timestamp

This tag is automatically assigned to every event in the decision engine. By default, the time is provided by the session clock and assigned to the event when it is inserted into the working memory of the decision engine. You can specify a custom time stamp attribute instead of the default time stamp added by the session clock.

Default parameter: The time added by the decision engine session clock

Supported parameters: Session clock time or custom time stamp attribute

@timestamp( ATTRIBUTE_NAME )
Example: Declare VoiceCall timestamp attribute
declare VoiceCall
  @role( event )
  @timestamp( callDateTime )
end
@duration

This tag determines the duration time for events in the decision engine. Events can be interval-based events or point-in-time events. Interval-based events have a duration time and persist in the working memory of the decision engine until their duration time has lapsed. Point-in-time events have no duration and are essentially interval-based events with a duration of zero. By default, every event in the decision engine has a duration of zero. You can specify a custom duration attribute instead of the default.

Default parameter: Null (zero)

Supported parameters: Custom duration attribute

@duration( ATTRIBUTE_NAME )
Example: Declare VoiceCall duration attribute
declare VoiceCall
  @role( event )
  @timestamp( callDateTime )
  @duration( callDuration )
end
@expires

This tag determines the time duration before an event expires in the working memory of the decision engine. By default, an event expires when the event can no longer match and activate any of the current rules. You can define an amount of time after which an event should expire. This tag definition also overrides the implicit expiration offset calculated from temporal constraints and sliding windows in the KIE base. This tag is available only when the decision engine is running in stream mode.

Default parameter: Null (event expires after event can no longer match and activate rules)

Supported parameters: Custom timeOffset attribute in the format [#d][#h][#m][#s][[ms]]

@expires( TIME_OFFSET )
Example: Declare expiration offset for VoiceCall events
declare VoiceCall
  @role( event )
  @timestamp( callDateTime )
  @duration( callDuration )
  @expires( 1h35m )
end
@typesafe

This tab determines whether a given fact type is compiled with or without type safety. By default, all type declarations are compiled with type safety enabled. You can override this behavior to type-unsafe evaluation, where all constraints are generated as MVEL constraints and executed dynamically. This is useful when dealing with collections that do not have any generics or mixed type collections.

Default parameter: true

Supported parameters: true, false

@typesafe( BOOLEAN )
Example: Declare VoiceCall for type-unsafe evaluation
declare VoiceCall
  @role( fact )
  @typesafe( false )
end
@serialVersionUID

This tag defines an identifying serialVersionUID value for a serializable class in a fact declaration. If a serializable class does not explicitly declare a serialVersionUID, the serialization run time calculates a default serialVersionUID value for that class based on various aspects of the class, as described in the Java Object Serialization Specification. However, for optimal deserialization results and for greater compatibility with serialized KIE sessions, set the serialVersionUID as needed in the relevant class or in your DRL declarations.

Default parameter: Null

Supported parameters: Custom serialVersionUID integer

@serialVersionUID( INTEGER )
Example: Declare serialVersionUID for a VoiceCall class
declare VoiceCall
  @serialVersionUID( 42 )
end
@key

This tag enables a fact type attribute to be used as a key identifier for the fact type. The generated class can then implement the equals() and hashCode() methods to determine if two instances of the type are equal to each other. The decision engine can also generate a constructor using all the key attributes as parameters.

Default parameter: None

Supported parameters: None

ATTRIBUTE_DEFINITION @key
Example: Declare Person type attributes as keys
declare Person
    firstName : String @key
    lastName : String @key
    age : int
end

For this example, the decision engine checks the firstName and lastName attributes to determine if two instances of Person are equal to each other, but it does not check the age attribute. The decision engine also implicitly generates three constructors: one without parameters, one with the @key fields, and one with all fields:

Example constructors from the key declarations
Person() // Empty constructor

Person( String firstName, String lastName )

Person( String firstName, String lastName, int age )

You can then create instances of the type based on the key constructors, as shown in the following example:

Example instance using the key constructor
Person person = new Person( "John", "Doe" );
4.2.2.5. Queries in DRL
query
Figure 172. Query

Queries in DRL files search the working memory of the decision engine for facts related to the rules in the DRL file. You add the query definitions in DRL files and then obtain the matching results in your application code. Queries search for a set of defined conditions and do not require when or then specifications. Query names are scoped to the rule unit, so each query name must be unique within the same rule unit. In Kogito, queries are automatically exposed as REST endpoints.

The following example is a query definition for an Alert object with a severity field set to HIGH:

Example query definition in a DRL file
package com.acme;
unit MonitoringService;

query highSeverity
    alerts : /alertData[ severity == "HIGH" ]
end

Kogito automatically exposes this query through an endpoint /high-severity.

For this example, assume that the MonitoringService rule unit class has the following form:

Example Java rule unit class
package com.acme;

import org.kie.kogito.rules.DataSource;
import org.kie.kogito.rules.DataStream;

public class MonitoringService implements RuleUnitData {
    private DataStream<Temperature> temperature = DataSource.createStream();
    private DataStream<Alert> alertData = DataSource.createStream();
    public DataStream<Temperature> getTemperature() { return temperature; }
    public DataStream<Alert> getAlertData() { return alertData; }
}

In this case, you can invoke the query using the following command:

Example POST request to the /high-severity endpoint
$ curl -X POST \
        -H 'Accept: application/json' \
        -H 'Content-Type: application/json' \
        -d '{ "eventData": [ { "type": "temperature", "value" : 20 }, { "type": "temperature", "value" : 100 } ] }' \
        http://localhost:8080/high-severity
Example response (JSON)
{
    "alerts" : [
        {
            "severity" : "HIGH",
            "message" : "Temperature exceeds threshold: 100"
        }
    ]
}

This example submits the data to the eventData data source and returns the result of the highSeverity query as a response.

4.2.2.6. Property-change settings and listeners for fact types

By default, the decision engine does not re-evaluate all fact patterns for fact types each time a rule is triggered, but instead reacts only to modified properties that are constrained or bound inside a given pattern. For example, if a rule calls modify() on a fact as part of the rule actions, this modification does not automatically trigger the re-evaluation of all patterns referring to the modified type. Only the patterns constrained on the changed properties of that fact are re-evaluated. This property reactivity behavior prevents unwanted recursions and results in more efficient rule evaluation. This behavior also avoids the need of using the no-loop rule attribute to prevent infinite recursion.

You can modify or disable this property reactivity behavior with the following options, and then use a property-change setting in your Java class or DRL files to fine-tune property reactivity as needed:

  • ALWAYS: (Default) All types are property reactive, but you can disable property reactivity for a specific type by using the @classReactive property-change setting.

  • ALLOWED: No types are property reactive, but you can enable property reactivity for a specific type by using the @propertyReactive property-change setting.

  • DISABLED: No types are property reactive. All property-change listeners are ignored.

To set the property reactivity behavior, update the drools.propertySpecific system property in the application.properties file of your Kogito project:

Example property reactivity setting in system properties
drools.propertySpecific=ALLOWED

The decision engine supports the following property-change settings and listeners for fact classes or declared DRL fact types:

@classReactive

If property reactivity is set to ALWAYS in the decision engine (all types are property reactive), this tag disables the default property reactivity behavior for a specific Java class or a declared DRL fact type. You can use this tag if you want the decision engine to re-evaluate all fact patterns for the specified fact type each time the rule is triggered, instead of reacting only to modified properties that are constrained or bound inside a given pattern.

Example: Disable default property reactivity in a DRL type declaration
declare Person
  @classReactive
    firstName : String
    lastName : String
end
Example: Disable default property reactivity in a Java class
@classReactive
public static class Person {
    private String firstName;
    private String lastName;
}
@propertyReactive

If property reactivity is set to ALLOWED in the decision engine (no types are property reactive unless specified), this tag enables property reactivity for a specific Java class or a declared DRL fact type. You can use this tag if you want the decision engine to react only to modified properties that are constrained or bound inside a given pattern for the specified fact type, instead of re-evaluating all fact patterns for the fact each time the rule is triggered.

Example: Enable property reactivity in a DRL type declaration (when reactivity is disabled globally)
declare Person
  @propertyReactive
    firstName : String
    lastName : String
end
Example: Enable property reactivity in a Java class (when reactivity is disabled globally)
@propertyReactive
public static class Person {
    private String firstName;
    private String lastName;
}
@propertyChangeSupport

For facts that implement support for property changes as defined in the JavaBeans Specification, this tag enables the decision engine to monitor changes in the fact properties.

Example: Declare property change support in JavaBeans object
declare Person
    @propertyChangeSupport
end
4.2.2.7. Rule attributes in DRL
rule attributes
Figure 173. Rule attributes

Rule attributes are additional specifications that you can add to business rules to modify rule behavior. In DRL files, you typically define rule attributes above the rule conditions and actions, with multiple attributes on separate lines, in the following format:

rule "rule_name"
    // Attribute
    // Attribute
    when
        // Conditions
    then
        // Actions
end

The following table lists the names and supported values of the attributes that you can assign to rules:

Table 90. Rule attributes
Attribute Value

salience

An integer defining the priority of the rule. Rules with a higher salience value are given higher priority when ordered in the activation queue.

Example: salience 10

enabled

A Boolean value. When the option is selected, the rule is enabled. When the option is not selected, the rule is disabled.

Example: enabled true

date-effective

A string containing a date and time definition. The rule can be activated only if the current date and time is after a date-effective attribute.

Example: date-effective "4-Sep-2018"

date-expires

A string containing a date and time definition. The rule cannot be activated if the current date and time is after the date-expires attribute.

Example: date-expires "4-Oct-2018"

no-loop

A Boolean value. When the option is selected, the rule cannot be reactivated (looped) if a consequence of the rule re-triggers a previously met condition. When the condition is not selected, the rule can be looped in these circumstances.

Example: no-loop true

activation-group

A string identifying an activation (or XOR) group to which you want to assign the rule. In activation groups, only one rule can be activated. The first rule to fire will cancel all pending activations of all rules in the activation group.

Example: activation-group "GroupName"

duration

A long integer value defining the duration of time in milliseconds after which the rule can be activated, if the rule conditions are still met.

Example: duration 10000

timer

A string identifying either int (interval) or cron timer definitions for scheduling the rule.

Example: timer ( cron:* 0/15 * * * ? ) (every 15 minutes)

calendar

A Quartz calendar definition for scheduling the rule.

Example: calendars "* * 0-7,18-23 ? * *" (exclude non-business hours)

auto-focus

A Boolean value, applicable only to rules within agenda groups. When the option is selected, the next time the rule is activated, a focus is automatically given to the agenda group to which the rule is assigned.

Example: auto-focus true

lock-on-active

A Boolean value, applicable only to rules within rule flow groups or agenda groups. When the option is selected, the next time the ruleflow group for the rule becomes active or the agenda group for the rule receives a focus, the rule cannot be activated again until the ruleflow group is no longer active or the agenda group loses the focus. This is a stronger version of the no-loop attribute, because the activation of a matching rule is discarded regardless of the origin of the update (not only by the rule itself). This attribute is ideal for calculation rules where you have a number of rules that modify a fact and you do not want any rule re-matching and firing again.

Example: lock-on-active true

dialect

A string identifying either JAVA or MVEL as the language to be used for code expressions in the rule. By default, the rule uses the dialect specified at the package level. Any dialect specified here overrides the package dialect setting for the rule.

Example: dialect "JAVA"

4.2.2.8. Rule conditions in DRL
rule
Figure 174. Rule
lhs
Figure 175. Conditional element in a rule

The when part of a DRL rule (also known as the Left Hand Side (LHS) of the rule) contains the conditions that must be met to execute an action. Conditions consist of a series of stated OOPath expressions of patterns and constraints, with optional bindings and supported rule condition elements (keywords), based on the available data objects in the package. OOPath is an object-oriented syntax extension to XPath for navigating through related elements while handling collections and filtering constraints.

For example, in a decision service that raises alerts when the temperature reaches or exceeds 80 degrees, a rule tooHot contains the when condition /temperature[value >= 80].

DRL uses when instead of if because if is typically part of a procedural execution flow during which a condition is checked at a specific point in time. In contrast, when indicates that the condition evaluation is not limited to a specific evaluation sequence or point in time, but instead occurs continually at any time. Whenever the condition is met, the actions are executed.

If the when section is empty, then the conditions are considered to be true and the actions in the then section are executed the first time the rules are fired. This is useful if you want to use rules to set up the decision engine state.

The following example rule uses empty conditions to insert a fact every time the rule is executed:

Example rule without conditions
rule "start-up"
  when
    // Empty
  then   // Actions to be executed once
    alerts.add( new Alert("INFO", "System started") );
end

Formally, the core grammar of an OOPath expression is defined in extended Backus-Naur form (EBNF) notation in the following way:

EBNF notation for OOPath expressions
OOPExpr = [ID ( ":" | ":=" )] ( "/" | "?/" ) OOPSegment { ( "/" | "?/" | "." ) OOPSegment } ;
OOPSegment = ID ["#" ID] ["[" ( Number | Constraints ) "]"]
OOPath expressions and constraints

An OOPath expression of a pattern in a DRL rule condition is the segment to be matched by the decision engine. An OOPath expression can potentially match each fact that is inserted into the working memory of the decision engine. It can also contain constraints to further define the facts to be matched.

In the simplest form, with no constraints, an OOPath expression matches a fact in the given data source. In the following example with a DataSource<Person> named person, the expression matches against all Person objects in the data source of the decision engine:

Example expression for a single fact type
/person

Patterns can also refer to superclasses or even interfaces, potentially matching facts from many different classes. For example, the following pattern matches all Student subtypes of the Person object:

Example pattern for subtypes
/person # Student

Square brackets in a pattern enclose the constraints, such as the following constraint on the person’s age:

Example pattern with a constraint
/person[ age == 50 ]

A constraint is an expression that returns true or false. Constraints in DRL are essentially Java expressions with some enhancements, such as property access, and some differences, such as equals() and !equals() semantics for == and != (instead of the usual same and not same semantics).

Any JavaBeans property can be accessed directly from pattern constraints. A JavaBeans property is exposed internally using a standard JavaBeans getter that takes no arguments and returns something. For example, the age property is written as age in DRL instead of the getter getAge():

DRL constraint syntax with JavaBeans properties
/person[ age == 50 ]

// This is equivalent to the following getter format:

/person[ getAge() == 50 ]

Kogito uses the standard JDK Introspector class to achieve this mapping and follows the standard JavaBeans specification. For optimal decision engine performance, use the property access format, such as age, instead of using getters explicitly, such as getAge().

Do not use property accessors to change the state of the object in a way that might affect the rules because the decision engine caches the results of the match between invocations for higher efficiency.

For example, do not use property accessors in the following ways:

public int getAge() {
    age++; // Do not do this.
    return age;
}
public int getAge() {
    Date now = DateUtil.now(); // Do not do this.
    return DateUtil.differenceInYears(now, birthday);
}

Instead of following the second example, insert a fact that wraps the current date in the working memory and update that fact between rule executions as needed.

However, if the getter of a property cannot be found, the compiler uses the property name as a fallback method name, without arguments:

Fallback method if object is not found
/person[ age == 50 ]

// If `Person.getAge()` does not exist, the compiler uses the following syntax:

/person[ age() == 50 ]

You can also nest access properties in patterns, as shown in the following example. Nested properties are indexed by the decision engine.

Example pattern with nested property access
/person[ address.houseNumber == 50 ]

// This is equivalent to the following expression:

/person[ getAddress().getHouseNumber() == 50 ]

You can use any Java expression that returns a boolean value as a constraint inside the parentheses of a pattern. Java expressions can be mixed with other expression enhancements, such as property access:

Example pattern with a constraint using property access and Java expression
/person[ age == 50 ]

You can change the evaluation priority by using parentheses, as in any logical or mathematical expression:

Example evaluation order of constraints
/person[ age > 100 && ( age % 10 == 0 ) ]

You can also reuse Java methods in constraints, as shown in the following example:

Example constraints with reused Java methods
/person[ Math.round( weight / ( height * height ) ) < 25.0 ]

Do not use constraints to change the state of the object in a way that might affect the rules because the decision engine caches the results of the match between invocations for higher efficiency. Any method that is executed on a fact in the rule conditions must be a read-only method. Also, the state of a fact should not change between rule invocations unless those facts are marked as updated in the working memory on every change.

For example, do not use a pattern constraint in the following ways:

/person[ incrementAndGetAge() == 10 ] // Do not do this.
/person[ System.currentTimeMillis() % 1000 == 0 ] // Do not do this.

Standard Java operator precedence applies to constraint operators in DRL, and DRL operators follow standard Java semantics except for the == and != operators.

The == operator uses null-safe equals() semantics instead of the usual same semantics. For example, the pattern /person[ firstName == "John" ] is similar to java.util.Objects.equals(person.getFirstName(), "John"), and because "John" is not null, the pattern is also similar to "John".equals(person.getFirstName()).

The != operator uses null-safe !equals() semantics instead of the usual not same semantics. For example, the pattern /person[ firstName != "John" ] is similar to !java.util.Objects.equals(person.getFirstName(), "John").

If the field and the value of a constraint are of different types, the decision engine uses type coercion to resolve the conflict and reduce compilation errors. For instance, if "ten" is provided as a string in a numeric evaluator, a compilation error occurs, whereas "10" is coerced to a numeric 10. In coercion, the field type always takes precedence over the value type:

Example constraint with a value that is coerced
/person[ age == "10" ] // "10" is coerced to 10

For groups of constraints, you can use a delimiting comma , to use implicit and connective semantics:

Example patterns with multiple constraints
// Person is at least 50 years old and weighs at least 80 kilograms:
/person[ age > 50, weight > 80 ]

// Person is at least 50 years old, weighs at least 80 kilograms, and is taller than 2 meters:
/person[ age > 50, weight > 80, height > 2 ]
Although the && and , operators have the same semantics, they are resolved with different priorities. The && operator precedes the || operator, and both the && and || operators together precede the , operator. Use the comma operator at the top-level constraint for optimal decision engine performance and human readability.

You cannot embed a comma operator in a composite constraint expression, such as in parentheses:

Example of misused comma in composite constraint expression
// Do not use the following format:
/person[ ( age > 50, weight > 80 ) || height > 2 ]

// Use the following format instead:
/person[ ( age > 50 && weight > 80 ) || height > 2 ]
Bound variables in patterns and constraints

You can bind variables to OOPath expressions of patterns and constraints to refer to matched objects in other portions of a rule. Bound variables can help you define rules more efficiently or more consistently with how you annotate facts in your data model.

For example, the following DRL rule uses the variable $p for an OOPath expression with the Person fact:

Pattern with a bound variable
rule "simple rule"
  when
    $p : /person
  then
    System.out.println( "Person " + p );
end

Similarly, you can also bind variables to nested properties, as shown in the following example:

// Two persons of the same age:
/person[ firstAge : age ]  // Binding
and
/person[ age == firstAge ] // Constraint expression

Ensure that you separate constraint bindings and constraint expressions for clearer and more efficient rule definitions. Although mixed bindings and expressions are supported, they can complicate patterns and affect evaluation efficiency.

// Do not use the following format:
/person[ age : age * 2 < 100 ]

// Use the following format instead:
/person[ age * 2 < 100, $age : age ]
Nested constraints and inline casts

In some cases, you might need to access multiple properties of a nested object, as shown in the following example:

Example pattern to access multiple properties
/person[ name == "mark", address.city == "london", address.country == "uk" ]

You can group these property accessors to nested objects for more readable rules, as shown in the following example:

Example pattern with grouped constraints
/person[ name == "mark"]/address[ city == "london", country == "uk" ]

When you work with nested objects, you can use the syntax TYPE#SUB_TYPE to cast to a subtype and make the getters from the parent type available to the subtype. You can use either the object name or fully qualified class name, and you can cast to one or multiple subtypes, as shown in the following examples:

Example patterns with inline casting to a subtype
// Inline casting with subtype name:
/person[ name == "mark"]/address#LongAddress[ country == "uk" ]

// Inline casting with fully qualified class name:
/person[ name == "mark"]/address#org.domain.LongAddress[ country == "uk" ]

// Multiple inline casts:
/person[ name == "mark" ]/address#LongAddress/country#DetailedCountry[ population > 10000000 ]

These example patterns cast Address to LongAddress, and additionally to DetailedCountry in the last example, making the parent getters available to the subtypes in each case.

Date literal in constraints

By default, the decision engine supports the date format dd-mmm-yyyy. You can customize the date format, including a time format mask if needed, by providing an alternative format mask with the system property drools.dateformat="dd-mmm-yyyy hh:mm". You can also customize the date format by changing the language locale with the drools.defaultlanguage and drools.defaultcountry system properties. For example, the locale of Thailand is set as drools.defaultlanguage=th and drools.defaultcountry=TH.

Example pattern with a date literal restriction
/person[ bornBefore < "27-Oct-2009" ]
Auto-boxing and primitive types

Drools attempts to preserve numbers in their primitive or object wrapper form, so a variable bound to an int primitive when used in a code block or expression will no longer need manual unboxing; unlike early Drools versions where all primitives were autoboxed, requiring manual unboxing. A variable bound to an object wrapper will remain as an object; the existing JDK 1.5 and JDK 5 rules to handle auto-boxing and unboxing apply in this case. When evaluating field constraints, the system attempts to coerce one of the values into a comparable format; so a primitive is comparable to an object wrapper.

Supported operators in DRL constraints

DRL supports standard Java semantics for operators in constraints, with some exceptions and with some additional operators that are unique in DRL. The following list summarizes the operators that are handled differently in DRL constraints than in standard Java semantics or that are unique in DRL constraints.

/, #

Use the / operator to group property accessors to nested objects, and use the # operator to cast to a subtype in nested objects. Casting to a subtype makes the getters from the parent type available to the subtype. You can use either the object name or fully qualified class name, and you can cast to one or multiple subtypes.

Example constraints with nested objects
// Ungrouped property accessors:
/person[ name == "mark", address.city == "london", address.country == "uk" ]

// Grouped property accessors:
/person[ name == "mark"]/address[ city == "london", country == "uk" ]
Example constraints with inline casting to a subtype
// Inline casting with subtype name:
/person[ name == "mark", address#LongAddress.country == "uk" ]

// Inline casting with fully qualified class name:
/person[ name == "mark", address#org.domain.LongAddress.country == "uk" ]

// Multiple inline casts:
/person[ name == "mark", address#LongAddress.country#DetailedCountry.population > 10000000 ]
!.

Use this operator to dereference a property in a null-safe way. The value to the left of the !. operator must be not null (interpreted as != null) in order to give a positive result for pattern matching.

Example constraint with null-safe dereferencing
/person[ $streetName : address!.street ]

// This is internally rewritten in the following way:

/person[ address != null, $streetName : address.street ]
[]

Use this operator to access a List value by index or a Map value by key.

Example constraints with List and Map access
// The following format is the same as `childList(0).getAge() == 18`:
/person[childList[0].age == 18]

// The following format is the same as `credentialMap.get("jdoe").isValid()`:
/person[credentialMap["jdoe"].valid]
<, <=, >, >=

Use these operators on properties with natural ordering. For example, for Date fields, the < operator means before, and for String fields, the operator means alphabetically before. These properties apply only to comparable properties.

Example constraints with before operator
/person[ birthDate < $otherBirthDate ]

/person[ firstName < $otherFirstName ]
==, !=

Use these operators as equals() and !equals() methods in constraints, instead of the usual same and not same semantics.

Example constraint with null-safe equality
/person[ firstName == "John" ]

// This is similar to the following formats:

java.util.Objects.equals(person.getFirstName(), "John")
"John".equals(person.getFirstName())
Example constraint with null-safe not equality
/person[ firstName != "John" ]

// This is similar to the following format:

!java.util.Objects.equals(person.getFirstName(), "John")
&&, ||

Use these operators to create an abbreviated combined relation condition that adds more than one restriction on a field. You can group constraints with parentheses () to create a recursive syntax pattern.

Example constraints with abbreviated combined relation
// Simple abbreviated combined relation condition using a single `&&`:
/person[age > 30 && < 40]

// Complex abbreviated combined relation using groupings:
/person[age ((> 30 && < 40) || (> 20 && < 25))]

// Mixing abbreviated combined relation with constraint connectives:
/person[age > 30 && < 40 || location == "london"]
abbreviatedCombinedRelationCondition
Figure 176. Abbreviated combined relation condition
abbreviatedCombinedRelationConditionGroup
Figure 177. Abbreviated combined relation condition withparentheses
matches, not matches

Use these operators to indicate that a field matches or does not match a specified Java regular expression. Typically, the regular expression is a String literal, but variables that resolve to a valid regular expression are also supported. These operators apply only to String properties. If you use matches against a null value, the resulting evaluation is always false. If you use not matches against a null value, the resulting evaluation is always true. As in Java, regular expressions that you write as String literals must use a double backslash \\ to escape.

Example constraint to match or not match a regular expression
/person[ country matches "(USA)?\\S*UK" ]

/person[ country not matches "(USA)?\\S*UK" ]
contains, not contains

Use these operators to verify whether a field that is an Array or a Collection contains or does not contain a specified value. These operators apply to Array or Collection properties, but you can also use these operators in place of String.contains() and !String.contains() constraints checks.

Example constraints with contains and not contains for a Collection
// Collection with a specified field:
/familyTree[ countries contains "UK" ]

/familyTree[ countries not contains "UK" ]


// Collection with a variable:
/familyTree[ countries contains $var ]

/familyTree[ countries not contains $var ]
Example constraints with contains and not contains for a String literal
// Sting literal with a specified field:
/person[ fullName contains "Jr" ]

/person[ fullName not contains "Jr" ]


// String literal with a variable:
/person[ fullName contains $var ]

/person[ fullName not contains $var ]
For backward compatibility, the excludes operator is a supported synonym for not contains.
memberOf, not memberOf

Use these operators to verify whether a field is a member of or is not a member of an Array or a Collection that is defined as a variable. The Array or Collection must be a variable.

Example constraints with memberOf and not memberOf with a Collection
/familyTree[ person memberOf $europeanDescendants ]

/familyTree[ person not memberOf $europeanDescendants ]
soundslike

Use this operator to verify whether a word has almost the same sound, using English pronunciation, as the given value (similar to the matches operator). This operator uses the Soundex algorithm.

Example constraint with soundslike
// Match firstName "Jon" or "John":
/person[ firstName soundslike "John" ]
str

Use this operator to verify whether a field that is a String starts with or ends with a specified value. You can also use this operator to verify the length of the String.

Example constraints with str
// Verify what the String starts with:
/message[ routingValue str[startsWith] "R1" ]

// Verify what the String ends with:
/message[ routingValue str[endsWith] "R2" ]

// Verify the length of the String:
/message[ routingValue str[length] 17 ]
in, notin

Use these operators to specify more than one possible value to match in a constraint (compound value restriction). This functionality of compound value restriction is supported only in the in and not in operators. The second operand of these operators must be a comma-separated list of values enclosed in parentheses. You can provide values as variables, literals, return values, or qualified identifiers. These operators are internally rewritten as a list of multiple restrictions using the operators == or !=.

compoundValueRestriction
Figure 178. compoundValueRestriction
Example constraints with in and notin
/person[ $color : favoriteColor ]
/color[ type in ( "red", "blue", $color ) ]

/person[ $color : favoriteColor ]
/color[ type notin ( "red", "blue", $color ) ]
Operator precedence in DRL pattern constraints

DRL supports standard Java operator precedence for applicable constraint operators, with some exceptions and with some additional operators that are unique in DRL. The following table lists DRL operator precedence where applicable, from highest to lowest precedence:

Table 91. Operator precedence in DRL pattern constraints
Operator type Operators Notes

Nested or null-safe property access

/, !.

Not standard Java semantics

List or Map access

[]

Not standard Java semantics

Constraint binding

:

Not standard Java semantics

Multiplicative

*, /%

Additive

+, -

Shift

>>, >>>, <<

Relational

<, <=, >, >=, instanceof

Equality

== !=

Uses equals() and !equals() semantics, not standard Java same and not same semantics

Non-short-circuiting AND

&

Non-short-circuiting exclusive OR

^

Non-short-circuiting inclusive OR

|

Logical AND

&&

Logical OR

||

Ternary

? :

Comma-separated AND

,

Not standard Java semantics

Supported rule condition elements in DRL (keywords)

DRL supports the following rule condition elements (keywords) that you can use with the patterns that you define in DRL rule conditions:

and

Use this to group conditional components into a logical conjunction. Infix and prefix and are supported. You can group patterns explicitly with parentheses (). By default, all listed patterns are combined with and when no conjunction is specified.

infixAnd
Figure 179. infixAnd
prefixAnd
Figure 180. prefixAnd
Example patterns with and
//Infix `and`:
colorType: /color/type and /person[ favoriteColor == colorType ]

//Infix `and` with grouping:
(colorType: /color/type and (/person[ favoriteColor == colorType ] or /person[ favoriteColor == colorType ])

// Prefix `and`:
(and colorType: /color/type /person[ favoriteColor == colorType ])

// Default implicit `and`:
colorType: /color/type
/person[ favoriteColor == colorType ]

Do not use a leading declaration binding with the and keyword (as you can with or, for example). A declaration can only reference a single fact at a time, and if you use a declaration binding with and, then when and is satisfied, it matches both facts and results in an error.

Example misuse of and
// Causes compile error:
$person : (/person[ name == "Romeo" ] and /person[ name == "Juliet"])
or

Use this to group conditional components into a logical disjunction. Infix and prefix or are supported. You can group patterns explicitly with parentheses (). You can also use pattern binding with or, but each pattern must be bound separately.

infixOr
Figure 181. infixOr
prefixOr
Figure 182. prefixOr
Example patterns with or
//Infix `or`:
colorType: /color/type or /person[ favoriteColor == colorType]

//Infix `or` with grouping:
colorType: /color/type or (/person[ favoriteColor == colorType] and /person[ favoriteColor == colorType])

// Prefix `or`:
(or colorType: /color/type /person[ favoriteColor == colorType])
Example patterns with or and pattern binding
pensioner : ( /person[ sex == "f", age > 60 ] or /person[ sex == "m", age > 65 ] )

(or pensioner : /person[ sex == "f", age > 60 ]
    pensioner : /person[ sex == "m", age > 65 ])

The behavior of the or condition element is different from the connective || operator for constraints and restrictions in field constraints. The decision engine does not directly interpret the or element but uses logical transformations to rewrite a rule with or as a number of sub-rules. This process ultimately results in a rule that has a single or as the root node and one sub-rule for each of its condition elements. Each sub-rule is activated and executed like any normal rule, with no special behavior or interaction between the sub-rules.

Therefore, consider the or condition element a shortcut for generating two or more similar rules that, in turn, can create multiple activations when two or more terms of the disjunction are true.

exists

Use this to specify facts and constraints that must exist. This option is triggered on only the first match, not subsequent matches. If you use this element with multiple patterns, enclose the patterns with parentheses ().

exists
Figure 183. Exists
Example patterns with exists
exists /person[ firstName == "John"]

exists (/person[ firstName == "John", age == 42 ])

exists (/person[ firstName == "John" ] and
        /person[ lastName == "Doe" ])
not

Use this to specify facts and constraints that must not exist. If you use this element with multiple patterns, enclose the patterns with parentheses ().

not
Figure 184. Not
Example patterns with not
not /person[ firstName == "John"]

not (/person[ firstName == "John", age == 42 )]

not (/person[ firstName == "John" ] and
     /person[ lastName == "Doe" ])
forall

Use this to verify whether all facts that match the first pattern match all the remaining patterns. When a forall construct is satisfied, the rule evaluates to true. This element is a scope delimiter, so it can use any previously bound variable, but no variable bound inside of it is available for use outside of it.

forall
Figure 185. Forall
Example rule with forall
rule "All full-time employees have red ID badges"
  when
    forall( $emp : /employee[ type == "fulltime" ]
                   /employee[ this == $emp, badgeColor = "red" ] )
  then
    // True, all full-time employees have red ID badges.
end

In this example, the rule selects all employee objects whose type is "fulltime". For each fact that matches this pattern, the rule evaluates the patterns that follow (badge color) and if they match, the rule evaluates to true.

To state that all facts of a given type in the working memory of the decision engine must match a set of constraints, you can use forall with a single pattern for simplicity.

Example rule with forall and a single pattern
rule "All full-time employees have red ID badges"
  when
    forall( /employee[ badgeColor = "red" ] )
  then
    // True, all full-time employees have red ID badges.
end

You can use forall constructs with multiple patterns or nest them with other condition elements, such as inside a not element construct.

Example rule with forall and multiple patterns
rule "All employees have health and dental care programs"
  when
    forall( $emp : /employee
            /healthCare[ employee == $emp ]
            /dentalCare[ employee == $emp ]
          )
  then
    // True, all employees have health and dental care.
end
Example rule with forall and not
rule "Not all employees have health and dental care"
  when
    not ( forall( $emp : /employee
            /healthCare[ employee == $emp ]
            /dentalCare[ employee == $emp ] )
        )
  then
    // True, not all employees have health and dental care.
end
The format forall( p1 p2 p3 …​) is equivalent to not( p1 and not( and p2 p3 …​ ) ).
accumulate

Use this to iterate over a collection of objects, execute custom actions for each of the elements, and return one or more result objects (if the constraints evaluate to true). You can use predefined functions in your accumulate conditions or implement custom functions as needed. You can also use the abbreviation acc for accumulate in rule conditions.

Use the following format to define accumulate conditions in rules:

Preferred format for accumulate
accumulate( SOURCE_PATTERN; FUNCTIONS [;CONSTRAINTS] )
accumulate
Figure 186. Accumulate
Although the decision engine supports alternate formats for the accumulate element for backward compatibility, this format is preferred for optimal performance in rules and applications.

The decision engine supports the following predefined accumulate functions. These functions accept any expression as input.

  • average

  • min

  • max

  • count

  • sum

  • collectList

  • collectSet

In the following example rule, min, max, and average are accumulate functions that calculate the minimum, maximum, and average temperature values over all the readings for each sensor:

Example rule with accumulate to calculate temperature values
rule "Raise alarm"
  when
    s : /sensor
    accumulate( /reading( sensor == $s, $temp : temperature );
                $min : min( $temp ),
                $max : max( $temp ),
                $avg : average( $temp );
                $min < 20, $avg > 70 )
  then
    // Raise the alarm.
end

The following example rule uses the average function with accumulate to calculate the average profit for all items in an order:

Example rule with accumulate to calculate average profit
rule "Average profit"
  when
    $order : /order
    accumulate( /orderItem( order == $order, $cost : cost, $price : price );
                $avgProfit : average( 1 - $cost / $price ) )
  then
    // Average profit for `$order` is `$avgProfit`.
end

To use custom, domain-specific functions in accumulate conditions, create a Java class that implements the org.kie.api.runtime.rule.AccumulateFunction interface. For example, the following Java class defines a custom implementation of an AverageData function:

Example Java class with custom implementation of average function
// An implementation of an accumulator capable of calculating average values

public class AverageAccumulateFunction implements org.kie.api.runtime.rule.AccumulateFunction<AverageAccumulateFunction.AverageData> {

    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {

    }

    public void writeExternal(ObjectOutput out) throws IOException {

    }

    public static class AverageData implements Externalizable {
        public int    count = 0;
        public double total = 0;

        public AverageData() {}

        public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
            count   = in.readInt();
            total   = in.readDouble();
        }

        public void writeExternal(ObjectOutput out) throws IOException {
            out.writeInt(count);
            out.writeDouble(total);
        }

    }

    /* (non-Javadoc)
     * @see org.kie.api.runtime.rule.AccumulateFunction#createContext()
     */
    public AverageData createContext() {
        return new AverageData();
    }

    /* (non-Javadoc)
     * @see org.kie.api.runtime.rule.AccumulateFunction#init(java.io.Serializable)
     */
    public void init(AverageData context) {
        context.count = 0;
        context.total = 0;
    }

    /* (non-Javadoc)
     * @see org.kie.api.runtime.rule.AccumulateFunction#accumulate(java.io.Serializable, java.lang.Object)
     */
    public void accumulate(AverageData context,
                           Object value) {
        context.count++;
        context.total += ((Number) value).doubleValue();
    }

    /* (non-Javadoc)
     * @see org.kie.api.runtime.rule.AccumulateFunction#reverse(java.io.Serializable, java.lang.Object)
     */
    public void reverse(AverageData context, Object value) {
        context.count--;
        context.total -= ((Number) value).doubleValue();
    }

    /* (non-Javadoc)
     * @see org.kie.api.runtime.rule.AccumulateFunction#getResult(java.io.Serializable)
     */
    public Object getResult(AverageData context) {
        return new Double( context.count == 0 ? 0 : context.total / context.count );
    }

    /* (non-Javadoc)
     * @see org.kie.api.runtime.rule.AccumulateFunction#supportsReverse()
     */
    public boolean supportsReverse() {
        return true;
    }

    /* (non-Javadoc)
     * @see org.kie.api.runtime.rule.AccumulateFunction#getResultType()
     */
    public Class< ? > getResultType() {
        return Number.class;
    }

}

To use the custom function in a DRL rule, import the function using the import accumulate statement:

Format to import a custom function
import accumulate CLASS_NAME FUNCTION_NAME
Example rule with the imported average function
import accumulate AverageAccumulateFunction.AverageData average

rule "Average profit"
  when
    $order : /order
    accumulate( /orderItem[ order == $order, $cost : cost, $price : price ];
                $avgProfit : average( 1 - $cost / $price ) )
  then
    // Average profit for `$order` is `$avgProfit`.
end

For backward compatibility, the decision engine also supports the configuration of accumulate functions through configuration files and system properties, but this is a deprecated method. To configure the average function from the previous example using the configuration file or system property, set a property as shown in the following example:

drools.accumulate.function.average = AverageAccumulateFunction.AverageData

Note that drools.accumulate.function is a required prefix, average is how the function is used in the DRL files, and AverageAccumulateFunction.AverageData is the fully qualified name of the class that implements the function behavior.

4.2.2.9. Rule actions in DRL

The then part of the rule (also known as the Right Hand Side (RHS) of the rule) contains the actions to be performed when the conditional part of the rule has been met. Rule actions are typically determined by one or more data sources that you define as part of your DRL rule unit. For example, if a bank requires loan applicants to have over 21 years of age (with a rule condition /applicant[ applicantName : name, age < 21 ]) and a loan applicant is under 21 years old, the then action of an "Underage" rule would be setApproved( false ) based on a defined data source, declining the loan because the applicant is under age.

The main purpose of rule actions is to to insert, delete, or modify data in the working memory of the decision engine. Effective rule actions are small, declarative, and readable. If you need to use imperative or conditional code in rule actions, then divide the rule into multiple smaller and more declarative rules.

Example rule for loan application age limit
rule "Underage"
  when
    /applicant[ applicantName : name, age < 21 ]
    $application : /loanApplication[ applicant == applicantName ]
  then
    $application.setApproved( false );
    $application.setExplanation( "Underage" );
end

For more information about using data sources for rule actions, see Rule units in DRL.

4.2.2.10. Comments in DRL files

DRL supports single-line comments prefixed with a double forward slash // and multi-line comments enclosed with a forward slash and asterisk /* …​ */. You can use DRL comments to annotate rules or any related components in DRL files. DRL comments are ignored by the decision engine when the DRL file is processed.

Example rule with comments
rule "Underage"
  // This is a single-line comment.
  when
    /applicant[ applicantName : name, age < 21 ]  // This is an in-line comment
    $application : /loanApplication[ applicant == applicantName ]
  then
    /* This is a multi-line comment
    in the rule actions. */
    $application.setApproved( false );
    $application.setExplanation( "Underage" );
end
multi line comment
Figure 187. Multi-line comment
The hash symbol # is not supported for DRL comments.
4.2.2.11. Error messages for DRL troubleshooting

Kogito provides standardized messages for DRL errors to help you troubleshoot and resolve problems in your DRL files. The error messages use the following format:

error message
Figure 188. Error message format for DRL file problems
  • 1st Block: Error code

  • 2nd Block: Line and column in the DRL source where the error occurred

  • 3rd Block: Description of the problem

  • 4th Block: Component in the DRL source (rule, function, query) where the error occurred

  • 5th Block: Pattern in the DRL source where the error occurred (if applicable)

Kogito supports the following standardized error messages:

101: no viable alternative

Indicates that the parser reached a decision point but could not identify an alternative.

Example rule with incorrect spelling
1: rule "simple rule"
2:   when
3:     exists /person
4:     exits /student  // Must be `exists`
5:   then
6: end
Error message
[ERR 101] Line 4:4 no viable alternative at input 'exits' in rule "simple rule"
Example rule without a rule name
1: package org.drools.examples;
2: rule    // Must be `rule "rule name"` (or `rule rule_name` if no spacing)
3:   when
4:     Object()
5:   then
6:     System.out.println("A RHS");
7: end
Error message
[ERR 101] Line 3:2 no viable alternative at input 'when'

In this example, the parser encountered the keyword when but expected the rule name, so it flags when as the incorrect expected token.

Example rule with incorrect syntax
1: rule "simple rule"
2:   when
3:     /student[ name == "Andy ]  // Must be `"Andy"`
4:   then
5: end
Error message
[ERR 101] Line 0:-1 no viable alternative at input '<eof>' in rule "simple rule" in pattern student
A line and column value of 0:-1 means the parser reached the end of the source file (<eof>) but encountered incomplete constructs, usually due to missing quotation marks "…​", apostrophes '…​', or parentheses (…​).
102: mismatched input

Indicates that the parser expected a particular symbol that is missing at the current input position.

Example rule with an incomplete rule statement
1: rule "simple rule"
2:   when
3:     $p : /person[
        // Must be a complete rule statement
Error message
[ERR 102] Line 0:-1 mismatched input '<eof>' expecting ']' in rule "simple rule" in pattern person
A line and column value of 0:-1 means the parser reached the end of the source file (<eof>) but encountered incomplete constructs, usually due to missing quotation marks "…​", apostrophes '…​', or parentheses (…​).
Example rule with incorrect syntax
1: package org.drools.examples;
2:
3: rule "Wrong syntax"
4:   when
5:     not /car[ ( type == "tesla", price == 10000 ) || ( type == "kia", price == 1000 ) ]
       // Must use `&&` operators instead of commas `,`
6:   then
7:     System.out.println("OK");
8: end
Error messages
[ERR 102] Line 5:36 mismatched input ',' expecting ')' in rule "Wrong syntax" in pattern car
[ERR 101] Line 5:57 no viable alternative at input 'type' in rule "Wrong syntax"
[ERR 102] Line 5:106 mismatched input ']' expecting 'then' in rule "Wrong syntax"

In this example, the syntactic problem results in multiple error messages related to each other. The single solution of replacing the commas , with && operators resolves all errors. If you encounter multiple errors, resolve one at a time in case errors are consequences of previous errors.

103: failed predicate

Indicates that a validating semantic predicate evaluated to false. These semantic predicates are typically used to identify component keywords in DRL files, such as declare, rule, exists, not, and others.

Example rule with an invalid keyword
 1: package nesting;
 2:
 3: import org.drools.compiler.Person
 4: import org.drools.compiler.Address
 5:
 6: Some text  // Must be a valid DRL keyword
 7:
 8: rule "test something"
 9:   when
10:     $p: /person[ name=="Michael" ]
11:   then
12:     $p.name = "other";
13:     System.out.println(p.name);
14: end
Error message
[ERR 103] Line 6:0 rule 'rule_key' failed predicate: {(validateIdentifierKey(DroolsSoftKeywords.RULE))}? in rule

The Some text line is invalid because it does not begin with or is not a part of a DRL keyword construct, so the parser fails to validate the rest of the DRL file.

This error is similar to 102: mismatched input, but usually involves DRL keywords.
104: trailing semi-colon not allowed

Indicates that an eval() clause in a rule condition uses a semicolon ; but must not use one.

Example rule with eval() and trailing semicolon
1: rule "simple rule"
2:   when
3:     eval( abc(); )  // Must not use semicolon `;`
4:   then
5: end
Error message
[ERR 104] Line 3:4 trailing semi-colon not allowed in rule "simple rule"
105: did not match anything

Indicates that the parser reached a sub-rule in the grammar that must match an alternative at least once, but the sub-rule did not match anything. The parser has entered a branch with no way out.

Example rule with invalid text in an empty condition
1: rule "empty condition"
2:   when
3:     None  // Must remove `None` if condition is empty
4:   then
5:      insert( new Person() );
6: end
Error message
[ERR 105] Line 2:2 required (...)+ loop did not match anything at input 'WHEN' in rule "empty condition"

In this example, the condition is intended to be empty but the word None is used. This error is resolved by removing None, which is not a valid DRL keyword, data type, or pattern construct.

4.2.2.12. Legacy DRL conventions

The following Drools Rule Language (DRL) conventions are no longer applicable or optimal in Kogito but might be available for backward compatibility.

Legacy functions in DRL
function
Figure 189. Function

Functions in DRL files put semantic code in your rule source file instead of in Java classes. Functions are especially useful if an action (then) part of a rule is used repeatedly and only the parameters differ for each rule. Above the rules in the DRL file, you can declare the function or import a static method from a helper class as a function, and then use the function by name in an action (then) part of the rule.

The following examples illustrate a function that is either declared or imported in a DRL file:

Example function declaration with a rule (option 1)
function String hello(String applicantName) {
    return "Hello " + applicantName + "!";
}

rule "Using a function"
  when
    // Empty
  then
    System.out.println( hello( "James" ) );
end
Example function import with a rule (option 2)
import function my.package.applicant.hello;

rule "Using a function"
  when
    // Empty
  then
    System.out.println( hello( "James" ) );
end
Legacy rule attributes

The following attributes were used in earlier versions of the decision engine to provide grouping of rules across a rule base. These attributes are superseded by DRL rule units and are only available for backward compatibility reasons. If you need to group your rules, use DRL rule units as a clearer and simpler grouping method.

Table 92. Legacy rule attributes
Attribute Value

agenda-group

A string identifying an agenda group to which you want to assign the rule. Agenda groups allow you to partition the agenda to provide more execution control over groups of rules. Only rules in an agenda group that has acquired a focus are able to be activated.

Example: agenda-group "GroupName"

ruleflow-group

A string identifying a rule flow group. In rule flow groups, rules can fire only when the group is activated by the associated rule flow.

Example: ruleflow-group "GroupName"

Legacy DRL rule condition syntax

In Kogito, the preferred syntax for DRL rule conditions is through OOPath expressions. For legacy use cases, you can write rules using traditional pattern matching. In this case, you must explicitly indicate the data source using the from clause, as shown in the following comparative examples:

Example PersonRules DRL file using OOPath notation
package org.acme
unit PersonRules;

import org.acme.Person;

rule isAdult
	when
		$person: /person[ age > 18 ]
	then
    modify($person) {
    	setAdult(true)
    };
end
Example PersonRules DRL file using traditional notation
package org.acme
unit PersonRules;

import org.acme.Person;

rule isAdult
	when
		$person: Person(age > 18) from person
	then
    modify($person) {
    	setAdult(true)
    };
end
Legacy DRL rule condition elements

The following rule condition elements (keywords) are obsolete in Kogito:

from

(Obsolete with OOPath notation)

Use this to specify a data source for a pattern. This enables the decision engine to reason over data that is not in the working memory. The data source can be a sub-field on a bound variable or the result of a method call. The expression used to define the object source is any expression that follows regular MVEL syntax. Therefore, the from element enables you to easily use object property navigation, execute method calls, and access maps and collection elements.

from
Figure 190. from
Example rule with from and pattern binding
rule "Validate zipcode"
  when
    Person( $personAddress : address )
    Address( zipcode == "23920W" ) from $personAddress
  then
    // Zip code is okay.
end
Example rule with from and a graph notation
rule "Validate zipcode"
  when
    $p : Person()
    $a : Address( zipcode == "23920W" ) from $p.address
  then
    // Zip code is okay.
end
Example rule with from to iterate over all objects
rule "Apply 10% discount to all items over US$ 100 in an order"
  when
    $order : Order()
    $item  : OrderItem( value > 100 ) from $order.items
  then
    // Apply discount to `$item`.
end

For large collections of objects, instead of adding an object with a large graph that the decision engine must iterate over frequently, add the collection directly to the KIE session and then join the collection in the condition, as shown in the following example:

when
  $order : Order()
  OrderItem( value > 100, order == $order )
Example rule with from and lock-on-active rule attribute
rule "Assign people in North Carolina (NC) to sales region 1"
  ruleflow-group "test"
  lock-on-active true
  when
    $p : Person()
    $a : Address( state == "NC" ) from $p.address
  then
    modify ($p) {} // Assign the person to sales region 1.
end

rule "Apply a discount to people in the city of Raleigh"
  ruleflow-group "test"
  lock-on-active true
  when
    $p : Person()
    $a : Address( city == "Raleigh" ) from $p.address
  then
    modify ($p) {} // Apply discount to the person.
end

Using from with lock-on-active rule attribute can result in rules not being executed. You can address this issue in one of the following ways:

  • Avoid using the from element when you can insert all facts into the working memory of the decision engine or use nested object references in your constraint expressions.

  • Place the variable used in the modify() block as the last sentence in your rule condition.

  • Avoid using the lock-on-active rule attribute when you can explicitly manage how rules within the same ruleflow group place activations on one another.

The pattern that contains a from clause cannot be followed by another pattern starting with a parenthesis. The reason for this restriction is that the DRL parser reads the from expression as "from $l (String() or Number())" and it cannot differentiate this expression from a function call. The simplest workaround to this is to wrap the from clause in parentheses, as shown in the following example:

Example rules with from used incorrectly and correctly
// Do not use `from` in this way:
rule R
  when
    $l : List()
    String() from $l
    (String() or Number())
  then
    // Actions
end

// Use `from` in this way instead:
rule R
  when
    $l : List()
    (String() from $l)
    (String() or Number())
  then
    // Actions
end
entry-point

(Superseded by rule unit data sources)

Use this to define an entry point, or event stream, corresponding to a data source for the pattern. This element is typically used with the from condition element. You can declare an entry point for events so that the decision engine uses data from only that entry point to evaluate the rules. You can declare an entry point either implicitly by referencing it in DRL rules or explicitly in your Java application.

Example rule with from entry-point
rule "Authorize withdrawal"
  when
    WithdrawRequest( $ai : accountId, $am : amount ) from entry-point "ATM Stream"
    CheckingAccount( accountId == $ai, balance > $am )
  then
    // Authorize withdrawal.
end
collect

(Obsolete with OOPath notation)

Use this to define a collection of objects that the rule can use as part of the condition. The rule obtains the collection either from a specified source or from the working memory of the decision engine. The result pattern of the collect element can be any concrete class that implements the java.util.Collection interface and provides a default no-arg public constructor. You can use Java collections like List, LinkedList, and HashSet, or your own class. If variables are bound before the collect element in a condition, you can use the variables to constrain both your source and result patterns. However, any binding made inside the collect element is not available for use outside of it.

collect
Figure 191. Collect
Example rule with collect
import java.util.List

rule "Raise priority when system has more than three pending alarms"
  when
    $system : System()
    $alarms : List( size >= 3 )
              from collect( Alarm( system == $system, status == 'pending' ) )
  then
    // Raise priority because `$system` has three or more `$alarms` pending.
end

In this example, the rule assesses all pending alarms in the working memory of the decision engine for each given system and groups them in a List. If three or more alarms are found for a given system, the rule is executed.

You can also use the collect element with nested from elements, as shown in the following example:

Example rule with collect and nested from
import java.util.LinkedList;

rule "Send a message to all parents"
  when
    $town : Town( name == 'Paris' )
    $mothers : LinkedList()
               from collect( Person( children > 0 )
                             from $town.getPeople()
                           )
  then
    // Send a message to all parents.
end
accumulate alternate syntax for a single function with return type

The accumulate syntax evolved over time with the goal of becoming more compact and expressive. Nevertheless, Kogito still supports previous syntaxes for backward compatibility purposes.

In case the rule is using a single accumulate function on a given accumulate, the author may add a pattern for the result object and use the "from" keyword to link it to the accumulate result.

Example: a rule to apply a 10% discount on orders over $100 could be written in the following way:

rule "Apply 10% discount to orders over US$ 100,00"
when
    $order : /order
    $total : Number( doubleValue > 100 )
             from accumulate( OrderItem( order == $order, $value : value ),
                              sum( $value ) )
then
    // apply discount to $order
end

In the above example, the accumulate element is using only one function (sum), and so, the rules author opted to explicitly write a pattern for the result type of the accumulate function (Number) and write the constraints inside it. There are no problems in using this syntax over the compact syntax presented before, except that is is a bit more verbose. Also note that it is not allowed to use both the return type and the functions binding in the same accumulate statement.

Compile-time checks are performed in order to ensure the pattern used with the "from" keyword is assignable from the result of the accumulate function used.

With this syntax, the "from" binds to the single result returned by the accumulate function, and it does not iterate.

In the above example, "$total" is bound to the result returned by the accumulate sum() function.

As another example however, if the result of the accumulate function is a collection, "from" still binds to the single result and it does not iterate:

rule "Person names"
when
  $x : Object() from accumulate(MyPerson( $val : name );
                                collectList( $val ) )
then
  // $x is a List
end

The bound "$x : Object()" is the List itself, returned by the collectList accumulate function used.

This is an important distinction to highlight, as the "from" keyword can also be used separately of accumulate, to iterate over the elements of a collection:

rule "Iterate the numbers"
when
    $xs : List()
    $x : Integer() from $xs
then
  // $x matches and binds to each Integer in the collection
end

While this syntax is still supported for backward compatibility purposes, for this and other reasons we encourage rule authors to make use instead of the preferred accumulate syntax (described previously), to avoid any potential pitfalls.

accumulate with inline custom code

Another possible syntax for the accumulate is to define inline custom code, instead of using accumulate functions.

The use of accumulate with inline custom code is not a good practice for several reasons, including difficulties on maintaining and testing rules that use them, as well as the inability of reusing that code. Implementing your own accumulate functions is very simple and straightforward, they are easy to unit test and to use. This form of accumulate is supported for backward compatibility only.

Only limited support for inline accumulate is provided while using the executable model. For example, you cannot use an external binding in the code while using the MVEL dialect:

rule R
dialect "mvel"
when
    String( $l : length )
    $sum : Integer() from accumulate (
                           Person( age > 18, $age : age ),
                           init( int sum = 0 * $l; ),
                           action( sum += $age; ),
                           reverse( sum -= $age; ),
                           result( sum )
                     )

The general syntax of the accumulate CE with inline custom code is:

RESULT_PATTERN from accumulate( SOURCE_PATTERN,
                                  init( INIT_CODE ),
                                  action( ACTION_CODE ),
                                  reverse( REVERSE_CODE ),
                                  result( RESULT_EXPRESSION ) )

The meaning of each of the elements is the following:

  • SOURCE_PATTERN: the source pattern is a regular pattern that the decision engine will try to match against each of the source objects.

  • INIT_CODE: this is a semantic block of code in the selected dialect that will be executed once for each tuple, before iterating over the source objects.

  • ACTION_CODE: this is a semantic block of code in the selected dialect that will be executed for each of the source objects.

  • REVERSE_CODE: this is an optional semantic block of code in the selected dialect that if present will be executed for each source object that no longer matches the source pattern. The objective of this code block is to undo any calculation done in the ACTION_CODE block, so that the decision engine can do decremental calculation when a source object is modified or deleted, hugely improving performance of these operations.

  • RESULT_EXPRESSION: this is a semantic expression in the selected dialect that is executed after all source objects are iterated.

  • RESULT_PATTERN: this is a regular pattern that the decision engine tries to match against the object returned from the RESULT_EXPRESSION. If it matches, the accumulate conditional element evaluates to true and the decision engine proceeds with the evaluation of the next CE in the rule. If it does not matches, the accumulate CE evaluates to false and the decision engine stops evaluating CEs for that rule.

It is easier to understand if we look at an example:

rule "Apply 10% discount to orders over US$ 100,00"
when
    $order : Order()
    $total : Number( doubleValue > 100 )
             from accumulate( OrderItem( order == $order, $value : value ),
                              init( double total = 0; ),
                              action( total += $value; ),
                              reverse( total -= $value; ),
                              result( total ) )
then
    // apply discount to $order
end

In the above example, for each Order in the Working Memory, the decision engine will execute the INIT_CODE initializing the total variable to zero. Then it will iterate over all OrderItem objects for that order, executing the action for each one (in the example, it will sum the value of all items into the total variable). After iterating over all OrderItem objects, it will return the value corresponding to the result expression (in the above example, the value of variable total). Finally, the decision engine will try to match the result with the Number pattern, and if the double value is greater than 100, the rule will fire.

The example used Java as the semantic dialect, and as such, note that the usage of the semicolon as statement delimiter is mandatory in the init, action and reverse code blocks. The result is an expression and, as such, it does not admit ';'. If the user uses any other dialect, he must comply to that dialect’s specific syntax.

As mentioned before, the REVERSE_CODE is optional, but it is strongly recommended that the user writes it in order to benefit from the improved performance on update and delete.

The accumulate CE can be used to execute any action on source objects. The following example instantiates and populates a custom object:

rule "Accumulate using custom objects"
when
    $person   : Person( $likes : likes )
    $cheesery : Cheesery( totalAmount > 100 )
                from accumulate( $cheese : Cheese( type == $likes ),
                                 init( Cheesery cheesery = new Cheesery(); ),
                                 action( cheesery.addCheese( $cheese ); ),
                                 reverse( cheesery.removeCheese( $cheese ); ),
                                 result( cheesery ) );
then
    // do something
end
eval

The conditional element eval is essentially a catch-all which allows any semantic code (that returns a primitive boolean) to be executed. This code can refer to variables that were bound in the conditions of the rule and functions in the rule package. Overuse of eval reduces the declarativeness of your rules and can result in a poorly performing decision engine. While eval can be used anywhere in the patterns, it is typically added as the last conditional element in the conditions of a rule.

eval
Figure 192. Eval

Instances of eval cannot be indexed and thus are not as efficient as Field Constraints. However this makes them ideal for being used when functions return values that change over time, which is not allowed within Field Constraints.

For those who are familiar with Kogito 2.x lineage, the old Kogito parameter and condition tags are equivalent to binding a variable to an appropriate type, and then using it in an eval node.

p1 : Parameter()
p2 : Parameter()
eval( p1.getList().containsKey( p2.getItem() ) )

p1 : Parameter()
p2 : Parameter()
// call function isValid in the LHS
eval( isValid( p1, p2 ) )

4.2.3. Creating DRL rules for your Kogito project

You can create and manage DRL rules for your Kogito project in your integrated development environment (IDE). For Kogito service, VSCode is the preferred IDE. In each DRL rule file, you define rule conditions, actions, and other components related to the rule, based on the data objects you create or import in the package.

In Kogito, you typically define DRL rules in rule units. A DRL rule unit is a module for rules and a unit of execution. A rule unit collects a set of rules with the declaration of the type of facts that the rules act on. A rule unit also serves as a unique namespace for each group of rules. A single rule base can contain multiple rule units. You typically store all the rules for a unit in the same file as the unit declaration so that the unit is self-contained.

For this procedure, create the following example DRL type declarations and DRL rule unit to define DRL rules in a decision service for a loan application:

Example DRL type declarations for a loan application
package org.mortgages;

declare Bankruptcy
    name: String
    yearOfOccurrence: int
end

declare Applicant
    name: String
    age: int
end

declare LoanApplication
    applicant: String
    approved: boolean
    explanation: String
end
Example DRL rule unit file for a loan application
package org.mortgages;
unit MortgageRules;

import org.kie.kogito.rules.DataSource;
import org.kie.kogito.rules.DataStream;

declare MortgageRules extends RuleUnitData
  bankruptcy: DataStore<Bankruptcy> = DataSource.createStore()
  applicant: DataStore<Applicant> = DataSource.createStore()
  application: DataStore<LoanApplication> = DataSource.createStore()
end

rule "Bankruptcy history"
	salience 10
	when
    $a : /loanApplication[ applicantName: applicant ]
    exists (/bankruptcy[ name == applicantName, yearOfOccurrence > 1990 || amountOwed > 100000 ])
	then
		$a.setApproved( false );
		$a.setExplanation( "has been bankrupt" );
		loanApplication.remove( $a );
end

rule "Underage"
	salience 15
	when
    /applicant[ applicantName : name, age < 21 ]
    $application : /loanApplication[ applicant == applicantName ]
	then
		$application.setApproved( false );
		$application.setExplanation( "Underage" );
		loanApplication.remove( $a );
end
Prerequisites
Procedure
  1. In your VSCode IDE, open your Kogito project and create a src/main/resources/org/mortgages folder. This folder serves as the package for your DRL files in this example.

  2. In your new src/main/resources/org/mortgages folder, add the following ApplicationTypes.drl file to define the fact types for the loan application service:

    Example DRL type declarations for a loan application
    package org.mortgages;
    
    declare Bankruptcy
        name: String
        yearOfOccurrence: int
    end
    
    declare Applicant
        name: String
        age: int
    end
    
    declare LoanApplication
        applicant: String
        approved: boolean
        explanation: String
    end

    This DRL file defines the fact types that you can declare in any rule units in the same package for the decision service. Declarations in DRL files define new fact types or metadata for fact types to be used by rules in a DRL files. If you declare these types directly in the DRL rule unit file, you cannot declare them in any other rule units.

    This example defines the following fact types:

    • Bankruptcy: Provides data for bankruptcy status, if applicable

    • Applicant: Provides data about the loan applicant

    • LoanApplication: Provides data about loan approval status for a specified applicant, with an explanation if needed

  3. In the same src/main/resources/org/mortgages folder of your Kogito project, create the following LoanApplication.drl file to declare the DRL rule unit and data sources:

    Example DRL file with rule unit and data sources
    package org.mortgages;
    unit MortgageRules;
    
    import org.kie.kogito.rules.DataSource;
    import org.kie.kogito.rules.DataStore;
    
    declare MortgageRules extends RuleUnitData
      bankruptcy: DataStore<Bankruptcy> = DataSource.createStore()
      applicant: DataStore<Applicant> = DataSource.createStore()
      application: DataStore<LoanApplication> = DataSource.createStore()
    end
    ...

    In this example, the rule unit is named MortgageRules and the previously defined fact types are declared as DataStore data sources.

    Data sources are typed sources of data that rule units can subscribe to for updates. You interact with the rule unit through the data sources it exposes. A data source can be a DataStream source for append-only storage, a DataStore source for writable storage to add or remove data, or a SingletonStore source for writable storage to set and clear a single element.

    This example uses the DataStore data source to enable application data to be added or removed as part of the decision service.

  4. To complete the DRL rule unit file, add the following rules for "Bankruptcy history" and "Underage" logic:

    Example DRL rule unit file for a loan application
    package org.mortgages;
    unit MortgageRules;
    
    import org.kie.kogito.rules.DataSource;
    import org.kie.kogito.rules.DataStream;
    
    declare MortgageRules extends RuleUnitData
      bankruptcy: DataStore<Bankruptcy> = DataSource.createStore()
      applicant: DataStore<Applicant> = DataSource.createStore()
      application: DataStore<LoanApplication> = DataSource.createStore()
    end
    
    rule "Bankruptcy history"
    	salience 10
    	when
        $a : /loanApplication[ applicantName: applicant ]
        exists (/bankruptcy[ name == applicantName, yearOfOccurrence > 1990 || amountOwed > 100000 ])
    	then
    		$a.setApproved( false );
    		$a.setExplanation( "has been bankrupt" );
    		loanApplication.remove( $a );
    end
    
    rule "Underage"
    	salience 15
    	when
        /applicant[ applicantName : name, age < 21 ]
        $application : /loanApplication[ applicant == applicantName ]
    	then
    		$application.setApproved( false );
    		$application.setExplanation( "Underage" );
    		loanApplication.remove( $a );
    end

    The example rules consist of the following rule components:

    • rule: Use this segment to define each rule in the DRL file. Rules consist of a rule name in the format rule "rule name", followed by optional attributes that define rule behavior, such as salience or no-loop, followed by when and then definitions. Each rule must have a unique name within the rule package.

      In this example, the "Bankruptcy history" rule has a defined salience of 10 and the "Underage" rule has a defined salience of 15. These values ensure that the "Bankruptcy history" rule is executed first.

    • when and then: Use the when portion to define the condition patterns and constraints in OOPath syntax and use the then portion to define the actions to be executed when the conditions are met.

      In this example, the "Bankruptcy history" rule states that if an applicant has owed more than 100,000 USD of unresolved debt since 1990 (beginning 1991), then the applicant is considered to have been bankrupt and is not approved for a loan. The application is removed from memory.

      If the applicant passes the bankruptcy check, then the "Underage" rule states that if the applicant is younger than 21 years old, then the applicant is not approved for the loan. The application is removed from memory.

      If the applicant passes both checks, then the loan is approved.

  5. After you define all components of the data sources and rules, save all DRL files.

4.2.4. Kogito service execution

After you design your Kogito service, you can build and run your application and then send REST API requests to the application to execute your services. The exact REST API requests that you can use depend on how you set up the application.

For example, consider a Kogito service that is set up to generate a /persons REST API endpoint and determines whether a specified customer is an adult or is underage. In this example, you can send the following POST request using a REST client or curl utility to add an adult and execute the service:

Example POST request body to add an adult (JSON)
{
  "person": {
    "name": "John Quark",
    "age": 20
  }
}
Example curl command to add an adult
curl -X POST http://localhost:8080/persons -H 'content-type: application/json' -H 'accept: application/json' -d '{"person": {"name":"John Quark", "age": 20}}'
Example response (JSON)
{
  "id": "3af806dd-8819-4734-a934-728f4c819682",
  "person": {
    "name": "John Quark",
    "age": 20,
    "adult": false
  },
  "isAdult": true
}

Note that for JSON marshaling, Kogito is relying on the default mappers of Quarkus and SpringBoot, which can be configured according to the specific requirements.

For information about creating, running, and testing an example application with Kogito services, see Creating and running your first Kogito services.

For information about deploying your Kogito service to OpenShift, see Deploying Kogito services on OpenShift.

4.3. Using spreadsheet decision tables in Kogito services

As a developer of business decisions, you can define business rules in a tabular format in spreadsheet decision tables and then include the spreadsheet file in your Kogito project. These rules are compiled into Drools Rule Language (DRL) for the decision service in your project.

4.3.1. Decision-authoring assets in Kogito

Kogito supports several assets that you can use to define business decisions for your decision service. Each decision-authoring asset has different advantages, and you might prefer to use one or a combination of multiple assets depending on your goals and needs.

The following table highlights the main decision-authoring assets supported in Kogito projects to help you decide or confirm the best method for defining decisions in your decision service.

Table 93. Decision-authoring assets supported in Kogito
Asset Highlights Authoring tools Documentation

Decision Model and Notation (DMN) models

  • Are decision models based on a notation standard defined by the Object Management Group (OMG)

  • Use graphical decision requirements diagrams (DRDs) that represent part or all of the overall decision requirements graph (DRG) to trace business decision flows

  • Use an XML schema that allows the DMN models to be shared between DMN-compliant platforms

  • Support Friendly Enough Expression Language (FEEL) to define decision logic in DMN decision tables and other DMN boxed expressions

  • Can be integrated efficiently with Business Process Model and Notation (BPMN) process models

  • Are optimal for creating comprehensive, illustrative, and stable decision flows

Kogito DMN modeler in VSCode or other DMN-compliant editor

Decision Model and Notation (DMN)

DRL rules

  • Are individual rules that you define directly in .drl text files

  • Provide the most flexibility for defining rules and other technicalities of rule behavior

  • Are optimal for creating rules that require advanced DRL options, such as rule units

  • Have strict syntax requirements for rules to be compiled properly

Any integrated development environment (IDE)

Drools Rule Language (DRL)

Spreadsheet decision tables

  • Are tabular sets of rules that you define in .xls or .xlsx spreadsheet files

  • Support template keys and values for creating rule templates

  • Are optimal for business environments that already rely on decision tables for rules

  • Have strict syntax requirements for rules to be compiled properly when used in Kogito

Spreadsheet editor

Spreadsheet decision tables

4.3.2. Spreadsheet decision tables

Spreadsheet decision tables are XLS or XLSX spreadsheet files that contain business rules defined in a tabular format. You can include spreadsheet decision tables as part of your Kogito project. Each row in a decision table is a rule, and each column is a condition, an action, or another rule attribute. After you create and include your spreadsheet decision tables in your project, the rules you defined are compiled into Drools Rule Language (DRL) rules for the decision service.

4.3.3. Decision table use case

An online shopping site lists the shipping charges for ordered items. The site provides free shipping under the following conditions:

  • The number of items ordered is 4 or more and the checkout total is $300 or more.

  • Standard shipping is selected (4 or 5 business days from the date of purchase).

The following are the shipping rates under these conditions:

Table 94. For orders less than $300
Number of items Delivery day Shipping charge in USD, N = Number of items

3 or fewer

Next day

2nd day

Standard

35

15

10

4 or more

Next day

2nd day

Standard

N*7.50

N*3.50

N*2.50

Table 95. For orders more than $300
Number of items Delivery day Shipping charge in USD, N = Number of items

3 or fewer

Next day

2nd day

Standard

25

10

N*1.50

4 or more

Next day

2nd day

Standard

N*5

N*2

FREE

These conditions and rates are shown in the following example spreadsheet decision table:

Decision table example
Figure 193. Decision table for shipping charges

In order for a decision table to be compiled in your Kogito project, the table must comply with certain structure and syntax requirements within an XLS or XLSX spreadsheet, as shown in this example. For more information, see Creating spreadsheet decision tables for your Kogito project.

4.3.4. Creating spreadsheet decision tables for your Kogito project

Spreadsheet decision tables (XLS or XLSX) require two key areas that define rule data: a RuleSet area and a RuleTable area. The RuleSet area of the spreadsheet defines elements that you want to apply globally to all rules in the same package (not only the spreadsheet), such as a rule set name or universal rule attributes. The RuleTable area defines the actual rules (rows) and the conditions, actions, and other rule attributes (columns) that constitute that rule table within the specified rule set. A spreadsheet of decision tables can contain multiple RuleTable areas, but only one RuleSet area.

For each Kogito project, try to include only one spreadsheet of decision tables, containing all necessary RuleTable definitions. Although you can include separate decision table spreadsheets, including multiple spreadsheets in the same project package can cause compilation errors from conflicting RuleSet or RuleTable attributes and is therefore not recommended.

Refer to the following sample spreadsheet as you define your decision table:

Decision table example
Figure 194. Sample spreadsheet decision table for shipping charges
Prerequisites
  • You have added the following dependency to the pom.xml file of your Kogito project to enable decision tables for decision services:

    Dependency to enable decision tables for decision services
    <dependency>
        <groupId>org.kie.kogito</groupId>
        <artifactId>drools-decisiontables</artifactId>
    </dependency>
Procedure
  1. In a new XLS or XLSX spreadsheet, go to the second or third column and label a cell RuleSet (row 1 in example). Reserve the column or columns to the left for descriptive metadata (optional).

  2. In the next cell to the right, enter a name for the RuleSet. This named rule set will contain all RuleTable rules defined in the rule package.

  3. Under the RuleSet cell, define any rule attributes (one per cell) that you want to apply globally to all rule tables in the package. Specify attribute values in the cells to the right. For example, you can enter an Import label and in the cell to the right, specify relevant data objects from other packages that you want to import into the package for the decision table (in the format package.name.object.name). For supported cell labels and values, see RuleSet definitions.

  4. Below the RuleSet area and in the same column as the RuleSet cell, skip a row and label a new cell RuleTable (row 7 in example) and enter a table name in the same cell. The name is used as the initial part of the name for all rules derived from this rule table, with the row number appended for distinction. You can override this automatic naming by inserting a NAME attribute column.

  5. Use the next four rows to define the following elements as needed (rows 8-11 in example):

    • Rule attributes: Conditions, actions, or other attributes. For supported cell labels and values, see RuleTable definitions.

    • Object types: The data objects to which the rule attributes apply. If the same object type applies to multiple columns, merge the object cells into one cell across multiple columns (as shown in the sample decision table), instead of repeating the object type in multiple cells. When an object type is merged, all columns below the merged range will be combined into one set of constraints within a single pattern for matching a single fact at a time. When an object is repeated in separate columns, the separate columns can create different patterns, potentially matching different or identical facts.

    • Constraints: Constraints on the object types.

    • Column label: (Optional) Any descriptive label for the column, as a visual aid. Leave blank if unused.

      As an alternative to populating both the object type and constraint cells, you can leave the object type cell or cells empty and enter the full expression in the corresponding constraint cell or cells. For example, instead of Order as the object type and itemsCount > $1 as a constraint (separate cells), you can leave the object type cell empty and enter Order( itemsCount > $1 ) in the constraint cell, and then do the same for other constraint cells.
  6. After you have defined all necessary rule attributes (columns), enter values for each column as needed, row by row, to generate rules (rows 12-17 in example). Cells with no data are ignored (such as when a condition or action does not apply).

    If you need to add more rule tables to this decision table spreadsheet, skip a row after the last rule in the previous table, label another RuleTable cell in the same column as the previous RuleTable and RuleSet cells, and create the new table following the same steps in this section (rows 19-29 in example).

  7. Save your XLS or XLSX spreadsheet to finish.

  8. In your VSCode IDE, import the XLS or XLSX spreadsheet file in the relevant folder of your Kogito project, typically in src/main/resources.

Only the first worksheet in a spreadsheet workbook is processed as a decision table when you include the spreadsheet in your Kogito project. Each RuleSet name combined with the RuleTable name must be unique across all decision table files in the same package.

After you include the decision table in your Kogito project, the rules are rendered as DRL rules like the following example, from the sample spreadsheet:

//row 12
rule "Basic_12"
salience 10
  when
    $order : /orders[ itemsCount > 0, itemsCount <= 3, deliverInDays == 1 ]
  then
    insert( new Charge( 35 ) );
end
Enabling white space used in cell values

By default, any white space before or after values in decision table cells is removed before the decision table is processed by the decision engine. To retain white space that you use intentionally before or after values in cells, set the drools.trimCellsInDTable system property to false in the application.properties file of your Kogito project:

Enable white space in application.properties
drools.trimCellsInDTable=false

You can also set this property as a start-up option when you build your Kogito project:

On Quarkus
mvn compile quarkus:dev -Ddrools.trimCellsInDTable=false
On Spring Boot
mvn spring-boot:run -Ddrools.trimCellsInDTable=false
4.3.4.1. RuleSet definitions

Entries in the RuleSet area of a decision table define DRL constructs and rule attributes that you want to apply to all rules in a package (not only in the spreadsheet). Entries must be in a vertically stacked sequence of cell pairs, where the first cell contains a label and the cell to the right contains the value. A decision table spreadsheet can have only one RuleSet area.

The following table lists the supported labels and values for RuleSet definitions:

Table 96. Supported RuleSet definitions
Label Value Usage

RuleSet

The package name for the generated DRL file. Optional, the default is rule_table.

Must be the first entry.

Unit

The name of the rule unit for the generated DRL file.

Recommended for optimal rule grouping and execution.

Sequential

true or false. If true, then salience is used to ensure that rules fire from the top down.

Optional, at most once. If omitted, no firing order is imposed.

SequentialMaxPriority

Integer numeric value.

Optional, at most once. In sequential mode, this option is used to set the start value of the salience. If omitted, the default value is 65535.

SequentialMinPriority

Integer numeric value.

Optional, at most once. In sequential mode, this option is used to check if this minimum salience value is not violated. If omitted, the default value is 0.

EscapeQuotes

true or false. If true, then quotation marks are escaped so that they appear literally in the DRL.

Optional, at most once. If omitted, quotation marks are escaped.

Import

A comma-separated list of Java classes to import from another package.

Optional, may be used repeatedly.

Queries

One or more query definitions, according to DRL syntax.

Optional, may be used repeatedly.

Declare

One or more declarative types, according to DRL syntax.

Optional, may be used repeatedly.

In some cases, Microsoft Office, LibreOffice, and OpenOffice might encode a double quotation mark differently, causing a compilation error. For example, “A” fails, but "A" succeeds.
4.3.4.2. RuleTable definitions

Entries in the RuleTable area of a decision table define conditions, actions, and other rule attributes for the rules in that rule table. A spreadsheet of decision tables can contain multiple RuleTable areas.

The following table lists the supported labels (column headers) and values for RuleTable definitions. For column headers, you can use either the given labels or any custom labels that begin with the letters listed in the table.

Table 97. Supported RuleTable definitions
Label Or custom label that begins with Value Usage

NAME

N

Provides the name for the rule generated from that row. The default is constructed from the text following the RuleTable tag and the row number.

At most one column.

DESCRIPTION

I

Results in a comment within the generated rule.

At most one column.

CONDITION

C

Code snippet and interpolated values for constructing a constraint within a pattern in a condition.

At least one per rule table.

ACTION

A

Code snippet and interpolated values for constructing an action for the consequence of the rule.

At least one per rule table.

METADATA

@

Code snippet and interpolated values for constructing a metadata entry for the rule.

Optional, any number of columns.

The following sections provide more details about how condition, action, and metadata columns use cell data:

Conditions

For columns headed CONDITION, the cells in consecutive lines result in a conditional element:

  • First cell: Text in the first cell below CONDITION develops into a pattern for the rule condition, and uses the snippet in the next line as a constraint. If the cell is merged with one or more neighboring cells, a single pattern with multiple constraints is formed. All constraints are combined into a parenthesized list and appended to the text in this cell.

    If this cell is empty, the code snippet in the cell below it must result in a valid conditional element on its own. For example, instead of Order as the object type and itemsCount > $1 as a constraint (separate cells), you can leave the object type cell empty and enter Order( itemsCount > $1 ) in the constraint cell, and then do the same for any other constraint cells.

    To include a pattern without constraints, you can write the pattern in front of the text of another pattern, with or without an empty pair of parentheses. You can also append a from clause to the pattern.

    If the pattern ends with eval, code snippets produce boolean expressions for inclusion into a pair of parentheses after eval.

  • Second cell: Text in the second cell below CONDITION is processed as a constraint on the object reference in the first cell. The code snippet in this cell is modified by interpolating values from cells farther down in the column. If you want to create a constraint consisting of a comparison using == with the value from the cells below, then the field selector alone is sufficient. Any other comparison operator must be specified as the last item within the snippet, and the value from the cells below is appended. For all other constraint forms, you must mark the position for including the contents of a cell with the symbol $param. Multiple insertions are possible if you use the symbols $1, $2, and so on, and a comma-separated list of values in the cells below. However, do not separate $1, $2, and so on, by commas, or the table will fail to process.

    To expand a text according to the pattern forall($delimiter){$snippet}, repeat the $snippet once for each of the values of the comma-separated list in each of the cells below, insert the value in place of the symbol $, and join these expansions by the given $delimiter. Note that the forall construct may be surrounded by other text.

    If the first cell contains an object, the completed code snippet is added to the conditional element from that cell. A pair of parentheses is provided automatically, as well as a separating comma if multiple constraints are added to a pattern in a merged cell. If the first cell is empty, the code s